CUBE CSS is a deliberate synthesis of ideas from BEM, ITCSS, and utility-first CSS, unified by a single philosophical shift: stop fighting the Cascade's inheritance model, and start designing with it.
1The Philosophy: Embrace The Cascade
Strict component isolation (as in some CSS-in-JS setups) treats inheritance as a bug to eliminate — every property is set explicitly, nothing is allowed to trickle down from an ancestor. CUBE CSS argues this throws away one of CSS's most powerful features: a well-designed inheritance chain (typography, color) lets you set a handful of rules at the root and have them apply sensibly everywhere, adjusting only where genuinely needed.
This philosophy shows up concretely in the Composition layer: instead of every component defining its own spacing logic, generic, inheritance-friendly patterns like .stack handle it once, and every component that opts into .stack benefits without redefining anything.
2The Four Layers, In Practice
Composition — generic, cosmetics-free layout primitives (.stack, .cluster, .grid-auto) reusable across the entire project. Utility — familiar single-purpose helper classes (.u-text-center), same concept as any utility layer. Block — CUBE's explicit recommendation is to use BEM here; it doesn't reinvent component naming. Exception — context-specific variations expressed through data or ARIA attribute selectors instead of extra classes, keeping state styling tied to the same source of truth JavaScript and accessibility already use.
The practical effect is a stylesheet where layout (Composition), one-off tweaks (Utility), component identity (Block), and state (Exception) are all clearly separated by *purpose*, not just by naming convention.
3Exceptions: A Genuinely Modern Take On State
SMACSS's .is-active and CUBE's [data-state='active'] look superficially similar, but the attribute-based approach has one meaningful advantage: it can directly reuse attributes that already carry meaning for accessibility and behavior, like aria-expanded or aria-hidden, instead of maintaining a parallel class that has to be kept manually in sync.
This reduces an entire category of bug: a state class silently drifting out of sync with its ARIA counterpart. When the CSS styles off the same attribute the accessibility tree reads, drift becomes structurally impossible — there's only one flag to update, not two.
4Step-by-Step Breakdown
Working With The Cascade, Not Against It. Older methodologies often treat the Cascade as a threat to be neutralized through low specificity and strict scoping. CUBE CSS, by Andy Bell, takes the opposite stance: the Cascade and inheritance are CSS's actual superpowers, and a good architecture should lean into them, not fight them.
Composition: Context-Agnostic Layout Primitives. Composition styles define layout without cosmetics — a .stack that adds consistent vertical spacing between children, a .cluster that wraps and gaps horizontal items. They're deliberately generic, reusable across completely different-looking components, similar to ITCSS's Objects layer.
Composition Layer. What is a CUBE CSS Composition rule primarily responsible for?
- →Setting a component's colors and typography
- →Generic, reusable layout behavior like spacing between children
- →Toggled interactive states
Utility And Block: The Familiar Pieces. Utilities are the same single-purpose helper classes seen in Scalable CSS (.text-center, .mt-4). Blocks are CUBE's component layer, using BEM-style naming directly — CUBE CSS doesn't reinvent component naming, it explicitly recommends pairing itself with BEM for this layer.
Utility And Block Layers. Which existing naming convention does CUBE CSS explicitly recommend using for its Block layer?
- →SMACSS's Module convention
- →BEM's Block, Element, Modifier convention
- →None — it invents its own entirely new naming scheme
Exception: Named, Deliberate State Changes. Exceptions are CUBE's answer to context-specific variation, using data attributes rather than extra classes: [data-state='disabled'] or [aria-expanded='true']. This deliberately overlaps with existing ARIA attributes where possible, so a single source of truth drives both accessibility and styling instead of two systems that can drift apart.
Exceptions Via Attributes. Why does CUBE CSS favor styling based on data/ARIA attributes for exceptions, rather than adding more classes?
- →Attribute selectors are always faster to match than classes
- →It reuses the same attribute JavaScript and assistive technology already rely on, avoiding two separate, driftable systems
- →Classes cannot express conditional state at all
CUBE CSS Model Complete. You now understand CUBE CSS as a deliberate synthesis: Composition borrows ITCSS's Objects idea, Block borrows BEM directly, Utility mirrors Scalable CSS's utility layer, and Exception offers a genuinely modern take on state by tying styling to the same attributes JavaScript and accessibility tooling already use.
Write A Single-Purpose Utility Class. CUBE CSS's Utility layer is made of small, single-purpose classes like this one.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Exception Styling Off ARIA Attributes Structurally Prevents State Drift
Because CUBE CSS Exceptions can select directly on aria-* attributes, the same value assistive technology reads is also what drives the visual state, eliminating an entire class of bugs where a class-based visual state disagrees with the announced accessibility state.
2Composition Layer Spacing Should Never Override User Zoom Or Text-Size Preferences
Because Composition patterns like .stack are applied broadly, any fixed pixel values used there have an outsized impact — prefer relative units (rem, em) so user font-size preferences continue to scale spacing proportionally.
SEO Implications
- 1
Cascade-Friendly Composition Reduces Total Declared CSS Rules
Because inheritance and generic Composition patterns handle spacing and layout across many components at once, CUBE CSS codebases often ship fewer total CSS declarations than fully isolated, per-component alternatives.
- 2
Attribute-Driven Exceptions Avoid Extra Class-Toggling JavaScript Overhead
Styling off attributes JavaScript already sets for behavior (rather than toggling a separate class) means one less DOM write per state change, a minor but real reduction in main-thread work on interaction-heavy pages.
Best Practices
Build Your Composition Layer Before Your Component Layer
Establishing generic layout primitives like .stack and .cluster first means every component you build afterward can lean on the cascade for spacing instead of reinventing it per component.
Prefer Attribute Selectors For Any State That Already Has An ARIA Equivalent
If a state already needs an aria-* attribute for accessibility, style off that same attribute instead of adding a parallel class — one fewer synchronization point to maintain correctly.
Frequent Bugs
A component's expanded/collapsed visual state occasionally disagrees with what screen readers announce.
The visual state was driven by a separate CSS class instead of the aria-expanded attribute; switch to an attribute selector so both stay perfectly in sync.
Every new component redefines its own internal spacing, leading to inconsistent gaps across the site.
Introduce a shared Composition layer (like .stack) that components opt into, instead of each one hand-rolling its own margin rules.
Real-World Examples
A Stack-Powered Card List
A list of cards using a shared .stack Composition class for consistent vertical rhythm, with each card itself named using BEM as CUBE recommends.
.stack > * + * { margin-top: var(--space-md); }
<div class="stack">
<div class="card">...</div>
<div class="card">...</div>
</div>