⚠️ Design system CSS not found. Check the path in cms/docs.config.js → designSystemPath, then re-run npm run docgen.

About Components

Component specification and build rules

Website / About Components
Download .md file
Open .md in new tab
On this page
  • How this system is organized
  • Naming convention
  • Token rule
  • Slot layout rule
  • File rule
  • Accessibility rule
  • Component status
  • Dark mode rule
  • How to add a new component
  • The website channel

This document defines the rules and conventions for building components in the By Default design system. All contributors must follow these patterns to keep the system consistent and predictable.


How this system is organized

Components are split across four layers so the design system can be lifted into other products without dragging the BrandOS docs site along with it:

  • foundation: tokens, layout primitives, utilities. Lives in assets/css/design-system.css. Ships with every product.
  • core: reusable components (button, card, dropdown, …) plus brand identity docs (brand-*.md). Lives in design-system.css. Ships with every product.
  • docs-site: components that only power this BrandOS docs site (asset-card, book-cover, dont-card). Lives in assets/css/docs-site.css. Does not ship.
  • app: BrandOS-specific tools, integrations, and project content (calculators, world clock, ad preview, project case studies). Does not ship.

Every cms/*.md doc declares its layer in frontmatter:

---
title: "Button"
section: "Design System"
layer: "core"
---

The layer field is required. The doc generator validates it on every build. See CLAUDE.md §17 (Layer Discipline) for the seven rules that govern this split. The one exemption: files starting with _ (e.g. _defaults.md) are generator config stubs, excluded from the build and from the layer requirement.


Naming convention

  • Base class: (e.g. , , )
  • Variation: data-* attributes (e.g. data-color="success", data-variant="outline", data-type="info")
  • State classes: — the shared vocabulary is , , , , , , . marks selection (a chosen option in a group), distinct from (the current or engaged item). Components may add scoped states (e.g. , , ) documented in their own doc — but reach for the shared vocabulary first
  • Utility overrides: use !important only on utility classes (e.g. )
  • JS hooks: use data-* attributes, never CSS class names

CUBE pattern: Components use data-* attributes for variation and .is-* classes for transient state only. Role classes (, ) compose with the base via token overrides. See individual component docs for details.

Three axes of variation

All variation across components follows three axes. Each resolves to the same --status-* token layer underneath.

Axis Attribute What it means Values Components
Colour data-color Visual colour, no inherent message meaning success/green, warning/yellow, danger/red, info/blue, accent/purple button, badge, tag
Type data-type Semantic message meaning, the content IS this type success, warning, danger, info, accent callout, toast
Variant data-variant Visual shape/hierarchy outline, faded, outline-faded, transparent, text (button); thumb (segmented control) button, segmented control

The distinction: Display components (badge, tag, button) use data-color. The colour is visual emphasis. Feedback components (callout, toast) use data-type. The type describes what the message means. Both use the same value names (success, warning, danger, info, accent) and resolve to the same --status-* tokens. Rebranding is a token change, not an attribute change.

One recorded exception: the progress ring's status variants are BEM modifiers (), not data-color — matching progress.progress-bar--success on the same doc page. Consistency with its own sibling beats consistency with the axis table; do not copy the pattern to new components.


Token rule

Every visual value in component CSS must reference a CSS custom property defined in :root. Never hardcode hex values, pixel values (except structural ones like border-radius: 50%), or raw font values.

Component tokens follow this pattern:

--component-property: var(--semantic-token);

Example (button component):

--button-color: var(--text-primary);
--button-bg: var(--button-color);
--button-border: var(--button-color);
--button-text-color: var(--text-inverted);

Slot layout rule

Components that lay content out in slots — Site Header and Bar today — separate an outer element from an inner one and push the trailing slot over with an auto margin:

.component        { /* sticky, background, border — full bleed */ }
.component-inner  { display: flex; align-items: center; }
.component-end    { margin-inline-start: auto; }

Never justify-content: space-between for this. With a single child it resolves to flex-start, so a bar carrying only a trailing slot renders at the leading edge — the exact opposite of what the markup says. The auto margin gets every combination right, and it means no slot is ever required to exist. If you find yourself emitting an empty <div> so the layout keeps its shape, the layout is wrong, not the markup.

The outer/inner split exists so a full-bleed background and border can run edge to edge while the contents line up with a content column.


File rule

What Where
Component CSS assets/css/design-system.css under a numbered section heading
Component JS (if needed) assets/js/component-name.js
Documentation source cms/component-name.md
Generated docs page website/component-name.html

Section headings in design-system.css follow the format:

/* ------ 16. BADGE ------ */

Accessibility rule

Every interactive component must include:

Requirement Details
ARIA roles Correct role attribute (e.g. role="tablist", role="tab", role="tabpanel")
ARIA attributes aria-selected, aria-controls, aria-labelledby, aria-current, aria-label as needed
Keyboard support Tab to focus, Enter/Space to activate, Escape to dismiss (where applicable), Arrow keys for navigation (tabs, menus)
Focus indicator box-shadow: 0 0 0 2px color-mix(in srgb, var(--input-focus), transparent 75%)
Screen reader text Use aria-label or visually hidden text for icon-only actions

Component status

