The Art of CSS
Discovering the latest features in CSS can transform the way we design and interact with web content.
https://leerob.io/blog/css homeworkblogguestbook How I'm Writing CSS in 2024 January 7, 2024 (Today) CSS in 2024 is amazing. * Cross-browser support for nesting, :has(), container queries, and more1 * Powerful and fast new CSS tools * Many frameworks and compilers to help optimize CSS loading performance This post will be a collection of my notes and thoughts about the CSS ecosystem and the tools I'm currently using. Design Constraints User Experience What does a great experience look like loading stylesheets when visiting a website? 1. Stylesheets should load as fast as possible (small file sizes) 2. Stylesheets should not re-download unless changed (proper caching headers) 3. The page content should have minimal or no layout shift 4. Fonts should load as fast as possible and minimize layout shift Developer Experience Our tools must help us create better user experiences. The developer experience, while important, can't come before the user experience. How can the DX of the styling tools we use help us create a better UX? 1. Prune unused styles, minify, and compress CSS for smaller file sizes 2. Generate hashed file names to enable safe, immutable caching2 3. Bundle CSS files together to make fewer network requests 4. Prevent naming collisions to avoid visual regressions What about to help us write more maintainable, enjoyable CSS? 1. Easy to delete styles when deleting corresponding UI code 2. Easy to adhere to a design system or set of themes 3. Editor feedback with TypeScript support, autocompletion, and linting 4. Receive tooling feedback in-editor to prevent errors (type checking, linting) CSS in 2024 It's never been easier to write great styles without any additional tooling. The example below uses many of the latest CSS features supported cross browser without any build step. You might not need Sass or Less anymore! styles.cssindex.html :root { --main-bg-color: #f3f4f6; --title-color: #262626; --text-color: #525252; --font-family: "Arial", sans-serif; } body { margin: 0; padding: 0; background-color: var(--main-bg-color); font-family: var(--font-family); } .blog-header, .blog-footer { text-align: center; padding: 1rem; background-color: var(--title-color); color: white; } .blog-post { container-type: inline-size; margin: 1rem; padding: 1rem; background-color: white; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); & .post-title { color: var(--title-color); margin: 0 0 1rem 0; text-wrap: balance; font-size: 1em; } & .post-content { color: var(--text-color); } } @container (min-inline-size: 500px) { .blog-post { padding: 1.5rem; & .post-title { font-size: 1.25em; } } } Open Sandbox Does that mean the tooling is no longer necessary? For some people, yes. Build Steps To meet the design constraints above, you'll likely need a build step. It's unlikely all your users are on the latest browser versions. But more importantly, there will always be newer syntax that isn't yet supported cross-browser you will want to use. You can manually write @supports rules to check for browser support, but that's only solving some of the problems. Rather than leaving the CSS optimization to humans, why not let the machines handle it? Compilation Compilers make the following workflow easy: 1. Automatically remove any unused styles, bundle files together to make fewer network requests, add vendor prefixes, and minify the output by removing white spaces and comments 2. Automatically generate unique file names, allowing frameworks to set caching headers like immutable signaling to browsers the content will never change 3. Specify target browsers (browserslist) and have syntax lowering to compile modern CSS features to work with those browsers Streaming CSS You visit Google to book a flight. It can't precompute your intent, so you're given a search bar for the initial UI. You search "Flight SFO to NYC" and the server streams in a flights widget to select dates. There is no way Google could have included every possible widget ahead of time. Currency conversions, timers, live sports scores, you name it. The UI and styles for these widgets need to be dynamically streamed in. React (and Next.js) now support this pattern with streaming SSR and CSS. In the React model, you define your UI as components which have dependencies on styles. How can we safely stream in styles for the a widget without affecting anything on the page? Styles need to be scoped, or atomic, so that if they load earlier than the DOM content they're intended to style, they don't alter the style of elements already on the page. For example, CSS Modules have styling rules scoped to the component that imports it. Tailwind uses atomic utility classes, which are compiled into a single stylesheet that's loaded before any classes are used. StyleX generates atomic classes as well. Global styles don't work well with streaming unless loaded at the beginning of the stream. My Recommendations CSS Modules CSS Modules are a small but impactful enhancement on top of vanilla CSS. They achieve our desired UX constraints and most (but not all) of our DX constraints. They're available in almost every modern bundler and framework. You can copy / paste existing CSS selectors and they'll work in a CSS Module without any changes. They can't generate atomic styles. They don't support using many themes (just CSS variables). And because the styling code lives outside your TypeScript files, you don't get type safety and autocompletion. But those constraints might be fine for you. Lightning CSS, which supports CSS Modules, is used by Vite, and soon by Tailwind and Next.js. Tools like postcss and autoprefixer are being replaced by faster, all-in-one Rust toolchains. Tailwind CSS Tailwind uses a compiler to generate only the classes used. So while the utility CSS framework contains many possible class names, only the classes used (e.g. "font-bold text-2xl") will be included in the single, compiled CSS file. Assuming you only write Tailwind code, your bundle will never be larger than the total set of used Tailwind classes. It's extremely unlikely you would use them all. This means you have a fixed upper bound on the size of the generated CSS file, which is then minified, compressed, and cached for the best performance. You don't have to only write Tailwind styles. Tailwind classes are just utilities for normal CSS that adhere to a design system. You can mix and match Tailwind with CSS Modules, for example. Tailwind doesn't come without tradeoffs. There's a bucket of tools that pair with it: * VSCode integration for autocompletion, linting, syntax highlighting, and more * Prettier integration for automatic sorting of class names The most controversial part about Tailwind is the syntax. It's both loved and hated. I didn't appreciate Tailwind until I built something with it, so I'd recommend trying that if your initial reaction is adverse.
Discovering the latest features in CSS can transform the way we design and interact with web content.
A journey through the evolution of web design, from static pages to dynamic, responsive experiences.
Discovering the latest features in CSS can transform the way we design and interact with web content.
A journey through the evolution of web design, from static pages to dynamic, responsive experiences.