# Changelog

> Release history of CSS Post Processor, from the first versions in 2016 to version 3.0 with :seen, :in-view, :stuck, :overflowing and :contains().

Source: https://csspp.dev/changelog/


## 3.2.1 — 2026-11-02

### Fixed

- While a stylesheet is first applied with `invisible` on (the default), transitions are paused. Before, elements with a `transition` animated from the browser's default styles on every page load, so buttons briefly faded from grey and links from blue.

## 3.2.0 — 2026-11-02

Pseudo-elements become usable from JavaScript and hoverable in CSS, in every browser.

### Added

- `::before:hover`, `::after:hover`, `::before:active` and `::after:active` in stylesheets.
- `css.on(type, selector, handler)`: delegated events that fire only when the pointer is on the pseudo-element, e.g. `css.on('click', '.chip::after', fn)`. `dialog::backdrop` clicks too. Returns a function that removes the listener.
- `css.pseudo(element, '::after')`: `style()`, `set()`, `text`, `exists`, `contains(x, y)`, `rect()` and `animate()` for one element's pseudo-element.
- `css.pseudos(selector)`: every rendered pseudo-element matching a selector, like `querySelectorAll()`.

## 3.1.0 — 2026-10-15

Twenty new built-in behaviours that replace small scripts almost every site copies.

### Added

- Navigation and scrolling: `:current` and `:current(exact)` for the active link, `:scrolled` and `:scrolled(80px)`, `:scrolling-down` and `:scrolling-up`, `:scroll-start` and `:scroll-end`, and `:target-in-view` for scrollspy menus.
- Images and layout: `img:loaded`, `img:broken`, `:wrapped` for flex and grid items that wrapped, `font-size: fit(min, max)` to fill a box, and `height: same` / `min-height: same` for equal heights.
- `:seen(300px)` and `:in-view(300px)`: a pixel margin that triggers before the element is visible, for lazy backgrounds.
- Values: `$[count]`, `$[year]`, `$[random(1,5)]` (stable per element) and `$[attr(data-x)]` (per element, usable inside `url()`).
- Forms and environment: `:filled`, `:dirty`, `:offline` and `:idle(30s)`.

### Changed

- One shared set of passive event listeners drives every scroll, form, image and idle state.
- Web fonts finishing loading now recalculate auto-updating values, so `fit()` and `same` measure the final font.

## 3.0.0 — 2026-10-01

The first public release under the MIT License, and the biggest update since the library began.

### Added

- `:seen` and `:seen(30%)`: matches once an element has been in view.
- `:in-view` and `:in-view(50%)`: matches while an element is in the viewport.
- `:stuck`: matches while a `position: sticky` element is pinned, at the top or bottom, in the page or in a scroll container.
- `:overflowing`: matches when content is larger than its box.
- `:contains("text")` and `:contains("text" i)`: select by text content.
- Numeric attribute comparisons: `[data-x<5]`, `[data-x<=5]`, `[data-x>5]`, `[data-x>=5]`.
- All new selectors work inside `:has()`, `:not()` and `:is()`, and update live when the DOM changes.
- Nesting: `&` anywhere (`.dark &`, `&-suffix`, `& + &`), declarations directly inside nested rules, and `@media`, `@supports` and `@container` inside rules.

### Changed

- `$[index]` gives each element a stable `data-css-i` attribute instead of relying on `:nth-child()`. It is now correct when a heading comes before the items, with several containers, with wrapped elements, with selector lists and with `::before` or `::after`.
- `$[index]` is recalculated after the page loads, so it works when the stylesheet is opened in `<head>`.
- The nesting engine was rewritten. Rules are written in source order, and repeated selectors are no longer merged.
- Released under the MIT License (previously GPL-3.0).

### Fixed

- `//` comments no longer delete URLs such as `url(https://...)` or `@import url(//...)`.
- Native `attr()` inside values, such as `content: attr(data-label)`, is no longer replaced.
- Commas inside `:is()`, `:where()`, `:not()` and `:has()` no longer break nested selectors.
- `&:hover` as the first nested block no longer moves the following rules under it.
- `&{}` blocks no longer discard the rules before them.
- A space before a pseudo-class (`.menu :focus-visible`) keeps its meaning.
- Text inside quotes is never changed.
- Auto-updating variables inside `@media` blocks stay inside them.
- `css.open()` accepts file names with hyphens and query strings.
- Runtime updates handle URLs containing `:`, multi-word properties and `!important` correctly.
- Repeated updates no longer add duplicate rules.

## 2.2 — 2023

- 7/12/23: `$attr` no longer overrides existing inline styles.
- 6/20/20: `vertical-align: middle!` and `bottom!` keep elements aligned when they resize.
- 3/20/20: added `attr()` to apply an attribute's CSS text as inline styles.
- 3/5/20: fixed default parameters being overwritten.
- 1/8/20: rewrote variable extraction.

## 2.1 — 2019

- 11/1/19: fixed argument extraction from variables.
- 10/18/19: when several stylesheets load at once, the page is shown after all of them finish.
- 10/17/19: fixed extraction of default arguments.
- Added the `index` variable, auto-updating variables with `!`, `min-`/`max-` margin and padding, and the `invisible` option.

## 1.x — 2016

- 11/1/16: added the `localCache` option.
- 5/20/16: improved `css.vars.w()` and `css.vars.h()`.
- 5/19/16: the page appears sooner, and `css_ready` fires once the document is interactive.
- 1/21/16: fixed whitespace handling in nested styles.
- 1/13/16: fixed passing a parameter equal to its default.
