Content selectors

Native CSS can match an attribute's exact text, but it can't compare numbers or look at an element's text content. css-pp adds both, and they stay up to date as the page changes.

:contains() — select by text#

:contains("text") matches elements whose text content includes the string:

CSS
.badge:contains("Sold out") { background: #fee2e2; color: #991b1b; }

Add i before the closing parenthesis for a case-insensitive match, the same way native attribute selectors work:

CSS
.status:contains("error" i) { color: #dc2626; }

Quotes are optional, but use them when the text contains commas, colons or parentheses: :contains("Price: $5, today").

Like jQuery's selector of the same name, :contains() looks at all the text inside an element, including its descendants. Qualify it with a class or tag, as in .badge:contains(...), so it doesn't also match every ancestor up to <body>.

Style the parent by its text#

Combine :contains() with :has() to style a container based on text further down:

CSS
/* dim a whole product card when its badge says sold out */
.product:has(.badge:contains("sold out" i)) { opacity: .5; }

/* flag table rows whose status cell says "Overdue" */
tr:has(td.status:contains("Overdue")) { background: #fff7ed; }

Numeric attribute comparisons#

Compare an attribute's value as a number with <, <=, > or >=:

CSS
.product[data-stock<5]     { outline: 2px solid #f59e0b; }
.price[data-amount>=100]   { font-weight: 700; }
.rating[data-score>4.5]    { color: #16a34a; }
.temp[data-celsius<0]      { color: #2563eb; }

Decimals and negative numbers work. Spaces around the operator are allowed: [data-stock < 5]. An element whose attribute is missing or not a number never matches.

The equality operators you already know (=, ^=, $=, *=) stay native. css-pp only handles the comparisons CSS lacks.

CSS
/* show the low-stock badge inside any card with fewer than 5 left */
.card .low-stock { display: none; }
.card:has([data-stock<5]) .low-stock { display: inline-block; }

Live updates#

Both selectors re-evaluate automatically when text changes, attributes change, or elements are added, for example by a framework, an AJAX call or a setAttribute(). You don't need to call anything.

JavaScript
// the card above updates on its own
document.querySelector('[data-stock]').setAttribute('data-stock', '3');

How it works#

Each distinct condition gets its own attribute. [data-stock<5] becomes [data-css-cmp-0] and :contains("sold out" i) becomes [data-css-contains-1], both with the same specificity as a class. css-pp tests only the elements the rest of the selector can match. A MutationObserver triggers a re-check when the page changes, throttled to at most one pass every 100 ms during bursts.