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.