Component CSS class Needs JS Docs page
Accordion , , Yes (accordion.js) accordion.md
Button No button.md
Form elements , , , , No form.md
Callout No callout.md
Disclosure details/summary No disclosure.md
Badge No badge.md
Card No card.md
Copy Button Yes (copy-button.js) copy-button.md
Breadcrumb No breadcrumb.md
Tabs , Yes (tabs.js) tabs.md
Progress , No progress.md
Tooltip [data-tooltip] No tooltip.md
Toast Yes (toast.js) toast.md
Code / Pre / Kbd code, pre, kbd No code.md
Nav , No nav.md
Site Header , No site-header.md
Footer , , , , No footer.md
Avatar No avatar.md
Bar , , , , , , Yes — bar.js (overflow) bar.md
Page Header , , , , , No page-header.md
Section Header , , No section-header.md
Login , , , , , , , , No login.md
Cell Input , , , Yes — cell-input.js (cell build + paint) cell-input.md
App Navigation , , , , No app-nav.md
Mark / Abbr / Figure mark, abbr, figure No mark.md
Skeleton , , No skeleton.md

Asset Card, Book Cover and Don't Card are documented separately as docs-site components. They only exist to power this BrandOS docs site and are not part of the portable design system. See Layer Discipline and CLAUDE.md §17.


Dark mode rule

Components must not contain dark-mode-specific CSS. They rely entirely on semantic token overrides in [data-theme="dark"] and @media (prefers-color-scheme: dark).

The only exception is when a component uses brand-palette tokens directly (avoid this). If unavoidable, add the override to both the [data-theme="dark"] block and the @media fallback block.

Current exceptions:

  • mark element: uses via token, requires dark mode override
  • Scrollbar: uses neutral scale tokens directly, requires dark mode override

How to add a new component

  1. Define tokens in :root (in design-system.css, after existing component tokens):

    /* -- Component tokens -- */
    --component-property: var(--semantic-token);
    
  2. Add dark mode overrides if the component uses non-semantic tokens. Add to both [data-theme="dark"] and @media (prefers-color-scheme: dark) blocks.

  3. Write CSS in design-system.css under a new numbered section:

    /* ------ N. COMPONENT NAME ------ */
    
  4. Write JS (only if needed) in assets/js/component-name.js. Follow the existing pattern: IIFE, named functions, version logged to console.

  5. Write documentation in cms/component-name.md following the standard frontmatter and content structure.

  6. Update this spec file: add the component to the status table above.

  7. Regenerate docs:

    cd cms/generator && npm run docgen
    

The website channel

The website is also a channel — one of the places the brand meets an audience — and the one where code, not export discipline, keeps it on-brand. Tokens carry the palette, type, spacing, and motion. If a page needs a value the tokens don't provide, the system is incomplete. Fix the tokens, not the page. The studio team owns this channel.

Identity here

  • Logo: the primary centred lockup holds the top nav; the avatar covers favicons and compact contexts. Variants, files, and clearspace: 4.1 Logo.
  • Colour and type: never chosen per page. Semantic tokens only: 4.2 Colour, 4.3 Typography. Embedded graphics and imagery follow the same palette.
  • Motion: transitions and animation run on the motion tokens (Motion); no hardcoded durations or easings.
  • Icons: brand icons only, prepared to system rules: 4.4 Iconography.

Specs

Asset Size Format Max weight Notes
Open Graph image 1200×630 (1.91:1) JPG/PNG 1 MB unique per page, absolute URL in the meta tag
X card image 1200×628 JPG/PNG 1 MB summary_large_image; usually the OG image reused
Favicon SVG source + 32×32 PNG + ICO SVG/PNG/ICO – avatar logo variant; lives in assets/icons/
Apple touch icon 180×180 PNG – assets/icons/apple-touch-icon.png
Page imagery ratio set at render – – + img-1x1 / img-3x2 / img-4x3 / img-16x9 / img-21x9, crop with the image component, not in the export

Specs verified against platform documentation: 2026-07-05.

The full meta-tag set is already wired into templates/page-template.html; the per-tag reference lives in SEO best practices.

Templates & assets

  • Page template: every new page starts from templates/page-template.html; the SEO head, layout hierarchy, and script order are already correct there.
  • Default OG image: assets/images/og/og-default.jpg, the fallback when a page has no bespoke share image. Bespoke OG images live alongside it in assets/images/og/.
  • Draft imagery: the placeholder service returns an SVG at any size: https://bydefault.design/image/{width}x{height}. Use it while layouts settle; replace before ship.

Checklist

  • Unique title (50–60 characters) and meta description (150–160)
  • OG image present at 1200×630, absolute URL, unique to the page
  • Favicon set referenced in the head
  • Zero hardcoded values: colour, type, spacing, and motion all from tokens
  • Imagery cropped by ratio classes, alt text on everything
  • Keyboard navigation works; WCAG 2.1 AA holds; Lighthouse 90+
On this page
  • How this system is organized
  • Naming convention
  • Token rule
  • Slot layout rule
  • File rule
  • Accessibility rule
  • Component status
  • Dark mode rule
  • How to add a new component
  • The website channel
Previous Image
Next Button

Was this page helpful?

We use this feedback to improve our documentation.

Thanks for your feedback

Send feedback

© 2026 By Default