# Frequently asked questions

> Answers about CSS Post Processor: browser support, performance, SEO, CSP, licensing, and how to select parents, detect stuck headers and select by text.

Source: https://csspp.dev/faq/


## About the library

### What is CSS Post Processor?

CSS Post Processor (css-pp) is a free, open-source JavaScript library that extends CSS in the browser. It adds variables with JavaScript expressions, mixins, nesting, per-element values through `$[index]`, and selectors native CSS lacks: `:seen`, `:in-view`, `:stuck`, `:overflowing`, `:contains()` and numeric attribute comparisons such as `[data-stock<5]`. It is 10.4 KB gzipped, has no dependencies and needs no build step.

### Is it a preprocessor or a postprocessor?

Both words fit. It's called a post processor because it processes CSS after it reaches the browser, rather than in a build step like Sass or LESS. That timing is what lets it use the live page: element counts, sizes, visibility, sticky state and text.

### Do I need Node.js, npm or a build step?

No. Add one script tag and call `css.open('/css/site.css')`. It is also published on npm, as `npm install css-pp`, for projects that prefer packages.

### Does it work with WordPress, Shopify, PHP or any CMS?

Yes. It is plain JavaScript that runs on any page, whatever generates the HTML. Load the script in your theme and open your stylesheet.

### How big is it?

29.5 KB minified and 10.4 KB gzipped for version 3.2.1.

### Which browsers are supported?

All current browsers. The core features are ES5. The state and content selectors use `IntersectionObserver`, `MutationObserver` and `ResizeObserver`, which every evergreen browser has supported since 2020. Without `IntersectionObserver`, `:seen` and `:in-view` act as if the element is visible, so content is never hidden.

### Is it free for commercial use?

Yes. It's released under the MIT License, so you can use it in commercial and closed-source projects. Keep the copyright notice in the file.

## Performance and SEO

### Does processing CSS in the browser slow the page down?

Very little. In Chrome on a desktop, a 74 KB production stylesheet with variables and nesting compiles in about 9 ms, and a 107 KB plain stylesheet in about 6 ms. Set `css.defaults.localCache = 1` so returning visitors render from a cached copy.

### Will it hurt SEO?

No. Your HTML and text aren't changed; only CSS is generated. Google renders JavaScript as part of indexing. Write hidden-until-seen styles as `.x:not(:seen) { opacity: 0 }`. Crawlers and browsers that don't run the script then skip the rule, so the content stays visible to them.

### What about a flash of unstyled content?

By default (`css.defaults.invisible = 1`), the page stays hidden until your stylesheets are processed, so nobody sees unstyled markup.

### Does it work with a Content Security Policy?

Partly. Variable expressions use `eval()`. Without `'unsafe-eval'`, plain values, mixins, nesting and the state and content selectors still work, but JavaScript expressions and `$[index]` don't.

## How do I…

### How do I select a parent element in CSS?

Use the native `:has()` pseudo-class: `.card:has(img)` selects cards that contain an image. `:has(> .child)` selects the direct parent of `.child` whatever element it is. Every current browser has supported it since December 2023. css-pp adds parent selection based on visibility, text, numbers and overflow, for example `.post:has(.excerpt:overflowing)`. See [Parent and sibling selectors](/docs/parent-selector/).

### How do I select previous siblings in CSS?

Put the sibling combinator inside `:has()`: `li:has(~ .current)` selects every item before `.current`, and `li:has(+ .current)` selects only the one immediately before it.

### How do I style a sticky header only when it is stuck?

With css-pp: `header:stuck { box-shadow: 0 2px 12px rgb(0 0 0 / .15) }`. It works in every browser, at the top or bottom, in the page or in a scrolling container. Native CSS only has Chromium's `scroll-state(stuck)` container queries.

### How do I select an element by its text content?

With css-pp: `.badge:contains("Sold out")`, or `:contains("sold out" i)` for a case-insensitive match. Native CSS has no text selector.

### How do I compare a number in an attribute selector?

With css-pp: `[data-stock<5]`, `[data-price>=100]`, `[data-score>4.5]`. Native attribute selectors only compare text.

### How do I detect truncated text with CSS?

With css-pp: `.title:overflowing` matches when the content is wider or taller than the box, for example when `text-overflow: ellipsis` is cutting it off. Combine it with `:has()` to show a "Read more" link only when needed.

### How do I reveal elements on scroll without JavaScript?

Load css-pp and write `.card:not(:seen) { opacity: 0; translate: 0 2rem }` with a transition on `.card`. Each card fades in the first time it enters the viewport. Use `:in-view` to animate every time it enters.

### How do I give every slide a different background image in one CSS rule?

`.slider > div { background-image: url(/img/slide-$[index].jpg) }`. css-pp replaces `$[index]` with each element's position: 0, 1, 2 and so on. Native CSS can't put a computed value inside `url()`.

### How do I highlight the active navigation link with CSS?

With css-pp: `nav a:current { font-weight: 700 }`. It matches links to the current page and to its parent sections, so "Docs" stays active on every docs page; `:current(exact)` matches the page alone. Native CSS specifies `:local-link` for this, but no browser supports it.

### How do I shrink or hide a header when the page scrolls?

With css-pp: `html:scrolled(80px) header { height: 56px }` shrinks it after 80px of scrolling, and `html:scrolling-down header { translate: 0 -100% }` hides it while scrolling down and brings it back on the way up.

### How do I detect when flex items wrap to a new line?

With css-pp: `li:wrapped` matches items that wrapped below the first row, so `nav:has(li:wrapped) .menu-button { display: block }` shows a hamburger menu exactly when the links stop fitting.

### How do I fade images in when they finish loading?

With css-pp: `img { transition: opacity .4s } img:not(:loaded) { opacity: 0 }`. `img:broken` matches images that failed to load, for fallbacks.

### How do I make text fill its container?

With css-pp: `h1 { font-size: fit(24px, 120px) }` sizes the text to fill the element's width on one line, between the two limits.

### How do I make cards the same height?

With css-pp: `.card h3 { height: same }` gives every matched element the height of the tallest one, across rows and containers. If the cards share one CSS grid, native `subgrid` works too.

### How do I detect a click on a pseudo-element like ::after?

Browsers report a click on `::after` as a click on the element, so plain JavaScript can't tell them apart. With css-pp: `css.on('click', '.chip::after', function (e) { ... })` fires only when the pointer is on the `::after`, in every browser. It uses the browser's own hit-testing, so it is exact even for rotated or inline pseudo-elements.

### How do I change a pseudo-element's style with JavaScript?

With css-pp: `css.pseudo(el, '::after').set('color', 'red')` styles that one element's `::after`, and `css.pseudo(el, '::after').text = 'New'` changes its text. Natively you can only read pseudo-element styles with `getComputedStyle(el, '::after')`.

### Can I use :hover on ::before or ::after?

Not in native CSS: `.x::after:hover` is invalid. With css-pp it works: `.chip::after:hover { color: red }` applies only while the pointer is on the pseudo-element, and `::after:active` while it is pressed.

## Help

### Where do I report a bug or ask a question?

Use the [contact page](/contact/). Include the browser, a short stylesheet that shows the problem, and what you expected to happen.

### Who maintains it?

Alex Bobrov, who created it and has maintained it since 2016. See [About](/about/).
