Using Lightning CSS with Astro


I love Astro! It is such a joy to work with for static sites. It takes me back to the early days of web development with a lot more power. Astro uses Vite under the hood for bundling, transforming, minifying, etc. all the CSS, JS, Typescript, etc, which in turn uses PostCSS.

PostCSS is pretty rad too! It allows us to use next-gen CSS syntax and features now. One of my favorite features of PostCSS, while now supported natively by all major browsers, was nesting selectors like you could with Sass or Less for many years.

Lightning CSS, a relative newcomer, does this too. About 100x faster. All thanks to Rust and a more robust CSS parser. The same Rust-based cssparser used in Mozilla Firefox.

So while I love PostCSS for what it did for vanilla CSS, who doesn’t want builds to be faster? To that end, here’s how I got Lightning CSS working in my Astro site.

Because Lightning CSS is an optional configuration, its a peerDependency of Vite. That means you’ll need to install it explicitly.

$ pnpm add -D lightningcss@latest browserslist@latest

Then configure Vite to use Lightning CSS instead of PostCSS.

// astro.config.ts
// Imports...
import {browserslistToTargets} from 'lightningcss';
import browserslist from "browserslist";

export default defineConfig({
  // Astro config ...
  vite: {
    build: {
      cssMinify: 'lightningcss',
    css: {
      transformer: 'lightningcss',
      lightningcss: {
        targets: browserslistToTargets(browserslist("defaults")),
        cssModules: true,
        drafts: {
          // Enable custom media queries
          customMedia: true,

The targets: browserslistToTargets(browserslist("defaults")) bit is key to telling Lightning to transpile CSS Custom media queries for browsers that don’t support it. You can tell it whatever versions you want to support, or declare them according to browserslist.

A quick note: If you’re using @open-props and you want to use custom media queries in your component files, you’ll need to @import "open-props/media" in the <style> tag in each component using them. That’s because custom media queries like --md-n-above require processing and those vars are unknown within components unless @imported.

And there ya go. You’re off to the races!

About the author

Hi there! My name is Spencer and I’m a Web developer with a passion for building User Interfaces and all things tech. In my free time, I enjoy spending time with my wife and our 4 dogs, as well as playing guitar and exploring the world. I am also a lover of science and enjoy learning about all manner of nerdy topics.