Deep Dive: Compounds
Why Compounds Exist
If primitives are the raw parts, compounds are the predictable bundles. They emerge when teams repeatedly combine the same primitives in the same ways. Instead of asking designers and developers to reinvent the bundle every time, the system codifies the convention.
Compounds give structure to combinations that look obvious in hindsight but are fragile in practice:
- A text input always needs a label for accessibility.
- A table row always assumes a parent table context.
- A card usually pairs heading, body, and actions in a fixed layout.
- A chip bundles a label with an optional dismiss button.
The compound layer is where convention becomes codified. It's where the design system says “these primitives always travel together, and here's how.”
Characteristics of Compounds
- Predictable combinations: The system declares which primitives belong together and how they relate. A TextField always includes a label, input, and optional helper/error text.
- Narrow scope: Compounds aren't meant to anticipate every possible combination — only the blessed ones that the system has validated and documented.
- Stable defaults: Compounds handle spacing, grouping, and visual rhythm once, so teams don't keep tweaking the same details across implementations.
- Consistent behavior: Accessibility rules like label associations, ARIA attributes, and keyboard support are guaranteed, not optional.
- Semantic structure: Compounds often expose “slots” (Header, Body, Footer) that enforce semantic organization while allowing flexible content.
Examples of Compounds
- TextField: Bundles Input, Label, HelperText, and ErrorMessage with proper ARIA associations.
- Card: Bundles Header, Body, and Footer with standardized spacing and optional interactive states.
- Chip/Tag: Bundles Label, optional Icon, and optional DismissButton with semantic variants.
- TableRow: Bundles TableCell primitives with semantics tied to the parent Table context.
- ListItem: Bundles leading content, primary/secondary text, and trailing actions in a consistent layout.
- Avatar: Bundles image, fallback initials, and optional status indicator.
Example: TextField Compound
The TextField is the canonical example of a compound. It bundles an Input primitive with Label, HelperText, and ErrorMessage, while managing all the accessibility associations automatically:
What Makes This a Good Compound
- Auto-generated IDs: Uses
useId()to ensure unique IDs without requiring consumers to manage them. - ARIA associations: Automatically links the input to its label, helper text, and error message via
aria-describedby. - Error announcements: Error messages have
role="alert"so screen readers announce them immediately. - Consistent spacing: The compound manages all internal spacing, ensuring visual rhythm across all instances.
- Size variants: All parts (label, input, helper text) scale together, maintaining proportions.
Example: Card Compound
Cards demonstrate the “slot” pattern, where the compound defines semantic areas (Header, Body, Footer) while allowing flexible content within each slot:
Card Compound Patterns
- Semantic slots:
Card.Header,Card.Body,Card.Footerenforce structure while allowing any content. - Interactive mode: When
interactiveis true, the card becomes keyboard-accessible with proper focus handling. - Selection state: Cards can indicate selection with visual feedback, useful for choice interfaces.
- Variant system: Different visual treatments (elevated, outlined, filled) for different contexts.
Example: Chip Compound
Chips demonstrate how compounds bundle interactive elements with proper accessibility, including dismissible actions and keyboard support:
The Work of the System at the Compound Layer
1. Define Conventions
- Establish what belongs together: label + input, icon + text, header + footer.
- Document approved variations (e.g., TextField can have optional helper text, but never hides the label).
- Specify the relationship between parts (e.g., error replaces helper text, not stacks with it).
2. Encode Blessed Combinations
- Bake spacing, order, and accessibility rules directly into the compound implementation.
- Example: a TextField enforces label placement above the input and links
aria-describedbyto error/helper text automatically. - Consumers can't accidentally break these associations because they're not exposed as options.
3. Allow Controlled Flexibility
- Compounds should allow flexibility through slots and optional props, but within defined boundaries.
- The key is to prevent unbounded prop creep — flexibility should follow the system's conventions, not bypass them.
- Example: A Card allows any content in its Body slot, but the spacing between Header and Body is fixed.
Pitfalls of Compounds
- Prop Explosion
- When compounds try to solve every variation, they mutate into composers. If you find yourself adding a boolean prop every sprint, you've crossed layers.
- Guardrail: Compounds support only the blessed variations. Complex orchestration belongs in composers.
- Breaking Accessibility by Accident
- A text field without a proper
<label>oraria-describedbyis a broken compound. Making accessibility optional defeats the purpose. - Guardrail: Accessibility associations must be baked in and impossible to disable.
- A text field without a proper
- Over-abstracting Visuals
- Avoid infinite layout variations. A Card that allows every combination of header/body/footer permutations becomes ungovernable.
- Guardrail: Fix the expected structure, allow slots for content variation.
- Duplication of Logic
- Don't reimplement primitive behaviors inside compounds. Don't reinvent Checkbox logic inside a “FilterRow” compound.
- Guardrail: Compounds compose primitives; they don't replace them.
- Leaking Internal Structure
- Exposing too many internal details (like specific CSS classes or DOM structure) makes compounds fragile to change.
- Guardrail: Expose semantic props and slots, not implementation details.
Compounds vs. Primitives vs. Composers
Understanding where compounds fit in the component hierarchy is crucial for proper system design:
- Primitives are single-purpose, atomic components (Button, Input, Icon). They have no opinion about how they're used together.
- Compounds are blessed combinations of primitives with baked-in conventions (TextField = Input + Label + Error). They encode “these things always go together.”
- Composers orchestrate state, focus, and interaction across multiple children (Modal, FormField, Toolbar). They manage complex behavior, not just structure.
A good rule of thumb: if you're managing state transitions, focus trapping, or multi-step interactions, you're in composer territory. If you're bundling parts that always appear together with consistent styling, you're in compound territory.
Why Compounds Are Critical
- Reduce cognitive load: Designers and engineers don't have to reassemble primitives every time or remember accessibility requirements.
- Prevent inconsistent conventions: Spacing, order, and accessibility are centralized and enforced.
- Free the system team: By pre-blessing common bundles, the system reduces requests for one-off variations.
- Create legibility: “TextField” communicates intent better than “Input + Label + Error stacked manually.”
- Enable safe updates: When the compound implementation improves, all consumers benefit automatically.
Summary
Compounds are the codified bundles of your design system. They represent the layer where the system says “these primitives always travel together, and here's the blessed way to combine them.”
- Examples: TextField, Card, Chip, TableRow, ListItem, Avatar
- Work of the system: Define conventions, encode blessed combinations, bake in accessibility
- Pitfalls: Prop explosion, accessibility drift, ungoverned permutations, logic duplication
- Key benefit: Impossible to forget accessibility or spacing conventions
If primitives are the boring DNA, compounds are the grammar rules — they make sure the words can be combined into predictable, legible sentences.
Next Steps
Compounds work well on their own, but they really shine when orchestrated by composers (which add state management and complex interactions) or combined into assemblies (which create complete product flows).
Return to the Component Complexity overview to see how all these layers work together.