# BrandOS Design System > Complete design token, layout, component, and CSS reference for By Default Studio's BrandOS platform. Use this as your authoritative guide for generating on-brand HTML and CSS. ## Read This First Before generating any code: 1. **Use the layout hierarchy** — every page section follows `section > .padding-global > .container-* > .block`. Skipping levels is a bug. 2. **Use semantic tokens, never primitives** — `var(--text-primary)`, not `var(--neutral-800)`; `var(--background-faded)`, not `var(--off-white)`. 3. **Use existing components and utilities first** — `.button`, `.card`, `.callout`, `.badge`, `.gap-*`, `.top-*`, `.bottom-*`, `.padding-*`, `.border-*`. Only write new CSS if the design system can't express the requirement. 4. **Light mode is the default.** Dark mode is opt-in via `data-theme="dark"` on a container — never component-level. 5. **Follow the standards** — HTML rules live in the Layout System and Component Conventions sections below. CSS rules live in the CSS Conventions section. JS rules live in the JavaScript Conventions section. Read them before writing code, not after. ## HTML Skeleton Every page section uses this exact structure. Copy-paste, then fill in the block. ```html

Heading

Body text

``` - `section` owns macro vertical spacing via `.top-*` / `.bottom-*` - `.padding-global` owns horizontal padding — never put padding on the section or container - `.container-*` owns width and centring — never put padding on it - `.block` owns internal spacing via `gap-*` — never put margins on its children ## Semantic HTML - ` ``` --- ## Grid Grids create **column-based layouts**. They are not spacing utilities. ### Base grid * Two equal columns * Default gap applied
Column 2
```html
Item
Item
``` ### Column modifiers
2
3
1
2
3
4
| Class | Columns | | ----------- | ------------- | | `.cols-3` | Three columns | | `.cols-4` | Four columns | ```html
Item 1
Item 2
Item 3
``` ### Item width | Class | Effect | | ------------- | --------------------- | | `.fit-content` | Item sizes to content | ```html
Flexible column
Fixed
``` ### Rules * Use grids for layout, not spacing * Use `gap-*` to adjust spacing * Combine column + gap modifiers as needed --- ## CSS Conventions ## Overview This guide ensures: - Readable, modular, and maintainable CSS - Clear organization with related styles grouped together - Easy navigation and long-term maintenance - Consistent commenting hierarchy - Logical file structure --- ## Organisation Principles ### Group Related Styles Together **Always keep related styles adjacent:** - All font sizes together - All spacing utilities together - All color tokens of the same type together - All modifiers for the same component together - All hover/active states immediately after their base class **Example - Typography Grouping:** ```css /* -- Headings -- */ h1 { font-size: var(--font-7xl); line-height: var(--line-height-s); font-weight: var(--font-weight-bold); } h2 { font-size: var(--font-6xl); line-height: var(--line-height-s); font-weight: var(--font-weight-bold); } /* All headings grouped together */ ``` **Example - Component Grouping:** ```css /* -- Button -- */ .button, button { padding: var(--space-s) var(--space-m); background: var(--background-primary); border: var(--border-s) solid var(--border-primary); } .button:hover { background: var(--background-secondary); } .button.is-outline { background: transparent; } /* All button-related styles together */ ``` --- ## Token Organisation Use CSS custom properties (variables) for reusable values. Organize tokens logically: **Example - Token Grouping:** ```css :root { /* -- Unit tokens -- */ --unit-s: 0.5rem; /* 8px */ --unit-m: 1rem; /* 16px */ /* -- Spacing scale -- */ --space-s: var(--unit-s); --space-m: var(--unit-m); /* -- Typography tokens -- */ --font-size-base: 1rem; --font-weight-bold: 700; } ``` **Principles:** - Group tokens by purpose (units, spacing, typography, colors) - Use subsections to organize within `:root` - Reference tokens when possible (e.g., `--space-m: var(--unit-m)`) - Keep related tokens adjacent --- ## Component Organization When writing component styles, follow this order: 1. **Base class first** 2. **States immediately after base** (hover, active, focus) 3. **Modifiers grouped together** (is-*, has-*) 4. **Sub-components after modifiers** **Example:** ```css /* -- Card -- */ .card { padding: var(--space-m); background: var(--background-primary); } .card:hover { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .card.is-compact { padding: var(--space-s); } /* All card-related styles together */ ``` --- ## Property Ordering for Readability Group related properties together within a selector: **Suggested order:** 1. Layout (display, position, grid, flex) 2. Box model (width, height, margin, padding) 3. Visual (background, border, box-shadow) 4. Typography (font, line-height, text-align) 5. Other (opacity, cursor, transition) **Example:** ```css .button { /* Layout */ display: inline-flex; /* Box model */ padding: var(--space-m) var(--space-l); /* Visual */ background: var(--background-primary); border: var(--border-s) solid var(--border-primary); /* Typography */ font-size: var(--font-m); font-weight: var(--font-weight-semi-bold); } ``` **Note**: The comments above are for demonstration only. In actual code, the visual grouping is what matters, not the comments. --- ## Commenting Hierarchy Use **three levels only**. Do not invent new formats. ### 1. Major Sections Used for top‑level structure of the file. **Format** ```css /* ------ Section Title ------ */ ``` **Rules** - Always uppercase - One line only - Blank line before and after - Use to mark major divisions in your CSS **Example** ```css /* ------ BASE STYLES ------ */ body { /* ... */ } ``` --- ### 2. Subsections Used to group related concepts inside a section. **Format** ```css /* -- Subsection name -- */ ``` **Rules** - Title case - One line - Use only when grouping related rules - Blank line before **Example** ```css /* -- Headings -- */ h1 { /* ... */ } h2 { /* ... */ } ``` --- ### 3. Inline / Feature Comments Used to clarify values or mark important behaviour. **Format** ```css /* Short clarification */ ``` **Rules** - Keep factual and brief - Prefer same-line comments - Do not explain obvious CSS **Example** ```css --unit-xs: 0.25rem; /* 4px */ ``` --- ## Responsive Code Organization ### Adding Breakpoints When adding responsive breakpoints, follow these guidelines: 1. **Choose breakpoints that match your design** - Common breakpoints include: - Mobile: `768px` (max-width) - Tablet: `1024px` (max-width) - Desktop: `1440px` (min-width) 2. **Keep breakpoints consistent** - Use the same breakpoints throughout your project 3. **Group responsive code together** - Place all media queries in a dedicated section at the end of your file **Example:** ```css /* ------ RESPONSIVE ------ */ /* Mobile */ @media (max-width: 768px) { :root { --font-size-base: 0.875rem; } .card { padding: var(--space-s); } } ``` ### Organization When organizing responsive code: 1. **Override tokens in `:root`** - Don't override individual classes when possible 2. **Group by token type** - Fonts together, spacing together 3. **Use subsections** - Clearly mark what's being overridden 4. **Always at the end** - Responsive code should come last in the file 5. **Comment breakpoints** - Label each breakpoint clearly (Mobile, Tablet, Desktop) **Example:** ```css /* ------ RESPONSIVE ------ */ /* Mobile */ @media (max-width: 768px) { :root { /* -- Mobile font sizes -- */ --font-size-base: 0.875rem; } .card { padding: var(--space-s); } } ``` ### Best Practices - Override tokens when possible, not individual classes - Scale down proportionally - Keep mobile values grouped by type - Use clear subsection comments --- ## Rules to Follow ### Organization - ✅ Group related styles together - ✅ Keep all font-related styles in one place - ✅ Keep all spacing utilities together - ✅ Keep all color tokens of the same type together - ✅ Place responsive code at the end - ✅ Keep related properties together within selectors ### Commenting - ✅ Use three-level commenting hierarchy - ✅ Do not mix comment styles - ✅ Do not skip levels - ✅ Do not add decorative separators - ✅ Prefer fewer comments over more - ✅ Comments should help *find* things, not explain theory ### Code Quality - ✅ Use design system tokens, over hardcode values - ✅ Use semantic tokens over primitive tokens when possible - ✅ Keep related properties together (e.g., all typography properties) - ✅ Place states immediately after base classes - ✅ Group modifiers together ### What to Avoid - ❌ Scattering related styles across the file - ❌ Mixing font sizes with spacing utilities - ❌ Placing responsive code in the middle of sections - ❌ Hardcoding values that should use tokens - ❌ Using primitive tokens directly in layouts when semantic tokens exist - ❌ Separating component states and modifiers from their base class --- ## Quick Reference | Task | Rule | |------|------| | Add new token | Group with related tokens (colors with colors, spacing with spacing) | | Add new utility class | Group with other utilities of the same type | | Add new component | Group all related styles together (base, states, modifiers, sub-components) | | Add responsive override | Place at end of file, override tokens when possible | | Add modifier to component | Place immediately after component base class | | Add state to component | Place immediately after base class, before modifiers | --- ## JavaScript Conventions ## Overview This structure ensures readable, modular, and maintainable JavaScript with clear organization. --- ## File Structure Every JavaScript file should follow this organizational pattern: ```js /** * Script Purpose: [Brief description] * Author: [Your Name] * Created: [Date] * Version: [Latest Version] * Last Updated: [Date] */ console.log("Script Name v1.0.0"); // //------- Utility Functions -------// // // Function Name function utilityFunction() { // ... implementation } // //------- Main Functions -------// // // Initialize Component function initComponent() { // ... implementation } // //------- Event Listeners -------// // // Setup Event Listeners function setupEventListeners() { // ... implementation } // //------- Initialize -------// // document.addEventListener("DOMContentLoaded", () => { initComponent(); setupEventListeners(); }); ``` --- ## Section Organization 1. **File Header** - Comment block with purpose, author, dates, version 2. **Console Log** - ONE `console.log()` at the top to confirm script loaded 3. **Utility Functions** - Reusable helper functions grouped together 4. **Main Functions** - Primary functionality grouped together 5. **Event Listeners** - Event handling functions grouped together 6. **Initialize** - All setup calls within `DOMContentLoaded` listener at the end --- ## Section Dividers Use clear headers when grouping similar functions: ```js // //------- Section Name -------// // ``` For standalone functions, use simple comments: ```js // Function Name function doSomething() { // ... implementation } ``` --- ## Naming Conventions | Type | Example | Rule | |------|---------|------| | Functions | `initSlider()`, `handleClick()` | Use **lower camelCase**, short & descriptive | | Variables | `mainElement`, `scrollPos` | Contextual and readable | | File Names | `slider.js`, `utils.js` | Use lowercase kebab-case | | Constants | `MAX_ITEMS`, `API_URL` | Use UPPER_SNAKE_CASE | --- ## Initialization Pattern 1. Define **utility functions** first 2. Define **main functions** grouped by section 3. Define **event listeners** separately 4. Call everything within a single `DOMContentLoaded` listener **at the end** --- ## Console Logging **Each script should include ONE `console.log()` at the top** to confirm the script has loaded. Don't add excessive console logs throughout your code. --- ## Rules to Follow ### Organization - ✅ Group related functions together - ✅ Use section dividers for major groupings - ✅ Place initialization at the end - ✅ Keep utility functions separate from main functions ### Commenting - ✅ Include file header with metadata - ✅ Comment each function with its purpose - ✅ Use section dividers for major groupings ### Code Quality - ✅ Use descriptive function names - ✅ Follow naming conventions consistently - ✅ Place initialization in `DOMContentLoaded` listener ### What to Avoid - ❌ Scattering related functions across the file - ❌ Mixing utility functions with main functions - ❌ Multiple initialization points - ❌ Excessive console logging --- ## Design Tokens These are the actual CSS custom property values from `design-system.css`. Always use semantic tokens (not primitives) in production code. ### 1. BRAND TOKENS | Token | Value | Note | | --- | --- | --- | | **Typography** | | | | `--font-primary` | `"Inclusive Sans", sans-serif` | | | `--font-secondary` | `"RecifeText", Georgia, serif` | | | `--font-tertiary` | `"Bugrino", sans-serif` | | | `--font-quaternary` | `"IBM Plex Mono", monospace` | | | **Palette** | | | | `--off-white` | `var(--neutral-50)` | | | `--warm-white` | `#f5ebe3` | | | `--warm-black` | `#221f1c` | | | `--off-black` | `var(--neutral-900)` | | | **Accent colours** | | | | `--red-lighter` | `#FFE8E3` | | | `--red-light` | `#FFD6CD` | | | `--red` | `#D92A27` | | | `--red-dark` | `#750D0B` | | | `--blue-lighter` | `#D5F3FF` | | | `--blue-light` | `#B1E6FC` | | | `--blue` | `#1A54D6` | | | `--blue-dark` | `#152F57` | | | `--yellow-lighter` | `#FFF3B8` | | | `--yellow-light` | `#FFEA83` | | | `--yellow` | `#FFB533` | | | `--yellow-dark` | `#7E5700` | | | `--green-lighter` | `#DBF7CC` | | | `--green-light` | `#B6D6A5` | | | `--green` | `#167255` | | | `--green-dark` | `#094C45` | | | `--purple-lighter` | `#F9E2FF` | | | `--purple-light` | `#F5CDFF` | | | `--purple` | `#AA4FE3` | | | `--purple-dark` | `#600E83` | | | **Base font sizes (rem-based, progressive step scale: +2px body, +4px heading, +8px display)** | | | | `--font-3xs` | `0.625rem` | 10px | | `--font-2xs` | `0.75rem` | 12px | | `--font-xs` | `0.875rem` | 14px | | `--font-s` | `1rem` | 16px | | `--font-m` | `1.125rem` | 18px | | `--font-l` | `1.25rem` | 20px | | `--font-xl` | `1.375rem` | 22px | | `--font-2xl` | `1.5rem` | 24px | | `--font-3xl` | `1.75rem` | 28px | | `--font-4xl` | `2rem` | 32px | | `--font-5xl` | `2.25rem` | 36px | | `--font-6xl` | `2.5rem` | 40px | | `--font-7xl` | `3rem` | 48px | | `--font-8xl` | `3.5rem` | 56px | | `--font-9xl` | `4rem` | 64px | | `--font-10xl` | `4.5rem` | 72px | | **Semantic font sizes** | | | | `--text-body` | `var(--font-m)` | | | **UI font sizes** | | | | `--button-font-size` | `var(--text-body)` | | | `--button-font-size-small` | `var(--font-xs)` | | | **Line height tokens** | | | | `--line-height-xs` | `0.7` | | | `--line-height-s` | `1` | | | `--line-height-m` | `1.3` | | | `--line-height-l` | `1.4` | | | `--line-height-xl` | `1.6` | | | `--line-height-2xl` | `1.8` | | | **Font weight tokens** | | | | `--font-weight-light` | `300` | | | `--font-weight-regular` | `400` | | | `--font-weight-medium` | `500` | | | `--font-weight-semi-bold` | `600` | | | `--font-weight-bold` | `700` | | | `--font-weight-extra-bold` | `800` | | | `--font-weight-black` | `900` | | | **Letter spacing tokens (em-based for proportional scaling)** | | | | `--letter-spacing-s` | `0.03em` | | | `--letter-spacing-m` | `0.06em` | | | `--letter-spacing-l` | `0.12em` | | | `--letter-spacing-xl` | `0.24em` | | ### 2. SYSTEM TOKENS - COLORS | Token | Value | Note | | --- | --- | --- | | **Neutrals** | | | | `--neutral-50` | `#fafafa` | | | `--neutral-100` | `#e5e5e5` | | | `--neutral-150` | `#d4d4d4` | | | `--neutral-200` | `#c4c4c4` | | | `--neutral-300` | `#a3a3a3` | | | `--neutral-400` | `#8a8a8a` | | | `--neutral-500` | `#737373` | | | `--neutral-600` | `#5c5c5c` | | | `--neutral-700` | `#474747` | | | `--neutral-800` | `#333333` | | | `--neutral-900` | `#1f1f1f` | | | `--neutral-950` | `#141414` | | | `--neutral-990` | `#0a0a0a` | | | **Black & White Alpha Tokens** | | | | `--black` | `#000000` | | | `--black-alpha-3` | `#00000008` | | | `--black-alpha-5` | `#0000000d` | | | `--black-alpha-10` | `#0000001a` | | | `--black-alpha-15` | `#00000026` | | | `--black-alpha-20` | `#00000033` | | | `--black-alpha-30` | `#0000004d` | | | `--black-alpha-40` | `#00000066` | | | `--black-alpha-50` | `#00000080` | | | `--black-alpha-60` | `#00000099` | | | `--black-alpha-70` | `#000000b3` | | | `--black-alpha-80` | `#000000cc` | | | `--black-alpha-90` | `#000000e6` | | | `--black-alpha-95` | `#000000f2` | | | `--white` | `#ffffff` | | | `--white-alpha-5` | `#ffffff0d` | | | `--white-alpha-10` | `#ffffff1a` | | | `--white-alpha-15` | `#ffffff26` | | | `--white-alpha-20` | `#ffffff33` | | | `--white-alpha-30` | `#ffffff4d` | | | `--white-alpha-40` | `#ffffff66` | | | `--white-alpha-50` | `#ffffff80` | | | `--white-alpha-60` | `#ffffff99` | | | `--white-alpha-70` | `#ffffffb3` | | | `--white-alpha-80` | `#ffffffcc` | | | `--white-alpha-90` | `#ffffffe6` | | | `--white-alpha-95` | `#fffffff2` | | | `--transparent` | `transparent` | | | **Color-Mix Alpha Scale** | | | | `--alpha-5` | `transparent 95%` | | | `--alpha-10` | `transparent 90%` | | | `--alpha-15` | `transparent 85%` | | | `--alpha-20` | `transparent 80%` | | | `--alpha-25` | `transparent 75%` | | | `--alpha-30` | `transparent 70%` | | | `--alpha-35` | `transparent 65%` | | | `--alpha-40` | `transparent 60%` | | | `--alpha-45` | `transparent 55%` | | | `--alpha-50` | `transparent 50%` | | | `--alpha-55` | `transparent 45%` | | | `--alpha-60` | `transparent 40%` | | | `--alpha-65` | `transparent 35%` | | | `--alpha-70` | `transparent 30%` | | | `--alpha-75` | `transparent 25%` | | | `--alpha-80` | `transparent 20%` | | | `--alpha-85` | `transparent 15%` | | | `--alpha-90` | `transparent 10%` | | | `--alpha-95` | `transparent 5%` | | | **Semantic colours** | | | | `--text-primary` | `var(--off-black)` | | | `--text-secondary` | `var(--neutral-800)` | | | `--text-plain` | `var(--black)` | | | `--text-faded` | `var(--black-alpha-50)` | | | `--text-accent` | `var(--blue)` | | | `--text-link` | `var(--text-accent)` | | | `--text-inverted` | `var(--off-white)` | | | `--text-sidebar` | `var(--text-primary)` | | | `--text-top-nav` | `var(--text-primary)` | | | **Text with Opacity** | | | | `--text-alpha-5` | `color-mix(in srgb, var(----text-primary), var(--alpha-5))` | | | `--text-alpha-10` | `color-mix(in srgb, var(----text-primary), var(--alpha-10))` | | | `--text-alpha-15` | `color-mix(in srgb, var(----text-primary), var(--alpha-15))` | | | `--text-alpha-20` | `color-mix(in srgb, var(----text-primary), var(--alpha-20))` | | | `--text-alpha-30` | `color-mix(in srgb, var(----text-primary), var(--alpha-30))` | | | `--text-alpha-40` | `color-mix(in srgb, var(----text-primary), var(--alpha-40))` | | | `--text-alpha-50` | `color-mix(in srgb, var(----text-primary), var(--alpha-50))` | | | `--background-primary` | `var(--neutral-50)` | | | `--background-secondary` | `var(--neutral-100)` | | | `--background-plain` | `var(--white)` | | | `--background-faded` | `var(--black-alpha-5)` | | | `--background-darker` | `var(--black-alpha-10)` | | | `--background-lighter` | `var(--white-alpha-10)` | | | `--background-modal` | `rgba(0, 0, 0, 0.75)` | | | `--background-sidebar` | `var(--background-primary)` | | | `--background-top-nav` | `var(--background-primary)` | | | **Solid colour backgrounds — for hero blocks, callouts, ad units, etc.** | | | | `--background-accent` | `var(--text-accent)` | | | `--background-black` | `var(--black)` | | | `--background-white` | `var(--white)` | | | `--background-blue` | `var(--blue)` | | | `--background-red` | `var(--red)` | | | `--background-green` | `var(--green)` | | | `--border-primary` | `var(--text-primary)` | | | `--border-secondary` | `var(--neutral-300)` | | | `--border-faded` | `var(--black-alpha-15)` | | | `--button-primary` | `var(--text-primary)` | | | `--button-text` | `var(--off-white)` | | | `--button-secondary` | `var(--black)` | | | `--button-secondary-text` | `var(--off-white)` | | | `--button-faded` | `color-mix(in srgb, var(--button-primary), var(--alpha-15))` | | | **Status colours (from brand book or defaults)** | | | | `--status-info` | `var(--blue)` | | | `--status-info-bg` | `var(--blue-lighter)` | | | `--status-success` | `var(--green)` | | | `--status-success-bg` | `var(--green-lighter)` | | | `--status-warning` | `var(--yellow-darker)` | | | `--status-warning-bg` | `var(--yellow-lighter)` | | | `--status-danger` | `var(--red)` | | | `--status-danger-bg` | `var(--red-lighter)` | | | `--status-accent` | `var(--purple)` | | | `--status-accent-bg` | `var(--purple-lighter)` | | | **Form semantic tokens** | | | | `--input-border` | `var(--border-secondary)` | | | `--input-background` | `var(--black-alpha-5)` | | | `--input-text` | `var(--text-plain)` | | | `--input-placeholder` | `var(--text-faded)` | | | `--input-focus` | `var(--text-accent)` | | | `--input-disabled-bg` | `var(--background-faded)` | | | `--input-disabled-text` | `var(--text-faded)` | | | `--checkbox-background` | `var(--neutral-100)` | | | `--checkbox-selected` | `var(--text-primary)` | | | `--checkbox-border` | `var(--border-faded)` | | | `--checkbox-checkmark` | `var(--off-white)` | | | `--toggle-track` | `var(--black-alpha-3)` | | | `--toggle-knob` | `var(--neutral-500)` | | | `--toggle-selected` | `var(--text-accent)` | | | `--toggle-knob-selected` | `var(--off-white)` | | | **Selection** | | | | `--selection-text` | `var(--background-primary)` | | | `--selection-background` | `var(--text-primary)` | | | **Card tokens** | | | | `--card-background` | `var(--background-primary)` | | | `--card-border` | `var(--border-faded)` | | | `--card-border-hover` | `var(--border-primary)` | | | `--card-padding` | `var(--space-xl)` | | | `--card-radius` | `var(--radius-m)` | | | **Tooltip tokens** | | | | `--tooltip-background` | `var(--warm-black)` | | | `--tooltip-text` | `var(--off-white)` | | | **Toast tokens** | | | | `--toast-background` | `var(--warm-black)` | | | `--toast-text` | `var(--off-white)` | | | **Tab tokens** | | | | `--tab-active-color` | `var(--text-primary)` | | | `--tab-inactive-color` | `var(--text-faded)` | | | `--tab-indicator-color` | `var(--text-primary)` | | | **Progress tokens** | | | | `--progress-track` | `var(--background-darker)` | | | `--progress-fill` | `var(--text-primary)` | | | **Divider tokens** | | | | `--divider-color` | `var(--border-faded)` | | | **Dropdown tokens** | | | | `--dropdown-background` | `var(--background-primary)` | | | `--dropdown-border` | `var(--border-faded)` | | | `--dropdown-item-hover` | `var(--background-faded)` | | | **Tag tokens** | | | | `--tag-background` | `var(--background-darker)` | | | `--tag-border` | `var(--border-faded)` | | | **Dialog tokens** | | | | `--dialog-background` | `var(--background-primary)` | | | `--dialog-max-width` | `560px` | | | `--dialog-shadow` | `0 8px 32px var(--black-alpha-20)` | | | `--dialog-backdrop` | `var(--background-modal)` | | | **Slider tokens** | | | | `--slider-track-background` | `var(--background-darker)` | | | `--slider-fill` | `var(--text-primary)` | | | `--slider-thumb-background` | `var(--text-primary)` | | | `--slider-thumb-border` | `var(--background-primary)` | | | **Rating tokens** | | | | `--rating-color` | `var(--yellow)` | | | `--rating-color-empty` | `var(--background-faded)` | | | `--rating-size` | `1.5rem` | | | **Mark tokens** | | | | `--mark-background` | `var(--yellow-light)` | | | `--mark-text` | `var(--warm-black)` | | ### 2b. THEME TOKENS - DARK MODE | Token | Value | Note | | --- | --- | --- | | **Unit tokens** | | | | `--none` | `0` | | | `--2xs` | `0.125rem` | | | **2px** | | | | `--xs` | `0.25rem` | | | **4px** | | | | `--s` | `0.5rem` | | | **8px** | | | | `--m` | `0.75rem` | | | **12px** | | | | `--l` | `1rem` | | | **16px** | | | | `--xl` | `1.5rem` | | | **24px** | | | | `--2xl` | `2rem` | | | **32px** | | | | `--3xl` | `2.5rem` | | | **40px** | | | | `--4xl` | `3rem` | | | **48px** | | | | `--5xl` | `3.5rem` | | | **56px** | | | | `--6xl` | `4rem` | | | **64px** | | | | `--7xl` | `4.5rem` | | | **72px** | | | | `--8xl` | `5rem` | | | **80px** | | | | `--9xl` | `5.5rem` | | | **88px** | | | | `--10xl` | `6rem` | | | **96px** | | | | `--11xl` | `6.5rem` | | | **104px** | | | | `--12xl` | `7rem` | | | **112px** | | | | `--13xl` | `7.5rem` | | | **120px** | | | | `--14xl` | `10rem` | | | **Spacing scale** | | | | `--space-none` | `var(--none)` | | | `--space-2xs` | `var(--2xs)` | | | `--space-xs` | `var(--xs)` | | | `--space-s` | `var(--s)` | | | `--space-m` | `var(--m)` | | | `--space-l` | `var(--l)` | | | `--space-xl` | `var(--xl)` | | | `--space-2xl` | `var(--2xl)` | | | `--space-3xl` | `var(--3xl)` | | | `--space-4xl` | `var(--4xl)` | | | `--space-5xl` | `var(--5xl)` | | | `--space-6xl` | `var(--6xl)` | | | `--space-7xl` | `var(--7xl)` | | | `--space-8xl` | `var(--8xl)` | | | `--space-9xl` | `var(--9xl)` | | | `--space-10xl` | `var(--10xl)` | | | `--space-11xl` | `var(--11xl)` | | | `--space-12xl` | `var(--12xl)` | | | `--space-13xl` | `var(--13xl)` | | | `--space-14xl` | `var(--14xl)` | | | **Section spacing variables** | | | | `--section-xs` | `var(--space-xl)` | | | `--section-s` | `var(--space-2xl)` | | | `--section-m` | `var(--space-6xl)` | | | `--section-l` | `var(--space-10xl)` | | | `--section-xl` | `var(--space-14xl)` | | | **Border width tokens** | | | | `--border-s` | `1.5px` | | | `--border-m` | `2px` | | | `--border-l` | `4px` | | | **Border composition variables** | | | | `--border-width` | `var(--border-s)` | | | `--border-style` | `solid` | | | `--border-color` | `var(--border-primary)` | | | **Border radius tokens** | | | | `--radius-xs` | `4px` | | | `--radius-s` | `6px` | | | `--radius-m` | `10px` | | | `--radius-l` | `16px` | | | `--radius-xl` | `24px` | | | `--radius-pill` | `999px` | | ### 4. SYSTEM TOKENS - MOTION | Token | Value | Note | | --- | --- | --- | | **Easing primitives** | | | | `--ease-in` | `cubic-bezier(0.4, 0, 1, 1)` | | | **fast start, slow end. for exits.** | | | | `--ease-out` | `cubic-bezier(0.16, 1, 0.3, 1)` | | | **slow start, slow end with long tail. for entrances.** | | | | `--ease-in-out` | `cubic-bezier(0.65, 0, 0.35, 1)` | | | **Duration primitives** | | | | `--duration-2xs` | `100ms` | | | `--duration-xs` | `200ms` | | | `--duration-s` | `400ms` | | | `--duration-m` | `600ms` | | | `--duration-l` | `800ms` | | | `--duration-xl` | `1200ms` | | | `--duration-2xl` | `1500ms` | | | **Page-level semantic motion** | | | | `--motion-page-open-duration` | `var(--duration-xl)` | | | **800ms** | | | | `--motion-page-open-easing` | `var(--ease-in-out)` | | | `--motion-page-close-duration` | `var(--duration-xl)` | | | **600ms** | | | | `--motion-page-close-easing` | `var(--ease-in-out)` | | | `--motion-page-swap-duration` | `var(--duration-xl)` | | | `--motion-page-swap-easing` | `var(--ease-in-out)` | | | `--motion-page-fade-duration` | `var(--duration-m)` | | | **400ms** | | | | `--motion-page-fade-easing` | `var(--ease-in-out)` | | ### DARK MODE OVERRIDES | Token | Value | Note | | --- | --- | --- | | **Text** | | | | `--text-primary` | `#e8e6e3` | | | `--text-secondary` | `#a8a5a2` | | | `--text-plain` | `#f0eeeb` | | | `--text-faded` | `rgba(255, 255, 255, 0.45)` | | | `--text-accent` | `var(--yellow)` | | | `--text-link` | `var(--text-accent)` | | | `--text-inverted` | `#1a1a1a` | | | `--text-sidebar` | `var(--text-primary)` | | | `--text-top-nav` | `var(--text-primary)` | | | **Background** | | | | `--background-primary` | `var(--neutral-800)` | | | `--background-secondary` | `#222222` | | | `--background-plain` | `#2a2a2a` | | | `--background-faded` | `rgba(255, 255, 255, 0.06)` | | | `--background-darker` | `rgba(255, 255, 255, 0.12)` | | | `--background-modal` | `var(--black-alpha-50)` | | | `--background-sidebar` | `var(--background-primary)` | | | `--background-top-nav` | `var(--background-primary)` | | | **Border** | | | | `--border-primary` | `#e8e6e3` | | | `--border-secondary` | `#3a3a3a` | | | `--border-faded` | `rgba(255, 255, 255, 0.12)` | | | **Black & white alpha (inverted for dark surfaces)** | | | | `--black-alpha-5` | `rgba(255, 255, 255, 0.05)` | | | `--black-alpha-10` | `rgba(255, 255, 255, 0.08)` | | | `--black-alpha-15` | `rgba(255, 255, 255, 0.12)` | | | `--black-alpha-20` | `rgba(255, 255, 255, 0.16)` | | | `--black-alpha-30` | `rgba(255, 255, 255, 0.22)` | | | `--black-alpha-50` | `rgba(255, 255, 255, 0.45)` | | | **Button** | | | | `--button-primary` | `var(--text-primary)` | | | `--button-text` | `var(--background-primary)` | | | `--button-secondary` | `#e8e6e3` | | | `--button-secondary-text` | `#1a1a1a` | | | `--button-faded` | `color-mix(in srgb, var(--button-primary), var(--alpha-80))` | | | **Status** | | | | `--status-info` | `var(--blue-light)` | | | `--status-success` | `var(--green-light)` | | | `--status-warning` | `var(--yellow-light)` | | | `--status-danger` | `var(--red-light)` | | | `--status-accent` | `var(--purple-light)` | | | **Form** | | | | `--input-border` | `#3a3a3a` | | | `--input-background` | `rgba(255, 255, 255, 0.08)` | | | `--input-text` | `#f0eeeb` | | | `--input-placeholder` | `rgba(255, 255, 255, 0.45)` | | | `--input-focus` | `var(--text-accent)` | | | `--input-disabled-bg` | `rgba(255, 255, 255, 0.06)` | | | `--input-disabled-text` | `rgba(255, 255, 255, 0.3)` | | | `--checkbox-background` | `#3a3a3a` | | | `--checkbox-selected` | `#e8e6e3` | | | `--checkbox-border` | `#555` | | | `--checkbox-checkmark` | `#1a1a1a` | | | `--toggle-track` | `#444` | | | `--toggle-knob` | `#888` | | | `--toggle-selected` | `#e8e6e3` | | | `--toggle-knob-selected` | `#1a1a1a` | | | **Card** | | | | `--card-background` | `var(--background-secondary)` | | | `--card-border` | `var(--border-faded)` | | | `--card-border-hover` | `var(--border-primary)` | | | **Tooltip / Toast** | | | | `--tooltip-background` | `var(--neutral-100)` | | | `--tooltip-text` | `var(--warm-black)` | | | `--toast-background` | `var(--neutral-100)` | | | `--toast-text` | `var(--warm-black)` | | | **Dropdown** | | | | `--dropdown-background` | `var(--background-plain)` | | | **Dialog** | | | | `--dialog-background` | `var(--background-secondary)` | | | `--dialog-shadow` | `0 8px 40px var(--black-alpha-60)` | | | **Mark** | | | | `--mark-background` | `color-mix(in srgb, var(--yellow), var(--alpha-60))` | | | `--mark-text` | `var(--off-white)` | | --- ## Color Usage Color tokens define the shared, reusable color values that power both design and code. This page shows what each color looks like. For the full token list with values, see the [Tokens](tokens.html) page. Colors are organised into two layers: **primitive tokens** (raw values) and **semantic tokens** (intent-based aliases). Always use semantic tokens in production code — primitives are the building blocks that semantic tokens reference. --- ## Brand Palette The core brand colors that define the project identity. These are set per-project in the Brand Tokens section of `design-system.css`.
off-white
warm-white
warm-black
off-black
### Accent Colors
red-lighter
red-light
red
red-dark
blue-lighter
blue-light
blue
blue-dark
yellow-lighter
yellow-light
yellow
yellow-dark
green-lighter
green-light
green
green-dark
purple-lighter
purple-light
purple
purple-dark
--- ## Primitive Scales ### Neutral Warm grey ramp from lightest to near-black, used for backgrounds, borders, and secondary text.
neutral-50
neutral-100
neutral-150
neutral-200
neutral-300
neutral-400
neutral-500
neutral-600
neutral-700
neutral-800
neutral-900
neutral-950
neutral-990
### Black Alpha Semi-transparent black values for overlays, shadows, borders, and tints.
black
black-alpha-95
black-alpha-90
black-alpha-80
black-alpha-70
black-alpha-60
black-alpha-50
black-alpha-40
black-alpha-30
black-alpha-20
black-alpha-15
black-alpha-10
black-alpha-5
black-alpha-3
### White Alpha Semi-transparent white values for highlights and light overlays. Shown on a dark background for visibility.
white
white-alpha-95
white-alpha-90
white-alpha-80
white-alpha-70
white-alpha-60
white-alpha-50
white-alpha-40
white-alpha-30
white-alpha-20
white-alpha-15
white-alpha-10
white-alpha-5
--- ## Semantic Colors Semantic colors map primitive tokens to **meaning and intent**. Use these in layouts and components — never use primitives directly. ### Text
text-primary
text-secondary
text-plain
text-faded
text-accent
text-link
text-inverted
### Background
background-primary
background-secondary
background-plain
background-faded
background-darker
### Solid colour backgrounds Solid colour blocks for hero sections, callouts, ad units, and any marketing surface that needs to lean on a brand colour. Each token resolves to the brand primitive when overridden in a client theme — otherwise the design system default applies.
background-accent
background-black
background-white
background-blue
background-red
background-green
### Border
border-primary
border-secondary
border-faded
### Button
button-primary
button-text
button-secondary
button-faded
### Status Status tokens now reference the dark shade of each accent colour family. `var(--status-info)` maps to `var(--blue-dark)` (previously `var(--green)`), aligning with the conventional use of blue for informational states. In dark mode, status tokens flip to the light shades for readability.
status-info
status-success
status-warning
status-danger
status-accent
--- ## Dark Mode The design system uses a `data-theme` attribute to switch between light and dark modes. **Light mode is the default** — dark mode is an opt-in override, not a baseline. Activate it by setting `data-theme="dark"` on any element; tokens inherit through the cascade. Dark mode values (e.g. `#1a1a1a`, `#e8e6e3`) are **overrides applied via `[data-theme="dark"]`** — never use them as primary values, and never hardcode them. If you're working from a dark-looking screenshot, confirm the intended theme before writing code. ### How It Works - **`:root`** — light mode tokens (always present) - **`[data-theme="dark"]`** — overrides semantic tokens with dark values - **`@media (prefers-color-scheme: dark)`** — no-JS fallback for users with a dark OS preference The toggle button in the site header sets `data-theme="dark"` on `` and persists the choice in `localStorage`. ### Scoped Usage You can apply dark mode to any element, not just the page:

Dark mode

Primary text

Faded text

Accent text

```html

This section uses dark mode tokens

``` All semantic tokens inside that element resolve to their dark values via CSS custom property inheritance. ### Customizing Dark Mode Edit the `[data-theme="dark"]` block in `design-system.css` (section 2b). When changing a value, also update the `@media (prefers-color-scheme: dark)` fallback (section 2c) to keep them in sync. --- ## Typography Typography tokens provide a **consistent, modular system** for all text across the products. They are designed for **clarity, readability, and hierarchy**, while remaining flexible across devices. The system uses three font families — a primary sans-serif for body and UI, a secondary serif for headings, and a tertiary monospace for code and labels. For the full list of typography token values, see the [Tokens](tokens.html) page. --- ## Font Sizes The full type scale used across the system. Reference these tokens when setting font sizes on any element. The scale uses a **progressive step** approach: +2px in the body range, +4px in the heading range, and +8px in the display range. This gives more granularity where it matters most. | Token | Value | px Equivalent | Step | | --- | --- | --- | --- | | `var(--font-3xs)` | 0.625rem | 10px | — | | `var(--font-2xs)` | 0.75rem | 12px | +2px | | `var(--font-xs)` | 0.875rem | 14px | +2px | | `var(--font-s)` | 1rem | 16px | +2px | | `var(--font-m)` | 1.125rem | 18px | +2px | | `var(--font-l)` | 1.25rem | 20px | +2px | | `var(--font-xl)` | 1.375rem | 22px | +2px | | `var(--font-2xl)` | 1.5rem | 24px | +2px | | `var(--font-3xl)` | 1.75rem | 28px | +4px | | `var(--font-4xl)` | 2rem | 32px | +4px | | `var(--font-5xl)` | 2.25rem | 36px | +4px | | `var(--font-6xl)` | 2.5rem | 40px | +4px | | `var(--font-7xl)` | 3rem | 48px | +8px | | `var(--font-8xl)` | 3.5rem | 56px | +8px | | `var(--font-9xl)` | 4rem | 64px | +8px | | `var(--font-10xl)` | 4.5rem | 72px | +8px | --- ## Headings All headings use `var(--font-secondary)` (RecifeText) at `var(--font-weight-regular)` (400). Each level steps down in size to create a clear visual hierarchy.

Heading 2

Every detail contributes to the whole

Heading 3

Structure creates clarity in complexity

Heading 4

Good defaults eliminate guesswork

Heading 5

Constraints unlock creative freedom

Heading 6

Small decisions compound over time
```html

Build systems that scale with your ambition

Every detail contributes to the whole

Structure creates clarity in complexity

``` | Element | Token | px | Mobile (≤768px) | Line Height | Value | | --- | --- | --- | --- | --- | --- | | `h1` | `var(--font-7xl)` | 48px | `var(--font-5xl)` (36px) | `var(--line-height-m)` | 1.3 | | `h2` | `var(--font-5xl)` | 36px | `var(--font-3xl)` (28px) | `var(--line-height-m)` | 1.3 | | `h3` | `var(--font-3xl)` | 28px | — | `var(--line-height-m)` | 1.3 | | `h4` | `var(--font-2xl)` | 24px | — | `var(--line-height-m)` | 1.3 | | `h5` | `var(--font-xl)` | 22px | — | `var(--line-height-m)` | 1.3 | | `h6` | `var(--font-xl)` | 22px | — | `var(--line-height-l)` | 1.4 | Headings use `var(--line-height-m)` (1.3) so multi-line titles have breathing room. On mobile (≤768px), `h1` and `h2` step down a level to prevent giant titles from dominating small viewports. --- ## Body Text The default paragraph style used for all running content. The `var(--text-body)` token controls the base size globally — changing it updates paragraphs, inputs, code, tables, and buttons at once. Size modifier classes let you step up or down from the default.

Large

Introductory text and section summaries that sit between headings and body text in the hierarchy.

Medium (default)

The default body text size. This is what you get without adding any size class — the baseline for all running content.

Small

Secondary content, supporting details, and supplementary information that doesn't need to compete with body text for attention.

Extra Small

Captions, footnotes, metadata, and fine print — available but not prominent.

```html

Lead paragraph text.

Introductory text.

Default body text (no class needed).

Smaller supporting text.

Captions and metadata.

``` | Element | Token | px | Line Height | Value | | --- | --- | --- | --- | --- | | `.text-size-xlarge` | `var(--font-3xl)` | 28px | `var(--line-height-l)` | 1.4 | | `.text-size-large` | `var(--font-xl)` | 22px | `var(--line-height-l)` | 1.4 | | `p` (default) | `var(--font-m)` | 18px | `var(--line-height-l)` | 1.4 | | `.text-size-small` | `var(--font-s)` | 16px | `var(--line-height-xl)` | 1.6 | | `.text-size-xsmall` | `var(--font-xs)` | 14px | `var(--line-height-xl)` | 1.6 | --- ## Eyebrow A small, uppercase label used to provide context above headings, within sections, or inline with other content. The `.eyebrow` class works on any element — `

`, ``, `

`, or anything else.
Latest Update

New components added to the library

Section Label

An eyebrow on an h2 resets it to the small uppercase style, useful when you need heading semantics without heading size.

```html

Case Study

Building a design system from the ground up

Latest Update

Section Label

``` | Element | Token | Line Height | Value | | --- | --- | --- | --- | | `.eyebrow` | `var(--font-xs)` | `var(--line-height-m)` | 1.3 | --- ## Blockquote Used for pullquotes and highlighted passages. Renders in the secondary serif font with a left border accent.

Long quote

A design system is more than a collection of components — it is a shared language between design and engineering. When done well, it reduces inconsistency, speeds up delivery, and creates a foundation that scales with the product.

```html

Good typography is invisible. Bad typography is everywhere.

``` | Element | Token | Line Height | Value | | --- | --- | --- | --- | | `blockquote` | `var(--font-m)` | `var(--line-height-l)` | 1.4 | --- ## Links Links use text-color underlines with a hover transition. The default state shows `var(--text-plain)` text with a `var(--text-link)` coloured underline. On hover, the text colour shifts to `var(--text-link)` and the underline moves down slightly. External links (`target="_blank"`) automatically show a share icon via `::after` that inherits the link colour.

External link

Typography is powered by Google Fonts for web delivery.

```html

Read the full brand guidelines before starting.

Typography is powered by Google Fonts for web delivery.

``` | Property | Value | | --- | --- | | Default colour | `var(--text-plain)` | | Underline colour | `var(--text-link)` | | Hover colour | `var(--text-link)` | | Underline offset | 2.5px → 4px on hover | | Underline thickness | 1.5px | | Transition | 0.3s all | | External icon | `::after` on `a[target="_blank"]`, inherits `currentColor` | --- ## Font Families The system uses four font stacks, each with a distinct role.

Secondary — RecifeText

Typography is the voice of design

Tertiary — Bugrino

Typography is the voice of design

Quaternary — IBM Plex Mono

Typography is the voice of design

```css body { font-family: var(--font-primary); } h1, h2, h3, h4, h5, h6 { font-family: var(--font-secondary); } code, pre, kbd { font-family: var(--font-quaternary); } ``` | Token | Font | Used For | | --- | --- | --- | | `var(--font-primary)` | Inclusive Sans | Body text, UI, labels | | `var(--font-secondary)` | RecifeText | Headings, blockquotes | | `var(--font-tertiary)` | Bugrino | Brand display, eyebrows, buttons, badges | | `var(--font-quaternary)` | IBM Plex Mono | Code, pre, kbd | --- ## Font Weight Available weight values from light to black. All headings default to regular weight.

Regular

Systems scale when decisions are shared

Medium

Tokens turn intention into consistency

Semi-Bold

Structure creates clarity in complexity

Bold

Good defaults eliminate guesswork

Extra-Bold

Constraints unlock creative freedom

Black

Build with purpose, not by accident
```css font-weight: var(--font-weight-bold); ``` | Token | Value | | --- | --- | | `var(--font-weight-light)` | 300 | | `var(--font-weight-regular)` | 400 | | `var(--font-weight-medium)` | 500 | | `var(--font-weight-semi-bold)` | 600 | | `var(--font-weight-bold)` | 700 | | `var(--font-weight-extra-bold)` | 800 | | `var(--font-weight-black)` | 900 | --- ## Line Height Vertical rhythm values from tight display text to loose body copy. Headings use tighter values; body text uses looser values for readability.

Small

Design tokens capture color, typography, spacing, and border values as reusable variables so that design and code stay in sync across every surface.

Medium

Design tokens capture color, typography, spacing, and border values as reusable variables so that design and code stay in sync across every surface.

Large

Design tokens capture color, typography, spacing, and border values as reusable variables so that design and code stay in sync across every surface.

Extra Large

Design tokens capture color, typography, spacing, and border values as reusable variables so that design and code stay in sync across every surface.

2X Large

Design tokens capture color, typography, spacing, and border values as reusable variables so that design and code stay in sync across every surface.

```css line-height: var(--line-height-l); ``` | Token | Value | Used For | | --- | --- | --- | | `var(--line-height-xs)` | 0.7 | Tight display text | | `var(--line-height-s)` | 1 | Tight display text | | `var(--line-height-m)` | 1.3 | Headings, eyebrows | | `var(--line-height-l)` | 1.4 | Body text, paragraphs | | `var(--line-height-xl)` | 1.6 | Small text, captions | | `var(--line-height-2xl)` | 1.8 | Spacious body text | --- ## Letter Spacing Tracking values used for labels, eyebrows, and display text. Values are em-based so they scale proportionally with font size.

Medium

Design System Components

Large

Design System Components

Extra Large

Design System Components
```css letter-spacing: var(--letter-spacing-xl); ``` | Token | Value | Used For | | --- | --- | --- | | `var(--letter-spacing-s)` | 0.03em | Subtle tracking | | `var(--letter-spacing-m)` | 0.06em | Medium tracking | | `var(--letter-spacing-l)` | 0.12em | Wide tracking | | `var(--letter-spacing-xl)` | 0.24em | Eyebrows, labels | --- ## Spacing Spacing tokens define **distance**, not intent. They are reused for gaps, padding, and margins depending on context. For the full list of spacing token values, see the [Tokens](tokens.html) page. The system is built in layers: **unit tokens** (raw values) → **space tokens** (semantic aliases) → **utility classes** (applied in HTML). Always use space tokens or utility classes — never hardcode pixel values. --- ## Space Scale The space scale provides a visual reference for the spacing tokens used throughout the system. | Token | Value | px Equivalent | | --- | --- | --- | | `var(--space-none)` | 0 | 0px | | `var(--space-2xs)` | 0.125rem | 2px | | `var(--space-xs)` | 0.25rem | 4px | | `var(--space-s)` | 0.5rem | 8px | | `var(--space-m)` | 0.75rem | 12px | | `var(--space-l)` | 1rem | 16px | | `var(--space-xl)` | 1.5rem | 24px | | `var(--space-2xl)` | 2rem | 32px | | `var(--space-3xl)` | 2.5rem | 40px | | `var(--space-4xl)` | 3rem | 48px | | `var(--space-5xl)` | 3.5rem | 56px | | `var(--space-6xl)` | 4rem | 64px | | `var(--space-7xl)` | 4.5rem | 72px | | `var(--space-8xl)` | 5rem | 80px | | `var(--space-9xl)` | 5.5rem | 88px | | `var(--space-10xl)` | 6rem | 96px |

xs · 4px

s · 8px

m · 12px

l · 16px

xl · 24px

2xl · 32px

3xl · 40px

4xl · 48px

6xl · 64px

8xl · 80px

10xl · 96px

```css padding: var(--space-m); gap: var(--space-xl); margin-bottom: var(--space-l); ``` --- ## Gap Gap modifiers control the space between child elements inside a `.block`. The default gap is `var(--space-m)`.
Second item
Third item

Extra Small

First item
Second item
Third item

Small

First item
Second item
Third item

Default gap

First item
Second item
Third item

Large

First item
Second item
Third item

Extra Large

First item
Second item
Third item
```html
Item one
Item two
Item three
``` | Class | Value | px Equivalent | | --- | --- | --- | | `.gap-none` | 0 | 0px | | `.gap-xs` | `var(--space-xs)` | 4px | | `.gap-s` | `var(--space-s)` | 8px | | `.gap-m` | `var(--space-m)` | 12px | | `.gap-l` | `var(--space-l)` | 16px | | `.gap-xl` | `var(--space-xl)` | 24px | | `.gap-2xl` | `var(--space-2xl)` | 32px | | `.gap-3xl` | `var(--space-3xl)` | 40px | --- ## Padding Padding utilities apply internal spacing to an element on all sides.

.padding-m

Content

.padding-l

Content

.padding-xl

Content
```html
``` | Class | Value | px Equivalent | | --- | --- | --- | | `.padding-s` | `var(--space-s)` | 8px | | `.padding-m` | `var(--space-m)` | 12px | | `.padding-l` | `var(--space-l)` | 16px | | `.padding-xl` | `var(--space-xl)` | 24px | | `.padding-2xl` | `var(--space-2xl)` | 32px | | `.padding-3xl` | `var(--space-3xl)` | 40px | --- ## Section Spacing Section spacing controls the vertical rhythm between major page sections. Apply `.top-*` and `.bottom-*` classes to `
` elements. These scale responsively between desktop and mobile.

.top-small

.top-medium

.top-large

.top-xl

Bottom spacing

.bottom-small

.bottom-medium

.bottom-large

.bottom-xl

```html
``` | Class | Token | Desktop | Mobile | | --- | --- | --- | --- | | `.top-small` / `.bottom-small` | `var(--section-s)` | 32px | 24px | | `.top-medium` / `.bottom-medium` | `var(--section-m)` | 64px | 32px | | `.top-large` / `.bottom-large` | `var(--section-l)` | 96px | 56px | | `.top-xl` / `.bottom-xl` | `var(--section-xl)` | 160px | 80px | --- ## Border System Borders use a **composable architecture** that separates positioning from styling. Structural classes define where the border appears, and combo classes modify width, style, and color independently. For border token values, see the [Tokens](tokens.html) page. --- ## Structure Structural classes define **where** the border appears. By default, borders use `var(--border-s)` width, solid style, and `var(--border-primary)` color.

Top

Content with top border

Bottom

Content with bottom border

Left

Content with left border

Right

Content with right border
```html
All sides
Top only
Bottom only
Left only
Right only
``` | Class | Effect | | --- | --- | | `.border` | Border on all sides | | `.border-top` | Top only | | `.border-bottom` | Bottom only | | `.border-left` | Left only | | `.border-right` | Right only | --- ## Width Width classes modify the border thickness. The default is `var(--border-s)`.

Medium

2px border

Large

4px border
```html
Medium border on all sides
``` | Class | Token | px Equivalent | | --- | --- | --- | | `.border-s` | `var(--border-s)` | 1.5px | | `.border-m` | `var(--border-m)` | 2px | | `.border-l` | `var(--border-l)` | 4px | --- ## Style Style classes modify the border appearance. The default is solid.

Dashed

Dashed border

Dotted

Dotted border
```html
Dashed border
``` | Class | Style | | --- | --- | | `.border-solid` | Solid (default) | | `.border-dashed` | Dashed | | `.border-dotted` | Dotted | --- ## Color Color classes modify the border color using semantic tokens.

Secondary

Medium, neutral border

Faded

Subtle, light border
```html
Subtle border
``` | Class | Token | | --- | --- | | `.border-primary` | `var(--border-primary)` | | `.border-secondary` | `var(--border-secondary)` | | `.border-faded` | `var(--border-faded)` | --- ## Radius Border radius tokens control corner rounding. Apply them directly via CSS — there are no utility classes for radius.

Small

6px radius

Medium

10px radius

Large

16px radius

Extra Large

24px radius

Pill

Fully rounded
```css border-radius: var(--radius-m); ``` | Token | Value | | --- | --- | | `var(--radius-xs)` | 4px | | `var(--radius-s)` | 6px | | `var(--radius-m)` | 10px | | `var(--radius-l)` | 16px | | `var(--radius-xl)` | 24px | | `var(--radius-pill)` | 999px | --- ## Composing Borders Combine structural, width, style, and color classes to build any border you need. Each class modifies a single concern.

Bottom + large + primary

Composed border

All sides + faded + dotted

Composed border
```html
Composed: top + medium + dashed + secondary
``` --- ## Component Conventions 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, sticky-bar, copy-button). 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: ```yaml --- 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. --- ## Naming convention - **Base class:** `.component-name` (e.g. `.badge`, `.card`, `.toast`) - **Modifiers:** `.component-name--modifier` (e.g. `.badge--success`, `.card--flush`) - **State classes:** `.is-state` (e.g. `.is-active`, `.is-open`, `.is-disabled`, `.is-hidden`, `.is-loading`) — shared across components - **Utility overrides:** use `!important` only on utility classes (e.g. `.gap-m`) - **JS hooks:** use `data-*` attributes, never CSS class names **Legacy note:** The button component uses `.is-outline`, `.is-faded`, `.is-small`, `.is-xsmall`, `.is-icon` as modifiers. These predate the `var(--modifier)` convention and are kept for backward compatibility. New components must use `var(--modifier)` syntax. --- ## 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: ```css --component-property: var(--semantic-token); ``` Example: ```css --card-background: var(--background-primary); --card-border: var(--border-faded); --card-radius: var(--radius-m); --card-padding: var(--space-xl); ``` --- ## 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 | `design-system/component-name.html` | Section headings in `design-system.css` follow the format: ```css /* ------ 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 | |-----------|-----------|----------|-----------| | Button | `.button` | No | `button.md` | | Form elements | `.form-group`, `.form-check`, `.form-toggle`, `.segmented-control` | No | `form.md` | | Callout | `.callout` | No | `callout.md` | | Disclosure | `details`/`summary` | No | `disclosure.md` | | Badge | `.badge` | No | `badge.md` | | Card | `.card` | No | `card.md` | | Breadcrumb | `.breadcrumb` | No | `breadcrumb.md` | | Tabs | `.tabs`, `.tab` | Yes (`tabs.js`) | `tabs.md` | | Progress | `.progress-bar` | No | `progress.md` | | Tooltip | `[data-tooltip]` | No | `tooltip.md` | | Toast | `.toast` | Yes (`toast.js`) | `toast.md` | | Code / Pre / Kbd | `code`, `pre`, `kbd` | No | `code.md` | | Mark / Abbr / Figure | `mark`, `abbr`, `figure` | No | `mark.md` | > Asset Card, Book Cover, Don't Card, Sticky Bar and Copy Button 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](../docs/setup.html) 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 `var(--yellow-light)` via `var(--mark-background)` 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): ```css /* -- 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: ```css /* ------ 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:** ```bash cd cms/generator && npm run docgen ``` --- ## Components ### Button Buttons are interactive elements used to trigger actions. They size to their content by default and should communicate **clear intent and hierarchy**. The `.button` class is required for styled buttons. The bare `

Disabled

```html ``` --- ## Outline Button `.is-outline` removes the filled background and uses a border instead. Use for secondary actions that shouldn't compete with the primary CTA.

Hover

Disabled

```html ``` --- ## Faded Button `.is-faded` applies a subtle background for low-priority or passive actions.

Hover

Disabled

```html ``` --- ## Outline + Faded `.is-outline.is-faded` combines both modifiers for tertiary or utility actions.

Hover

Disabled

```html ``` --- ## Small Button `.is-small` reduces font size and padding for dense UI areas.

Disabled

```html ``` --- ## Icon Button `.is-icon` creates a circular button designed for icons only. Always include `aria-label` for accessibility.

Disabled

```html ``` --- ## Button Group `.button-group` is a flex container for grouping multiple buttons together. It provides consistent spacing, wraps on smaller screens, and vertically centres buttons of different sizes.

Centred

Right-aligned

Mixed sizes

```html
...
...
``` | Class | Effect | | --- | --- | | `.button-group` | Flex container with consistent gap | | `.justify-center` | Centre-aligns the group | | `.justify-end` | Right-aligns the group | --- ## All Variants A side-by-side comparison of every button style at default size. --- ## Copy Button The `.copy-btn` adds clipboard copy functionality to any button. It copies the value from `data-copy` and shows a "Copied!" state. Three variants are available. See the [Copy Button](copy-button.html) docs for full details. Copy
Copied!

Icon Only

Ghost

```html ``` Requires `assets/js/copy-button.js`. --- ## Usage Rules - Buttons should **never stretch full width** by default - Use one clear primary button per section when possible - Use outline or faded styles to reduce visual competition - Use icon buttons **only** when the icon meaning is clear - If a button feels too prominent or too quiet, change the modifier — not the base styles ### Card Cards are surface containers that group related content with a border and background. They support padding modifiers and an interactive (clickable) variant. --- ## Tokens | Token | Default (Light) | Default (Dark) | Purpose | |-------|-----------------|----------------|---------| | `var(--card-background)` | `var(--background-primary)` | `var(--background-secondary)` | Surface colour | | `var(--card-border)` | `var(--border-faded)` | `var(--border-faded)` | Border colour | | `var(--card-radius)` | `var(--radius-m)` | — | Corner radius | | `var(--card-padding)` | `var(--space-xl)` | — | Internal padding | --- ## Basic usage ```html

Card content goes here.

``` --- ## Flush card Remove internal padding with `.card--flush` — useful when the card contains a full-bleed image or nested content that manages its own spacing. ```html
``` --- ## Interactive card Wrap in an `` tag with `.card--interactive` for clickable cards. Adds hover shadow and focus ring. ### Clickable card ```html

Clickable card

This entire card is a link.

``` ### Image card Combine `.card--flush` with `.card--interactive` to create a clickable card with a full-bleed image. The `.img` and `.card-image` classes handle display and border-radius. Use a padded inner wrapper for the text content. ```html Description

Card with image

Supporting text below the image.

``` --- ## Accessibility notes - Use semantic HTML inside cards (`

`, `

`, `