TLDR
Dialtone now uses CSS Cascade Layers (@layer) to organize all styles into a predictable hierarchy. This improves specificity control, makes overrides more predictable, and eliminates the need for complex selector specificity hacks.
For Consumers: No breaking changes. Styles work exactly as before, just more reliably.
For Contributors: All new styles must be wrapped in the appropriate @layer block. See the CSS Cascade Layers Guide for details.
What Changed
All Dialtone CSS is now organized into four cascade layers:
@layer dialtone.reset, dialtone.base, dialtone.components, dialtone.utilities;
Layer Hierarchy
dialtone.reset- CSS resets (normalize.css, typography resets)dialtone.base- Design tokens, fonts, themes, global stylesdialtone.components- Component styles (buttons, inputs, modals, etc.)dialtone.utilities- Utility classes (spacing, colors, layout)
Why This Matters
Before: Specificity Wars
Previously, ensuring utilities could override components required:
- Adding
!importantto every utility - Carefully managing selector specificity
- Loading CSS files in the correct order
- Hoping third-party CSS didn't break things
After: Predictable Cascade
With cascade layers:
- Utilities always override components. Layer order guarantees it.
- No specificity hacks needed. Layer priority beats specificity.
- Clear organization. Every style has a clear home.
- Third-party CSS control. Can layer external CSS between Dialtone layers.
How Layers Work
Normal Cascade (Highest to Lowest Priority)
- Unlayered styles (highest)
dialtone.utilitiesdialtone.componentsdialtone.basedialtone.reset(lowest)
With !important (Order Reverses!)
dialtone.reset !important(highest)dialtone.base !importantdialtone.components !importantdialtone.utilities !important- Unlayered
!important(lowest)
Why utilities use
!important: Dialtone utilities are in the last layer with!important, giving them the highest effective priority for overriding component styles while staying organized.
For Consumers
No Breaking Changes
Your existing code continues to work. Utility classes override components just as before, but now with guaranteed layer ordering.
Writing Overrides
If you need to override Dialtone styles, create your own layer after utilities:
@layer dialtone.reset, dialtone.base, dialtone.components, dialtone.utilities, app;
@layer app {
.my-custom-styles {
/* Your overrides here */
}
}
See Using CSS Layers with Dialtone for detailed guidance.
Third-Party CSS
If you're using third-party CSS that conflicts with Dialtone, wrap it in a layer:
@layer dialtone.reset, dialtone.base, dialtone.components, third-party, dialtone.utilities;
@layer third-party {
@import 'some-library/styles.css';
}
This ensures Dialtone utilities can still override third-party styles.
For Contributors
All Styles Must Be Layered
When adding new styles, wrap them in the appropriate layer:
Components:
@layer dialtone.components {
.d-my-component {
/* styles */
}
}
Utilities:
@layer dialtone.utilities {
.d-my-util { property: value !important; }
}
Cross-Layer Mixins
To share styles between layers, extract parametric mixins outside @layer blocks:
// Outside @layer for cross-file access
._my-mixin() {
display: flex;
align-items: center;
}
@layer dialtone.components {
.d-component { ._my-mixin(); }
}
Validation
The build pipeline now validates that all Dialtone classes are properly layered. Unlayered classes will fail CI.
See the CSS Layers Contributor Guide for complete documentation.
Examples
Utility Classes Override Components
<!-- Component default: blue background -->
<button class="d-btn d-bgc-critical">
<!-- Utility wins: red background -->
</button>
Responsive Utilities
Responsive utilities are now in the same layer as base utilities, ensuring consistent behavior:
<div class="d-d-none lg:d-d-block">
<!-- Hidden by default, visible on large screens -->
</div>
App Overrides
@layer app.overrides {
.d-btn--custom {
border-radius: 999px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
}
Browser Support
CSS Cascade Layers are supported in all modern browsers:
- Chrome 99+
- Firefox 97+
- Safari 15.4+
- Edge 99+
For older browsers, styles still apply (unlayered), maintaining visual consistency with potentially different cascade behavior.
Learn More
Migration Path
No migration required for consumers. Existing Dialtone usage continues to work identically.
If you have custom CSS that conflicts with Dialtone, consider wrapping it in a layer as described in Using CSS Layers with Dialtone.