Example source and output of CSS Blocks. Text content here.
Development of CSS Blocks and OptiCSS
Following the realization that selector performance and file size were hurting our site speed, we needed to make progress quickly to alleviate the issue. The team worked hard to establish new best practices, including converting our application to use BEM-style selectors and to prefer shared class names in our templates over mixins in our stylesheets. The choice to adopt BEM was controversial; many developers disliked the ergonomics of it and found it too constraining. There were several meetings, but eventually a decision was made to proceed.
A small army of front-end engineers worked hard to perform the migration away from repeatedly invoking Sass mixins. There was one week where I code reviewed more than 80 pull requests! Thanks to everyone’s hard work, we shaved a little more than 1s off of our initial load time. After that, our CSS grew more slowly and we continued to improve our load times by employing other strategies, like lazy loading stylesheets. Despite those improvements, our measurements still showed considerable time being spent on CSS; we needed to keep working on this problem, but at least we had bought ourselves some time to step back and think about things more holistically.
That BEM project had been operating in parallel with several other performance projects. When that initiative ended, a small team of engineers was spun up to think about what a performance-first approach to building our web applications at LinkedIn might look like. Tom Dale recently wrote about how that project led us to build the LinkedIn homepage experience in two different UI frameworks. Both of those front-ends were built using CSS Blocks and OptiCSS.
During this homepage experiment, the team worked to ensure that we could style our web applications with a focus on performance. We wanted to make sure that such applications could thrive in high-latency, low-bandwidth environments with expensive data. Based on what we saw from some of the fastest websites in India, we gave ourselves a first page view budget of 10KB of compressed CSS. Considering that our website currently serves 1.9MB of CSS (156KB compressed), this goal seemed wildly optimistic—but, as you read above, we ultimately beat the CSS file size budget by more than 10%!
Beyond the performance goals, we wanted to make sure that, wherever possible, our existing use cases would be supported and that the numerous annoyances that we saw in our developers’ workflow could be obviated. We asked ourselves:
- Was there a way to write CSS that kept our styles clean, with low specificity, without redundancy in our output or our authored selectors?
- Could we allow for composition and inheritance of styles?
- Could there be a way to write selectors without specificity wars or manually tweaking document orders?
- How could we support our design system’s needs to distribute a style library, evolve designs, and ensure adherence to our style guide where it matters while still enabling applications to experiment?
On the performance front, we wondered whether we could use tooling on our CSS that would produce styles as small and as fast as the sites that use atomic-style classes, without significantly changing the way we authored the styles for application components. We wondered whether, as friendly as CSS is to compression algorithms, could we do even better?
We evaluated the state of the art in CSS frameworks, including recent developments in the React and Vue communities towards using CSS-in-JS, and we saw a lot of fantastic ideas there. In those environments, CSS was able to participate in performance-centric capabilities like code-splitting and inlining critical styles. The authoring systems themselves made sure that the styles associated with removed functionality would be removed as well. We wanted all these great capabilities too.
Nothing existed like what we started to imagine, and so we brought all of those ideas together into a CSS framework based on static analysis and where the whole application would be analyzed and optimized. Because of the dramatically different syntaxes of Preact and Glimmer applications (the two UI frameworks used in our homepage experiment), CSS Blocks was designed to accommodate different types of template analyzers and rewriters.
The results of our initial production deployment of CSS Blocks went so much better than we could have expected. The way the framework shaped the code we wrote resulted in dramatic benefits to our stylesheets and promoted decoupled styles and components. Even before we had OptiCSS working and enabled, CSS Blocks was paying dividends for the engineers building the application.
Collaborate with us
We still need to produce some tools and remaining features that we think are needed to scale up from supporting a small team of engineers to supporting several hundred front-end engineers across various teams and applications. But from this point forward, we want to invite the community to be part of that maturation process so that we do not optimize for LinkedIn’s own needs and use cases at the expense of others. We want everyone to benefit from the same performance gains for their website, developer experience, and user experience.
CSS Blocks is opinionated and we understand that it may not be the right choice for many teams. But we think that OptiCSS, and the new approach that it takes with template analysis and rewriting, is a game changer for front-end development. It would be a shame to couple it to just one flavor of stylesheet authoring. As such, we built OptiCSS so that it is very configurable and flexible. It is designed to work with many different styling frameworks, including frameworks that would qualify as “CSS-in-JS.” In some cases, those frameworks are well-positioned to produce output that is even friendlier to optimization than CSS Blocks. We want to collaborate with other CSS and styling frameworks to make OptiCSS a foundational technology that can power the next generation of CSS authoring.
These projects are still under active development. Today’s first public release is an alpha release for developers to preview, kick the tires, and decide if these projects are as exciting for you as they are for us. If they are, we hope you’ll join us as a collaborator.
Until we release 1.0, the APIs are unstable and minor releases may have backwards incompatibilities. We urge you to wait for our 1.0 release before using CSS Blocks in production. We think that, with your help and feedback, we can ship the 1.0 release later this year. If you’re not currently in a position to help fix the bugs that are blocking you or to be involved by building other important infrastructure, integrations, or tools, we’d ask that you wait to adopt CSS Blocks until our 1.0 release. At that point, we’ll be in a position to offer you the level of debugging tools and support that you (and our engineers here at LinkedIn) need.
At this point, you probably want to know a whole lot more about the code and how this framework and optimizer work, so we invite you to continue your investigation at our GitHub repos for CSS Blocks and OptiCSS, where you will find documentation and fairly well-commented code.
Special thanks to Adam Miller, who has worked tirelessly with me on these projects since almost the beginning.
Thanks to our team members Sarah Clatterbuck, James Baker, Kris Baxter, Diego Buthay, Tom Dale, Chad Hietala, Casey Klimkowski, Kacey Mack, Asa Kusuma, Mark Pascual, Marius Seritan, Mahir Shah, and Sara Todd.