This document provides comprehensive information for agentic code generators to effectively use the CodeSignal Design System.
- System Overview
- Installation & Setup
- Design Tokens
- Components
- Usage Patterns
- Best Practices
- File Structure
The CodeSignal Design System is a CSS-based design system organized into Foundations (design tokens) and Components (reusable UI elements). All components are built using CSS custom properties (CSS variables) for theming and consistency.
- Semantic over Primitive: Always prefer semantic tokens (e.g.,
--Colors-Text-Body-Default) over base scale tokens - Dark Mode Support: All components automatically adapt to dark mode via
@media (prefers-color-scheme: dark) - CSS-First: Components are primarily CSS-based with minimal JavaScript (Dropdown, Numeric Slider, and Modal use JS)
- Accessibility: Components follow WCAG guidelines and support keyboard navigation
<!-- 1. Fonts (Work Sans) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- 2. Foundations (Required for all components) -->
<link rel="stylesheet" href="/design-system/colors/colors.css">
<link rel="stylesheet" href="/design-system/spacing/spacing.css">
<link rel="stylesheet" href="/design-system/typography/typography.css">
<!-- 3. Components (Include only what you need) -->
<link rel="stylesheet" href="/design-system/components/button/button.css">
<link rel="stylesheet" href="/design-system/components/boxes/boxes.css">
<link rel="stylesheet" href="/design-system/components/dropdown/dropdown.css">
<link rel="stylesheet" href="/design-system/components/horizontal-cards/horizontal-cards.css">
<link rel="stylesheet" href="/design-system/components/icons/icons.css">
<link rel="stylesheet" href="/design-system/components/input/input.css">
<link rel="stylesheet" href="/design-system/components/modal/modal.css">
<link rel="stylesheet" href="/design-system/components/numeric-slider/numeric-slider.css">
<link rel="stylesheet" href="/design-system/components/split-panel/split-panel.css">
<link rel="stylesheet" href="/design-system/components/table/table.css">
<link rel="stylesheet" href="/design-system/components/tags/tags.css">@import url('/design-system/colors/colors.css');
@import url('/design-system/spacing/spacing.css');
@import url('/design-system/typography/typography.css');
@import url('/design-system/components/button/button.css');<script type="module">
import Dropdown from '/design-system/components/dropdown/dropdown.js';
import HorizontalCards from '/design-system/components/horizontal-cards/horizontal-cards.js';
import Modal from '/design-system/components/modal/modal.js';
import NumericSlider from '/design-system/components/numeric-slider/numeric-slider.js';
import SplitPanel from '/design-system/components/split-panel/split-panel.js';
</script>Avoid using these directly - use semantic tokens instead for theming support.
Pattern: --Colors-Base-[Family]-[Step]
Families:
Primary: Brand blue colors (20-1400 scale)Neutral: Grays, white, black (00-1400 scale)Accent-Green: Success statesAccent-Sky-Blue: Info statesAccent-Yellow: Warning statesAccent-Orange: Warning statesAccent-Red: Error/Danger states
Example:
--Colors-Base-Primary-700: #1062FB;
--Colors-Base-Neutral-600: #ACB4C7;
--Colors-Base-Accent-Green-600: #10B981;Always use these for automatic dark mode support and consistency.
Categories:
-
Primary Colors
--Colors-Primary-Default--Colors-Primary-Medium--Colors-Primary-Strong
-
Backgrounds
--Colors-Backgrounds-Main-Default--Colors-Backgrounds-Main-Top--Colors-Backgrounds-Main-Medium--Colors-Backgrounds-Main-Strong
-
Text Colors
--Colors-Text-Body-Default--Colors-Text-Body-Secondary--Colors-Text-Body-Medium--Colors-Text-Body-Strong--Colors-Text-Body-Strongest
-
Icon Colors
--Colors-Icon-Default--Colors-Icon-Primary--Colors-Icon-Secondary
-
Stroke/Border Colors
--Colors-Stroke-Default--Colors-Stroke-Strong--Colors-Stroke-Strongest
-
Alert Colors
--Colors-Alert-Success-Default,--Colors-Alert-Success-Medium--Colors-Alert-Error-Default,--Colors-Alert-Error-Medium--Colors-Alert-Warning-Default,--Colors-Alert-Warning-Medium--Colors-Alert-Info-Default,--Colors-Alert-Info-Medium
Pattern: --UI-Spacing-spacing-[size]
Available Sizes:
none: 0min: 2pxxxs: 4pxxs: 6pxs: 8pxmxs: 12pxms: 16pxm: 18pxml: 20pxmxl: 24pxl: 28pxxl: 32pxxxl: 36pxxxxl: 48px4xl: 60pxmax: 90px
Usage:
padding: var(--UI-Spacing-spacing-m);
margin: var(--UI-Spacing-spacing-s);
gap: var(--UI-Spacing-spacing-mxl);Pattern: --UI-Radius-radius-[size]
Available Sizes:
none: 0min: 2pxxxs: 4pxxs: 6pxs: 8pxm: 12pxml: 16pxmxl: 20pxl: 24pxxl: 32px
Usage:
border-radius: var(--UI-Radius-radius-m);Pattern: --UI-Input-[size]
Available Sizes:
min: 26pxxs: 32pxsm: 40pxmd: 48px (default)lg: 60px
Usage:
height: var(--UI-Input-md);- Body & Labels:
Work Sans(sans-serif) - Must be loaded from Google Fonts - Headings:
Founders Grotesk(sans-serif) - Included via@font-face - Code:
JetBrains Mono(monospace) - Included via@font-face
Body Text (Work Sans):
.body-xxsmall(13px).body-xsmall(14px).body-small(15px).body-medium(16px).body-large(17px).body-xlarge(19px).body-xxlarge(21px).body-xxxlarge(24px)
Body Elegant (Founders Grotesk):
.body-elegant-xxsmall(22px).body-elegant-xsmall(26px).body-elegant-small(32px).body-elegant-medium(38px)
Headings (Founders Grotesk, 500 weight):
.heading-xxxsmall(16px).heading-xxsmall(22px).heading-xsmall(22px).heading-small(24px).heading-medium(32px).heading-large(38px).heading-xlarge(48px).heading-xxlarge(64px)
Labels (Work Sans, 600 weight, uppercase):
.label-small(10px).label-medium(11px).label-large(14px)
Label Numbers (Work Sans, 500 weight):
.label-number-xsmall(11px).label-number-small(12px).label-number-medium(14px).label-number-large(15px)
Base Class: .button (required)
Variants:
.button-primary: Primary action (Brand Blue background).button-secondary: Secondary action (Outlined style).button-tertiary: Tertiary/Ghost (Subtle background).button-danger: Destructive action (Red).button-success: Positive action (Green).button-text: Text button (Neutral text, no background).button-text-primary: Primary text button (Brand color text)
Sizes:
.button-xsmall: 32px height.button-small: 40px height- Default: 48px height (medium)
.button-large: 60px height
States:
- Standard pseudo-classes:
:hover,:focus,:active,:disabled - Utility classes:
.hover,.focus,.active,.disabled
Example:
<button class="button button-primary button-large">Submit</button>
<button class="button button-secondary button-small">Cancel</button>
<button class="button button-danger" disabled>Delete</button>Dependencies: colors.css, spacing.css, typography.css
Base Class: .box (required)
Variants:
.box.selected: Selected state (Primary border).box.emphasized: Emphasized state (Neutral border).box.shadowed: Soft shadow.box.card: Card-style shadow.box.non-interactive: Disables hover, focus, and active state changes. Useful for containers that are not clickable/interactive. Can be combined with other variants (e.g.,.box.card.non-interactive)
States:
- Standard pseudo-classes:
:hover,:focus,:active - Utility classes:
.hover,.focus,.selected - Note: The
.non-interactiveclass overrides all interactive states, preventing visual changes on hover, focus, or active while preserving base styling (shadows, borders, etc.)
Example:
<div class="box">Default content</div>
<div class="box selected">Selected content</div>
<div class="box card">Card content</div>
<div class="box card non-interactive">Non-interactive card (no hover/focus effects)</div>Dependencies: colors.css, spacing.css
Base Class: .input (required)
Input Types:
type="text": Standard text input (default)type="number": Numeric input with styled spinner buttons
States:
- Standard pseudo-classes:
:hover,:focus,:disabled - Utility classes:
.hover,.focus
Features:
- Automatic focus ring (primary color with reduced opacity)
- Styled number input spinners
- Dark mode support
Example:
<input type="text" class="input" placeholder="Enter text...">
<input type="number" class="input" placeholder="Enter number...">
<input type="text" class="input" disabled placeholder="Disabled">Dependencies: colors.css, spacing.css, typography.css
Base Class: .input-checkbox (required wrapper)
Structure: Checkboxes require a specific HTML structure with a label wrapper:
<label class="input-checkbox">
<input type="checkbox">
<span class="input-checkbox-box">
<span class="input-checkbox-checkmark"></span>
</span>
<span class="input-checkbox-label">Checkbox Label</span>
</label>Sizes:
- Default: 32px checkbox box, large label text (17px)
.input-checkbox-small: 26px checkbox box, medium label text (16px).input-checkbox-xsmall: 20px checkbox box, small label text (14px)
States:
- Default: White background with gray border
- Hover: Blue border (primary color)
- Checked: Blue background with white checkmark icon
- Disabled: Reduced opacity (0.45 for box, 0.2 for label)
Example:
<!-- Default Checkbox -->
<label class="input-checkbox">
<input type="checkbox">
<span class="input-checkbox-box">
<span class="input-checkbox-checkmark"></span>
</span>
<span class="input-checkbox-label">Checkbox Label</span>
</label>
<!-- Checked Checkbox -->
<label class="input-checkbox">
<input type="checkbox" checked>
<span class="input-checkbox-box">
<span class="input-checkbox-checkmark"></span>
</span>
<span class="input-checkbox-label">Checkbox Label</span>
</label>
<!-- Small Checkbox -->
<label class="input-checkbox input-checkbox-small">
<input type="checkbox">
<span class="input-checkbox-box">
<span class="input-checkbox-checkmark"></span>
</span>
<span class="input-checkbox-label">Checkbox Label</span>
</label>Dependencies: colors.css, spacing.css, typography.css
Base Class: .input-radio (required wrapper)
Structure:
Radio buttons require a specific HTML structure and must share the same name attribute to function as a group:
<label class="input-radio">
<input type="radio" name="group-name">
<span class="input-radio-circle">
<span class="input-radio-dot"></span>
</span>
<span class="input-radio-label">Radio Label</span>
</label>Sizes:
- Default: 32px radio circle, large label text (17px)
.input-radio-small: 26px radio circle, medium label text (16px).input-radio-xsmall: 20px radio circle, small label text (14px)
States:
- Default: White background with gray border
- Hover: Blue border (primary color)
- Checked: Blue-filled circle with white inner dot (ellipse icon)
- Disabled: Reduced opacity (0.45 for circle, 0.2 for label)
Example:
<!-- Default Radio -->
<label class="input-radio">
<input type="radio" name="option">
<span class="input-radio-circle">
<span class="input-radio-dot"></span>
</span>
<span class="input-radio-label">Radio Label</span>
</label>
<!-- Checked Radio -->
<label class="input-radio">
<input type="radio" name="option" checked>
<span class="input-radio-circle">
<span class="input-radio-dot"></span>
</span>
<span class="input-radio-label">Radio Label</span>
</label>
<!-- Radio Group -->
<div>
<label class="input-radio">
<input type="radio" name="size" value="small">
<span class="input-radio-circle">
<span class="input-radio-dot"></span>
</span>
<span class="input-radio-label">Small</span>
</label>
<label class="input-radio">
<input type="radio" name="size" value="medium" checked>
<span class="input-radio-circle">
<span class="input-radio-dot"></span>
</span>
<span class="input-radio-label">Medium</span>
</label>
</div>Features:
- Custom-styled circular buttons with blue-filled checked state
- White inner ellipse icon when checked
- Group behavior (radio buttons with same
namework as a group) - Focus ring for accessibility
- Dark mode support
Dependencies: colors.css, spacing.css, typography.css, icons/icons.css (for ellipse icon)
Base Class: .tag or .tag.default (required)
Variants:
.tag/.tag.default: Primary tag (Brand Blue background).tag.secondary: Secondary tag (Neutral gray background).tag.outline: Outline tag (Transparent with border).tag.success: Success tag (Green background).tag.error: Error tag (Red background).tag.warning: Warning tag (Yellow background).tag.info: Info tag (Sky Blue background)
States:
- Standard pseudo-classes:
:hover,:focus,:active - Utility classes:
.hover,.focus,.active
Example:
<div class="tag">Default</div>
<div class="tag success">Completed</div>
<div class="tag error">Failed</div>
<div class="tag outline">Filter</div>Dependencies: colors.css, spacing.css, typography.css
Structure: Use a scroll wrapper so wide tables can overflow horizontally on small screens.
Classes:
.table-scroll(required wrapper): Block container with border, radius, horizontal scrolling, and touch-friendly overflow.table(required on<table>): Base typography, borders, and cell padding for editorial, read-only tables
Example:
<div class="table-scroll">
<table class="table">
<thead>
<tr>
<th>Country</th>
<th>Capital</th>
</tr>
</thead>
<tbody>
<tr>
<td>France</td>
<td>Paris</td>
</tr>
</tbody>
</table>
</div>Features:
- Header and body cell styling with semantic color tokens (dark mode via
prefers-color-scheme) - Last row has no bottom border; top corners of the header align with the wrapper radius
<code>inside cells useswhite-space: nowrapfor inline snippets
Dependencies: colors.css, spacing.css, typography.css
Base Class: .icon (required)
Icon Names:
Use .icon-[name] where [name] is derived from SVG filename (e.g., Icon=Academy.svg → .icon-academy)
Available Icons (80+ icons):
.icon-academy.icon-assessment.icon-interview.icon-jobs.icon-course- ... (see
icons.cssfor full list)
Sizes:
.icon-small: 16px.icon-medium: 24px (default).icon-large: 32px.icon-xlarge: 48px
Colors:
- Default: Uses
currentColor(inherits text color) .icon-primary: Primary brand color.icon-secondary: Secondary neutral color.icon-success: Success green color.icon-danger: Danger red color.icon-warning: Warning yellow color
Implementation Note:
Icons use mask-image with background-color for color control. SVGs in data URIs use black fills (black = visible in mask).
Example:
<span class="icon icon-jobs"></span>
<span class="icon icon-jobs icon-large icon-primary"></span>
<span class="icon icon-academy icon-small icon-success"></span>Dependencies: colors.css, spacing.css
Import:
import Dropdown from '/design-system/components/dropdown/dropdown.js';Initialization:
const dropdown = new Dropdown(selector, options);Configuration Options:
| Option | Type | Default | Description |
|---|---|---|---|
items |
Array | [] |
Array of {value, label} objects |
placeholder |
String | 'Select option' |
Placeholder text |
selectedValue |
String | null |
Initial selected value |
width |
String/Number | 'auto' |
Fixed width (ignored if growToFit is true) |
growToFit |
Boolean | false |
Auto-resize to fit content |
onSelect |
Function | null |
Callback (value, item) on selection |
API Methods:
getValue(): Returns current selected valuesetValue(value): Sets selected value programmaticallyopen(): Opens dropdown menuclose(): Closes dropdown menutoggleOpen(): Toggles open statedestroy(): Removes event listeners and clears container
Example:
const dropdown = new Dropdown('#my-dropdown', {
placeholder: 'Choose an option',
items: [
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' },
{ value: '3', label: 'Option 3' }
],
onSelect: (value, item) => {
console.log('Selected:', value, item);
}
});
// Later...
dropdown.setValue('2');
const currentValue = dropdown.getValue();Dependencies: colors.css, spacing.css, typography.css
Import:
import NumericSlider from '/design-system/components/numeric-slider/numeric-slider.js';Initialization:
const slider = new NumericSlider(selector, options);Configuration Options:
| Option | Type | Default | Description |
|---|---|---|---|
type |
String | 'single' |
Slider type: 'single' (one handle) or 'range' (two handles) |
min |
Number | 0 |
Minimum value (must be less than max) |
max |
Number | 100 |
Maximum value (must be greater than min) |
step |
Number | 1 |
Step increment for value changes |
value |
Number/Array | null |
Initial value. For single: number. For range: [minValue, maxValue] array |
showInputs |
Boolean | false |
If true, displays input fields for direct value entry |
theme |
String | 'default' |
Theme preset: 'default' (neutral track, primary filled/handles) or 'primary' (all primary) |
trackTheme |
String | null |
Override track color: 'neutral' or 'primary' |
filledTheme |
String | null |
Override filled track color: 'neutral' or 'primary' |
handleTheme |
String | null |
Override handle color: 'neutral' or 'primary' |
continuousUpdates |
Boolean | false |
If true, fires onChange continuously during drag (throttled by throttleMs). Final value always sent on drag end. |
throttleMs |
Number | 16 |
Throttle interval in ms for continuous updates (~60fps at 16ms). Only applies when continuousUpdates is true. |
disabled |
Boolean | false |
If true, disables the slider |
onChange |
Function | null |
Callback (value, source) when value changes. Fires on drag end (always), track click, keyboard, and during drag if continuousUpdates is true. |
onInputChange |
Function | null |
Callback (value, source) when value changes via input field |
API Methods:
getValue(): Returns current value(s). For single: number. For range:[min, max]arraysetValue(value, source, triggerCallback): Sets value programmatically. Values are clamped and validatedsetDisabled(disabled): Enables or disables the sliderdestroy(): Removes event listeners and cleans up DOM
Features:
- Single value mode (one handle) or range mode (two handles)
- Optional input fields (positioned on either side for range sliders)
- Mouse, touch, and keyboard interaction
- Automatic value clamping and step snapping
- Customizable themes (track, filled track, and handles can be themed separately)
- Full accessibility support (ARIA attributes, keyboard navigation)
Example:
// Single value slider
const slider = new NumericSlider('#slider', {
type: 'single',
min: 0,
max: 100,
value: 50,
showInputs: true,
onChange: (value) => {
console.log('Value:', value);
}
});
// Range slider with custom theme
const rangeSlider = new NumericSlider('#range-slider', {
type: 'range',
min: 0,
max: 100,
value: [20, 60],
showInputs: true,
trackTheme: 'neutral',
filledTheme: 'primary',
handleTheme: 'primary',
onChange: (values) => {
console.log('Range:', values[0], '-', values[1]);
}
});
// Continuous updates during drag (for live previews)
const liveSlider = new NumericSlider('#live-slider', {
type: 'single',
value: 50,
continuousUpdates: true, // Fire onChange during drag
throttleMs: 16, // ~60fps (default)
onChange: (value) => {
// Fires continuously while dragging (throttled) AND on drag end
updatePreview(value);
}
});
// Later...
slider.setValue(75);
rangeSlider.setValue([30, 70]);
const currentValue = slider.getValue(); // 75
const currentRange = rangeSlider.getValue(); // [30, 70]Dependencies: colors.css, spacing.css, typography.css, input/input.css
Import:
import SplitPanel from '/design-system/components/split-panel/split-panel.js';Initialization:
const splitPanel = new SplitPanel(selector, options);Configuration Options:
| Option | Type | Default | Description |
|---|---|---|---|
orientation |
String | 'horizontal' |
Panel orientation: 'horizontal' (left/right) or 'vertical' (top/bottom) |
initialSplit |
Number | 50 |
Initial split percentage (0-100) for left/top panel |
minLeft |
Number | 10 |
Minimum percentage (0-100) allowed for left/top panel |
minRight |
Number | 10 |
Minimum percentage (0-100) allowed for right/bottom panel |
disabled |
Boolean | false |
If true, disables resizing of the split panel |
onChange |
Function | null |
Callback (percent) when split changes |
API Methods:
getSplit(): Returns current split percentage (0-100)setSplit(percent, skipCallback): Sets split percentage programmatically. Values are clamped to min/max constraintsgetLeftPanel(): Returns the left/top panel elementgetRightPanel(): Returns the right/bottom panel elementsetDisabled(disabled): Enables or disables the split paneldestroy(): Removes event listeners and cleans up DOM
Features:
- Resizable panels with draggable divider
- Mouse, touch, and keyboard interaction (arrow keys, Home, End)
- Configurable minimum panel sizes
- Automatic resize handling when container resizes
- Visual feedback: divider line expands to 4px and turns primary blue when focused/dragging
- Dark mode support
- Full accessibility support (ARIA attributes, keyboard navigation)
Example:
// Basic split panel
const splitPanel = new SplitPanel('#my-split-panel', {
initialSplit: 40,
minLeft: 20,
minRight: 30,
onChange: (percent) => {
console.log('Split changed to:', percent + '%');
}
});
// Add content to panels
splitPanel.getLeftPanel().innerHTML = '<p>Left Panel Content</p>';
splitPanel.getRightPanel().innerHTML = '<p>Right Panel Content</p>';
// Vertical orientation
const verticalPanel = new SplitPanel('#vertical-panel', {
orientation: 'vertical',
initialSplit: 50,
minLeft: 25,
minRight: 25
});
// Later...
splitPanel.setSplit(60);
const currentSplit = splitPanel.getSplit(); // 60
splitPanel.setDisabled(true);
splitPanel.destroy();Dependencies: colors.css, spacing.css
Import:
import Modal from '/design-system/components/modal/modal.js';Initialization:
const modal = new Modal(options);Configuration Options:
| Option | Type | Default | Description |
|---|---|---|---|
size |
String | 'medium' |
Modal size: 'small' (400px), 'medium' (600px), 'large' (900px), 'xlarge' (1200px) |
title |
String | null |
Modal title text. If null, header is hidden |
content |
String/Element | null |
Modal content. Can be HTML string, DOM element, CSS selector (e.g., '#my-content'), or template content |
showCloseButton |
Boolean | true |
If true, displays close button (X) in header |
footerButtons |
Array | null |
Array of button configs [{label, type, onClick}]. If null, footer is hidden |
closeOnOverlayClick |
Boolean | true |
If true, clicking overlay closes modal |
closeOnEscape |
Boolean | true |
If true, pressing Escape closes modal |
onOpen |
Function | null |
Callback triggered when modal opens. Receives (modal) instance |
onClose |
Function | null |
Callback triggered when modal closes. Receives (modal) instance |
API Methods:
open(): Opens the modal and locks body scrollclose(): Closes the modal and restores body scrollupdateContent(content): Updates the modal content dynamically. Accepts same content types as constructorupdateTitle(title): Updates the modal title. Creates title element if it doesn't existdestroy(): Removes the modal from DOM and cleans up event listeners
Content Types:
- HTML String:
content: '<p>HTML content</p>' - DOM Element:
content: document.createElement('div') - CSS Selector:
content: '#my-content'orcontent: '.my-class'(clones the element) - Template Content:
content: template.content.cloneNode(true)
Help Modal Variant:
- Static Method:
Modal.createHelpModal(options)- Convenience method optimized for help/documentation content - Defaults: xlarge size, footer with close button
- Recommended: Use HTML
<template>elements for help content - Styled Elements: Automatically styles
.toc(table of contents),<section>,<details>(FAQ),<code>, and images - Example:
const helpModal = Modal.createHelpModal({ title: 'Help', content: template.content.cloneNode(true) })
Features:
- Multiple content insertion methods (HTML string, DOM element, CSS selector, template)
- Flexible sizing (small, medium, large, xlarge)
- Optional header and footer
- Customizable footer buttons with click handlers
- Body scroll locking when open
- Focus management for accessibility
- Keyboard navigation (Escape to close)
- ARIA attributes for screen readers
- Help modal variant with specialized styling for documentation content
Example:
// Basic modal with HTML string
const modal = new Modal({
title: 'Basic Modal',
content: '<p class="body-medium">This is a basic modal.</p>',
footerButtons: [
{ label: 'Close', type: 'primary' }
]
});
modal.open();
// Modal with CSS selector content
const modal2 = new Modal({
title: 'Selector Modal',
content: '#my-hidden-content',
footerButtons: [
{ label: 'Cancel', type: 'secondary' },
{ label: 'Save', type: 'primary', onClick: () => console.log('Saved!') }
]
});
modal2.open();
// Modal with form content
const formHTML = `
<form>
<input type="text" class="input" placeholder="Name">
<input type="email" class="input" placeholder="Email">
</form>
`;
const formModal = new Modal({
size: 'large',
title: 'User Information',
content: formHTML,
footerButtons: [
{ label: 'Cancel', type: 'secondary', onClick: () => formModal.close() },
{ label: 'Submit', type: 'primary', onClick: () => { alert('Submitted!'); formModal.close(); } }
]
});
formModal.open();
// Help modal with template
const template = document.querySelector('#help-template');
const helpModal = Modal.createHelpModal({
title: 'Help',
content: template.content.cloneNode(true)
});
helpModal.open();
// Later...
modal.close();
modal.updateContent('<p>Updated content</p>');
modal.destroy();Dependencies: colors.css, spacing.css, typography.css, button/button.css
Import:
import HorizontalCards from '/design-system/components/horizontal-cards/horizontal-cards.js';Initialization:
const horizontalCards = new HorizontalCards(container, options);Configuration Options:
| Option | Type | Default | Description |
|---|---|---|---|
cards |
Array | [] |
Array of card objects. Each card can have title (HTML), description (HTML), actionPlaceholder, or actionHtml. |
cardWidth |
Number | 480 |
Width of each card in pixels. |
cardGap |
Number | 24 |
Gap between cards in pixels (matches --UI-Spacing-spacing-mxl). |
scrollOffset |
Number | 520 |
Number of pixels to scroll per navigation action (typically cardWidth + cardGap). |
showNavigation |
Boolean | true |
If true, displays previous/next navigation buttons. |
onCardChange |
Function | null |
Callback function triggered when the visible card changes. Receives (index, card). |
Card Object Structure:
| Property | Type | Required | Description |
|---|---|---|---|
title |
String | No | Card title displayed as a heading. Supports HTML content (e.g., <strong>, <em>, <code>, links, etc.). |
description |
String | No | Card description text. Supports HTML content (e.g., <strong>, <em>, <code>, links, spans with styling, etc.). |
actionPlaceholder |
String | No | Placeholder text for the action area (displays in a dashed border box). |
actionHtml |
String | No | Custom HTML content for the action area. If provided, actionPlaceholder is ignored. |
API Methods:
getCurrentIndex(): Returns the current visible card index (0-based).getCurrentCard(): Returns the current visible card object.scrollToNext(): Scrolls to the next card.scrollToPrevious(): Scrolls to the previous card.scrollToIndex(index): Scrolls to a specific card by index.destroy(): Removes event listeners and clears the container.
Features:
- Smooth horizontal scrolling with card centering
- Optional navigation buttons (previous/next)
- Keyboard navigation (arrow keys)
- Touch/swipe support for mobile devices
- Automatic card centering in viewport
- HTML support in title and description fields
- Responsive design
- Dark mode support
- Accessibility (ARIA attributes, keyboard support)
Example:
const cards = new HorizontalCards('#my-cards', {
cards: [
{
title: 'Boss 1',
description: 'You start your presentation with a bold vision and a simple chart showing the potential for rapid market growth.',
actionPlaceholder: 'Add label'
},
{
title: 'Card with <strong>HTML</strong>',
description: 'This description has <strong>bold text</strong> and <em>italic text</em>.',
actionHtml: '<button class="button button-primary">Click Me</button>'
}
],
onCardChange: (index, card) => {
console.log('Current card:', index, card);
}
});
// Later...
cards.scrollToIndex(2);
const currentCard = cards.getCurrentCard();Dependencies: colors.css, spacing.css, typography.css
Components can be combined and nested:
<!-- Button with icon -->
<button class="button button-primary">
<span class="icon icon-jobs icon-small"></span>
Submit
</button>
<!-- Tag with icon -->
<div class="tag success">
<span class="icon icon-check icon-small"></span>
Completed
</div>
<!-- Box with input and button -->
<div class="box">
<input type="text" class="input" placeholder="Search...">
<button class="button button-primary button-small">Search</button>
</div>You can extend components using CSS custom properties:
.my-custom-button {
/* Inherit button styles */
composes: button button-primary;
/* Override with custom properties */
--Colors-Base-Primary-700: #custom-color;
}Use standard CSS media queries with design tokens:
@media (max-width: 768px) {
.responsive-box {
padding: var(--UI-Spacing-spacing-s);
}
}✅ DO:
color: var(--Colors-Text-Body-Default);
padding: var(--UI-Spacing-spacing-m);
border-radius: var(--UI-Radius-radius-m);❌ DON'T:
color: #333;
padding: 16px;
border-radius: 8px;✅ DO:
<button class="button button-primary">Click</button>❌ DON'T:
<button class="btn btn-primary">Click</button>✅ DO: Use semantic tokens (automatic dark mode)
background: var(--Colors-Backgrounds-Main-Default);❌ DON'T: Use hardcoded colors
background: #ffffff;✅ DO: Load foundations before components
<!-- Foundations first -->
<link rel="stylesheet" href="colors/colors.css">
<link rel="stylesheet" href="spacing/spacing.css">
<link rel="stylesheet" href="typography/typography.css">
<!-- Then components -->
<link rel="stylesheet" href="components/button/button.css">✅ DO:
<span class="icon icon-jobs icon-large icon-primary"></span>❌ DON'T: Use inline SVG or img tags for icons
<img src="icon.svg" alt="icon">- Always include proper
alttext for images - Use semantic HTML (
<button>,<input>, etc.) - Ensure keyboard navigation works
- Test with screen readers
design-system/
├── colors/
│ ├── colors.css # Color tokens (base + semantic)
│ ├── README.md
│ └── test.html
├── spacing/
│ ├── spacing.css # Spacing, radius, input height tokens
│ ├── README.md
│ └── test.html
├── typography/
│ ├── typography.css # Typography classes and font definitions
│ ├── README.md
│ └── test.html
├── fonts/
│ └── FoundersGrotesk-*.woff2
├── components/
│ ├── button/
│ │ ├── button.css
│ │ ├── README.md
│ │ └── test.html
│ ├── boxes/
│ │ ├── boxes.css
│ │ ├── README.md
│ │ └── test.html
│ ├── dropdown/
│ │ ├── dropdown.css
│ │ ├── dropdown.js
│ │ ├── README.md
│ │ └── test.html
│ ├── horizontal-cards/
│ │ ├── horizontal-cards.css
│ │ ├── horizontal-cards.js
│ │ ├── README.md
│ │ └── test.html
│ ├── icons/
│ │ ├── icons.css # 80+ icon definitions
│ │ ├── README.md
│ │ └── test.html
│ ├── input/
│ │ ├── input.css
│ │ ├── README.md
│ │ └── test.html
│ ├── modal/
│ │ ├── modal.css
│ │ ├── modal.js
│ │ ├── README.md
│ │ └── test.html
│ ├── numeric-slider/
│ │ ├── numeric-slider.css
│ │ ├── numeric-slider.js
│ │ ├── README.md
│ │ └── test.html
│ ├── split-panel/
│ │ ├── split-panel.css
│ │ ├── split-panel.js
│ │ ├── README.md
│ │ └── test.html
│ ├── table/
│ │ ├── table.css
│ │ ├── README.md
│ │ └── test.html
│ └── tags/
│ ├── tags.css
│ ├── README.md
│ └── test.html
├── test.html # Test bed navigation
├── README.md
├── llms.txt # Quick reference for LLMs
└── agents.md # This file
Each component includes a test.html file demonstrating usage. The main test bed is available at:
http://[your-server]/design-system/test.html
This provides:
- Sidebar navigation to all components
- Interactive examples
- Code snippets
- Visual regression testing
- Design System Version: Current
- Browser Support: Modern browsers (Chrome, Firefox, Safari, Edge)
- CSS Features Used: CSS Custom Properties, CSS Grid, Flexbox, CSS Masks
- JavaScript: ES6 Modules (Dropdown, Numeric Slider, and Modal components)
- Main README: See
README.mdfor overview - Component READMEs: Each component has detailed documentation
- Test Files: See
test.htmlfiles for examples - Demo Site: https://codesignal.github.io/learn_bespoke-design-system/test.html
For questions or issues:
- Check component-specific README files
- Review test.html examples
- Inspect CSS files for available tokens and classes