CSS @container style(): Variant Styles Without JavaScript

Conditional styles without JavaScript class toggling

Variant styling used to mean JavaScript reading a data attribute, then adding a class like .card--primary so CSS could match it. @container style() lets the CSS respond to a custom property directly, no script involved.

Old way CSS + JS toggling
.card--primary   {  background: oklch(0.55 0.2 260);  color: #fff;}.card--danger   {  background: oklch(0.55 0.2 30);  color: #fff;}// JS: el.querySelectorAll('.card').forEach(c => c.classList.add('card--' + el.dataset.variant));
Modern
Pure CSS
@property --variant {  syntax: "<custom-ident>";  initial-value: default;  inherits: true;}.card-wrap   {  container-name: card;}@container card style(--variant: primary)   {  .card {    background: oklch(0.55 0.2 260);    color: #fff;  }}@container card style(--variant: danger)   {  .card {    background: oklch(0.55 0.2 30);    color: #fff;  }}/* no JS, no class toggling */
Newly available Since 2026 88% global usage

Since 2026 this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

Works in all modern browsers. May need a fallback for older browsers.

111+
151+
18+
111+

Style queries for custom properties became Baseline Newly Available in May 2026 when Firefox 151 shipped. Chrome and Edge have supported them since version 111 (March 2023), Safari since version 18 (September 2024). This snippet uses custom property queries only. Style queries on regular CSS properties (like font-weight or color) are not yet supported in any browser.

Set --variant on the container, child cards style themselves automatically

Default

--variant: default

Primary

--variant: primary

Danger

--variant: danger

The container sets --variant. No JS, no added classes.

No JavaScript needed

The old pattern required a script to read data-variant and add a matching class. The CSS now does the reading itself.

Container-driven

Set --variant: primary on any ancestor and every child component inside it picks up the style automatically. Swap the value, the whole subtree updates.

Composes with @property

Register the custom property with @property to give it a known type and initial value. This makes the query more reliable and lets you set a fallback for containers that do not specify a variant.

Old Approach
JS class toggling
Read value, add .card--variant class
Modern Approach
@container style()
CSS responds to the custom property directly
Pairs With
@property
Registers type and initial value

How it works

A style query targets an ancestor container based on the current computed value of a custom property on that ancestor. Write @container style(--variant: primary) and any element inside a container where --variant resolves to primary will receive the nested rules. The container needs a container-name if you want to target a specific level, or leave it unnamed to match the nearest qualifying ancestor.

Registering the custom property with @property is strongly recommended. Without registration, the property has no known type and no initial value, so an unset container may behave unpredictably across browsers. With syntax: "<custom-ident>" and initial-value: default, the query for primary reliably misses containers that never set the property.

Style queries on custom properties are distinct from style queries on regular CSS properties (like style(font-weight: bold)), which are not yet supported in any browser. Everything in this snippet uses custom properties only, which is the supported subset.

ESC