-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Description
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
@containerblocks vs inline attribute selectors - Learning curve - Less familiar pattern for contributors
Technical Requirements
- May need
@propertyregistration 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.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels