diff --git a/README.md b/README.md
index 8661e83b..8cdd2dcd 100644
--- a/README.md
+++ b/README.md
@@ -57,190 +57,11 @@ An extremely fast CSS parser, transformer, and minifier written in Rust. Use it
- Opt-in support for locally scoped CSS variables and other dashed identifiers.
- `:local()` and `:global()` selectors
- The `composes` property
+- **Custom transforms** – The Lightning CSS visitor API can be used to implement custom transform plugins.
## Documentation
-Lightning CSS can be used from [Parcel](https://parceljs.org), as a standalone library from JavaScript or Rust, using a standalone CLI, or wrapped as a plugin within any other tool.
-
-### From Node
-
-See the [TypeScript definitions](https://github.com/parcel-bundler/lightningcss/blob/master/node/index.d.ts) for full API docs.
-
-Here is a simple example that compiles the input CSS for Safari 13.2, and minifies the output.
-
-```js
-const css = require('lightningcss');
-
-let {code, map} = css.transform({
- filename: 'style.css',
- code: Buffer.from('.foo { color: red }'),
- minify: true,
- sourceMap: true,
- targets: {
- // Semver versions are represented using a single 24-bit number, with one component per byte.
- // e.g. to represent 13.2.0, the following could be used.
- safari: (13 << 16) | (2 << 8)
- }
-});
-```
-
-You can also convert the results of running `browserslist` into targets which can be passed to Lightning CSS:
-
-```js
-const browserslist = require('browserslist');
-const css = require('lightningcss');
-
-let targets = css.browserslistToTargets(browserslist('>= 0.25%'));
-```
-
-Bundling is also possible by using the `bundle` API. This processes `@import` rules and inlines them. This API requires filesystem access, so it does not accept `code` directly via the API.
-
-```js
-let {code, map} = css.bundle({
- filename: 'style.css',
- minify: true
-});
-```
-
-The `bundleAsync` API is an asynchronous version of `bundle`, which also accepts a custom `resolver` object. This allows you to provide custom JavaScript functions for resolving `@import` specifiers to file paths, and reading files from the file system (or another source). The `read` and `resolve` functions are both optional, and may either return a string synchronously, or a Promise for asynchronous resolution.
-
-```js
-let {code, map} = await css.bundleAsync({
- filename: 'style.css',
- minify: true,
- resolver: {
- read(filePath) {
- return fs.readFileSync(filePath, 'utf8');
- },
- resolve(specifier, from) {
- return path.resolve(path.dirname(from), specifier);
- }
- }
-});
-```
-
-Note that using a custom resolver can slow down bundling significantly, especially when reading files asynchronously. Use `readFileSync` rather than `readFile` if possible for better performance, or omit either of the methods if you don't need to override the default behavior.
-
-### From Rust
-
-See the Rust API docs on [docs.rs](https://docs.rs/lightningcss).
-
-### With Parcel
-
-Parcel includes Lightning CSS as the default CSS transformer. You should also add a `browserslist` property to your `package.json`, which defines the target browsers that your CSS will be compiled for.
-
-While Lightning CSS handles the most commonly used PostCSS plugins like `autoprefixer`, `postcss-preset-env`, and CSS modules, you may still need PostCSS for more custom plugins like TailwindCSS. If that's the case, your PostCSS config will be picked up automatically. You can remove the plugins listed above from your PostCSS config, and they'll be handled by Lightning CSS.
-
-You can also configure Lightning CSS in the `package.json` in the root of your project. Currently, three options are supported: `drafts`, which can be used to enable CSS nesting and custom media queries, `pseudoClasses`, which allows replacing some pseudo classes like `:focus-visible` with normal classes that can be applied via JavaScript (e.g. polyfills), and `cssModules`, which enables CSS modules globally rather than only for files ending in `.module.css`, or accepts an options object.
-
-```json
-{
- "@parcel/transformer-css": {
- "cssModules": true,
- "drafts": {
- "nesting": true,
- "customMedia": true
- },
- "pseudoClasses": {
- "focusVisible": "focus-ring"
- }
- }
-}
-```
-
-See the [Parcel docs](https://parceljs.org/languages/css) for more details.
-
-### From Deno or in browser
-
-The `lightningcss-wasm` package can be used in Deno or directly in browsers. This uses a WebAssembly build of Lightning CSS. Use `TextEncoder` and `TextDecoder` convert code from a string to a typed array and back.
-
-```js
-import init, {transform} from 'https://unpkg.com/lightningcss-wasm';
-
-await init();
-
-let {code, map} = transform({
- filename: 'style.css',
- code: new TextEncoder().encode('.foo { color: red }'),
- minify: true,
-});
-
-console.log(new TextDecoder().decode(code));
-```
-
-### With webpack
-
-css-minimizer-webpack-plugin has builtin support for Lightning CSS. Install Lightning CSS in your project, and configure the plugin as documented [in its README](https://github.com/webpack-contrib/css-minimizer-webpack-plugin#using-custom-minifier-lightningcss-previously-parcelcss).
-
-### From the CLI
-
-Lightning CSS includes a standalone CLI that can be used to compile, minify, and bundle CSS files. It can be used when you only need to compile CSS, and don't need more advanced functionality from a larger build tool such as code splitting and support for other languages.
-
-To use the CLI, install the `lightningcss-cli` package with an npm compatible package manager:
-
-```shell
-npm install lightningcss-cli
-```
-
-Then, you can run the `lightningcss` command via `npx`, `yarn`, or by setting up a script in your package.json.
-
-```json
-{
- "scripts": {
- "build": "lightningcss --minify --nesting --bundle --targets '>= 0.25%' --sourcemap input.css -o output.css"
- }
-}
-```
-
-To see all of the available options, use the `--help` argument:
-
-```shell
-npx lightningcss --help
-```
-
-#### Browserslist configuration
-
-If the `--browserslist` option is provided, then `lightningcss` finds browserslist configuration,
-selects queries by environment and loads the resulting queries as targets.
-
-Configuration discovery and targets resolution is modeled after the original `browserslist` nodeJS package.
-The configuration is resolved in the following order:
-
-- If a `BROWSERSLIST` environment variable is present, then load targets from its value. This is analog to the `--targets` CLI option.
- _Example:_ `BROWSERSLIST="firefox ESR" lightningcss [OPTIONS] `
-- If a `BROWSERSLIST_CONFIG` environment variable is present, then resolve the file at the provided path.
- Then parse and use targets from `package.json` or any browserslist configuration file pointed to by the environment variable.
- _Example:_ `BROWSERSLIST_CONFIG="../config/browserslist" lightningcss [OPTIONS] `
-- If none of the above apply, then find, parse and use targets from the first `browserslist`, `.browserslistrc`
- or `package.json` configuration file in any parent directory.
-
-Browserslist configuration files may contain sections denoted by angular brackets `[]`.
-Use these to specify different targets for different environments.
-Targets which are not placed in a section are added to `defaults` and used if no section matches the environment.
-
-_Example:_
-
-```
-# Defaults, applied when no other section matches the provided environment.
-firefox ESR
-
-[staging]
-# Targets applied only to the staging environment.
-samsung >= 4
-```
-
-When using parsed configuration from `browserslist`, `.browserslistrc` or `package.json` configuration files,
-the environment determined by
-
-- the `BROWSERSLIST_ENV` environment variable if present,
-- otherwise the `NODE_ENV` environment variable if present,
-- otherwise `production` is used.
-
-If no targets are found for the resulting environment, then the `defaults` configuration section is used.
-
-### Error recovery
-
-By default, Lightning CSS is strict, and will error when parsing an invalid rule or declaration. However, sometimes you may encounter a third party library that you can't easily modify, which unintentionally contains invalid syntax, or IE-specific hacks. In these cases, you can enable the `errorRecovery` option (or `--error-recovery` CLI flag). This will skip over invalid rules and declarations, omitting them in the output, and producing a warning instead of an error. You should also open an issue or PR to fix the issue in the library if possible.
+Lightning CSS can be used from [Parcel](https://parceljs.org), as a standalone library from JavaScript or Rust, using a standalone CLI, or wrapped as a plugin within any other tool. See the [Lightning CSS website](https://lightningcss.dev/docs.html) for documentation.
## Benchmarks
diff --git a/node/ast.d.ts b/node/ast.d.ts
index eecaac3e..f3121024 100644
--- a/node/ast.d.ts
+++ b/node/ast.d.ts
@@ -6857,7 +6857,7 @@ export type DefaultAtRule = null;
*
* // Serialize it to a string. let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, ".foo, .bar {\n color: red;\n}\n"); ```
*/
-export interface StyleSheetParser {
+export interface StyleSheet {
/**
* A list of top-level rules within the style sheet.
*/
diff --git a/package.json b/package.json
index 2e07558d..deefb376 100644
--- a/package.json
+++ b/package.json
@@ -48,25 +48,33 @@
"@mdn/browser-compat-data": "^5.1.6",
"@napi-rs/cli": "^2.6.2",
"autoprefixer": "^10.4.8",
- "caniuse-lite": "^1.0.30001373",
"codemirror": "^6.0.1",
"cssnano": "^5.0.8",
"esbuild": "^0.13.10",
"flowgen": "^1.21.0",
"jest-diff": "^27.4.2",
"json-schema-to-typescript": "^11.0.2",
+ "markdown-it-anchor": "^8.6.6",
+ "markdown-it-prism": "^2.3.0",
+ "markdown-it-table-of-contents": "^0.6.0",
"napi-wasm": "^1.0.1",
"node-fetch": "^3.1.0",
- "parcel": "^2.7.0",
+ "parcel": "^2.8.2",
"patch-package": "^6.5.0",
"path-browserify": "^1.0.1",
"postcss": "^8.3.11",
+ "posthtml-include": "^1.7.4",
+ "posthtml-markdownit": "^1.3.1",
+ "posthtml-prism": "^1.0.4",
"process": "^0.11.10",
"puppeteer": "^12.0.1",
- "sharp": "^0.29.1",
+ "sharp": "^0.31.1",
"util": "^0.12.4",
"uvu": "^0.5.6"
},
+ "resolutions": {
+ "lightningcss": "link:."
+ },
"scripts": {
"prepare": "patch-package",
"build": "node scripts/build.js && node scripts/build-flow.js",
@@ -74,8 +82,8 @@
"prepublishOnly": "node scripts/build-flow.js",
"wasm:build": "cargo build --target wasm32-unknown-unknown -p lightningcss_node && cp target/wasm32-unknown-unknown/debug/lightningcss_node.wasm wasm/. && node scripts/build-wasm.js",
"wasm:build-release": "cargo build --target wasm32-unknown-unknown -p lightningcss_node --release && cp target/wasm32-unknown-unknown/release/lightningcss_node.wasm wasm/. && node scripts/build-wasm.js",
- "website:start": "parcel website/index.html website/playground/index.html",
- "website:build": "yarn wasm:build-release && parcel build website/index.html website/playground/index.html",
+ "website:start": "parcel 'website/*.html' website/playground/index.html",
+ "website:build": "yarn wasm:build-release && parcel build 'website/*.html' website/playground/index.html",
"build-ast": "cargo run --example schema --features jsonschema && node scripts/build-ast.js",
"test": "uvu node/test"
}
diff --git a/src/stylesheet.rs b/src/stylesheet.rs
index 7b34809a..25e410e2 100644
--- a/src/stylesheet.rs
+++ b/src/stylesheet.rs
@@ -68,7 +68,10 @@ pub use crate::printer::PseudoClasses;
#[cfg_attr(
feature = "jsonschema",
derive(schemars::JsonSchema),
- schemars(bound = "T: schemars::JsonSchema, T::AtRule: schemars::JsonSchema")
+ schemars(
+ rename = "StyleSheet",
+ bound = "T: schemars::JsonSchema, T::AtRule: schemars::JsonSchema"
+ )
)]
pub struct StyleSheet<'i, 'o, T: AtRuleParser<'i> = DefaultAtRuleParser> {
/// A list of top-level rules within the style sheet.
diff --git a/website/.posthtmlrc b/website/.posthtmlrc
new file mode 100644
index 00000000..def9e84b
--- /dev/null
+++ b/website/.posthtmlrc
@@ -0,0 +1,28 @@
+{
+ "plugins": {
+ "posthtml-include": {},
+ "posthtml-markdownit": {
+ "markdownit": {
+ "html": true
+ },
+ "plugins": [
+ {
+ "plugin": "markdown-it-anchor"
+ },
+ {
+ "plugin": "markdown-it-table-of-contents",
+ "options": {
+ "containerHeaderHtml": "
Lightning CSS is over 100x faster than comparable JavaScript-based tools. It can minify over 2.7 million lines of code per second on a single thread.
Lightning CSS is written in Rust, a native systems programming language. It was built with performance in mind from the start, designed to make efficient use of memory, and limit AST passes.
Lightning CSS lets you use modern CSS features and future syntax today. Features such as CSS nesting, custom media queries, high gamut color spaces, logical properties, and new selector features are automatically converted to more compatible syntax based on your browser targets.
Lightning CSS also automatically adds vendor prefixes for your browser targets, so you can keep your source code clean and repetition free.
Lightning CSS supports CSS modules, which locally scope classes, ids, @keyframes, CSS variables, and more. This ensures that there are no unintended name clashes between different CSS files.
Lightning CSS generates a mapping of the original names to scoped names, which can be used from your JavaScript. This also enables unused classes and variables to be tree-shaken.
.heading {
composes: typography from './typography.css';
@@ -168,6 +172,7 @@
CSS modules
Browser grade
Lightning CSS is written in Rust, using the cssparser and selectors crates created by Mozilla and used by Firefox. These provide a solid CSS-parsing foundation on top of which Lightning CSS implements support for all specific CSS rules and properties.
Lightning CSS fully parses every CSS rule, property, and value just as a browser would. This reduces duplicate work for transformers, leading to improved performance and minification.
color: black;
}
+ .parser p:last-of-type a {
+ font-size: 0.8em;
+ }
+
@media (width < 1200px) {
.parser {
flex-direction: column;
diff --git a/website/minification.html b/website/minification.html
new file mode 100644
index 00000000..c9b51522
--- /dev/null
+++ b/website/minification.html
@@ -0,0 +1 @@
+
diff --git a/website/pages/bundling.md b/website/pages/bundling.md
new file mode 100644
index 00000000..2a259316
--- /dev/null
+++ b/website/pages/bundling.md
@@ -0,0 +1,177 @@
+
+
+# Bundling
+
+Lightning CSS supports bundling dependencies referenced by CSS `@import` rules into a single output file. When calling the Lightning CSS API, use the `bundle` or `bundleAsync` function instead of `transform`. When using the CLI, enable the `--bundle` flag.
+
+This API requires filesystem access, so it does not accept `code` directly via the API. Instead, the `filename` option is used to read the entry file directly.
+
+```js
+import { bundle } from 'lightningcss';
+
+let { code, map } = bundle({
+ filename: 'style.css',
+ minify: true
+});
+```
+
+## Dependencies
+
+CSS files can contain dependencies referenced by `@import` syntax, as well as references to classes in other files via [CSS modules](css-modules.html).
+
+### @import
+
+The [`@import`](https://developer.mozilla.org/en-US/docs/Web/CSS/@import) at-rule can be used to inline another CSS file into the same CSS bundle as the containing file. This means that at runtime a separate network request will not be needed to load the dependency. Referenced files should be relative to the containing CSS file.
+
+```css
+@import 'other.css';
+```
+
+`@import` rules must appear before all other rules in a stylesheet except `@charset` and `@layer` statement rules. Later import rules will cause an error to be emitted.
+
+### CSS modules
+
+Dependencies are also bundled when referencing another file via [CSS modules composition](css-modules.html#dependencies) or [external variables](css-modules.html#local-css-variables). See the linked CSS modules documentation for more details.
+
+## Conditional imports
+
+The `@import` rule can be conditional by appending a media query or `supports()` query. Lightning CSS will preserve this behavior by wrapping the inlined rules in `@media` and `@supports` rules as needed.
+
+```css
+/* a.css */
+@import "b.css" print;
+@import "c.css" supports(display: grid);
+
+.a { color: red }
+```
+
+```css
+/* b.css */
+.b { color: green }
+```
+
+```css
+/* c.css */
+.c { display: grid }
+```
+
+compiles to:
+
+```css
+@media print {
+ .b { color: green }
+}
+
+@supports (display: grid) {
+ .c { display: grid }
+}
+
+.a { color: red }
+```
+
+
+
+**Note**: There are currently two cases where combining conditional rules is unsupported:
+
+1. Importing the same CSS file with only a media query, and again with only a supports query. This would require duplicating all rules in the file.
+2. Importing a file with a negated media type (e.g. `not print`) within another file with a negated media type.
+
+
+
+## Cascade layers
+
+Imported CSS rules can also be placed into a CSS cascade layer, allowing you to control the order they apply. Nested imports will be placed into nested layers.
+
+```css
+/* a.css */
+@import "b.css" layer(foo);
+.a { color: red }
+```
+
+```css
+/* b.css */
+@import "c.css" layer(bar);
+.b { color: green }
+```
+
+```css
+/* c.css */
+.c { color: green }
+```
+
+compiles to:
+
+```css
+@layer foo.bar {
+ .c { color: green }
+}
+
+@layer foo {
+ .b { color: green }
+}
+
+.a { color: red }
+```
+
+
+
+**Note**: There are two unsupported layer combinations that will currently emit a compiler error:
+
+1. Importing the same CSS file with different layer names. This would require duplicating all imported rules multiple times.
+2. Nested anonymous layers.
+
+
+
+## Bundling order
+
+When `@import` rules are processed in browsers, if the same file appears more than once, the _last_ instance applies. This is the opposite from behavior in other languages like JavaScript. Lightning CSS folllows this behavior when bundling so that the output behaves the same as if it were not bundled.
+
+```css
+/* index.css */
+@import "a.css";
+@import "b.css";
+@import "a.css";
+```
+
+```css
+/* a.css */
+body { background: green }
+```
+
+```css
+/* b.css */
+body { background: red }
+```
+
+compiles to:
+
+```css
+body { background: green }
+```
+
+## Custom resolvers
+
+The `bundleAsync` API is an asynchronous version of `bundle`, which also accepts a custom `resolver` object. This allows you to provide custom JavaScript functions for resolving `@import` specifiers to file paths, and reading files from the file system (or another source). The `read` and `resolve` functions are both optional, and may either return a string synchronously, or a Promise for asynchronous resolution.
+
+```js
+import { bundleAsync } from 'lightningcss';
+
+let { code, map } = await bundleAsync({
+ filename: 'style.css',
+ minify: true,
+ resolver: {
+ read(filePath) {
+ return fs.readFileSync(filePath, 'utf8');
+ },
+ resolve(specifier, from) {
+ return path.resolve(path.dirname(from), specifier);
+ }
+ }
+});
+```
+
+Note that using a custom resolver can slow down bundling significantly, especially when reading files asynchronously. Use `readFileSync` rather than `readFile` if possible for better performance, or omit either of the methods if you don't need to override the default behavior.
diff --git a/website/pages/css-modules.md b/website/pages/css-modules.md
new file mode 100644
index 00000000..d66016c3
--- /dev/null
+++ b/website/pages/css-modules.md
@@ -0,0 +1,261 @@
+
+
+# CSS modules
+
+By default, CSS identifiers are global. If two files define the same class names, ids, custom properties, `@keyframes`, etc., they will potentially clash and overwrite each other. To solve this, Lightning CSS supports [CSS modules](https://github.com/css-modules/css-modules).
+
+CSS modules treat the classes defined in each file as unique. Each class name or identifier is renamed to include a unique hash, and a mapping is exported to JavaScript to allow referencing them.
+
+To enable CSS modules, provide the `cssModules` option when calling the Lightning CSS API. When using the CLI, enable the `--css-modules` flag.
+
+```js
+import { transform } from 'lightningcss';
+
+let { code, map, exports } = transform({
+ // ...
+ cssModules: true,
+ code: Buffer.from(`
+ .logo {
+ background: skyblue;
+ }
+ `)
+});
+```
+
+This returns an `exports` object in addition to the compiled code and source map. Each property in the `exports` object maps from the original name in the source CSS to the compiled (i.e. hashed) name. You can use this mapping in your JavaScript or template files to reference the compiled classes and identifiers.
+
+The exports object for the above example might look like this:
+
+```js
+{
+ logo: {
+ name: '8h19c6_logo',
+ isReferenced: false,
+ composes: []
+ }
+}
+```
+
+## Class composition
+
+Style rules in CSS modules can reference other classes with the `composes` property. This causes the referenced class to be applied whenever the composed class is used, effectively providing a form of style mixins.
+
+```css
+.bg-indigo {
+ background: indigo;
+}
+
+.indigo-white {
+ composes: bg-indigo;
+ color: white;
+}
+```
+
+In the above example, whenever the `indigo-white` class is applied, the `bg-indigo` class will be applied as well. This is indicated in the `exports` object returned by Lightning CSS as follows:
+
+```js
+{
+ 'bg-indigo': {
+ name: '8h19c6_bg-indigo',
+ isReferenced: true,
+ composes: []
+ },
+ 'indigo-white': {
+ name: '8h19c6_indigo-white',
+ isReferenced: false,
+ composes: [{
+ type: 'local',
+ name: '8h19c6_bg-indigo'
+ }]
+ }
+}
+```
+
+Multiple classes can be composed at once by separating them with spaces.
+
+```css
+.logo {
+ composes: bg-indigo padding-large;
+}
+```
+
+### Dependencies
+
+You can also reference class names defined in a different CSS file using the `from` keyword:
+
+```css
+.logo {
+ composes: bg-indigo from "./colors.module.css";
+}
+```
+
+This outputs an exports object with the dependency information. It is the caller's responsibility to resolve this dependency and apply the target class name when using the `transform` API. When using the `bundle` API, this is handled automatically.
+
+```js
+{
+ logo: {
+ name: '8h19c6_logo',
+ isReferenced: false,
+ composes: [{
+ type: 'dependency',
+ name: 'bg-indigo',
+ specifier: './colors.module.css'
+ }]
+ }
+}
+```
+
+### Global composition
+
+Global (i.e. non-hashed) classes can also be composed using the `global` keyword:
+
+```css
+.search {
+ composes: search-widget from global;
+}
+```
+
+## Global exceptions
+
+Within a CSS module, all class and id selectors are local by default. You can also opt out of this behavior for a single selector using the `:global` pseudo class.
+
+```css
+.foo :global(.bar) {
+ color: red;
+}
+
+.foo .bar {
+ color: green;
+}
+```
+
+compiles to:
+
+```css
+.EgL3uq_foo .bar {
+ color: red;
+}
+
+.EgL3uq_foo .EgL3uq_bar {
+ color: #ff0;
+}
+```
+
+## Local CSS variables
+
+By default, class names, id selectors, and the names of `@keyframes`, `@counter-style`, and CSS grid lines and areas are scoped to the module they are defined in. Scoping for CSS variables and other [``](https://www.w3.org/TR/css-values-4/#dashed-idents) names can also be enabled using the `dashedIdents` option when calling the Lightning CSS API. When using the CLI, enable the `--css-modules-dashed-idents` flag.
+
+```js
+let { code, map, exports } = transform({
+ // ...
+ cssModules: {
+ dashedIdents: true
+ },
+});
+```
+
+When enabled, CSS variables will be renamed so they don't conflict with variable names defined in other files. Referencing a variable uses the standard `var()` syntax, which Lightning CSS will update to match the locally scoped variable name.
+
+```css
+:root {
+ --accent-color: hotpink;
+}
+
+.button {
+ background: var(--accent-color);
+}
+```
+
+becomes:
+
+```css
+:root {
+ --EgL3uq_accent-color: hotpink;
+}
+
+.EgL3uq_button {
+ background: var(--EgL3uq_accent-color);
+}
+```
+
+You can also reference variables defined in other files using the `from` keyword:
+
+```css
+.button {
+ background: var(--accent-color from "./vars.module.css");
+}
+```
+
+Global variables may be referenced using the `from global` syntax.
+
+```css
+.button {
+ color: var(--color from global);
+}
+```
+
+The same syntax also applies to other CSS values that use the [``](https://www.w3.org/TR/css-values-4/#dashed-idents) syntax. For example, the [@font-palette-values](https://drafts.csswg.org/css-fonts-4/#font-palette-values) rule and [font-palette](https://drafts.csswg.org/css-fonts-4/#propdef-font-palette) property use the `` syntax to define and refer to custom font color palettes, and will be scoped and referenced the same way as CSS variables.
+
+## Custom naming patterns
+
+By default, Lightning CSS prepends the hash of the filename to each class name and identifier in a CSS file. You can configure this naming pattern using the `pattern` when calling the Lightning CSS API. When using the CLI, provide the `--css-modules-pattern` option.
+
+A pattern is a string with placeholders that will be filled in by Lightning CSS. This allows you to add custom prefixes or adjust the naming convention for scoped classes.
+
+```js
+let { code, map, exports } = transform({
+ // ...
+ cssModules: {
+ pattern: 'my-company-[name]-[hash]-[local]'
+ }
+});
+```
+
+The following placeholders are currently supported:
+
+* `[name]` - The base name of the file, without the extension.
+* `[hash]` - A hash of the full file path.
+* `[local]` - The original class name or identifier.
+
+
+
+### CSS Grid
+
+**Note:** CSS grid line names can be ambiguous due to automatic postfixing done by the browser, which generates line names ending with `-start` and `-end` for each grid template area. When using CSS grid, your `"pattern"` configuration must end with the `[local]` placeholder so that these references work correctly.
+
+```js
+let { code, map, exports } = transform({
+ // ...
+ cssModules: {
+ // ❌ [local] must be at the end so that
+ // auto-generated grid line names work
+ pattern: '[local]-[hash]'
+ // ✅ do this instead
+ pattern: '[hash]-[local]'
+ }
+});
+```
+
+```css
+.grid {
+ grid-template-areas: "nav main";
+}
+
+.nav {
+ grid-column-start: nav-start;
+}
+```
+
+
+
+## Unsupported features
+
+Lightning CSS does not currently implement all CSS modules features available in other implementations. Some of these may be added in the future.
+
+* Non-function syntax for the `:local` and `:global` pseudo classes.
+* The `@value` rule – superseded by standard CSS variables.
+* The `:import` and `:export` ICSS rules.
diff --git a/website/pages/docs.md b/website/pages/docs.md
new file mode 100644
index 00000000..e24b64f7
--- /dev/null
+++ b/website/pages/docs.md
@@ -0,0 +1,179 @@
+
+
+# Getting Started
+
+Lightning CSS can be used as a library from JavaScript or Rust, or from a standalone CLI. It can also be wrapped as a plugin in other build tools, and it is the built into [Parcel](https://parceljs.org) out of the box.
+
+## From Node
+
+First, install Lightning CSS using a package manager such as npm or Yarn.
+
+```shell
+npm install --save-dev lightningcss
+```
+
+Once installed, import the module and call one of the Lightning CSS APIs. The `transform` function compiles a CSS stylesheet from a [Node Buffer](https://nodejs.org/api/buffer.html). This example minifies the input CSS, and outputs the compiled code and a source map.
+
+```js
+import { transform } from 'lightningcss';
+
+let { code, map } = transform({
+ filename: 'style.css',
+ code: Buffer.from('.foo { color: red }'),
+ minify: true,
+ sourceMap: true
+});
+```
+
+See [Transpilation](transpilation.html) for details about syntax lowering and vendor prefixing CSS for your browser targets, and the draft syntax support in Lightning CSS. You can also use the `bundle` API to process `@import` rules and inline them – see [Bundling](bundling.html) for details.
+
+The [TypeScript definitions](https://github.com/parcel-bundler/lightningcss/blob/master/node/index.d.ts) also include documentation for all API options.
+
+## From Rust
+
+Lightning CSS can also be used as a Rust library to parse, transform, and minify CSS. See the Rust API docs on [docs.rs](https://docs.rs/lightningcss).
+
+## With Parcel
+
+[Parcel](https://parceljs.org) includes Lightning CSS as the default CSS transformer. You should also add a `browserslist` property to your `package.json`, which defines the target browsers that your CSS will be compiled for.
+
+While Lightning CSS handles the most commonly used PostCSS plugins like `autoprefixer`, `postcss-preset-env`, and CSS modules, you may still need PostCSS for more custom plugins like TailwindCSS. If that's the case, your PostCSS config will be picked up automatically. You can remove the plugins listed above from your PostCSS config, and they'll be handled by Lightning CSS.
+
+You can also configure Lightning CSS in the `package.json` in the root of your project. Currently, three options are supported: [drafts](transpilation.html#draft-syntax), which can be used to enable CSS nesting and custom media queries, [pseudoClasses](transpilation.html#pseudo-class-replacement), which allows replacing some pseudo classes like `:focus-visible` with normal classes that can be applied via JavaScript (e.g. polyfills), and [cssModules](css-modules.html), which enables CSS modules globally rather than only for files ending in `.module.css`, or accepts an options object.
+
+```json
+{
+ "@parcel/transformer-css": {
+ "cssModules": true,
+ "drafts": {
+ "nesting": true,
+ "customMedia": true
+ },
+ "pseudoClasses": {
+ "focusVisible": "focus-ring"
+ }
+ }
+}
+```
+
+See the [Parcel docs](https://parceljs.org/languages/css) for more details.
+
+## From Deno or in browser
+
+The `lightningcss-wasm` package can be used in Deno or directly in browsers. This uses a WebAssembly build of Lightning CSS. Use `TextEncoder` and `TextDecoder` convert code from a string to a typed array and back.
+
+```js
+import init, { transform } from 'https://unpkg.com/lightningcss-wasm';
+
+await init();
+
+let {code, map} = transform({
+ filename: 'style.css',
+ code: new TextEncoder().encode('.foo { color: red }'),
+ minify: true,
+});
+
+console.log(new TextDecoder().decode(code));
+```
+
+Note that the `bundle` and visitor APIs are not currently available in the WASM build.
+
+## With webpack
+
+[css-minimizer-webpack-plugin](https://webpack.js.org/plugins/css-minimizer-webpack-plugin/) has built in support for Lightning CSS. To use it, first install Lightning CSS in your project with a package manager like npm or Yarn:
+
+```shell
+npm install --save-dev lightningcss css-minimizer-webpack-plugin browserslist
+```
+
+Next, configure `css-minifier-webpack-plugin` to use Lightning CSS as the minifier. You can provide options using the `minimizerOptions` object. See [Transpilation](transpilation.html) for details.
+
+```js
+// webpack.config.js
+const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
+const lightningcss = require('lightningcss');
+const browserslist = require('browserslist');
+
+module.exports = {
+ optimization: {
+ minimize: true,
+ minimizer: [
+ new CssMinimizerPlugin({
+ minify: CssMinimizerPlugin.lightningCssMinify,
+ minimizerOptions: {
+ targets: lightningcss.browserslistToTargets(browserslist('>= 0.25%'))
+ },
+ }),
+ ],
+ },
+};
+```
+
+## With Vite
+
+[vite-plugin-lightningcss](https://github.com/lawrencecchen/vite-plugin-lightningcss) provides support for [transpilation](transpilation.html) using Lightning CSS in Vite.
+
+First, install it into your project:
+
+```shell
+npm install --save-dev vite-plugin-lightningcss
+```
+
+Then, add it to your Vite config. You can pass options to the `lightningcss` plugin, including a `browserslist` config and other options documented in [Transpilation](transpilation.html).
+
+```js
+// vite.config.ts
+import lightningcss from 'vite-plugin-lightningcss';
+
+export default {
+ plugins: [
+ lightningcss({
+ browserslist: '>= 0.25%',
+ }),
+ ],
+};
+```
+
+Note that Vite uses PostCSS and esbuild internally for processing and minifying CSS even with this plugin, but it can still be a good alterntive to PostCSS plugins like autoprefixer and postcss-preset-env.
+
+## From the CLI
+
+Lightning CSS includes a standalone CLI that can be used to compile, minify, and bundle CSS files. It can be used when you only need to compile CSS, and don't need more advanced functionality from a larger build tool such as code splitting and support for other languages.
+
+To use the CLI, install the `lightningcss-cli` package with an npm compatible package manager:
+
+```shell
+npm install --save-dev lightningcss-cli
+```
+
+Then, you can run the `lightningcss` command via `npx`, `yarn`, or by setting up a script in your package.json.
+
+```json
+{
+ "scripts": {
+ "build": "lightningcss --minify --bundle --targets '>= 0.25%' input.css -o output.css"
+ }
+}
+```
+
+To see all of the available options, use the `--help` argument:
+
+```shell
+npx lightningcss --help
+```
+
+## Error recovery
+
+By default, Lightning CSS is strict, and will error when parsing an invalid rule or declaration. However, sometimes you may encounter a third party library that you can't easily modify, which unintentionally contains invalid syntax, or IE-specific hacks. In these cases, you can enable the `errorRecovery` option (or `--error-recovery` CLI flag). This will skip over invalid rules and declarations, omitting them in the output, and producing a warning instead of an error. You should also open an issue or PR to fix the issue in the library if possible.
+
+## Source maps
+
+Lightning CSS supports generating source maps when compiling, minifying, and bundling your source code to make debugging easier. Use the `sourceMap` option to enable it when using the API, or the `--sourcemap` CLI flag.
+
+If the input CSS came from another compiler such as SASS or Less, you can also pass an input source map to Lightning CSS using the `inputSourceMap` API option. This will map compiled locations back to their location in the original source code.
+
+Finally, the `projectRoot` option can be used to make file paths in source maps relative to a root directory. This makes build stable between machines.
diff --git a/website/pages/minification.md b/website/pages/minification.md
new file mode 100644
index 00000000..0dcd5b79
--- /dev/null
+++ b/website/pages/minification.md
@@ -0,0 +1,284 @@
+
+
+# Minification
+
+Lightning CSS can optimize your CSS to make it smaller, which can help improve the loading performance of your website. When using the Lightning CSS API, enable the `minify` option, or when using the CLI, use the `--minify` flag.
+
+```js
+import { transform } from 'lightningcss';
+
+let { code, map } = transform({
+ // ...
+ minify: true
+});
+```
+
+## Optimizations
+
+The Lightning CSS minifier includes many optimizations to generate the smallest possible output for all rules, properties, and values in your stylesheet. Lightning CSS does not perform any optimizations that change the behavior of your CSS unless it can prove that it is safe to do so. For example, only adjacent style rules are merged to avoid changing the order and potentially breaking the styles.
+
+### Shorthands
+
+Lightning CSS will combine longhand properties into shorthands when all of the constituent longhand properties are defined. For example:
+
+```css
+.foo {
+ padding-top: 1px;
+ padding-left: 2px;
+ padding-bottom: 3px;
+ padding-right: 4px;
+}
+```
+
+minifies to:
+
+```css
+.foo{padding:1px 4px 3px 2px}
+```
+
+This is supported across most shorthand properties defined in the CSS spec.
+
+### Merge adjacent rules
+
+Lightning CSS will merge adjacent style rules with the same selectors or declarations.
+
+```css
+.a {
+ color: red;
+}
+
+.b {
+ color: red;
+}
+
+.c {
+ color: green;
+}
+
+.c {
+ padding: 10px;
+}
+```
+
+becomes:
+
+```css
+.a,.b{color:red}.c{color:green;padding:10px}
+```
+
+In addition to style rules, Lightning CSS will also merge adjacent `@media`, `@supports`, and `@container` rules with identical queries, and adjacent `@layer` rules with the same layer name.
+
+Lightning CSS will not merge rules that are not adjacent, e.g. if another rule is between rules with the same declarations or selectors. This is because changing the order of the rules could cause the behavior of the compiled CSS to differ from the input CSS.
+
+### Remove prefixes
+
+Lightning CSS will remove vendor prefixed properties that are not needed according to your configured browser targets. This is more likely to affect precompiled libraries that include unused prefixes rather than your own code.
+
+For example, when compiling for modern browsers, prefixed versions of the `transition` property will be removed, since the unprefixed version is supported by all browsers.
+
+```css
+.button {
+ -webkit-transition: background 200ms;
+ -moz-transition: background 200ms;
+ transition: background 200ms;
+}
+```
+
+becomes:
+
+```css
+.button{transition:background .2s}
+```
+
+See [Transpilation](transpilation.html) for more on how to configure browser targets.
+
+### Reduce calc
+
+Lightning CSS will reduce `calc()` and other math expressions to constant values where possible. When different units are used, the terms are reduced as much as possible.
+
+```css
+.foo {
+ width: calc(100px * 2);
+ height: calc(((75.37% - 63.5px) - 900px) + (2 * 100px));
+}
+```
+
+minifies to:
+
+```css
+.foo{width:200px;height:calc(75.37% - 763.5px)}
+```
+
+Note that `calc()` expressions with variables are currently left unmodified by Lightning CSS.
+
+### Minify colors
+
+Lightning CSS will minify colors to the smallest format possible without changing the color gamut. For example, named colors as well as `rgb()` and `hsl()` colors are converted to hex notation, using hex alpha notation when supported by your browser targets.
+
+```css
+.foo {
+ color: rgba(255, 255, 0, 0.8)
+}
+```
+
+minifies to:
+
+```css
+.foo{color:#ff0c}
+```
+
+Note that only colors in the RGB gamut (including HSL and HWB) are converted to hex. Colors in other color spaces such as LAB or P3 are preserved.
+
+In addition to static colors, Lightning CSS also supports many color functions such as `color-mix()` and relative colors. When all components are known, Lightning CSS precomputes the result of these functions and outputs a static color. This both reduces the size and makes the syntax compatible with more browser targets.
+
+```css
+.foo {
+ color: rgb(from rebeccapurple r calc(g * 2) b);
+ background: color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%));
+}
+```
+
+minifies to:
+
+```css
+.foo{color:#669;background:#545c3d}
+```
+
+Note that these conversions cannot be performed when any of the components include CSS variables.
+
+### Normalizing values
+
+Lightning CSS parses all properties and values according to the CSS specification, filling in defaults where appropriate. When minifying, it omits default values where possible since the browser will fill those in when parsing.
+
+```css
+.foo {
+ background: 0% 0% / auto repeat scroll padding-box border-box red;
+}
+```
+
+minifies to:
+
+```css
+.foo{background:red}
+```
+
+In addition to removing defaults, Lightning CSS also omits quotes, whitespace, and optional delimeters where possible. It also converts values to shorter equivalents where possible.
+
+```css
+.foo {
+ font-weight: bold;
+ background-position: center center;
+ background-image: url("logo.png");
+}
+```
+
+minifies to:
+
+```css
+.foo{background-image:url(logo.png);background-position:50%;font-weight:700}
+```
+
+### CSS grid templates
+
+Lightning CSS will minify the `grid-template-areas` property to remove unnecessary whitespace and placeholders in template strings.
+
+```css
+.foo {
+ grid-template-areas: "head head"
+ "nav main"
+ "foot ....";
+}
+```
+
+minifies to:
+
+```css
+.foo{grid-template-areas:"head head""nav main""foot."}
+```
+
+### Reduce transforms
+
+Lightning CSS will reduce CSS transform functions to shorter equivalents where possible.
+
+```css
+.foo {
+ transform: translate(0, 50px);
+}
+```
+
+minifies to:
+
+```css
+.foo{transform:translateY(50px)}
+```
+
+In addition, the `matrix()` and `matrix3d()` functions are converted to their equivalent transforms when shorter:
+
+```css
+.foo {
+ transform: matrix3d(1, 0, 0, 0, 0, 0.707106, 0.707106, 0, 0, -0.707106, 0.707106, 0, 100, 100, 10, 1);
+}
+```
+
+minifies to:
+
+```css
+.foo{transform:translate3d(100px,100px,10px)rotateX(45deg)}
+```
+
+When a matrix would be shorter, individual transform functions are converted to a single matrix instead:
+
+```css
+.foo {
+ transform: translate(100px, 200px) rotate(45deg) skew(10deg) scale(2);
+}
+```
+
+minifies to:
+
+```css
+.foo{transform:matrix(1.41421,1.41421,-1.16485,1.66358,100,200)}
+```
+
+## Unused symbols
+
+If you know that certain class names, ids, `@keyframes` rules, CSS variables, or other CSS identifiers are unused (for example as part of a larger full project analysis), you can use the `unusedSymbols` option to remove them.
+
+```js
+let { code, map } = transform({
+ // ...
+ minify: true,
+ unusedSymbols: ['foo', 'fade-in', '--color']
+});
+```
+
+With this configuration, the following CSS:
+
+```css
+:root {
+ --color: red;
+}
+
+.foo {
+ color: var(--color);
+}
+
+@keyframes fade-in {
+ from { opacity: 0 }
+ to { opacity: 1 }
+}
+
+.bar {
+ color: green;
+}
+```
+
+minifies to:
+
+```css
+.bar{color:green}
+```
diff --git a/website/pages/transforms.md b/website/pages/transforms.md
new file mode 100644
index 00000000..eb155554
--- /dev/null
+++ b/website/pages/transforms.md
@@ -0,0 +1,310 @@
+
+
+# Custom transforms
+
+The Lightning CSS visitor API can be used to implement custom transform plugins in JavaScript. It is designed to enable custom non-standard extensions to CSS, making your code easier to author while shipping standard CSS to the browser. You can implement extensions such as custom shorthand properties or additional at-rules (e.g. mixins), build time transforms (e.g. convert units, inline constants, etc.), CSS rule analysis, and much more.
+
+Custom transforms have a build time cost: it can be around 2x slower to compile with a JS visitor than without. This means visitors should generally be used to implement custom, non-standard CSS extensions. Common standard transforms such as compiling modern standard CSS features (and draft specs) for older browsers should be done in Rust as part of Lightning CSS itself. Please open an issue if there's a feature we don't handle yet.
+
+## Visitors
+
+Custom transforms are implemented by passing a `visitor` object to the Lightning CSS Node API. A visitor includes one or more functions which are called for specific value types such as `Rule`, `Property`, or `Length`. In general, you should try to be as specific as possible about the types of values you want to handle. This way, Lightning CSS needs to call into JS as infrequently as possible, with the smallest objects possible, which improves performance. See the [TypeScript definitions](https://github.com/parcel-bundler/lightningcss/blob/master/node/index.d.ts#L101-L129) for a full list of available visitor functions.
+
+Visitors can return a new value to update it. Each visitor accepts a different type of value, and usually expects the same type in return. This example multiplies all lengths by 2:
+
+```js
+import { transform } from 'lightningcss';
+
+let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ .foo {
+ width: 12px;
+ }
+ `),
+ visitor: {
+ Length(length) {
+ return {
+ unit: length.unit,
+ value: length * 2
+ }
+ }
+ }
+});
+
+assert.equal(res.code.toString(), '.foo{width:24px}');
+```
+
+Some visitor functions accept an array as a return value, enabling you to replace one value with multiple, or remove a value by returning an empty array. You can also provide an object instead of a function to further reduce the number of times a visitor is called. For example, when providing a `Property` visitor, you can use an object with keys for specific property names. This improves performance by only calling your visitor function when needed.
+
+This example adds `-webkit-overflow-scrolling: touch` before any `overflow` properties.
+
+```js
+let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ .foo {
+ overflow: auto;
+ }
+ `),
+ visitor: {
+ Property: {
+ overflow(property) {
+ return [{
+ property: 'custom',
+ value: {
+ name: '-webkit-overflow-scrolling',
+ value: [{
+ type: 'token',
+ value: {
+ type: 'ident',
+ value: 'touch'
+ }
+ }]
+ }
+ }, property];
+ },
+ }
+ }
+});
+
+assert.equal(res.code.toString(), '.foo{-webkit-overflow-scrolling:touch;overflow:auto}');
+```
+
+## Value types
+
+The Lightning CSS AST is very detailed – each CSS property has a specific value type with all parts fully normalized. For example, a shorthand property such as `background` includes values for all of its sub-properties such as `background-color`, `background-image`, `background-position`, etc. This makes it both easier and faster for custom transforms to correctly handle all value types without reimplementing parsing. See the [TypeScript definitions](https://github.com/parcel-bundler/lightningcss/blob/master/node/ast.d.ts) for full documentation of all values.
+
+Known property values can be either _parsed_ or _unparsed_. Parsed values are fully expanded following the CSS specification. Unparsed values could not be parsed according to the grammar, and are stored as raw CSS tokens. This may occur because the value is invalid, or because it included unknown values such as CSS variables. Each property visitor function will need to handle both types of values.
+
+```js
+transform({
+ code: Buffer.from(`
+ .foo { width: 12px }
+ .bar { width: var(--w) }
+ `),
+ visitor: {
+ Property: {
+ width(v) {
+ if (v.property === 'unparsed') {
+ // Handle unparsed value, e.g. `var(--w)`
+ } else {
+ // Handle parsed value, e.g. `12px`
+ }
+ }
+ }
+ }
+});
+```
+
+Unknown properties, including custom properties, have the property type "custom". These values are also stored as raw CSS tokens. To visit custom properties, use the `custom` visitor function, or an object to filter by name. For example, to handle a custom `size` property and expand it to `width` and `height`, the following transform might be used.
+
+```js
+let res = transform({
+ minify: true,
+ code: Buffer.from(`
+ .foo {
+ size: 12px;
+ }
+ `),
+ visitor: {
+ Property: {
+ custom: {
+ size(property) {
+ // Handle the size property when the value is a length.
+ if (property.value[0].type === 'length') {
+ let value = {
+ type: 'length-percentage',
+ value: { type: 'dimension', value: property.value[0].value }
+ };
+
+ return [
+ { property: 'width', value },
+ { property: 'height', value }
+ ];
+ }
+ }
+ }
+ }
+ }
+});
+
+assert.equal(res.code.toString(), '.foo{width:12px;height:12px}');
+```
+
+## Entry and exit visitors
+
+By default, visitors are called when traversing downward through the tree (a pre-order traversal). This means each node is visited before its children. Sometimes it is useful to process a node after its children instead (a post-order traversal). This can be done by using an `Exit` visitor function, such as `FunctionExit`.
+
+For example, if you had a function visitor to double a length argument, and a visitor to replace an environment variable with a value, you could use an exit visitor to process the function after its arguments.
+
+```js
+let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ .foo {
+ padding: double(env(--branding-padding));
+ }
+ `),
+ visitor: {
+ FunctionExit: {
+ // This will run after the EnvironmentVariable visitor, below.
+ double(f) {
+ if (f.arguments[0].type === 'length') {
+ return {
+ type: 'length',
+ value: {
+ unit: f.arguments[0].value.unit,
+ value: f.arguments[0].value.value * 2
+ }
+ };
+ }
+ }
+ },
+ EnvironmentVariable: {
+ // This will run before the FunctionExit visitor, above.
+ '--branding-padding': () => ({
+ type: 'length',
+ value: {
+ unit: 'px',
+ value: 20
+ }
+ })
+ }
+ }
+});
+
+assert.equal(res.code.toString(), '.foo{padding:40px}');
+```
+
+## Composing visitors
+
+Multiple visitors can be combined into one using the `composeVisitors` function. This lets you reuse visitors between projects by publishing them as plugins. The AST is visited in a single pass, running the functions from each visitor object as if they were written together.
+
+```js
+import { transform, composeVisitors } from 'lightningcss';
+
+let environmentVisitor = {
+ EnvironmentVariable: {
+ '--branding-padding': () => ({
+ type: 'length',
+ value: {
+ unit: 'px',
+ value: 20
+ }
+ })
+ }
+};
+
+let doubleFunctionVisitor = {
+ FunctionExit: {
+ double(f) {
+ if (f.arguments[0].type === 'length') {
+ return {
+ type: 'length',
+ value: {
+ unit: f.arguments[0].value.unit,
+ value: f.arguments[0].value.value * 2
+ }
+ };
+ }
+ }
+ }
+};
+
+let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ .foo {
+ padding: double(env(--branding-padding));
+ }
+ `),
+ visitor: composeVisitors([environmentVisitor, doubleFunctionVisitor])
+});
+
+assert.equal(res.code.toString(), '.foo{padding:40px}');
+```
+
+Each visitor object has the opportunity to visit every value once. If a visitor returns a new value, that value is visited by the other visitor objects but not again by the original visitor that created it. If other visitors subsequently modify the value, the previous visitors will not revisit the value. This is to avoid infinite loops.
+
+## Examples
+
+For examples of visitors that perform a variety of real world tasks, see the Lightning CSS [visitor tests](https://github.com/parcel-bundler/lightningcss/blob/master/node/test/visitor.test.mjs).
+
+## Publishing a plugin
+
+Visitor plugins can be published to npm in order to share them with others. Plugin packages simply consist of an exported visitor object, which users can compose with other plugins via the `composeVisitors` function as described above.
+
+```js
+// lightningcss-plugin-double-function
+export default {
+ FunctionExit: {
+ double(f) {
+ // ...
+ }
+ }
+};
+```
+
+Plugins can also export a function in order to accept options.
+
+```js
+// lightningcss-plugin-env
+export default (values) => ({
+ EnvironmentVariable(env) {
+ return values[env.name];
+ }
+});
+```
+
+Plugin package names should start with `lightningcss-plugin-` and be descriptive about what they do, e.g. `lightningcss-plugin-double-function`. In addition, they should include the `lightningcss-plugin` keyword in their package.json so people can find them on npm.
+
+```json
+{
+ "name": "lightningcss-plugin-double-function",
+ "keywords": ["lightningcss-plugin"],
+ "main": "plugin.mjs"
+}
+```
+
+## Using plugins
+
+To use a published visitor plugin, install the package from npm, import it, and use the `composeVisitors` function as described above.
+
+```js
+import { transform, composeVisitors } from 'lightningcss';
+import environmentVisitor from 'lightningcss-plugin-environment';
+import doubleFunctionVisitor from 'lightningcss-plugin-double-function';
+
+let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ .foo {
+ padding: double(env(--branding-padding));
+ }
+ `),
+ visitor: composeVisitors([
+ environmentVisitor({
+ '--branding-padding': {
+ type: 'length',
+ value: {
+ unit: 'px',
+ value: 20
+ }
+ }
+ }),
+ doubleFunctionVisitor
+ ])
+});
+
+assert.equal(res.code.toString(), '.foo{padding:40px}');
+```
diff --git a/website/pages/transpilation.md b/website/pages/transpilation.md
new file mode 100644
index 00000000..04995775
--- /dev/null
+++ b/website/pages/transpilation.md
@@ -0,0 +1,569 @@
+
+
+# Transpilation
+
+Lightning CSS includes support for transpiling modern CSS syntax to support older browsers, including vendor prefixing and syntax lowering.
+
+## Browser targets
+
+By default Lightning CSS does not perform any transpilation of CSS syntax for older browsers. This means that if you write your code using modern syntax or without vendor prefixes, that’s what Lightning CSS will output. You can declare your app’s supported browsers using the `targets` option. When this is declared, Lightning CSS will transpile your code accordingly to ensure compatibility with your supported browsers.
+
+Targets are defined using an object that specifies the minimum version of each browser you want to support. The easiest way to build a targets object is to use [browserslist](https://browserslist.dev). This lets you use a query that automatically updates over time as new browser versions are released, market share changes, etc. The following example will return a targets object listing browsers with >= 0.25% market share.
+
+```js
+import browserslist from 'browserslist';
+import { transform, browserslistToTargets } from 'lightningcss';
+
+// Call this once per build.
+let targets = browserslistToTargets(browserslist('>= 0.25%'));
+
+// Use `targets` for each file you transform.
+let { code, map } = transform({
+ // ...
+ targets
+});
+```
+
+For the best performance, you should call browserslist once for your whole build process, and reuse the same `targets` object when calling `transform` for each file.
+
+Under the hood, `targets` are represented using an object that maps browser names to minimum versions. Version numbers are represented using a single 24-bit number, with one semver component (major, minor, patch) per byte. For example, this targets object would represent Safari 13.2.0.
+
+```js
+let targets = {
+ safari: (13 << 16) | (2 << 8)
+};
+```
+
+### CLI
+
+When using the CLI, targets can be provided by passing a [browserslist](https://browserslist.dev) query to the `--targets` option. Alternatively, if the `--browserslist` option is provided, then `lightningcss` finds browserslist configuration, selects queries by environment and loads the resulting queries as targets.
+
+Configuration discovery and targets resolution is modeled after the original `browserslist` Node package. The configuration is resolved in the following order:
+
+- If a `BROWSERSLIST` environment variable is present, then load targets from its value.
+- If a `BROWSERSLIST_CONFIG` environment variable is present, then load the browserslist configuration from the file at the provided path.
+- If none of the above apply, then find, parse and use targets from the first `browserslist`, `.browserslistrc`, or `package.json` configuration file in any parent directory.
+
+Browserslist configuration files may contain sections denoted by square brackets. Use these to specify different targets for different environments. Targets which are not placed in a section are added to `defaults` and used if no section matches the environment.
+
+```ini
+# Defaults, applied when no other section matches the provided environment.
+firefox ESR
+
+# Targets applied only to the staging environment.
+[staging]
+samsung >= 4
+```
+
+When using parsed configuration from `browserslist`, `.browserslistrc`, or `package.json` configuration files, the environment is determined by:
+
+- the `BROWSERSLIST_ENV` environment variable if present
+- the `NODE_ENV` environment variable if present
+- otherwise `"production"` is used.
+
+If no targets are found for the resulting environment, then the `defaults` configuration section is used.
+
+## Vendor prefixing
+
+Based on your configured browser targets, Lightning CSS automatically adds vendor prefixed fallbacks for many CSS features. For example, when using the [`image-set()`](https://developer.mozilla.org/en-US/docs/Web/CSS/image/image-set()) function, Lightning CSS will output a fallback `-webkit-image-set()` value as well, since Chrome does not yet support the unprefixed value.
+
+```css
+.logo {
+ background: image-set(url(logo.png) 2x, url(logo.png) 1x);
+}
+```
+
+compiles to:
+
+```css
+.logo {
+ background: -webkit-image-set(url(logo.png) 2x, url(logo.png) 1x);
+ background: image-set("logo.png" 2x, "logo.png");
+}
+```
+
+In addition, if your CSS source code (or more likely a library) includes unnecessary vendor prefixes, Lightning CSS will automatically remove them to reduce bundle sizes. For example, when compiling for modern browsers, prefixed versions of the `transition` property will be removed, since the unprefixed version is supported by all browsers.
+
+```css
+.button {
+ -webkit-transition: background 200ms;
+ -moz-transition: background 200ms;
+ transition: background 200ms;
+}
+```
+
+becomes:
+
+```css
+.button {
+ transition: background .2s;
+}
+```
+
+## Syntax lowering
+
+Lightning CSS automatically compiles many modern CSS syntax features to more compatible output that is supported in your target browsers.
+
+### Color mix
+
+The [`color-mix()`](https://drafts.csswg.org/css-color-5/#color-mix) function allows you to mix two colors by the specified amount in a certain color space. Lightning CSS will evaluate this function statically when all components are known (i.e. not variables).
+
+```css
+.foo {
+ color: color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40%));
+}
+```
+
+compiles to:
+
+```css
+.foo {
+ color: #706a43;
+}
+```
+
+### Relative colors
+
+Relative colors allow you to modify the components of a color using math functions. In addition, you can convert colors between color spaces. Lightning CSS performs these calculations statically when all components are known (i.e. not variables).
+
+This example lightens `slateblue` by 10% in the LCH color space.
+
+```css
+.foo {
+ color: lch(from slateblue calc(l + 10%) c h);
+}
+```
+
+compiles to:
+
+```css
+.foo {
+ color: lch(54.5711% 65.7776 296.794);
+}
+```
+
+### LAB colors
+
+Lightning CSS will convert [`lab()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lab()), [`lch()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lch()), [`oklab()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklab), and [`oklch()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch) colors to fallback values for unsupported browsers when needed. These functions allow you to define colors in higher gamut color spaces, making it possible to use colors that cannot be represented by RGB.
+
+```css
+.foo {
+ color: lab(40% 56.6 39);
+}
+```
+
+compiles to:
+
+```css
+.foo {
+ color: #b32323;
+ color: color(display-p3 .643308 .192455 .167712);
+ color: lab(40% 56.6 39);
+}
+```
+
+As shown above, a `display-p3` fallback is included in addition to RGB when a target browser supports the P3 color space. This preserves high color gamut colors when possible.
+
+### Color function
+
+Lightning CSS converts the [`color()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color()) function to RGB when needed for compatibility with older browsers. This allows you to use predefined color spaces such as `display-p3`, `xyz`, and `a98-rgb`.
+
+```css
+.foo {
+ color: color(a98-rgb 0.44091 0.49971 0.37408);
+}
+```
+
+compiles to:
+
+```css
+.foo {
+ background-color: #6a805d;
+ background-color: color(a98-rgb .44091 .49971 .37408);
+}
+```
+
+### HWB colors
+
+Lightning CSS converts [`hwb()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hwb) colors to RGB.
+
+```css
+.foo {
+ color: hwb(194 0% 0%);
+}
+```
+
+compiles to:
+
+```css
+.foo {
+ color: #00c4ff;
+}
+```
+
+### Color notation
+
+Space separated color notation is converted to hex when needed. Hex colors with alpha are also converted to `rgba()` when unsupported by all targets.
+
+```css
+.foo {
+ color: #7bffff80;
+ background: rgb(123 255 255);
+}
+```
+
+compiles to:
+
+```css
+.foo {
+ color: rgba(123, 255, 255, .5);
+ background: #7bffff;
+}
+```
+
+### Logical properties
+
+CSS [logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties) allow you to define values in terms of writing direction, so that UIs mirror in right-to-left languages. Lightning CSS will compile these to use the `:dir()` selector when unsupported. If the `:dir()` selector is unsupported, it is compiled as described [below](#%3Adir()-selector).
+
+```css
+.foo {
+ border-start-start-radius: 20px
+}
+```
+
+compiles to:
+
+```css
+.foo:dir(ltr) {
+ border-top-left-radius: 20px;
+}
+
+.foo:dir(rtl) {
+ border-top-right-radius: 20px;
+}
+```
+
+
+### :dir() selector
+
+The [`:dir()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:dir) selector matches elements based on the writing direction. Lightning CSS compiles this to use the `:lang()` selector when unsupported, which approximates this behavior as closely as possible.
+
+```css
+a:dir(rtl) {
+ color:red
+}
+```
+
+compiles to:
+
+```css
+a:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) {
+ color: red;
+}
+```
+
+If multiple arguments to `:lang()` are unsupported, it is compiled as described [below](#%3Alang()-selector).
+
+### :lang() selector
+
+The [`:lang()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:lang) selector matches elements based on their language. Some browsers do not support multiple arguments to this function, so Lightning CSS compiles them to use `:is()` when needed.
+
+```css
+a:lang(en, fr) {
+ color:red
+}
+```
+
+compiles to:
+
+```css
+a:is(:lang(en), :lang(fr)) {
+ color: red;
+}
+```
+
+When the `:is()` selector is unsupported, it is compiled as described [below](#%3Ais()-selector).
+
+### :is() selector
+
+The [`:is()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:is) matches when one of its arguments matches. Lightning CSS falls back to the `:-webkit-any` and `:-moz-any` prefixed selectors.
+
+```css
+p:is(:first-child, .lead) {
+ margin-top: 0;
+}
+```
+
+compiles to:
+
+```css
+p:-webkit-any(:first-child, .lead) {
+ margin-top: 0;
+}
+
+p:-moz-any(:first-child, .lead) {
+ margin-top: 0;
+}
+
+p:is(:first-child, .lead) {
+ margin-top: 0;
+}
+```
+
+
+
+**Note**: The prefixed versions of these selectors do not support complex selectors (e.g. selectors with combinators). Lightning CSS will only output prefixes if the arguments are simple selectors. Complex selectors in `:is()` are not currently compiled.
+
+