# Getting started with CSS Post Processor

> Install CSS Post Processor with one script tag, open a stylesheet with css.open(), and start using variables, nesting, $[index], :seen, :stuck and :contains().

Source: https://csspp.dev/docs/


CSS Post Processor (css-pp) is a small JavaScript library that runs in the browser and extends CSS. You add one script, point it at a stylesheet, and that stylesheet can use variables, mixins, nesting, per-element values and selectors that native CSS does not have.

There is no build step, no Node.js and no configuration file. Ordinary CSS passes through unchanged, so you can adopt it one rule at a time.

## Install

Pick whichever fits your project. All three serve the same file.

```html Self-hosted
<script src="/js/css-pp.min.js"></script>
```

```html CDN (jsDelivr)
<script src="https://cdn.jsdelivr.net/npm/css-pp@3.2.1/dist/css-pp.min.js"
        integrity="sha384-T1bI8VjOVeN7L8QkEg/v3zEkxyzVreA9qchbx+0gd9tOItYJlbVFgRiodhzslST/"
        crossorigin="anonymous"></script>
```

```bash npm
npm install css-pp
```

The minified build is 29.5 KB (10.4 KB gzipped). You can download it from the [download page](/download/).

## Open a stylesheet

Call `css.open()` with the path to your stylesheet. The library fetches the file, processes it and injects the result into a `<style>` element.

```html index.html
<script src="/js/css-pp.min.js"></script>
<script>
  css.defaults.debug = 0;     // don't throw on mistakes in production
  css.open('/css/site.css');
</script>
```

The stylesheet is fetched with `XMLHttpRequest`, so it must come from the same origin as the page, or from a server that allows cross-origin requests.

The injected `<style>` element gets an `id` taken from the file name without its extension. `/css/site.css` becomes `<style id="site">`. You use that name later to change variables at runtime with [`css.set()`](/docs/javascript-api/#css-set).

### Inline styles

To process CSS that is already in the page, give the element an `id` and pass it with a leading `#`:

```html
<style id="main">
  $brand{#0ea5e9}
  .button { background: $brand; }
</style>
<script>css.open('#main');</script>
```

The browser applies the raw `<style>` first and skips anything it doesn't understand. css-pp then replaces the contents with the processed CSS.

## Your first stylesheet

```css site.css
/* variables */
$brand{#0ea5e9}
$radius{12px}

/* nesting */
.card {
  border-radius: $radius;
  &:hover { outline: 2px solid $brand; }
  h2 { color: $brand; }
}

/* a different image for every slide */
.slider > div { background-image: url(/img/slide-$[index].jpg); }

/* fade cards in the first time they are seen */
.card:not(:seen) { opacity: 0; translate: 0 1.5rem; }
.card { transition: opacity .6s, translate .6s; }
```

The first block compiles to ordinary CSS: `.card{border-radius:12px}`, `.card:hover{outline:2px solid #0ea5e9}` and so on. The `$[index]` rule becomes one rule per matched slide. `:seen` becomes an attribute selector that an `IntersectionObserver` switches on.

## Options

Set options on `css.defaults` before the first `css.open()` call.

| Option | Default | What it does |
|---|---|---|
| `debug` | `1` | Throws an error for undefined variables and similar mistakes, and exposes `window.css_vars`. Set it to `0` in production. |
| `invisible` | `1` | Hides the page (`visibility: hidden` on `<html>`) until every opened stylesheet is processed, so visitors never see unstyled content. |
| `localCache` | `0` | Caches the stylesheet source in `localStorage`. The next visit renders from the cache immediately and refreshes it in the background. |
| `resizing` | `100` | Milliseconds to throttle the recalculation of [auto-updating variables](/docs/variables/#auto-updating-variables) while the window is resized. |

## Know when styles are ready

css-pp fires a `css_ready` event on `document` for each stylesheet once it is processed and the DOM is ready. The event's `id` property is the stylesheet name.

```js
document.addEventListener('css_ready', function (e) {
  console.log('styles ready:', e.id);
});
```

## Browser support

The core (variables, mixins, nesting, `$[index]`) is plain ES5 and runs in every browser in use today. The [state](/docs/state-selectors/) and [content](/docs/content-selectors/) selectors rely on `IntersectionObserver`, `MutationObserver` and `ResizeObserver`, which every evergreen browser has supported since 2020.

If `IntersectionObserver` is missing, `:seen` and `:in-view` behave as if the element is visible, so hidden-until-seen content is never lost.

## Next steps

- [Variables, mixins and JavaScript](/docs/variables/): values, parameters, expressions and responsive variables
- [Nesting](/docs/nesting/): `&`, `&:hover`, `.dark &` and `@media` inside rules
- [`$[index]`](/docs/index-variable/): per-element values, even inside `url()`
- [State selectors](/docs/state-selectors/): `:seen`, `:in-view`, `:stuck` and `:overflowing`
- [Scroll and navigation](/docs/scroll-navigation/): `:current`, `:scrolled`, `:scrolling-down`, `:scroll-end` and scrollspy
- [Media, layout and forms](/docs/media-forms/): `:loaded`, `:broken`, `:wrapped`, `:filled`, `:dirty`, `:offline` and `:idle`
- [Pseudo-elements](/docs/pseudo-elements/): click, hover, style and measure `::before` and `::after`
- [Content selectors](/docs/content-selectors/): `:contains()` and numeric attribute comparisons
- [Recipes](/recipes/): copy-paste solutions for common UI problems
