Container Style Queries: API Simplification Analysis
Current Approach: Data Attributes
The current API pattern requires data-size (and similar props) on every child element:
// checkbox.api.ts
export function createQdsCheckboxApi(props, normalize) {
const size = props.size || "md"
return {
getControlBindings() {
return normalize.element({
className: checkboxClasses.control,
"data-size": size, // repeated
})
},
getIndicatorBindings() {
return normalize.element({
className: checkboxClasses.indicator,
"data-size": size, // repeated
})
},
getLabelBindings() {
return normalize.element({
className: checkboxClasses.label,
"data-size": size, // repeated
})
},
}
}
CSS selects each element individually:
.qui-checkbox__control {
--control-size: var(--sizing-70);
&[data-size="md"] { --control-size: var(--sizing-80); }
&[data-size="lg"] { --control-size: var(--sizing-90); }
}
.qui-checkbox__label {
font: var(--font-static-body-xs-default);
&[data-size="md"] { font: var(--font-static-body-sm-default); }
&[data-size="lg"] { font: var(--font-static-body-lg-default); }
}
Proposed Approach: Container Style Queries
Set a CSS custom property on the root once, children query it:
// checkbox.api.ts - simplified
export function createQdsCheckboxApi(props, normalize) {
const size = props.size || "md"
return {
getRootBindings() {
return normalize.label({
className: checkboxClasses.root,
style: { "--qui-size": size }, // single declaration
})
},
getControlBindings() {
return normalize.element({
className: checkboxClasses.control,
// no data-size needed
})
},
// ... other bindings become static
}
}
CSS uses container style queries:
.qui-checkbox__control {
--control-size: var(--sizing-70);
@container style(--qui-size: md) {
--control-size: var(--sizing-80);
}
@container style(--qui-size: lg) {
--control-size: var(--sizing-90);
}
}
.qui-checkbox__label {
font: var(--font-static-body-xs-default);
@container style(--qui-size: md) {
font: var(--font-static-body-sm-default);
}
@container style(--qui-size: lg) {
font: var(--font-static-body-lg-default);
}
}
Tradeoff Comparison
| Aspect |
Data Attributes (Current) |
Container Style Queries |
| API complexity |
Bindings recreated on prop change |
Static bindings, root sets --size once |
| DOM verbosity |
data-size on every child element |
Single custom property on root |
| DevTools debugging |
Easy - visible in Elements panel |
Harder - requires Computed Styles panel |
| Browser support |
Universal |
Chrome 111+, Safari 18+, Firefox 128+ |
| CSS complexity |
Simple attribute selectors |
@container style() nesting |
| Performance |
More DOM attributes, more binding calls |
Fewer DOM mutations |
| Specificity |
Attribute selectors ([data-size]) |
Container queries (no specificity impact) |
Browser Support Details
Container style queries for custom properties:
- Chrome/Edge: 111+ (March 2023)
- Safari: 18+ (September 2024)
- Firefox: tbd
Note: Style queries for regular CSS properties (not custom properties) are not yet supported in any browser.
Considerations
Advantages
- Single source of truth - Size declared once on root
- Simpler API - Child bindings become constant/memoizable
- Reduced DOM churn - Fewer attributes to update on prop changes
- No specificity wars - Container queries don't add selector specificity
Disadvantages
- Browser support - Not available in older browsers
- Debugging friction - Cannot inspect active size from DOM tree alone
- CSS verbosity - More
@container blocks vs inline attribute selectors
- Learning curve - Less familiar pattern for contributors
Technical Requirements
- May need
@property registration for reliable value matching:
@property --qui-size {
syntax: "<custom-ident>";
inherits: true;
initial-value: sm;
}
Recommendation
The decision hinges on:
- Target browser support - If supporting older browsers, this is a non-starter without a fallback strategy
- Developer experience priority - Data attributes win for debuggability
- Performance priority - Container queries win for reduced DOM mutations
A hybrid approach is possible: use container style queries internally while still exposing data-size on the root for debugging visibility.
Container Style Queries: API Simplification Analysis
Current Approach: Data Attributes
The current API pattern requires
data-size(and similar props) on every child element:CSS selects each element individually:
Proposed Approach: Container Style Queries
Set a CSS custom property on the root once, children query it:
CSS uses container style queries:
Tradeoff Comparison
--sizeoncedata-sizeon every child element@container style()nesting[data-size])Browser Support Details
Container style queries for custom properties:
Note: Style queries for regular CSS properties (not custom properties) are not yet supported in any browser.
Considerations
Advantages
Disadvantages
@containerblocks vs inline attribute selectorsTechnical Requirements
@propertyregistration for reliable value matching:Recommendation
The decision hinges on:
A hybrid approach is possible: use container style queries internally while still exposing
data-sizeon the root for debugging visibility.