├── fonts ├── JetBrainsMono-Medium.woff2 ├── FoundersGrotesk-Light.woff2 ├── FoundersGrotesk-Medium.woff2 ├── WorkSans-VariableFont_wght.ttf ├── JetBrainsMono-Medium-Italic.woff2 ├── FoundersGrotesk-Light-Italic.woff2 ├── FoundersGrotesk-Medium-Italic.woff2 └── WorkSans-Italic-VariableFont_wght.ttf ├── spacing ├── spacing.css └── README.md ├── components ├── boxes │ ├── README.md │ ├── boxes.css │ └── test.html ├── button │ ├── README.md │ └── button.css ├── dropdown │ ├── README.md │ ├── dropdown.css │ └── test.html ├── icons │ └── README.md ├── tags │ ├── README.md │ ├── test.html │ └── tags.css ├── modal │ ├── help-modal-template.html │ ├── modal.css │ ├── README.md │ └── modal.js ├── horizontal-cards │ ├── README.md │ ├── horizontal-cards.css │ └── test.html ├── numeric-slider │ ├── numeric-slider.css │ └── README.md └── input │ └── README.md ├── colors └── README.md ├── typography ├── README.md ├── typography.css └── test.html ├── test-server.js ├── LICENSE.md ├── README.md ├── llms.txt └── test.html /fonts/JetBrainsMono-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSignal/learn_bespoke-design-system/main/fonts/JetBrainsMono-Medium.woff2 -------------------------------------------------------------------------------- /fonts/FoundersGrotesk-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSignal/learn_bespoke-design-system/main/fonts/FoundersGrotesk-Light.woff2 -------------------------------------------------------------------------------- /fonts/FoundersGrotesk-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSignal/learn_bespoke-design-system/main/fonts/FoundersGrotesk-Medium.woff2 -------------------------------------------------------------------------------- /fonts/WorkSans-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSignal/learn_bespoke-design-system/main/fonts/WorkSans-VariableFont_wght.ttf -------------------------------------------------------------------------------- /fonts/JetBrainsMono-Medium-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSignal/learn_bespoke-design-system/main/fonts/JetBrainsMono-Medium-Italic.woff2 -------------------------------------------------------------------------------- /fonts/FoundersGrotesk-Light-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSignal/learn_bespoke-design-system/main/fonts/FoundersGrotesk-Light-Italic.woff2 -------------------------------------------------------------------------------- /fonts/FoundersGrotesk-Medium-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSignal/learn_bespoke-design-system/main/fonts/FoundersGrotesk-Medium-Italic.woff2 -------------------------------------------------------------------------------- /fonts/WorkSans-Italic-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSignal/learn_bespoke-design-system/main/fonts/WorkSans-Italic-VariableFont_wght.ttf -------------------------------------------------------------------------------- /spacing/spacing.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --UI-Spacing-spacing-none: 0; 3 | --UI-Spacing-spacing-min: 2px; 4 | --UI-Spacing-spacing-xxs: 4px; 5 | --UI-Spacing-spacing-xs: 6px; 6 | --UI-Spacing-spacing-s: 8px; 7 | --UI-Spacing-spacing-mxs: 12px; 8 | --UI-Spacing-spacing-ms: 16px; 9 | --UI-Spacing-spacing-m: 18px; 10 | --UI-Spacing-spacing-ml: 20px; 11 | --UI-Spacing-spacing-mxl: 24px; 12 | --UI-Spacing-spacing-l: 28px; 13 | --UI-Spacing-spacing-xl: 32px; 14 | --UI-Spacing-spacing-xxl: 36px; 15 | --UI-Spacing-spacing-xxxl: 48px; 16 | --UI-Spacing-spacing-4xl: 60px; 17 | --UI-Spacing-spacing-max: 90px; 18 | 19 | --UI-Radius-radius-none: 0; 20 | --UI-Radius-radius-min: 2px; 21 | --UI-Radius-radius-xxs: 4px; 22 | --UI-Radius-radius-xs: 6px; 23 | --UI-Radius-radius-s: 8px; 24 | --UI-Radius-radius-m: 12px; 25 | --UI-Radius-radius-ml: 16px; 26 | --UI-Radius-radius-mxl: 20px; 27 | --UI-Radius-radius-l: 24px; 28 | --UI-Radius-radius-xl: 32px; 29 | 30 | --UI-Input-min: 26px; 31 | --UI-Input-xs: 32px; 32 | --UI-Input-sm: 40px; 33 | --UI-Input-md: 48px; 34 | --UI-Input-lg: 60px; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /spacing/README.md: -------------------------------------------------------------------------------- 1 | # Spacing & Layout Design System 2 | 3 | This directory contains the spacing, radius, and sizing definitions for the application. 4 | 5 | ## Usage 6 | 7 | Import the CSS file in your HTML or CSS: 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | or 14 | 15 | ```css 16 | @import url('/design-system/spacing/spacing.css'); 17 | ``` 18 | 19 | ## Spacing Scale 20 | 21 | Use these variables for padding, margin, gap, and positioning. 22 | 23 | Pattern: `--UI-Spacing-spacing-[Size]` 24 | 25 | - `none`: 0 26 | - `min`: 2px 27 | - `xxs`: 4px 28 | - `xs`: 6px 29 | - `s`: 8px 30 | - `mxs`: 12px 31 | - `ms`: 16px 32 | - `m`: 18px 33 | - `ml`: 20px 34 | - `mxl`: 24px 35 | - `l`: 28px 36 | - `xl`: 32px 37 | - `xxl`: 36px 38 | - `xxxl`: 48px 39 | - `4xl`: 60px 40 | - `max`: 90px 41 | 42 | ## Border Radius 43 | 44 | Use these variables for border-radius properties. 45 | 46 | Pattern: `--UI-Radius-radius-[Size]` 47 | 48 | - `none`: 0 49 | - `min`: 2px 50 | - `xxs`: 4px 51 | - `xs`: 6px 52 | - `s`: 8px 53 | - `m`: 12px 54 | - `ml`: 16px 55 | - `mxl`: 20px 56 | - `l`: 24px 57 | - `xl`: 32px 58 | 59 | ## Input Sizing 60 | 61 | Standard heights for inputs, buttons, and interactive elements. 62 | 63 | Pattern: `--UI-Input-[Size]` 64 | 65 | - `min`: 26px 66 | - `xs`: 32px 67 | - `sm`: 40px 68 | - `md`: 48px 69 | - `lg`: 60px 70 | 71 | -------------------------------------------------------------------------------- /components/boxes/README.md: -------------------------------------------------------------------------------- 1 | # Box Component 2 | 3 | A flexible container component matching the CodeSignal Design System. 4 | 5 | ## Usage 6 | 7 | Import the CSS file in your HTML or CSS: 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | or 14 | 15 | ```css 16 | @import url('/design-system/components/boxes/boxes.css'); 17 | ``` 18 | 19 | ## Classes 20 | 21 | ### Base Class 22 | - `.box`: The base class required for all box containers. Provides default padding, radius, and background. 23 | 24 | ### Variants 25 | - `.box.selected`: Selected state with a primary color border. 26 | - `.box.emphasized`: Emphasized state with a neutral border (useful for active states). 27 | - `.box.shadowed`: Adds a soft shadow (`--Colors-Shadow-Soft`). 28 | - `.box.card`: Adds a card-style shadow (`--Colors-Shadow-Card`). 29 | - `.box.input-group`: Input group variant with reduced padding and gap spacing, designed for grouping input elements together. 30 | 31 | ### States 32 | The component supports standard pseudo-classes (`:hover`, `:focus`, `:active`) and utility classes for manual state application: 33 | - `.hover` 34 | - `.focus` 35 | - `.selected` (acts as active/checked state) 36 | 37 | ## Examples 38 | 39 | ```html 40 | 41 |
42 | Content 43 |
44 | 45 | 46 |
47 | Selected Content 48 |
49 | 50 | 51 |
52 | Card Content 53 |
54 | 55 | 56 |
57 | 58 | 59 |
60 | ``` 61 | 62 | ## Dependencies 63 | 64 | This component relies on variables from: 65 | - `design-system/colors/colors.css` 66 | - `design-system/spacing/spacing.css` 67 | -------------------------------------------------------------------------------- /components/button/README.md: -------------------------------------------------------------------------------- 1 | # Button Component 2 | 3 | A versatile button component matching the CodeSignal Design System. 4 | 5 | ## Usage 6 | 7 | Import the CSS file in your HTML or CSS: 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | or 14 | 15 | ```css 16 | @import url('/design-system/components/button/button.css'); 17 | ``` 18 | 19 | ## Classes 20 | 21 | ### Base Class 22 | - `.button`: The base class required for all buttons. 23 | 24 | ### Variants 25 | - `.button-primary`: Primary action button (Brand Blue). 26 | - `.button-secondary`: Secondary action button (Outlined). 27 | - `.button-tertiary`: Tertiary/Ghost button (Subtle background). 28 | - `.button-danger`: Destructive action button (Red). 29 | - `.button-success`: Positive action button (Green). 30 | - `.button-text`: Default Text button (Neutral text, no background). 31 | - `.button-text-primary`: Primary Text button (Brand color text, no background). 32 | 33 | ### Sizes 34 | - `.button-xsmall`: Extra small size (32px height). 35 | - `.button-small`: Small size (40px height). 36 | - `.button-large`: Large size (60px height). 37 | - Default size is Medium (48px height) if no size class is provided. 38 | 39 | ### States 40 | The component supports standard pseudo-classes (`:hover`, `:focus`, `:active`, `:disabled`) and utility classes for manual state application: 41 | - `.hover` 42 | - `.focus` 43 | - `.active` 44 | - `.disabled` 45 | 46 | ## HTML Example 47 | 48 | ```html 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | ## Dependencies 63 | 64 | This component relies on variables from: 65 | - `design-system/colors/colors.css` 66 | - `design-system/spacing/spacing.css` 67 | - `design-system/typography/typography.css` 68 | 69 | -------------------------------------------------------------------------------- /components/dropdown/README.md: -------------------------------------------------------------------------------- 1 | # Dropdown Component 2 | 3 | A customizable, accessible dropdown component matching the CodeSignal Design System. 4 | 5 | ## Usage 6 | 7 | ### 1. Import Assets 8 | 9 | Include the CSS and JS files: 10 | 11 | ```html 12 | 13 | 17 | ``` 18 | 19 | ### 2. Create Container 20 | 21 | Add a container element to your HTML where the dropdown will be rendered: 22 | 23 | ```html 24 |
25 | ``` 26 | 27 | ### 3. Initialize Component 28 | 29 | ```javascript 30 | const dropdown = new Dropdown('#my-dropdown', { 31 | placeholder: 'Select an option', 32 | items: [ 33 | { value: '1', label: 'Option 1' }, 34 | { value: '2', label: 'Option 2' } 35 | ], 36 | onSelect: (value, item) => { 37 | console.log('Selected:', value); 38 | } 39 | }); 40 | ``` 41 | 42 | ## Configuration Options 43 | 44 | | Option | Type | Default | Description | 45 | | :--- | :--- | :--- | :--- | 46 | | `items` | Array | `[]` | Array of objects with `value` and `label` properties. | 47 | | `placeholder` | String | `'Select option'` | Text displayed when no item is selected. | 48 | | `selectedValue` | String | `null` | Initial selected value. | 49 | | `width` | String/Number | `'auto'` | Fixed width of the dropdown (e.g., `200`, `'100%'`). Ignored if `growToFit` is true. | 50 | | `growToFit` | Boolean | `false` | If `true`, the dropdown automatically resizes to fit the selected content. | 51 | | `onSelect` | Function | `null` | Callback function triggered when an item is selected. Receives `(value, item)`. | 52 | 53 | ## API Methods 54 | 55 | - **`getValue()`**: Returns the current selected value. 56 | - **`setValue(value)`**: Programmatically sets the selected value. 57 | - **`open()`**: Opens the dropdown menu. 58 | - **`close()`**: Closes the dropdown menu. 59 | - **`toggleOpen()`**: Toggles the open state. 60 | - **`destroy()`**: Removes event listeners and clears the container. 61 | 62 | ## Dependencies 63 | 64 | This component relies on variables from: 65 | - `design-system/colors/colors.css` 66 | - `design-system/spacing/spacing.css` 67 | - `design-system/typography/typography.css` 68 | 69 | -------------------------------------------------------------------------------- /colors/README.md: -------------------------------------------------------------------------------- 1 | # Colors Design System 2 | 3 | This directory contains the color system definitions for the application. 4 | 5 | ## Usage 6 | 7 | Import the CSS file in your HTML or CSS: 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | or 14 | 15 | ```css 16 | @import url('/design-system/colors/colors.css'); 17 | ``` 18 | 19 | ## Variable Structure 20 | 21 | The color system is divided into two layers: 22 | 23 | ### 1. Base Scales (Primitive Tokens) 24 | 25 | These are the raw color values defined on numbered scales. **Avoid using these directly** in your components if a semantic alternative exists. 26 | 27 | Pattern: `--Colors-Base-[Family]-[Scale]-[Step]` 28 | 29 | Examples: 30 | - `--Colors-Base-Primary-700` 31 | - `--Colors-Base-Neutral-00` 32 | - `--Colors-Base-Accent-Green-500` 33 | - `--Colors-Base-Neutral-Alphas-1000-25` (Alpha variants) 34 | 35 | Families: 36 | - `Primary`: Main brand colors (Blue) 37 | - `Neutral`: Grays, White, Black 38 | - `Accent-Green`: Success states 39 | - `Accent-Sky-Blue`: Info states 40 | - `Accent-Yellow`: Warning states 41 | - `Accent-Orange`: Warning states 42 | - `Accent-Red`: Error/Danger states 43 | 44 | ### 2. Semantic Names (Contextual Tokens) 45 | 46 | These variables map the base colors to specific usage contexts. **Prefer using these variables** to ensure consistency and support for theming (e.g., Dark Mode). 47 | 48 | Categories: 49 | 50 | - **Primary**: Brand colors 51 | - e.g., `--Colors-Primary-Default`, `--Colors-Primary-Medium` 52 | - **Backgrounds**: Surface colors 53 | - e.g., `--Colors-Backgrounds-Main-Default`, `--Colors-Backgrounds-Main-Top` 54 | - **Text**: Typography colors 55 | - e.g., `--Colors-Text-Body-Default`, `--Colors-Text-Body-Strong` 56 | - **Icon**: Iconography colors 57 | - e.g., `--Colors-Icon-Default`, `--Colors-Icon-Primary` 58 | - **Stroke**: Border and divider colors 59 | - e.g., `--Colors-Stroke-Default`, `--Colors-Stroke-Strong` 60 | - **Alert**: Feedback colors (Success, Error, Warning, Info) 61 | - e.g., `--Colors-Alert-Success-Default`, `--Colors-Alert-Error-Medium` 62 | 63 | ## Dark Mode 64 | 65 | The system automatically handles Dark Mode via the `@media (prefers-color-scheme: dark)` query. By using the **Semantic Names**, your components will automatically adapt to the user's system preference. 66 | 67 | -------------------------------------------------------------------------------- /components/icons/README.md: -------------------------------------------------------------------------------- 1 | # Icons Component 2 | 3 | Scalable SVG icons using standard CodeSignal Design System tokens. Icons are embedded as CSS content using SVG data URIs. 4 | 5 | ## Usage 6 | 7 | Import the CSS file: 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | or 14 | 15 | ```css 16 | @import url('/design-system/components/icons/icons.css'); 17 | ``` 18 | 19 | ## Classes 20 | 21 | ### Base Class 22 | - `.icon`: Required base class. Sets display, size, and positioning properties. 23 | 24 | ### Icon Names 25 | Use the specific icon class to set the SVG image. Icon names are derived from SVG filenames (e.g., `Icon=Academy.svg` becomes `.icon-academy`): 26 | 27 | - `.icon-academy` 28 | - `.icon-assessment` 29 | - `.icon-ellipse` 30 | - `.icon-eraser` 31 | - `.icon-interview` 32 | - `.icon-jobs` 33 | - `.icon-course` 34 | - ... (see `icons.css` for full list of 80+ icons) 35 | 36 | ### Sizes 37 | - `.icon-small` or `.icon.icon-small`: 16px 38 | - `.icon-medium` or `.icon.icon-medium`: 24px (default) 39 | - `.icon-large` or `.icon.icon-large`: 32px 40 | - `.icon-xlarge` or `.icon.icon-xlarge`: 48px 41 | 42 | ### Colors 43 | Icons use `currentColor` by default, meaning they will take the text color of their parent or their own `color` property. Color utility classes are also available: 44 | 45 | - `.icon-primary` or `.icon.icon-primary`: Primary brand color 46 | - `.icon-secondary` or `.icon.icon-secondary`: Secondary neutral color 47 | - `.icon-success` or `.icon.icon-success`: Success green color 48 | - `.icon-danger` or `.icon.icon-danger`: Danger red color 49 | - `.icon-warning` or `.icon.icon-warning`: Warning yellow color 50 | 51 | ## HTML Example 52 | 53 | ```html 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ``` 66 | 67 | ## Dependencies 68 | 69 | This component relies on variables from: 70 | - `design-system/colors/colors.css` 71 | - `design-system/spacing/spacing.css` 72 | - `design-system/typography/typography.css` 73 | 74 | -------------------------------------------------------------------------------- /typography/README.md: -------------------------------------------------------------------------------- 1 | # Typography Design System 2 | 3 | This directory contains the typography system definitions for the application. 4 | 5 | ## Usage 6 | 7 | Import the CSS file in your HTML or CSS: 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | or 14 | 15 | ```css 16 | @import url('/design-system/typography/typography.css'); 17 | ``` 18 | 19 | ## Font Families 20 | 21 | The system uses two main font families: 22 | 23 | - **Body & Labels**: `Work Sans` (Variable, sans-serif) 24 | - **Headings & Elegant**: `Founders Grotesk` (sans-serif) 25 | - **Code**: `JetBrains Mono` (monospace) 26 | 27 | ## CSS Classes 28 | 29 | Use these utility classes to apply typography styles to your elements. 30 | 31 | ### Body Text 32 | Standard body text styles using `Work Sans`. 33 | 34 | - `.body-xxsmall` (13px) 35 | - `.body-xsmall` (14px) 36 | - `.body-small` (15px) 37 | - `.body-medium` (16px) 38 | - `.body-large` (17px) 39 | - `.body-xlarge` (19px) 40 | - `.body-xxlarge` (21px) 41 | - `.body-xxxlarge` (24px) 42 | 43 | ### Body Elegant 44 | Elegant body styles using `Founders Grotesk`. 45 | 46 | - `.body-elegant-xxsmall` (22px) 47 | - `.body-elegant-xsmall` (26px) 48 | - `.body-elegant-small` (32px) 49 | - `.body-elegant-medium` (38px) 50 | 51 | ### Headings 52 | Heading styles using `Founders Grotesk` with medium weight (500). 53 | 54 | - `.heading-xxxsmall` (16px) 55 | - `.heading-xxsmall` (22px) 56 | - `.heading-xsmall` (22px) 57 | - `.heading-small` (24px) 58 | - `.heading-medium` (32px) 59 | - `.heading-large` (38px) 60 | - `.heading-xlarge` (48px) 61 | - `.heading-xxlarge` (64px) 62 | 63 | ### Labels 64 | Uppercase labels using `Work Sans` with semi-bold weight (600). 65 | 66 | - `.label-small` (10px) 67 | - `.label-medium` (11px) 68 | - `.label-large` (14px) 69 | 70 | ### Label Numbers 71 | Numeric labels using `Work Sans` with medium weight (500). 72 | 73 | - `.label-number-xsmall` (11px) 74 | - `.label-number-small` (12px) 75 | - `.label-number-medium` (14px) 76 | - `.label-number-large` (15px) 77 | 78 | ### Code 79 | Code/monospace text styles using `JetBrains Mono` with semi-bold weight (600). 80 | 81 | - `.code-small` (11px) 82 | - `.code-medium` (16px) 83 | - `.code-large` (17px) 84 | - `.code` (19px) 85 | 86 | ## CSS Variables 87 | 88 | While classes are preferred for consistency, you can access the raw values via CSS variables if needed for custom components. 89 | 90 | Pattern: `--Fonts-[Category]-[Size]` 91 | 92 | Examples: 93 | - `--Fonts-Body-Default-md` 94 | - `--Fonts-Headlines-xl` 95 | - `--Fonts-Special-sm` 96 | 97 | -------------------------------------------------------------------------------- /components/tags/README.md: -------------------------------------------------------------------------------- 1 | # Tag Component 2 | 3 | A versatile tag component matching the CodeSignal Design System. Tags are used to display labels, categories, or status indicators. 4 | 5 | ## Usage 6 | 7 | Import the CSS file in your HTML or CSS: 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | or 14 | 15 | ```css 16 | @import url('/design-system/components/tags/tags.css'); 17 | ``` 18 | 19 | ## Classes 20 | 21 | ### Base Class 22 | - `.tag` or `.tag.default`: The base class required for all tags. Provides default styling (Primary variant). 23 | 24 | ### Variants 25 | - `.tag` / `.tag.default`: Primary tag (Brand Blue background). 26 | - `.tag.secondary`: Secondary tag (Neutral gray background). 27 | - `.tag.outline`: Outline tag (Transparent background with border). 28 | - `.tag.success`: Success tag (Green background, for positive states). 29 | - `.tag.error`: Error tag (Red background, for error/danger states). 30 | - `.tag.warning`: Warning tag (Yellow background, for warning states). 31 | - `.tag.info`: Info tag (Sky Blue background, for informational states). 32 | 33 | ### States 34 | The component supports standard pseudo-classes (`:hover`, `:focus`, `:active`) and utility classes for manual state application: 35 | - `.hover`: Applies hover state styling. 36 | - `.focus`: Applies focus state styling (includes focus ring). 37 | - `.active`: Applies active state styling (includes border). 38 | 39 | ## HTML Examples 40 | 41 | ```html 42 | 43 |
Tag Label
44 | 45 | 46 |
Category
47 | 48 | 49 |
Filter
50 | 51 | 52 |
Completed
53 | 54 | 55 |
Failed
56 | 57 | 58 |
Pending
59 | 60 | 61 |
New
62 | 63 | 64 |
Hover State
65 | 66 | 67 |
Focus State
68 | 69 | 70 |
Active State
71 | ``` 72 | 73 | ## Dark Mode 74 | 75 | The component automatically adapts to dark mode via the `@media (prefers-color-scheme: dark)` query. All variants are optimized for both light and dark themes. 76 | 77 | ## Dependencies 78 | 79 | This component relies on variables from: 80 | - `design-system/colors/colors.css` 81 | - `design-system/spacing/spacing.css` 82 | - `design-system/typography/typography.css` 83 | 84 | -------------------------------------------------------------------------------- /test-server.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3001; 6 | const DESIGN_SYSTEM_DIR = __dirname; 7 | const PUBLIC_DIR = path.join(__dirname, '..'); 8 | 9 | const MIME_TYPES = { 10 | '.html': 'text/html; charset=utf-8', 11 | '.css': 'text/css; charset=utf-8', 12 | '.js': 'application/javascript; charset=utf-8', 13 | '.json': 'application/json; charset=utf-8', 14 | '.md': 'text/markdown; charset=utf-8', 15 | '.svg': 'image/svg+xml', 16 | '.png': 'image/png', 17 | '.jpg': 'image/jpeg', 18 | '.jpeg': 'image/jpeg', 19 | '.gif': 'image/gif', 20 | '.ico': 'image/x-icon', 21 | '.woff': 'font/woff', 22 | '.woff2': 'font/woff2', 23 | '.ttf': 'font/ttf', 24 | '.txt': 'text/plain; charset=utf-8' 25 | }; 26 | 27 | function isPathInside(child, parent) { 28 | const relative = path.relative(parent, child); 29 | return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative); 30 | } 31 | 32 | function sendFile(res, filePath, status = 200) { 33 | const ext = path.extname(filePath).toLowerCase(); 34 | const contentType = MIME_TYPES[ext] || 'application/octet-stream'; 35 | res.writeHead(status, { 36 | 'Content-Type': contentType, 37 | 'Cache-Control': 'no-store' 38 | }); 39 | const read = fs.createReadStream(filePath); 40 | read.on('error', () => { 41 | res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' }); 42 | res.end('Internal Server Error'); 43 | }); 44 | read.pipe(res); 45 | } 46 | 47 | const server = http.createServer((req, res) => { 48 | const urlObj = new URL(req.url, 'http://localhost'); 49 | let pathname = decodeURIComponent(urlObj.pathname || '/'); 50 | 51 | // Default to test.html 52 | if (pathname === '/') { 53 | pathname = '/test.html'; 54 | } 55 | 56 | // Remove leading slash and resolve path 57 | const relativePath = pathname.startsWith('/') ? pathname.slice(1) : pathname; 58 | 59 | // Resolve file path - check in design-system directory first, then public directory 60 | let fsPath = path.join(DESIGN_SYSTEM_DIR, relativePath); 61 | 62 | // If file doesn't exist in design-system, try public directory 63 | if (!fs.existsSync(fsPath)) { 64 | fsPath = path.join(PUBLIC_DIR, relativePath); 65 | } 66 | 67 | // Prevent directory traversal 68 | if (!isPathInside(fsPath, DESIGN_SYSTEM_DIR) && !isPathInside(fsPath, PUBLIC_DIR)) { 69 | res.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' }); 70 | res.end('Forbidden'); 71 | return; 72 | } 73 | 74 | fs.stat(fsPath, (err, stats) => { 75 | if (err) { 76 | res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); 77 | res.end('Not Found'); 78 | return; 79 | } 80 | 81 | if (stats.isDirectory()) { 82 | const indexPath = path.join(fsPath, 'index.html'); 83 | if (fs.existsSync(indexPath)) { 84 | sendFile(res, indexPath, 200); 85 | } else { 86 | res.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' }); 87 | res.end('Forbidden'); 88 | } 89 | return; 90 | } 91 | 92 | sendFile(res, fsPath, 200); 93 | }); 94 | }); 95 | 96 | server.listen(PORT, () => { 97 | console.log(`Design System Test Server running at http://localhost:${PORT}`); 98 | console.log(`Open http://localhost:${PORT} in your browser`); 99 | }); 100 | 101 | -------------------------------------------------------------------------------- /components/boxes/boxes.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Box Component Styles 3 | * Matches CodeSignal Design System 4 | */ 5 | 6 | :root { 7 | /* Box Variables */ 8 | --Colors-Backgrounds-Box-Default: var(--Colors-Base-Neutral-00); 9 | --Colors-Box-Background: var(--Colors-Base-Neutral-00); 10 | --Colors-Box-Background-Hover: var(--Colors-Base-Neutral-00); 11 | --Colors-Box-Stroke: var(--Colors-Base-Neutral-150); 12 | --Colors-Box-Stroke-Hover: var(--Colors-Base-Neutral-300); 13 | --Colors-Box-Stroke-Selected-Emphasis: var(--Colors-Base-Primary-600); 14 | --Colors-Box-Stroke-Selected-Hover: var(--Colors-Base-Primary-750); 15 | --Colors-Box-Stroke-Active: var(--Colors-Base-Neutral-300); 16 | --Colors-Box-Stroke-Active-Hover: var(--Colors-Base-Neutral-400); 17 | --Colors-Box-Focus: rgba(200, 205, 219, 0.30); 18 | --Colors-Shadow-Soft: rgba(76, 90, 123, 0.04); 19 | --Colors-Shadow-Card: rgba(76, 90, 123, 0.09); 20 | --Colors-Box-Focus-Shadow: rgba(200, 205, 219, 0.30); 21 | } 22 | 23 | @media (prefers-color-scheme: dark) { 24 | :root { 25 | --Colors-Backgrounds-Box-Default: var(--Colors-Base-Neutral-1250); 26 | --Colors-Box-Background: var(--Colors-Base-Neutral-1200); 27 | --Colors-Box-Background-Hover: var(--Colors-Base-Neutral-1150); 28 | --Colors-Box-Stroke: rgba(45, 56, 85, 0.00); 29 | --Colors-Box-Stroke-Hover: var(--Colors-Base-Neutral-1250); 30 | --Colors-Box-Stroke-Selected-Hover: var(--Colors-Base-Primary-500); 31 | --Colors-Box-Stroke-Active: var(--Colors-Base-Neutral-1100); 32 | --Colors-Box-Stroke-Active-Hover: var(--Colors-Base-Neutral-1000); 33 | --Colors-Box-Focus: rgba(17, 25, 46, .40); 34 | --Colors-Shadow-Soft: rgba(13, 21, 40, 0.16); 35 | --Colors-Shadow-Card: rgba(13, 21, 40, 0.32); 36 | --Colors-Box-Focus-Shadow: rgba(17, 25, 46, 0.40); 37 | } 38 | } 39 | 40 | 41 | .box { 42 | display: flex; 43 | padding: var(--UI-Spacing-spacing-mxl); 44 | justify-content: center; 45 | align-items: center; 46 | gap: var(--UI-Spacing-spacing-none); 47 | align-self: stretch; 48 | 49 | border-radius: var(--UI-Radius-radius-s); 50 | background: var(--Colors-Box-Background); 51 | border: 1.5px solid transparent; 52 | } 53 | 54 | .box:hover, 55 | .box.hover { 56 | border: 1.5px solid var(--Colors-Box-Stroke-Hover); 57 | background: var(--Colors-Box-Background-Hover); 58 | } 59 | 60 | .box:focus, 61 | .box.focus { 62 | border: 1.5px solid var(--Colors-Box-Stroke-Hover); 63 | box-shadow: 0 0 0 4px var(--Colors-Box-Focus-Shadow); 64 | background: var(--Colors-Box-Background-Hover); 65 | } 66 | 67 | .box:active, 68 | .box.selected { 69 | border: 1.5px solid var(--Colors-Box-Stroke-Selected-Emphasis); 70 | } 71 | 72 | .box.selected:hover, 73 | .box.selected.hover { 74 | border: 1.5px solid var(--Colors-Box-Stroke-Selected-Hover); 75 | } 76 | 77 | .box.emphasized { 78 | border: 1.5px solid var(--Colors-Box-Stroke-Active); 79 | } 80 | 81 | .box.emphasized:hover, 82 | .box.emphasized.hover { 83 | border: 1.5px solid var(--Colors-Box-Stroke-Active-Hover); 84 | } 85 | 86 | .box.shadowed { 87 | box-shadow: 0 5px 0 -2px var(--Colors-Shadow-Soft); 88 | } 89 | 90 | .box.shadowed:focus, 91 | .box.shadowed.focus { 92 | box-shadow: 0 0 0 4px var(--Colors-Box-Focus); 93 | } 94 | 95 | .box.card { 96 | box-shadow: 0 3px 2px 0 var(--Colors-Shadow-Card); 97 | } 98 | 99 | .box.card:focus, 100 | .box.card.focus { 101 | box-shadow: 0 0 0 4px var(--Colors-Box-Focus); 102 | } 103 | 104 | .box.input-group { 105 | display: flex; 106 | padding: var(--UI-Spacing-spacing-s) var(--UI-Spacing-spacing-ml); 107 | align-items: flex-start; 108 | gap: var(--UI-Spacing-spacing-xs); 109 | align-self: stretch; 110 | border-radius: var(--UI-Radius-radius-s); 111 | background: var(--Colors-Backgrounds-Box-Default); 112 | justify-content: flex-start; /* Left-align content */ 113 | text-align: left; /* Ensure text (and inline elements) are left-aligned */ 114 | border-left: 5px solid var(--Colors-Primary-Default); 115 | } 116 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Elastic License 2.0 2 | 3 | URL: https://www.elastic.co/licensing/elastic-license 4 | 5 | ## Acceptance 6 | 7 | By using the software, you agree to all of the terms and conditions below. 8 | 9 | ## Copyright License 10 | 11 | The licensor grants you a non-exclusive, royalty-free, worldwide, 12 | non-sublicensable, non-transferable license to use, copy, distribute, make 13 | available, and prepare derivative works of the software, in each case subject to 14 | the limitations and conditions below. 15 | 16 | ## Limitations 17 | 18 | You may not provide the software to third parties as a hosted or managed 19 | service, where the service provides users with access to any substantial set of 20 | the features or functionality of the software. 21 | 22 | You may not move, change, disable, or circumvent the license key functionality 23 | in the software, and you may not remove or obscure any functionality in the 24 | software that is protected by the license key. 25 | 26 | You may not alter, remove, or obscure any licensing, copyright, or other notices 27 | of the licensor in the software. Any use of the licensor’s trademarks is subject 28 | to applicable law. 29 | 30 | ## Patents 31 | 32 | The licensor grants you a license, under any patent claims the licensor can 33 | license, or becomes able to license, to make, have made, use, sell, offer for 34 | sale, import and have imported the software, in each case subject to the 35 | limitations and conditions in this license. This license does not cover any 36 | patent claims that you cause to be infringed by modifications or additions to 37 | the software. If you or your company make any written claim that the software 38 | infringes or contributes to infringement of any patent, your patent license for 39 | the software granted under these terms ends immediately. If your company makes 40 | such a claim, your patent license ends immediately for work on behalf of your 41 | company. 42 | 43 | ## Notices 44 | 45 | You must ensure that anyone who gets a copy of any part of the software from you 46 | also gets a copy of these terms. 47 | 48 | If you modify the software, you must include in any modified copies of the 49 | software prominent notices stating that you have modified the software. 50 | 51 | ## No Other Rights 52 | 53 | These terms do not imply any licenses other than those expressly granted in 54 | these terms. 55 | 56 | ## Termination 57 | 58 | If you use the software in violation of these terms, such use is not licensed, 59 | and your licenses will automatically terminate. If the licensor provides you 60 | with a notice of your violation, and you cease all violation of this license no 61 | later than 30 days after you receive that notice, your licenses will be 62 | reinstated retroactively. However, if you violate these terms after such 63 | reinstatement, any additional violation of these terms will cause your licenses 64 | to terminate automatically and permanently. 65 | 66 | ## No Liability 67 | 68 | *As far as the law allows, the software comes as is, without any warranty or 69 | condition, and the licensor will not be liable to you for any damages arising 70 | out of these terms or the use or nature of the software, under any kind of 71 | legal claim.* 72 | 73 | ## Definitions 74 | 75 | The **licensor** is the entity offering these terms, and the **software** is the 76 | software the licensor makes available under these terms, including any portion 77 | of it. 78 | 79 | **you** refers to the individual or entity agreeing to these terms. 80 | 81 | **your company** is any legal entity, sole proprietorship, or other kind of 82 | organization that you work for, plus all organizations that have control over, 83 | are under the control of, or are under common control with that 84 | organization. **control** means ownership of substantially all the assets of an 85 | entity, or the power to direct its management and policies by vote, contract, or 86 | otherwise. Control can be direct or indirect. 87 | 88 | **your licenses** are all the licenses granted to you for the software under 89 | these terms. 90 | 91 | **use** means anything you do with the software requiring one of your licenses. 92 | 93 | **trademark** means trademarks, service marks, and similar rights. 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeSignal Design System 2 | 3 | This directory contains the core design tokens and reusable components for the application. 4 | 5 | ## Demo 6 | 7 | The demo of the design system can be found here: [Demo Site](https://codesignal.github.io/learn_bespoke-design-system/test.html) 8 | 9 | ## Structure 10 | 11 | The design system is organized into **Foundations** and **Components**. 12 | 13 | ### Foundations 14 | 15 | Base definitions that drive the visual style. 16 | 17 | - **[Colors](colors/README.md)**: Base scales and semantic color tokens. 18 | - **[Typography](typography/README.md)**: Font families, sizes, and utility classes. 19 | - **[Spacing](spacing/README.md)**: Spacing, sizing, and radius tokens. 20 | 21 | ### Components 22 | 23 | Reusable UI elements built using the foundations. 24 | 25 | - **[Button](components/button/README.md)** ([CSS](components/button/button.css)): Primary, secondary, and utility buttons. 26 | - **[Boxes](components/boxes/README.md)** ([CSS](components/boxes/boxes.css)): Container components. 27 | - **[Dropdown](components/dropdown/README.md)** ([CSS](components/dropdown/dropdown.css), [JS](components/dropdown/dropdown.js)): Customizable dropdown menus. 28 | - **[Horizontal Cards](components/horizontal-cards/README.md)** ([CSS](components/horizontal-cards/horizontal-cards.css), [JS](components/horizontal-cards/horizontal-cards.js)): Horizontal scrolling card carousel with navigation buttons and card centering. 29 | - **[Icons](components/icons/README.md)** ([CSS](components/icons/icons.css)): Scalable SVG icons with size and color variants. 30 | - **[Input](components/input/README.md)** ([CSS](components/input/input.css)): Text, number, checkbox, and radio button inputs with various states and sizes. 31 | - **[Modal](components/modal/README.md)** ([CSS](components/modal/modal.css), [JS](components/modal/modal.js)): Customizable modal dialogs with flexible content insertion. Includes a help modal variant optimized for documentation. 32 | - **[Numeric Slider](components/numeric-slider/README.md)** ([CSS](components/numeric-slider/numeric-slider.css), [JS](components/numeric-slider/numeric-slider.js)): Single value and range sliders with optional input fields. 33 | - **[Tags](components/tags/README.md)** ([CSS](components/tags/tags.css)): Label and status indicator tags. 34 | 35 | ## Usage 36 | 37 | ### Styles 38 | Include the relevant CSS files in your HTML. For a full integration, you typically include: 39 | 40 | ```html 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | **Note on Fonts:** 64 | - **Work Sans**: Must be imported via Google Fonts (as shown above). 65 | - **Founders Grotesk**: Included via `@font-face` in `typography.css`. 66 | - **JetBrains Mono**: Included via `@font-face` in `typography.css`. 67 | 68 | ### Components 69 | Components have their own CSS and JS files located within their respective directories. See individual component READMEs for implementation details. 70 | 71 | ## Test Bed 72 | 73 | You can view and test the design system elements by opening the test harness: 74 | 75 | `http://[your-server]/design-system/test.html` 76 | 77 | This provides a sidebar navigation to explore colors, typography, and interactive component demos. 78 | 79 | -------------------------------------------------------------------------------- /components/modal/help-modal-template.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 19 | 20 | 21 |
22 |

Overview

23 |

This page explains how to use the : .

24 | 25 | 26 |
27 | 28 | 29 |
30 |

Getting Started

31 |

To begin using the :

32 |
    33 |
  1. First step...
  2. 34 |
  3. Second step...
  4. 35 |
  5. Third step...
  6. 36 |
37 | 38 | 39 |
40 | 41 | 42 |
43 |

Key Features

44 | 45 |

Feature 1

46 |

Description of feature 1 and how to use it.

47 | 48 |

Feature 2

49 |

Description of feature 2 and how to use it.

50 | 51 |

Feature 3

52 |

Description of feature 3 and how to use it.

53 | 54 | 55 | 56 |
57 | 58 | 59 |
60 |

Workflow

61 |

Here's the typical workflow for using this application:

62 |
    63 |
  1. Step 1: Description of first workflow step
  2. 64 |
  3. Step 2: Description of second workflow step
  4. 65 |
  5. Step 3: Description of third workflow step
  6. 66 |
  7. Step 4: Description of fourth workflow step
  8. 67 |
68 | 69 |

Tips & Best Practices

70 | 75 |
76 | 77 | 78 |
79 |

Shortcuts

80 | 87 |
88 | 89 | 90 |
91 |

Troubleshooting / FAQ

92 | 93 |
94 | Common question 1? 95 |

Answer to common question 1 with helpful details.

96 |
97 | 98 |
99 | Common question 2? 100 |

Answer to common question 2 with helpful details.

101 |
102 | 103 |
104 | Common question 3? 105 |

Answer to common question 3 with helpful details.

106 |
107 | 108 |
109 | How do I add images to the help? 110 |

Place image files in the help/img/ directory and reference them with relative paths like <img src="./img/example.png" alt="Description">

111 |
112 | 113 |
114 | Is my work automatically saved? 115 |

Yes, the app automatically saves your work. You'll see status messages indicating when saves occur.

116 |
117 |
118 | 119 | 137 | -------------------------------------------------------------------------------- /components/boxes/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Box Component Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 53 | 54 | 55 |

Box Component Test

56 | 57 |
58 |

Default Box

59 |
60 |

This is a default box with content inside.

61 |
62 |
63 |

This is a default box with content inside. (hover)

64 |
65 |
66 |

This is a default box with content inside. (focus)

67 |
68 |
69 | 70 |
71 |

Selected Box

72 |
73 |

This is a default box with content inside. (selected)

74 |
75 |
76 |

This is a default box with content inside. (selected hover)

77 |
78 |
79 |

This is a default box with content inside. (selected focus)

80 |
81 |
82 | 83 |
84 |

Emphasized Box

85 |
86 |

This is a default box with content inside. (emphasized)

87 |
88 |
89 |

This is a default box with content inside. (emphasized hover)

90 |
91 |
92 |

This is a default box with content inside. (emphasized focus)

93 |
94 |
95 | 96 |
97 |

Shadowed Box

98 |
99 |

This is a default box with content inside. (shadowed)

100 |
101 |
102 |

This is a default box with content inside. (shadowed hover)

103 |
104 |
105 |

This is a default box with content inside. (shadowed focus)

106 |
107 |
108 | 109 |
110 |

Card Box

111 |
112 |

This is a default box with content inside. (card)

113 |
114 |
115 |

This is a default box with content inside. (card hover)

116 |
117 |
118 |

This is a default box with content inside. (card focus)

119 |
120 |
121 | 122 |
123 |

Input Group Box

124 |
125 | 𝑓(x) = m·x + b 126 |
127 |
128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /components/horizontal-cards/README.md: -------------------------------------------------------------------------------- 1 | # Horizontal Cards Component 2 | 3 | A horizontal scrolling card carousel component matching the CodeSignal Design System. Displays cards in a horizontal scrollable container with optional navigation buttons and visual indicators for scrollable content. 4 | 5 | ## Usage 6 | 7 | ### 1. Import Assets 8 | 9 | Include the CSS and JS files: 10 | 11 | ```html 12 | 13 | 17 | ``` 18 | 19 | ### 2. Create Container 20 | 21 | Add a container element to your HTML where the horizontal cards will be rendered: 22 | 23 | ```html 24 |
25 | ``` 26 | 27 | ### 3. Initialize Component 28 | 29 | ```javascript 30 | const horizontalCards = new HorizontalCards('#my-horizontal-cards', { 31 | cards: [ 32 | { 33 | title: 'Card Title', 34 | description: 'Card description text', 35 | actionPlaceholder: 'Add label' 36 | }, 37 | { 38 | title: 'Another Card', 39 | description: 'More description text', 40 | actionHtml: '' 41 | } 42 | ], 43 | onCardChange: (index, card) => { 44 | console.log('Current card:', index, card); 45 | } 46 | }); 47 | ``` 48 | 49 | ## Configuration Options 50 | 51 | | Option | Type | Default | Description | 52 | | :--- | :--- | :--- | :--- | 53 | | `cards` | Array | `[]` | Array of card objects. Each card can have `title`, `description`, `actionPlaceholder`, or `actionHtml`. | 54 | | `cardWidth` | Number | `480` | Width of each card in pixels. | 55 | | `cardGap` | Number | `24` | Gap between cards in pixels (matches `--UI-Spacing-spacing-mxl`). | 56 | | `scrollOffset` | Number | `520` | Number of pixels to scroll per navigation action (typically `cardWidth + cardGap`). | 57 | | `showNavigation` | Boolean | `true` | If `true`, displays previous/next navigation buttons. | 58 | | `onCardChange` | Function | `null` | Callback function triggered when the visible card changes. Receives `(index, card)`. | 59 | 60 | ## Card Object Structure 61 | 62 | Each card in the `cards` array can have the following properties: 63 | 64 | | Property | Type | Required | Description | 65 | | :--- | :--- | :--- | :--- | 66 | | `title` | String | No | Card title displayed as a heading. Supports HTML content (e.g., ``, ``, ``, links, etc.). | 67 | | `description` | String | No | Card description text. Supports HTML content (e.g., ``, ``, ``, links, spans with styling, etc.). | 68 | | `actionPlaceholder` | String | No | Placeholder text for the action area (displays in a dashed border box). | 69 | | `actionHtml` | String | No | Custom HTML content for the action area. If provided, `actionPlaceholder` is ignored. | 70 | 71 | ## API Methods 72 | 73 | - **`getCurrentIndex()`**: Returns the current visible card index (0-based). 74 | - **`getCurrentCard()`**: Returns the current visible card object. 75 | - **`scrollToNext()`**: Scrolls to the next card. 76 | - **`scrollToPrevious()`**: Scrolls to the previous card. 77 | - **`scrollToIndex(index)`**: Scrolls to a specific card by index. 78 | - **`destroy()`**: Removes event listeners and clears the container. 79 | 80 | ## Features 81 | 82 | - **Smooth Scrolling**: Cards scroll smoothly when navigating. 83 | - **Visual Indicators**: Fade gradients appear on the left/right edges when content is scrollable. 84 | - **Keyboard Navigation**: Arrow keys (Left/Right) navigate between cards. 85 | - **Touch/Swipe Support**: Swipe gestures on touch devices. 86 | - **Responsive**: Adapts to different screen sizes. 87 | - **Accessibility**: Proper ARIA attributes and keyboard support. 88 | - **Dark Mode**: Automatically adapts to dark mode preferences. 89 | 90 | ## Examples 91 | 92 | ### Basic Usage 93 | 94 | ```javascript 95 | const cards = new HorizontalCards('#my-cards', { 96 | cards: [ 97 | { 98 | title: 'Boss 1', 99 | description: 'Description text here', 100 | actionPlaceholder: 'Add label' 101 | }, 102 | { 103 | title: 'Boss 2', 104 | description: 'More description text', 105 | actionPlaceholder: 'Add label' 106 | } 107 | ] 108 | }); 109 | ``` 110 | 111 | ### Without Navigation Buttons 112 | 113 | ```javascript 114 | const cards = new HorizontalCards('#my-cards', { 115 | cards: sampleCards, 116 | showNavigation: false 117 | }); 118 | ``` 119 | 120 | ### Custom Action Area 121 | 122 | ```javascript 123 | const cards = new HorizontalCards('#my-cards', { 124 | cards: [ 125 | { 126 | title: 'Custom Card', 127 | description: 'Card with custom action', 128 | actionHtml: '' 129 | } 130 | ] 131 | }); 132 | ``` 133 | 134 | ### HTML Content in Title and Description 135 | 136 | ```javascript 137 | const cards = new HorizontalCards('#my-cards', { 138 | cards: [ 139 | { 140 | title: 'Card with Bold Title', 141 | description: 'This description has bold text, italic text, and a link.', 142 | actionPlaceholder: 'Add label' 143 | }, 144 | { 145 | title: 'Card with Italic and Code', 146 | description: 'You can use colored spans and other HTML elements.', 147 | actionPlaceholder: 'Add label' 148 | } 149 | ] 150 | }); 151 | ``` 152 | 153 | ### Programmatic Navigation 154 | 155 | ```javascript 156 | const cards = new HorizontalCards('#my-cards', { 157 | cards: sampleCards 158 | }); 159 | 160 | // Scroll to specific card 161 | cards.scrollToIndex(2); 162 | 163 | // Get current card 164 | const currentCard = cards.getCurrentCard(); 165 | console.log('Current card:', currentCard); 166 | ``` 167 | 168 | ## Dependencies 169 | 170 | This component relies on variables from: 171 | - `design-system/colors/colors.css` 172 | - `design-system/spacing/spacing.css` 173 | - `design-system/typography/typography.css` 174 | 175 | ## Browser Support 176 | 177 | - Modern browsers (Chrome, Firefox, Safari, Edge) 178 | - Supports touch devices with swipe gestures 179 | - Keyboard navigation support 180 | 181 | -------------------------------------------------------------------------------- /components/tags/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Tags Component Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 54 | 55 | 56 |

Tags Component Test

57 | 58 |
59 |

Primary Tag

60 |
61 |
62 | 63 |
Tag Label
64 | 65 | 66 |
Hover
67 | 68 | 69 |
Focus
70 | 71 | 72 |
Active
73 |
74 |
75 | 76 |

Secondary Tag

77 |
78 |
79 | 80 |
Tag Label
81 | 82 | 83 |
Hover
84 | 85 | 86 |
Focus
87 | 88 | 89 |
Active
90 |
91 |
92 | 93 |

Outline Tag

94 |
95 |
96 | 97 |
Tag Label
98 | 99 | 100 |
Hover
101 | 102 | 103 |
Focus
104 | 105 | 106 |
Active
107 |
108 |
109 | 110 |

Success Tag

111 |
112 |
113 | 114 |
Tag Label
115 | 116 | 117 |
Hover
118 | 119 | 120 |
Focus
121 | 122 | 123 |
Active
124 |
125 |
126 | 127 |

Error Tag

128 |
129 |
130 | 131 |
Tag Label
132 | 133 | 134 |
Hover
135 | 136 | 137 |
Focus
138 | 139 | 140 |
Active
141 |
142 |
143 | 144 |

Warning Tag

145 |
146 |
147 | 148 |
Tag Label
149 | 150 | 151 |
Hover
152 | 153 | 154 |
Focus
155 | 156 | 157 |
Active
158 |
159 |
160 | 161 |

Info Tag

162 |
163 |
164 | 165 |
Tag Label
166 | 167 | 168 |
Hover
169 | 170 | 171 |
Focus
172 | 173 | 174 |
Active
175 |
176 |
177 |
178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /components/horizontal-cards/horizontal-cards.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Horizontal Cards Component Styles 3 | * Matches CodeSignal Design System 4 | */ 5 | 6 | :root { 7 | /* Horizontal Cards Variables */ 8 | --Colors-Horizontal-Cards-Card-Background: var(--Colors-Backgrounds-Main-Top); 9 | --Colors-Horizontal-Cards-Card-Shadow: rgba(76, 90, 123, 0.09); 10 | --Colors-Horizontal-Cards-Nav-Button-Background: var(--Colors-Backgrounds-Main-Top); 11 | --Colors-Horizontal-Cards-Nav-Button-Border: var(--Colors-Stroke-Default); 12 | --Colors-Horizontal-Cards-Nav-Button-Hover-Border: var(--Colors-Stroke-Strong); 13 | --Colors-Horizontal-Cards-Nav-Button-Icon: var(--Colors-Icon-Default); 14 | --Colors-Horizontal-Cards-Nav-Button-Disabled: var(--Colors-Base-Neutral-300); 15 | --Colors-Horizontal-Cards-Fade-Gradient: rgba(255, 255, 255, 0); 16 | --Colors-Shadow-Medium-Soft: rgba(22, 44, 96, 0.12); 17 | } 18 | 19 | @media (prefers-color-scheme: dark) { 20 | :root { 21 | --Colors-Horizontal-Cards-Card-Background: var(--Colors-Backgrounds-Main-Medium); 22 | --Colors-Horizontal-Cards-Card-Shadow: rgba(13, 21, 40, 0.32); 23 | --Colors-Horizontal-Cards-Nav-Button-Background: var(--Colors-Backgrounds-Main-Medium); 24 | --Colors-Horizontal-Cards-Nav-Button-Border: rgba(45, 56, 85, 0.00); 25 | --Colors-Horizontal-Cards-Nav-Button-Hover-Border: var(--Colors-Stroke-Strong); 26 | --Colors-Horizontal-Cards-Nav-Button-Icon: var(--Colors-Icon-Default); 27 | --Colors-Horizontal-Cards-Nav-Button-Disabled: var(--Colors-Base-Neutral-1100); 28 | --Colors-Horizontal-Cards-Fade-Gradient: rgba(20, 28, 48, 0); 29 | --Colors-Shadow-Medium-Soft: rgba(13, 21, 40, 0.4); 30 | } 31 | } 32 | 33 | /* Container */ 34 | .horizontal-cards-container { 35 | position: relative; 36 | width: 100%; 37 | display: flex; 38 | flex-direction: column; 39 | gap: var(--UI-Spacing-spacing-mxs); 40 | } 41 | 42 | /* Scrollable Cards Wrapper */ 43 | .horizontal-cards-scroll { 44 | position: relative; 45 | width: 100%; 46 | overflow-x: auto; 47 | overflow-y: hidden; 48 | scroll-behavior: smooth; 49 | scrollbar-width: none; /* Firefox */ 50 | -ms-overflow-style: none; /* IE and Edge */ 51 | /* Hide scrollbar for Chrome, Safari and Opera */ 52 | scrollbar-width: none; 53 | } 54 | 55 | .horizontal-cards-scroll::-webkit-scrollbar { 56 | display: none; 57 | } 58 | 59 | /* Cards Track */ 60 | .horizontal-cards-track { 61 | display: flex; 62 | align-items: stretch; 63 | gap: var(--UI-Spacing-spacing-mxl); 64 | padding: var(--UI-Spacing-spacing-mxs) 0; 65 | width: max-content; 66 | min-width: 100%; 67 | position: relative; 68 | z-index: 0; 69 | } 70 | 71 | /* Individual Card */ 72 | .horizontal-cards-card { 73 | flex-shrink: 0; 74 | width: 480px; 75 | min-width: 480px; 76 | background: var(--Colors-Horizontal-Cards-Card-Background); 77 | border-radius: var(--UI-Radius-radius-ml); 78 | padding: var(--UI-Spacing-spacing-ms); 79 | box-shadow: 0 3px 2px 0 var(--Colors-Horizontal-Cards-Card-Shadow); 80 | display: flex; 81 | flex-direction: column; 82 | gap: var(--UI-Spacing-spacing-none); 83 | transition: opacity 0.3s ease, transform 0.3s ease; 84 | align-self: stretch; 85 | } 86 | 87 | .horizontal-cards-card:not(.horizontal-cards-card-active) { 88 | opacity: 0.5; 89 | } 90 | 91 | /* Card Content */ 92 | .horizontal-cards-card-content { 93 | display: flex; 94 | flex-direction: column; 95 | gap: 0; 96 | flex: 1 0 auto; 97 | padding: var(--UI-Spacing-spacing-xl) var(--UI-Spacing-spacing-mxl); 98 | min-height: min-content; 99 | } 100 | 101 | .horizontal-cards-card-title { 102 | /* Uses .heading-small and .strong classes from typography.css */ 103 | color: var(--Colors-Text-Body-Strong); 104 | margin: 0; 105 | margin-bottom: 0; 106 | flex-shrink: 0; 107 | } 108 | 109 | .horizontal-cards-card-description { 110 | /* Uses .body-medium class from typography.css */ 111 | color: var(--Colors-Text-Body-Default); 112 | margin: 0; 113 | margin-top: 32px; 114 | flex-shrink: 0; 115 | overflow-wrap: break-word; 116 | word-wrap: break-word; 117 | } 118 | 119 | .horizontal-cards-card-action { 120 | border: 1.5px dashed var(--Colors-Stroke-Default); 121 | border-radius: var(--UI-Radius-radius-s); 122 | min-height: 36px; 123 | display: flex; 124 | align-items: center; 125 | justify-content: center; 126 | padding: var(--UI-Spacing-spacing-s); 127 | background: transparent; 128 | margin-top: var(--UI-Spacing-spacing-xl); 129 | } 130 | 131 | .horizontal-cards-card-action-placeholder { 132 | font-family: var(--body-family); 133 | font-size: var(--Fonts-Body-Default-xxs); 134 | font-weight: 500; 135 | line-height: 1.35; 136 | letter-spacing: -0.13px; 137 | color: var(--Colors-Text-Body-Light); 138 | text-align: center; 139 | } 140 | 141 | /* Navigation Buttons */ 142 | .horizontal-cards-nav { 143 | display: flex; 144 | gap: var(--UI-Spacing-spacing-xs); 145 | align-items: center; 146 | justify-content: flex-end; 147 | margin-top: var(--UI-Spacing-spacing-mxs); 148 | width: 100%; 149 | } 150 | 151 | .horizontal-cards-nav-button { 152 | width: 32px; 153 | height: 32px; 154 | background: var(--Colors-Horizontal-Cards-Nav-Button-Background); 155 | border: 1.5px solid var(--Colors-Horizontal-Cards-Nav-Button-Border); 156 | border-radius: var(--UI-Radius-radius-xs); 157 | display: flex; 158 | align-items: center; 159 | justify-content: center; 160 | cursor: pointer; 161 | transition: border-color 0.2s ease, background-color 0.2s ease; 162 | padding: 0; 163 | box-shadow: 0px 2px 3px 0px var(--Colors-Shadow-Medium-Soft); 164 | } 165 | 166 | .horizontal-cards-nav-button:last-child { 167 | margin-right: var(--UI-Spacing-spacing-mxl); 168 | } 169 | 170 | .horizontal-cards-nav-button:hover:not(:disabled) { 171 | border-color: var(--Colors-Horizontal-Cards-Nav-Button-Hover-Border); 172 | } 173 | 174 | .horizontal-cards-nav-button:focus { 175 | outline: 2px solid var(--Colors-Primary-Medium); 176 | outline-offset: 2px; 177 | } 178 | 179 | .horizontal-cards-nav-button:disabled { 180 | cursor: not-allowed; 181 | opacity: 0.45; 182 | border-color: var(--Colors-Horizontal-Cards-Nav-Button-Disabled); 183 | } 184 | 185 | .horizontal-cards-nav-button-icon { 186 | width: 16px; 187 | height: 16px; 188 | display: flex; 189 | align-items: center; 190 | justify-content: center; 191 | color: var(--Colors-Horizontal-Cards-Nav-Button-Icon); 192 | } 193 | 194 | .horizontal-cards-nav-button:disabled .horizontal-cards-nav-button-icon { 195 | color: var(--Colors-Horizontal-Cards-Nav-Button-Disabled); 196 | } 197 | 198 | /* Responsive adjustments */ 199 | @media (max-width: 768px) { 200 | .horizontal-cards-card { 201 | width: 320px; 202 | min-width: 320px; 203 | } 204 | 205 | .horizontal-cards-track { 206 | padding: var(--UI-Spacing-spacing-mxs) var(--UI-Spacing-spacing-ms); 207 | } 208 | } 209 | 210 | -------------------------------------------------------------------------------- /llms.txt: -------------------------------------------------------------------------------- 1 | # CodeSignal Design System - LLM Reference 2 | 3 | ## Quick Start 4 | 5 | Include these CSS files in order: 6 | 1. colors/colors.css 7 | 2. spacing/spacing.css 8 | 3. typography/typography.css 9 | 4. components/[component]/[component].css 10 | 11 | Also include Work Sans font from Google Fonts. 12 | 13 | ## Design Tokens 14 | 15 | ### Colors 16 | - Base scales: `--Colors-Base-[Family]-[Step]` (e.g., `--Colors-Base-Primary-700`) 17 | - Semantic: `--Colors-[Category]-[Context]` (e.g., `--Colors-Text-Body-Default`) 18 | - Prefer semantic tokens over base scales for theming support 19 | 20 | ### Spacing 21 | - Pattern: `--UI-Spacing-spacing-[size]` (none, min, xxs, xs, s, mxs, ms, m, ml, mxl, l, xl, xxl, xxxl, 4xl, max) 22 | - Radius: `--UI-Radius-radius-[size]` (none, min, xxs, xs, s, m, ml, mxl, l, xl) 23 | - Input heights: `--UI-Input-[size]` (min: 26px, xs: 32px, sm: 40px, md: 48px, lg: 60px) 24 | 25 | ### Typography 26 | - Classes: `.body-[size]`, `.heading-[size]`, `.label-[size]` 27 | - Fonts: Work Sans (body), Founders Grotesk (headings), JetBrains Mono (code) 28 | 29 | ## Components 30 | 31 | ### Button 32 | - Base: `.button` 33 | - Variants: `.button-primary`, `.button-secondary`, `.button-tertiary`, `.button-danger`, `.button-success`, `.button-text`, `.button-text-primary` 34 | - Sizes: `.button-xsmall`, `.button-small`, `.button-large` (default: medium/48px) 35 | - Example: `` 36 | 37 | ### Box 38 | - Base: `.box` 39 | - Variants: `.box.selected`, `.box.emphasized`, `.box.shadowed`, `.box.card` 40 | - Example: `
Content
` 41 | 42 | ### Input 43 | - Base: `.input` 44 | - Types: `type="text"` or `type="number"` 45 | - States: `.hover`, `.focus`, `:disabled` 46 | - Example: `` 47 | 48 | ### Checkbox 49 | - Base: `.input-checkbox` wrapper with `input[type="checkbox"]` 50 | - Structure: `` 51 | - Sizes: `.input-checkbox-small`, `.input-checkbox-xsmall` (default: 32px) 52 | - States: Default, Hover (blue border), Checked (blue background + white checkmark), Disabled 53 | - Example: `` 54 | 55 | ### Radio 56 | - Base: `.input-radio` wrapper with `input[type="radio"]` 57 | - Structure: `` 58 | - Sizes: `.input-radio-small`, `.input-radio-xsmall` (default: 32px) 59 | - States: Default, Hover (blue border), Checked (blue filled circle + white inner dot), Disabled 60 | - Example: `` 61 | 62 | ### Tag 63 | - Base: `.tag` or `.tag.default` 64 | - Variants: `.tag.secondary`, `.tag.outline`, `.tag.success`, `.tag.error`, `.tag.warning`, `.tag.info` 65 | - States: `.hover`, `.focus`, `.active` 66 | - Example: `
Completed
` 67 | 68 | ### Icon 69 | - Base: `.icon` 70 | - Icon names: `.icon-[name]` (e.g., `.icon-jobs`, `.icon-academy`) 71 | - Sizes: `.icon-small` (16px), `.icon-medium` (24px, default), `.icon-large` (32px), `.icon-xlarge` (48px) 72 | - Colors: `.icon-primary`, `.icon-secondary`, `.icon-success`, `.icon-danger`, `.icon-warning` 73 | - Example: `` 74 | 75 | ### Dropdown (JS Component) 76 | - Import: `import Dropdown from '/design-system/components/dropdown/dropdown.js'` 77 | - Initialize: `new Dropdown(selector, { items, placeholder, onSelect, ... })` 78 | - Methods: `getValue()`, `setValue(value)`, `open()`, `close()`, `toggleOpen()`, `destroy()` 79 | 80 | ### Horizontal Cards (JS Component) 81 | - Import: `import HorizontalCards from '/design-system/components/horizontal-cards/horizontal-cards.js'` 82 | - Initialize: `new HorizontalCards(selector, { cards, showNavigation, onCardChange, ... })` 83 | - Cards: Array of objects with `title` (HTML), `description` (HTML), `actionHtml`, or `actionPlaceholder` 84 | - Methods: `getCurrentIndex()`, `getCurrentCard()`, `scrollToNext()`, `scrollToPrevious()`, `scrollToIndex(index)`, `destroy()` 85 | - Features: Horizontal scrolling, card centering, navigation buttons, keyboard/touch support 86 | 87 | ### Numeric Slider (JS Component) 88 | - Import: `import NumericSlider from '/design-system/components/numeric-slider/numeric-slider.js'` 89 | - Initialize: `new NumericSlider(selector, { type, min, max, value, showInputs, theme, ... })` 90 | - Types: `'single'` (one handle) or `'range'` (two handles) 91 | - Methods: `getValue()`, `setValue(value)`, `setDisabled(disabled)`, `destroy()` 92 | - Themes: `'default'` (neutral track, primary filled/handles) or `'primary'` (all primary) 93 | - Can override individual parts: `trackTheme`, `filledTheme`, `handleTheme` 94 | 95 | ### Modal (JS Component) 96 | - Import: `import Modal from '/design-system/components/modal/modal.js'` 97 | - Initialize: `new Modal({ size, title, content, footerButtons, ... })` 98 | - Sizes: `'small'` (400px), `'medium'` (600px), `'large'` (900px), `'xlarge'` (1200px) 99 | - Content: HTML string, DOM element, CSS selector (e.g., `'#my-content'`), or template content 100 | - Methods: `open()`, `close()`, `updateContent(content)`, `updateTitle(title)`, `destroy()` 101 | - Footer buttons: Array of `{label, type, onClick}` configs 102 | - Help Modal: `Modal.createHelpModal({ title, content, ... })` - Convenience method for help/documentation modals (xlarge size, footer with close button, styled for help content) 103 | 104 | ## Best Practices 105 | 106 | 1. Always use semantic color tokens (e.g., `--Colors-Text-Body-Default`) over base scales 107 | 2. Components support dark mode automatically via `prefers-color-scheme` 108 | 3. Use spacing tokens for consistent padding/margins 109 | 4. Include component CSS files after foundation CSS files 110 | 5. Icons use `mask-image` with `background-color` for color control 111 | 112 | ## File Structure 113 | 114 | ``` 115 | design-system/ 116 | ├── colors/colors.css 117 | ├── spacing/spacing.css 118 | ├── typography/typography.css 119 | └── components/ 120 | ├── button/button.css 121 | ├── boxes/boxes.css 122 | ├── dropdown/dropdown.css + dropdown.js 123 | ├── horizontal-cards/horizontal-cards.css + horizontal-cards.js 124 | ├── icons/icons.css 125 | ├── input/input.css 126 | ├── modal/modal.css + modal.js 127 | ├── numeric-slider/numeric-slider.css + numeric-slider.js 128 | └── tags/tags.css 129 | ``` 130 | 131 | -------------------------------------------------------------------------------- /components/numeric-slider/numeric-slider.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Numeric Slider Component Styles 3 | * Matches CodeSignal Design System 4 | */ 5 | 6 | :root { 7 | /* Slider Variables - Light Mode */ 8 | --Colors-Slider-Track-Neutral: var(--Colors-Base-Neutral-200); 9 | --Colors-Slider-Track-Primary: var(--Colors-Base-Primary-100); 10 | --Colors-Slider-Thumb-Neutral: var(--Colors-Base-Neutral-650); 11 | --Colors-Slider-Thumb-Primary: var(--Colors-Base-Primary-600); 12 | --Colors-Slider-Handle-Neutral: var(--Colors-Base-Neutral-400); 13 | --Colors-Slider-Handle-Neutral-Inner: var(--Colors-Base-Neutral-100); 14 | --Colors-Slider-Handle-Primary: var(--Colors-Base-Primary-400); 15 | --Colors-Slider-Handle-Primary-Inner: var(--Colors-Base-Primary-100); 16 | --Colors-Slider-Handle-Marker: var(--Colors-Base-Primary-600); 17 | --Colors-Slider-Handle-Marker-Inner: var(--Colors-Base-Primary-400); 18 | --Colors-Slider-Handle-Hover: var(--Colors-Base-Primary-600); 19 | --Colors-Slider-Handle-Pressed: var(--Colors-Base-Primary-700); 20 | --Colors-Input-Border-Default: var(--Colors-Base-Neutral-200); 21 | } 22 | 23 | @media (prefers-color-scheme: dark) { 24 | :root { 25 | /* Slider Variables - Dark Mode */ 26 | --Colors-Slider-Track-Neutral: rgba(6, 12, 28, 0.5); 27 | --Colors-Slider-Track-Primary: rgba(6, 12, 28, 0.5); 28 | --Colors-Slider-Thumb-Neutral: var(--Colors-Base-Neutral-800); 29 | --Colors-Slider-Thumb-Primary: var(--Colors-Base-Primary-800); 30 | --Colors-Slider-Handle-Neutral: var(--Colors-Base-Primary-600); 31 | --Colors-Slider-Handle-Neutral-Inner: var(--Colors-Base-Primary-350); 32 | --Colors-Slider-Handle-Primary: var(--Colors-Base-Primary-600); 33 | --Colors-Slider-Handle-Primary-Inner: var(--Colors-Base-Primary-350); 34 | --Colors-Slider-Handle-Marker: var(--Colors-Base-Primary-600); 35 | --Colors-Slider-Handle-Marker-Inner: var(--Colors-Base-Primary-350); 36 | --Colors-Slider-Handle-Hover: var(--Colors-Base-Primary-600); 37 | --Colors-Slider-Handle-Pressed: var(--Colors-Base-Primary-700); 38 | --Colors-Input-Border-Default: var(--Colors-Base-Neutral-1150); 39 | } 40 | } 41 | 42 | .numeric-slider-container { 43 | position: relative; 44 | display: flex; 45 | align-items: center; 46 | gap: var(--UI-Spacing-spacing-xs); 47 | width: 100%; 48 | } 49 | 50 | .numeric-slider-container.with-inputs { 51 | gap: var(--UI-Spacing-spacing-xs); 52 | } 53 | 54 | /* Input fields - extends input component styles */ 55 | .numeric-slider-input { 56 | flex-shrink: 0; 57 | min-width: 28px; 58 | max-width: 30px; 59 | height: var(--UI-Input-sm); 60 | text-align: center; 61 | font-weight: 500; 62 | padding-left: var(--UI-Spacing-spacing-mxs); 63 | padding-right: var(--UI-Spacing-spacing-mxs); 64 | padding-top: var(--UI-Spacing-spacing-none); 65 | padding-bottom: var(--UI-Spacing-spacing-none); 66 | border: 1.5px solid var(--Colors-Input-Border-Default, #394563); 67 | } 68 | 69 | /* Hide spinner buttons for numeric slider inputs */ 70 | /* WebKit browsers (Chrome, Safari, Edge) */ 71 | .numeric-slider-input::-webkit-inner-spin-button, 72 | .numeric-slider-input::-webkit-outer-spin-button { 73 | -webkit-appearance: none; 74 | appearance: none; 75 | margin: 0; 76 | display: none; 77 | } 78 | 79 | /* Firefox */ 80 | .numeric-slider-input { 81 | -moz-appearance: textfield; 82 | appearance: textfield; 83 | } 84 | 85 | /* Slider wrapper */ 86 | .numeric-slider-wrapper { 87 | position: relative; 88 | flex: 1; 89 | height: 16px; 90 | display: flex; 91 | align-items: center; 92 | cursor: pointer; 93 | } 94 | 95 | /* Slider track */ 96 | .numeric-slider-track { 97 | position: relative; 98 | width: 100%; 99 | height: 8px; 100 | border-radius: var(--UI-Radius-radius-s); 101 | background: var(--Colors-Slider-Track-Neutral); 102 | transition: background-color 0.2s ease; 103 | } 104 | 105 | .numeric-slider-track.theme-primary { 106 | background: var(--Colors-Slider-Track-Primary); 107 | } 108 | 109 | /* Filled track (between handles for range, or from start to handle for single) */ 110 | .numeric-slider-filled { 111 | position: absolute; 112 | height: 8px; 113 | border-radius: var(--UI-Radius-radius-s); 114 | background: var(--Colors-Slider-Thumb-Neutral); 115 | transition: background-color 0.2s ease; 116 | pointer-events: none; 117 | } 118 | 119 | .numeric-slider-filled.theme-primary { 120 | background: var(--Colors-Slider-Thumb-Primary); 121 | } 122 | 123 | /* Slider handle */ 124 | .numeric-slider-handle { 125 | position: absolute; 126 | width: 16px; 127 | height: 16px; 128 | top: -4px; 129 | border-radius: 50%; 130 | background: var(--Colors-Slider-Handle-Neutral); 131 | border: none; 132 | cursor: grab; 133 | transform: translateX(-50%); 134 | transition: transform 0.1s ease, background-color 0.2s ease, box-shadow 0.2s ease; 135 | z-index: 2; 136 | display: flex; 137 | align-items: center; 138 | justify-content: center; 139 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); 140 | } 141 | 142 | .numeric-slider-handle::before { 143 | content: ''; 144 | position: absolute; 145 | width: 4px; 146 | height: 4px; 147 | border-radius: 50%; 148 | background: var(--Colors-Slider-Handle-Neutral-Inner); 149 | } 150 | 151 | .numeric-slider-handle.theme-primary { 152 | background: var(--Colors-Slider-Handle-Primary); 153 | } 154 | 155 | .numeric-slider-handle.theme-primary::before { 156 | background: var(--Colors-Slider-Handle-Primary-Inner); 157 | } 158 | 159 | /* Marker style handles always use marker colors, regardless of theme */ 160 | .numeric-slider-handle.style-marker { 161 | background: var(--Colors-Slider-Handle-Marker); 162 | } 163 | 164 | .numeric-slider-handle.style-marker::before { 165 | background: var(--Colors-Slider-Handle-Marker-Inner); 166 | } 167 | 168 | /* Ensure marker style overrides theme-primary */ 169 | .numeric-slider-handle.style-marker.theme-primary { 170 | background: var(--Colors-Slider-Handle-Marker); 171 | } 172 | 173 | .numeric-slider-handle.style-marker.theme-primary::before { 174 | background: var(--Colors-Slider-Handle-Marker-Inner); 175 | } 176 | 177 | .numeric-slider-handle:hover { 178 | transform: translateX(-50%) scale(1.25); 179 | background: var(--Colors-Slider-Handle-Hover); 180 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); 181 | } 182 | 183 | .numeric-slider-handle:active, 184 | .numeric-slider-handle.dragging { 185 | cursor: grabbing; 186 | transform: translateX(-50%) scale(1.25); 187 | background: var(--Colors-Slider-Handle-Pressed); 188 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); 189 | } 190 | 191 | .numeric-slider-handle:focus { 192 | outline: 2px solid var(--Colors-Primary-Medium); 193 | outline-offset: 2px; 194 | } 195 | 196 | .numeric-slider-handle:focus:not(:focus-visible) { 197 | outline: none; 198 | } 199 | 200 | /* Disabled state */ 201 | .numeric-slider-container.disabled { 202 | opacity: 0.5; 203 | pointer-events: none; 204 | cursor: not-allowed; 205 | } 206 | 207 | .numeric-slider-container.disabled .numeric-slider-handle { 208 | cursor: not-allowed; 209 | } 210 | 211 | /* Hide inputs when configured */ 212 | .numeric-slider-container.hide-inputs .numeric-slider-input { 213 | display: none; 214 | } 215 | 216 | 217 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Design System Test Bed 7 | 8 | 9 | 10 | 11 | 12 | 94 | 95 | 96 |
97 | 123 | 124 |
125 | 126 |
127 |
128 | 129 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /typography/typography.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Founders Grotesk'; 3 | src: url('/design-system/fonts/FoundersGrotesk-Light.woff2') format('woff2'), 4 | url('/fonts/FoundersGrotesk-Light.woff2') format('woff2'), 5 | url('../fonts/FoundersGrotesk-Light.woff') format('woff'); 6 | font-weight: 300; 7 | font-style: normal; 8 | -webkit-font-smoothing: antialiased; 9 | } 10 | 11 | @font-face { 12 | font-family: 'Founders Grotesk'; 13 | src: url('/design-system/fonts/FoundersGrotesk-Light-Italic.woff2') format('woff2'), 14 | url('/fonts/FoundersGrotesk-Light-Italic.woff2') format('woff2'), 15 | url('../fonts/FoundersGrotesk-Light-Italic.woff') format('woff'); 16 | font-weight: 300; 17 | font-style: italic; 18 | -webkit-font-smoothing: antialiased; 19 | } 20 | 21 | @font-face { 22 | font-family: 'Founders Grotesk'; 23 | src: url('/design-system/fonts/FoundersGrotesk-Medium.woff2') format('woff2'), 24 | url('/fonts/FoundersGrotesk-Medium.woff2') format('woff2'), 25 | url('../fonts/FoundersGrotesk-Medium.woff') format('woff'); 26 | font-weight: 500; 27 | font-style: normal; 28 | -webkit-font-smoothing: antialiased; 29 | } 30 | 31 | @font-face { 32 | font-family: 'JetBrains Mono'; 33 | src: url('/design-system/fonts/JetBrainsMono-Medium-Italic.woff2') format('woff2'), 34 | url('/fonts/JetBrainsMono-Medium-Italic.woff2') format('woff2'), 35 | url('../fonts/JetBrainsMono-Medium-Italic.woff') format('woff'); 36 | font-weight: 500; 37 | font-style: italic; 38 | -webkit-font-smoothing: antialiased; 39 | } 40 | 41 | @font-face { 42 | font-family: 'Founders Grotesk'; 43 | src: url('/design-system/fonts/FoundersGrotesk-Medium-Italic.woff2') format('woff2'), 44 | url('/fonts/FoundersGrotesk-Medium-Italic.woff2') format('woff2'), 45 | url('../fonts/FoundersGrotesk-Medium-Italic.woff') format('woff'); 46 | font-weight: 500; 47 | font-style: italic; 48 | -webkit-font-smoothing: antialiased; 49 | } 50 | 51 | :root { 52 | --body-family: "Work Sans"; 53 | --body-elegant-family: "Founders Grotesk"; 54 | --heading-family: "Founders Grotesk"; 55 | --label-family: "Work Sans"; 56 | --label-number-family: "Work Sans"; 57 | --code-family: "JetBrains Mono"; 58 | 59 | --Fonts-Body-Default-xxs: 13px; 60 | --Fonts-Body-Default-xs: 14px; 61 | --Fonts-Body-Default-sm: 15px; 62 | --Fonts-Body-Default-md: 16px; 63 | --Fonts-Body-Default-lg: 17px; 64 | --Fonts-Body-Default-xl: 19px; 65 | --Fonts-Body-Default-xxl: 21px; 66 | --Fonts-Body-Default-xxxl: 24px; 67 | 68 | --Fonts-Body-Elegant-xxs: 22px; 69 | --Fonts-Body-Elegant-xs: 26px; 70 | --Fonts-Body-Elegant-sm: 32px; 71 | --Fonts-Body-Elegant-md: 38px; 72 | 73 | --Fonts-Headlines-xxl: 64px; 74 | --Fonts-Headlines-xl: 48px; 75 | --Fonts-Headlines-l: 38px; 76 | --Fonts-Headlines-md: 32px; 77 | --Fonts-Headlines-sm: 24px; 78 | --Fonts-Headlines-xsm: 22px; 79 | --Fonts-Headlines-xxsm: 22px; 80 | --Fonts-Headlines-xxxsm: 16px; 81 | 82 | --Fonts-Special-xl: 15px; 83 | --Fonts-Special-lg: 14px; 84 | --Fonts-Special-sm: 12px; 85 | --Fonts-Special-xs: 11px; 86 | --Fonts-Special-xxs: 10px; 87 | } 88 | 89 | /* Base body class with shared properties */ 90 | .body, 91 | .body-xxsmall, 92 | .body-xsmall, 93 | .body-small, 94 | .body-medium, 95 | .body-large, 96 | .body-xlarge, 97 | .body-xxlarge, 98 | .body-xxxlarge { 99 | font-family: var(--body-family); 100 | font-style: normal; 101 | font-weight: 400; 102 | -webkit-font-smoothing: antialiased; 103 | } 104 | 105 | .body-xxsmall { 106 | font-size: var(--Fonts-Body-Default-xxs); 107 | line-height: 135%; 108 | letter-spacing: -0.13px; 109 | } 110 | 111 | .body-xsmall { 112 | font-size: var(--Fonts-Body-Default-xs); 113 | line-height: 135%; 114 | letter-spacing: -0.14px; 115 | } 116 | 117 | .body-small { 118 | font-size: var(--Fonts-Body-Default-sm); 119 | line-height: 135%; 120 | letter-spacing: -0.15px; 121 | } 122 | 123 | .body-medium { 124 | font-size: var(--Fonts-Body-Default-md); 125 | line-height: 145%; 126 | letter-spacing: -0.16px; 127 | } 128 | 129 | .body-large { 130 | font-size: var(--Fonts-Body-Default-lg); 131 | line-height: 140%; 132 | letter-spacing: -0.17px; 133 | } 134 | 135 | .body-xlarge { 136 | font-size: var(--Fonts-Body-Default-xl); 137 | line-height: 140%; 138 | letter-spacing: -0.19px; 139 | } 140 | 141 | .body-xxlarge { 142 | font-size: var(--Fonts-Body-Default-xxl); 143 | line-height: 140%; 144 | letter-spacing: -0.21px; 145 | } 146 | 147 | .body-xxxlarge { 148 | font-size: var(--Fonts-Body-Default-xxxl); 149 | line-height: 140%; 150 | letter-spacing: -0.24px; 151 | } 152 | 153 | .body-elegant, 154 | .body-elegant-xxsmall, 155 | .body-elegant-xsmall, 156 | .body-elegant-small, 157 | .body-elegant-medium { 158 | font-family: var(--body-elegant-family); 159 | font-style: normal; 160 | font-weight: 300; 161 | } 162 | 163 | .body-elegant-xxsmall { 164 | font-size: var(--Fonts-Body-Elegant-xxs); 165 | line-height: 138%; 166 | letter-spacing: -0.22px; 167 | } 168 | 169 | .body-elegant-xsmall { 170 | font-size: var(--Fonts-Body-Elegant-xs); 171 | line-height: 138%; 172 | letter-spacing: -0.26px; 173 | } 174 | 175 | .body-elegant-small { 176 | font-size: var(--Fonts-Body-Elegant-sm); 177 | line-height: 138%; 178 | letter-spacing: -0.32px; 179 | } 180 | 181 | .body-elegant-medium { 182 | font-size: var(--Fonts-Body-Elegant-md); 183 | line-height: 138%; 184 | letter-spacing: -0.38px; 185 | } 186 | 187 | 188 | .heading, 189 | .heading-xxlarge, 190 | .heading-xlarge, 191 | .heading-large, 192 | .heading-medium, 193 | .heading-small, 194 | .heading-xsmall, 195 | .heading-xxsmall { 196 | font-family: var(--heading-family); 197 | font-style: normal; 198 | font-weight: 500; 199 | -webkit-font-smoothing: antialiased; 200 | } 201 | 202 | .heading-xxlarge { 203 | font-size: var(--Fonts-Headlines-xxl); 204 | line-height: 100%; 205 | letter-spacing: -0.64px; 206 | } 207 | 208 | .heading-xlarge { 209 | font-size: var(--Fonts-Headlines-xl); 210 | line-height: 100%; 211 | letter-spacing: -0.48px; 212 | } 213 | 214 | .heading-large { 215 | font-size: var(--Fonts-Headlines-l); 216 | line-height: 110%; 217 | letter-spacing: -0.38px; 218 | } 219 | 220 | .heading-medium { 221 | font-size: var(--Fonts-Headlines-md); 222 | line-height: 116%; 223 | letter-spacing: -0.32px; 224 | } 225 | 226 | .heading-small { 227 | font-size: var(--Fonts-Headlines-sm); 228 | line-height: 120%; 229 | letter-spacing: -0.24px; 230 | } 231 | 232 | .heading-xsmall { 233 | font-size: var(--Fonts-Headlines-xsm); 234 | line-height: 120%; 235 | letter-spacing: -0.22px; 236 | } 237 | 238 | .heading-xxsmall { 239 | font-size: var(--Fonts-Headlines-xxsm); 240 | line-height: 120%; 241 | letter-spacing: -0.22px; 242 | } 243 | 244 | .heading-xxxsmall { 245 | font-size: var(--Fonts-Headlines-xxxsm); 246 | line-height: 120%; 247 | letter-spacing: -0.16px; 248 | } 249 | 250 | .label, 251 | .label-large, 252 | .label-medium, 253 | .label-small { 254 | font-family: var(--label-family); 255 | font-style: normal; 256 | font-weight: 600; 257 | text-transform: uppercase; 258 | -webkit-font-smoothing: antialiased; 259 | } 260 | 261 | .label-large { 262 | font-size: var(--Fonts-Special-lg); 263 | line-height: normal; 264 | letter-spacing: 1.4px; 265 | } 266 | 267 | .label-medium { 268 | font-size: var(--Fonts-Special-xs); 269 | line-height: normal; 270 | letter-spacing: 1.1px; 271 | } 272 | 273 | .label-small { 274 | font-size: var(--Fonts-Special-xxs); 275 | line-height: 82%; 276 | letter-spacing: 1px; 277 | } 278 | 279 | .label-number, 280 | .label-number-xsmall, 281 | .label-number-small, 282 | .label-number-medium, 283 | .label-number-large { 284 | font-family: var(--label-number-family); 285 | font-style: normal; 286 | font-weight: 500; 287 | line-height: 83%; 288 | } 289 | 290 | .label-number-xsmall { 291 | font-size: var(--Fonts-Special-xs); 292 | } 293 | 294 | .label-number-small { 295 | font-size: var(--Fonts-Special-sm, 12px); 296 | } 297 | 298 | .label-number-medium { 299 | font-size: var(--Fonts-Special-lg); 300 | } 301 | 302 | .label-number-large { 303 | font-size: var(--Fonts-Special-xl); 304 | } 305 | 306 | .code, 307 | .code-small, 308 | .code-medium, 309 | .code-large { 310 | color: var(--Colors-Text-Body-Medium); 311 | font-family: var(--code-family); 312 | font-style: normal; 313 | font-weight: 600; 314 | line-height: 140%; 315 | -webkit-font-smoothing: antialiased; 316 | } 317 | 318 | .code { 319 | font-size: var(--Fonts-Body-Default-xl); 320 | } 321 | 322 | .code-small { 323 | font-size: var(--Fonts-Special-xs); 324 | } 325 | 326 | .code-medium { 327 | font-size: var(--Fonts-Body-Default-md); 328 | } 329 | 330 | .code-large { 331 | font-size: var(--Fonts-Body-Default-lg); 332 | } 333 | 334 | .strong { 335 | font-weight: 500; 336 | } 337 | -------------------------------------------------------------------------------- /components/dropdown/dropdown.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Dropdown Component Styles 3 | * Matches CodeSignal Design System - Light Mode Default Toggle 4 | */ 5 | 6 | :root { 7 | /* Colors - Light Mode */ 8 | --Colors-Dropdown-Toggle-Text: var(--Colors-Text-Body-Medium); 9 | --Colors-Dropdown-Icon: var(--Colors-Base-Neutral-800); 10 | --Colors-Dropdown-Icon-Hover: var(--Colors-Icon-Medium); 11 | --Colors-Dropdown-Icon-Active: var(--Colors-Icon-Primary); 12 | --Colors-Dropdown-Panel: var(--Colors-Backgrounds-Main-Top); 13 | --Colors-Dropdown-Panel-Stroke: var(--Colors-Stroke-Default); 14 | --Colors-Dropdown-Toggle: var(--Colors-Backgrounds-Main-Top); 15 | --Colors-Dropdown-Toggle-Background: var(--Colors-Backgrounds-Main-Top); 16 | --Colors-Dropdown-Toggle-Stroke: var(--Colors-Stroke-Default); 17 | --Colors-Dropdown-Toggle-Hover: var(--Colors-Backgrounds-Main-Top); 18 | --Colors-Dropdown-Toggle-Hover-Border: var(--Colors-Stroke-Medium); 19 | --Colors-Dropdown-Panel-Stroke-Hover: var(--Colors-Stroke-Medium); 20 | --Colors-Shadow-Float: rgba(22, 44, 96, 0.12); 21 | --Colors-Shadow-Medium-Soft: rgba(22, 44, 96, 0.12); 22 | --Colors-Menu-Option-Hover: var(--Colors-Base-Neutral-20); 23 | --Colors-Menu-Option-Background: var(--Colors-Backgrounds-Main-Top); 24 | --Colors-Menu-Option-Background-Selected: var(--Colors-Primary-Lightest); 25 | --Colors-Menu-Tab-Active: var(--Colors-Backgrounds-Main-Top); 26 | 27 | /* Spacing */ 28 | --Menu-Item-Padding-Horizontal: var(--UI-Spacing-spacing-mxs); 29 | 30 | /* Radius */ 31 | --Menu-Item-Roundness: var(--UI-Radius-radius-s); 32 | } 33 | 34 | /* Dark Mode */ 35 | @media (prefers-color-scheme: dark) { 36 | :root { 37 | /* Colors - Dark Mode */ 38 | --Colors-Dropdown-Toggle-Text: var(--Colors-Text-Body-Default); 39 | --Colors-Dropdown-Icon-Hover: var(--Colors-Icon-Medium); 40 | --Colors-Dropdown-Icon-Active: var(--Colors-Icon-Primary); 41 | --Colors-Dropdown-Panel: var(--Colors-Backgrounds-Main-Medium); 42 | --Colors-Dropdown-Panel-Stroke: rgba(45, 56, 85, 0.00); 43 | --Colors-Dropdown-Toggle: var(--Colors-Base-Neutral-1200); 44 | --Colors-Dropdown-Toggle-Background: var(--Colors-Backgrounds-Main-Medium); 45 | --Colors-Dropdown-Toggle-Stroke: rgba(45, 56, 85, 0.00); 46 | --Colors-Dropdown-Toggle-Hover: var(--Colors-Base-Neutral-1050); 47 | --Colors-Dropdown-Toggle-Hover-Border: rgba(193, 199, 215, 0.35); 48 | --Colors-Dropdown-Panel-Stroke-Hover: var(--Colors-Stroke-Strong); 49 | --Colors-Shadow-Float: rgba(13, 21, 40, 0.3); 50 | --Colors-Shadow-Medium-Soft: rgba(13, 21, 40, 0.4); 51 | --Colors-Menu-Option-Hover: rgba(193, 199, 215, 0.1); 52 | --Colors-Menu-Option-Background: var(--Colors-Backgrounds-Main-Medium); 53 | --Colors-Menu-Option-Background-Selected: var(--Colors-Base-Neutral-1050); 54 | --Colors-Menu-Tab-Active: var(--Colors-Text-Body-Strong); 55 | } 56 | } 57 | 58 | .dropdown-container { 59 | position: relative; 60 | display: inline-block; 61 | width: 100%; 62 | } 63 | 64 | .dropdown-container.grow-to-fit { 65 | width: auto; 66 | min-width: 200px; 67 | } 68 | 69 | /* Toggle Button */ 70 | .dropdown-toggle { 71 | width: 100%; 72 | height: var(--UI-Input-sm); 73 | background: var(--Colors-Dropdown-Toggle) !important; 74 | border: 1px solid var(--Colors-Dropdown-Toggle-Stroke) !important; 75 | border-radius: var(--UI-Radius-radius-s) !important; 76 | padding: 0; 77 | cursor: pointer; 78 | position: relative; 79 | transition: border-color 0.2s ease, box-shadow 0.2s ease; 80 | font-family: "Work Sans", ui-sans-serif, system-ui, sans-serif; 81 | box-shadow: 0px 2px 3px 0px var(--Colors-Shadow-Medium-Soft); 82 | } 83 | 84 | .dropdown-toggle:hover { 85 | background: var(--Colors-Dropdown-Toggle-Hover) !important; 86 | border-color: var(--Colors-Dropdown-Panel-Stroke-Hover) !important; 87 | box-shadow: 0 2px 8px rgba(76, 90, 123, 0.08); 88 | } 89 | 90 | .dropdown-toggle:focus { 91 | outline: none; 92 | } 93 | 94 | .dropdown-toggle:focus-visible { 95 | outline: 2px solid var(--Colors-Primary-Medium); 96 | outline-offset: 2px; 97 | } 98 | 99 | .dropdown-toggle-content { 100 | display: flex; 101 | align-items: center; 102 | justify-content: space-between; 103 | gap: var(--UI-Spacing-spacing-mxs); 104 | height: 100%; 105 | padding: var(--UI-Spacing-spacing-mxs) var(--UI-Spacing-spacing-mxs) var(--UI-Spacing-spacing-mxs) var(--UI-Spacing-spacing-ms); 106 | box-sizing: border-box; 107 | } 108 | 109 | .dropdown-toggle-label { 110 | flex: 1; 111 | text-align: left; 112 | font-weight: 500; 113 | vertical-align: middle; 114 | color: var(--Colors-Dropdown-Toggle-Text); 115 | white-space: nowrap; 116 | overflow: hidden; 117 | text-overflow: ellipsis; 118 | transition: color 0.2s ease; 119 | } 120 | 121 | .dropdown-container.grow-to-fit .dropdown-toggle-label { 122 | overflow: visible; 123 | text-overflow: clip; 124 | } 125 | 126 | .dropdown-toggle:hover .dropdown-toggle-label { 127 | color: var(--Colors-Text-Body-Strong); 128 | } 129 | 130 | .dropdown-container.open .dropdown-toggle-label { 131 | color: var(--Colors-Text-Body-Strong); 132 | } 133 | 134 | .dropdown-toggle-icon { 135 | display: block; 136 | flex-shrink: 0; 137 | width: var(--UI-Spacing-spacing-ml); 138 | height: var(--UI-Spacing-spacing-ml); 139 | position: relative; 140 | } 141 | 142 | .dropdown-toggle-icon svg { 143 | display: block; 144 | width: 10px; 145 | height: 5px; 146 | position: absolute; 147 | top: 7.5px; 148 | left: 5px; 149 | } 150 | 151 | .dropdown-toggle-icon svg path { 152 | transition: stroke 0.2s ease; 153 | } 154 | 155 | .dropdown-toggle:hover .dropdown-toggle-icon svg path { 156 | stroke: var(--Colors-Dropdown-Icon-Hover); 157 | } 158 | 159 | .dropdown-container.open .dropdown-toggle-icon svg path { 160 | stroke: var(--Colors-Dropdown-Icon-Active); 161 | } 162 | 163 | /* Dropdown Menu Panel */ 164 | .dropdown-menu { 165 | position: absolute; 166 | top: calc(100% + 4px); 167 | left: 0; 168 | width: 240px; 169 | height: auto; 170 | max-height: 284px; 171 | background: var(--Colors-Dropdown-Panel); 172 | border: 1px solid var(--Colors-Dropdown-Panel-Stroke); 173 | border-radius: var(--UI-Radius-radius-s); 174 | box-shadow: 0px 12px 38px 0px var(--Colors-Shadow-Float); 175 | z-index: 1000; 176 | display: none; 177 | overflow: visible; 178 | opacity: 1; 179 | } 180 | 181 | .dropdown-container.grow-to-fit .dropdown-menu { 182 | width: 100%; 183 | min-width: 200px; 184 | } 185 | 186 | .dropdown-container.open .dropdown-menu { 187 | display: block; 188 | } 189 | 190 | .dropdown-menu-list { 191 | display: flex; 192 | flex-direction: column; 193 | gap: var(--UI-Spacing-spacing-xxs); 194 | padding: var(--UI-Spacing-spacing-mxs); 195 | overflow-y: visible; 196 | } 197 | 198 | /* Menu Items */ 199 | .dropdown-menu-item { 200 | display: flex; 201 | align-items: center; 202 | width: 216px; 203 | height: 40px !important; 204 | min-height: 40px !important; 205 | flex-shrink: 0 !important; 206 | padding: var(--UI-Spacing-spacing-none) var(--Menu-Item-Padding-Horizontal) !important; 207 | gap: var(--UI-Spacing-spacing-s); 208 | background: var(--Colors-Dropdown-Panel) !important; 209 | border: none !important; 210 | border-radius: var(--Menu-Item-Roundness) !important; 211 | cursor: pointer; 212 | font-family: "Work Sans", ui-sans-serif, system-ui, sans-serif; 213 | font-size: 16px; 214 | font-weight: 400; 215 | line-height: 24px; 216 | letter-spacing: -0.24px; 217 | color: var(--Colors-Text-Body-Default); 218 | text-align: left; 219 | transition: background-color 0.15s ease; 220 | box-sizing: border-box; 221 | opacity: 1; 222 | } 223 | 224 | .dropdown-container.grow-to-fit .dropdown-menu-item { 225 | width: 100%; 226 | min-width: 216px; 227 | } 228 | 229 | .dropdown-menu-item:hover:not(.selected) { 230 | background: var(--Colors-Menu-Option-Hover) !important; 231 | } 232 | 233 | .dropdown-menu-item:focus { 234 | outline: 2px solid var(--Colors-Primary-Medium); 235 | outline-offset: -2px; 236 | } 237 | 238 | .dropdown-menu-item.selected { 239 | width: 216px; 240 | height: 40px; 241 | border-radius: var(--Menu-Item-Roundness); 242 | padding: var(--UI-Spacing-spacing-none) var(--Menu-Item-Padding-Horizontal) !important; 243 | gap: var(--UI-Spacing-spacing-s); 244 | opacity: 1; 245 | background: var(--Colors-Menu-Option-Background-Selected) !important; 246 | font-family: "Work Sans", ui-sans-serif, system-ui, sans-serif; 247 | font-weight: 500; 248 | font-style: normal; 249 | font-size: 16px; 250 | line-height: 24px; 251 | letter-spacing: -0.24px; 252 | color: var(--Colors-Text-Body-Strong) !important; 253 | } 254 | 255 | .dropdown-menu-item.selected .dropdown-menu-item-label { 256 | color: var(--Colors-Text-Body-Strong) !important; 257 | font-weight: 500; 258 | } 259 | 260 | .dropdown-menu-item-content { 261 | display: flex; 262 | align-items: center; 263 | gap: var(--UI-Spacing-spacing-s); 264 | width: 100%; 265 | } 266 | 267 | .dropdown-menu-item-checkmark { 268 | display: flex; 269 | align-items: center; 270 | justify-content: center; 271 | flex-shrink: 0; 272 | width: 16px; 273 | height: 16px; 274 | position: relative; 275 | } 276 | 277 | .dropdown-menu-item-checkmark svg { 278 | display: block; 279 | width: 16px; 280 | height: 16px; 281 | position: relative; 282 | } 283 | 284 | .dropdown-menu-item-label { 285 | flex: 1; 286 | white-space: nowrap; 287 | overflow: hidden; 288 | text-overflow: ellipsis; 289 | } 290 | 291 | .dropdown-container.grow-to-fit .dropdown-menu-item-label { 292 | overflow: visible; 293 | text-overflow: clip; 294 | } 295 | 296 | /* Responsive adjustments */ 297 | @media (max-width: 768px) { 298 | .dropdown-menu { 299 | width: 100%; 300 | min-width: 240px; 301 | } 302 | } 303 | 304 | -------------------------------------------------------------------------------- /components/input/README.md: -------------------------------------------------------------------------------- 1 | # Input Component 2 | 3 | A versatile input component matching the CodeSignal Design System. Supports text and number inputs with various states and styling options. 4 | 5 | ## Usage 6 | 7 | Import the CSS file in your HTML or CSS: 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | or 14 | 15 | ```css 16 | @import url('/design-system/components/input/input.css'); 17 | ``` 18 | 19 | ## Classes 20 | 21 | ### Base Class 22 | - `.input`: The base class required for all input fields. 23 | 24 | ### Input Types 25 | - `type="text"`: Standard text input (default). 26 | - `type="number"`: Numeric input with styled spinner buttons. 27 | - `type="checkbox"`: Checkbox input with custom styling (see Checkbox section below). 28 | - `type="radio"`: Radio button input with custom styling (see Radio section below). 29 | 30 | ### States 31 | The component supports standard pseudo-classes (`:hover`, `:focus`, `:disabled`) and utility classes for manual state application: 32 | - `.hover`: Applies hover state styling (stronger border color). 33 | - `.focus`: Applies focus state styling (primary border color and focus ring). 34 | - `:disabled`: Disabled state (reduced opacity and not-allowed cursor). 35 | 36 | ## HTML Examples 37 | 38 | ```html 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ``` 60 | 61 | ## Checkbox Input 62 | 63 | The checkbox component provides a custom-styled checkbox input with support for multiple sizes and states. 64 | 65 | ### Checkbox Structure 66 | 67 | Checkboxes require a specific HTML structure: 68 | 69 | ```html 70 | 77 | ``` 78 | 79 | ### Checkbox Sizes 80 | 81 | - **Default**: 32px checkbox box, large label text (17px) 82 | - `.input-checkbox-small`: 26px checkbox box, medium label text (16px) 83 | - `.input-checkbox-xsmall`: 20px checkbox box, small label text (14px) 84 | 85 | ### Checkbox States 86 | 87 | - **Default**: White background with gray border 88 | - **Hover**: Blue border (primary color) 89 | - **Checked**: Blue background with white checkmark icon 90 | - **Disabled**: Reduced opacity (0.45 for box, 0.2 for label) 91 | 92 | ### Checkbox Examples 93 | 94 | ```html 95 | 96 | 103 | 104 | 105 | 112 | 113 | 114 | 121 | 122 | 123 | 130 | 131 | 132 | 139 | ``` 140 | 141 | ### Checkbox Features 142 | 143 | - **Custom Styling**: Native checkbox is hidden and replaced with a custom-styled box 144 | - **Checkmark Icon**: White checkmark appears when checked, using SVG mask for crisp rendering 145 | - **Focus Ring**: Accessible focus indicator with primary color ring 146 | - **Accessibility**: Proper label association and keyboard navigation support 147 | - **Responsive**: Adapts to dark mode automatically 148 | 149 | ## Radio Input 150 | 151 | The radio component provides a custom-styled radio button input with support for multiple sizes and states. Radio buttons are used when only one option can be selected from a group. 152 | 153 | ### Radio Structure 154 | 155 | Radio buttons require a specific HTML structure and must share the same `name` attribute to function as a group: 156 | 157 | ```html 158 | 165 | ``` 166 | 167 | ### Radio Sizes 168 | 169 | - **Default**: 32px radio circle, large label text (17px) 170 | - `.input-radio-small`: 26px radio circle, medium label text (16px) 171 | - `.input-radio-xsmall`: 20px radio circle, small label text (14px) 172 | 173 | ### Radio States 174 | 175 | - **Default**: White background with gray border 176 | - **Hover**: Blue border (primary color) 177 | - **Checked**: Blue border with blue inner dot 178 | - **Disabled**: Reduced opacity (0.45 for circle, 0.2 for label) 179 | 180 | ### Radio Examples 181 | 182 | ```html 183 | 184 | 191 | 192 | 193 | 200 | 201 | 202 |
203 | 210 | 217 | 224 |
225 | 226 | 227 | 234 | 235 | 236 | 243 | 244 | 245 | 252 | ``` 253 | 254 | ### Radio Features 255 | 256 | - **Custom Styling**: Native radio button is hidden and replaced with a custom-styled circle 257 | - **Inner Dot**: Blue dot appears when checked, indicating selection 258 | - **Focus Ring**: Accessible focus indicator with primary color ring 259 | - **Group Behavior**: Radio buttons with the same `name` attribute work as a group (only one can be selected) 260 | - **Accessibility**: Proper label association and keyboard navigation support 261 | - **Responsive**: Adapts to dark mode automatically 262 | 263 | ## Features 264 | 265 | ### Number Input Spinner Buttons 266 | Number inputs (`type="number"`) include styled spinner buttons that: 267 | - Are properly sized and positioned within the input 268 | - Have subtle opacity that increases on hover and focus 269 | - Work consistently across WebKit browsers (Chrome, Safari, Edge) and Firefox 270 | - Maintain the input's width without requiring extra padding 271 | 272 | ### Focus Ring 273 | When focused, inputs display a subtle focus ring using the primary color with reduced opacity for better accessibility. 274 | 275 | ## Dark Mode 276 | 277 | The component automatically adapts to dark mode via the `@media (prefers-color-scheme: dark)` query. All states are optimized for both light and dark themes. 278 | 279 | ## Dependencies 280 | 281 | This component relies on variables from: 282 | - `design-system/colors/colors.css` 283 | - `design-system/spacing/spacing.css` 284 | - `design-system/typography/typography.css` 285 | 286 | -------------------------------------------------------------------------------- /components/tags/tags.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Tag Component Styles 3 | * Matches CodeSignal Design System 4 | */ 5 | 6 | :root { 7 | /* Tag Variables */ 8 | --UI-Radius-tag-medium: var(--UI-Radius-radius-s); 9 | 10 | --Colors-Tags-Primary: var(--Colors-Base-Primary-50); 11 | --Colors-Tags-Primary-Text: var(--Colors-Base-Primary-950); 12 | --Colors-Tags-Primary-Stroke: var(--Colors-Base-Primary-250); 13 | --Colors-Tags-Primary-Hover: var(--Colors-Base-Primary-100); 14 | --Colors-Tags-Primary-Focus: rgba(160, 193, 255, 0.20); 15 | 16 | --Colors-Tags-Secondary: var(--Colors-Base-Neutral-150); 17 | --Colors-Tags-Secondary-Text: var(--Colors-Base-Neutral-1200); 18 | --Colors-Tags-Secondary-Stroke: var(--Colors-Base-Neutral-400); 19 | --Colors-Tags-Secondary-Hover: var(--Colors-Base-Neutral-250); 20 | --Colors-Tags-Secondary-Focus: rgba(200, 205, 219, 0.25); 21 | 22 | --Colors-Tags-Outline: var(--Colors-Base-Neutral-00); 23 | --Colors-Tags-Outline-Text: var(--Colors-Base-Neutral-1200); 24 | --Colors-Tags-Outline-Stroke: var(--Colors-Base-Neutral-200); 25 | --Colors-Tags-Outline-Background-Hover: var(--Colors-Base-Neutral-20); 26 | --Colors-Tags-Outline-Hover: var(--Colors-Base-Neutral-400); 27 | --Colors-Tags-Outline-Focus: rgba(200, 205, 219, 0.25); 28 | --Colors-Tags-Outline-Selected: var(--Colors-Base-Neutral-550); 29 | 30 | --Colors-Tags-Success: var(--Colors-Base-Accent-Green-50); 31 | --Colors-Tags-Success-Text: var(--Colors-Base-Accent-Green-1000); 32 | --Colors-Tags-Success-Stroke: var(--Colors-Base-Accent-Green-300); 33 | --Colors-Tags-Success-Hover: var(--Colors-Base-Accent-Green-100); 34 | --Colors-Tags-Success-Focus: rgba(77, 217, 155, 0.16); 35 | 36 | --Colors-Tags-Error: var(--Colors-Base-Accent-Red-50); 37 | --Colors-Tags-Error-Text: var(--Colors-Base-Accent-Red-1000); 38 | --Colors-Tags-Error-Stroke: var(--Colors-Base-Accent-Red-100); 39 | --Colors-Tags-Error-Hover: var(--Colors-Base-Accent-Red-100); 40 | --Colors-Tags-Error-Focus: rgba(255, 169, 180, 0.20); 41 | 42 | --Colors-Tags-Warning: var(--Colors-Base-Accent-Yellow-50); 43 | --Colors-Tags-Warning-Text: var(--Colors-Base-Accent-Yellow-1000); 44 | --Colors-Tags-Warning-Stroke: var(--Colors-Base-Accent-Yellow-200); 45 | --Colors-Tags-Warning-Hover: var(--Colors-Base-Accent-Yellow-100); 46 | --Colors-Tags-Warning-Focus: rgba(255, 220, 102, 0.22); 47 | 48 | --Colors-Tags-Info: var(--Colors-Base-Accent-Sky-Blue-50); 49 | --Colors-Tags-Info-Text: var(--Colors-Base-Accent-Sky-Blue-1000); 50 | --Colors-Tags-Info-Stroke: var(--Colors-Base-Accent-Sky-Blue-300); 51 | --Colors-Tags-Info-Hover: var(--Colors-Base-Accent-Sky-Blue-200); 52 | --Colors-Tags-Info-Focus: rgba(131, 220, 255, 0.25); 53 | } 54 | 55 | @media (prefers-color-scheme: dark) { 56 | :root { 57 | --Colors-Tags-Primary: var(--Colors-Base-Primary-300); 58 | --Colors-Tags-Primary-Text: var(--Colors-Base-Primary-1300); 59 | --Colors-Tags-Primary-Stroke: var(--Colors-Base-Primary-150); 60 | --Colors-Tags-Primary-Hover: var(--Colors-Base-Primary-400); 61 | --Colors-Tags-Primary-Focus: var(--Colors-Base-Neutral-1150); 62 | 63 | --Colors-Tags-Secondary: var(--Colors-Base-Neutral-1150); 64 | --Colors-Tags-Secondary-Text: var(--Colors-Base-Neutral-400); 65 | --Colors-Tags-Secondary-Stroke: var(--Colors-Base-Neutral-950); 66 | --Colors-Tags-Secondary-Hover: var(--Colors-Base-Neutral-1200); 67 | --Colors-Tags-Secondary-Focus: var(--Colors-Base-Neutral-1150); 68 | 69 | --Colors-Tags-Outline: var(--Colors-Base-Neutral-1300); 70 | --Colors-Tags-Outline-Text: var(--Colors-Base-Neutral-450); 71 | --Colors-Tags-Outline-Stroke: var(--Colors-Base-Neutral-1100); 72 | --Colors-Tags-Outline-Background-Hover: var(--Colors-Base-Neutral-1250); 73 | --Colors-Tags-Outline-Hover: var(--Colors-Base-Neutral-900); 74 | --Colors-Tags-Outline-Focus: var(--Colors-Base-Neutral-1150); 75 | --Colors-Tags-Outline-Selected: var(--Colors-Base-Neutral-700); 76 | 77 | --Colors-Tags-Success: var(--Colors-Base-Accent-Green-200); 78 | --Colors-Tags-Success-Text: var(--Colors-Base-Neutral-1300); 79 | --Colors-Tags-Success-Stroke: var(--Colors-Base-Accent-Green-50); 80 | --Colors-Tags-Success-Hover: var(--Colors-Base-Accent-Green-300); 81 | --Colors-Tags-Success-Focus: var(--Colors-Base-Neutral-1150); 82 | 83 | --Colors-Tags-Error: var(--Colors-Base-Accent-Red-100); 84 | --Colors-Tags-Error-Text: var(--Colors-Base-Neutral-1300); 85 | --Colors-Tags-Error-Stroke: var(--Colors-Base-Accent-Red-50); 86 | --Colors-Tags-Error-Hover: var(--Colors-Base-Accent-Red-200); 87 | --Colors-Tags-Error-Focus: var(--Colors-Base-Neutral-1150); 88 | 89 | --Colors-Tags-Warning: var(--Colors-Base-Accent-Yellow-100); 90 | --Colors-Tags-Warning-Text: var(--Colors-Base-Neutral-1300); 91 | --Colors-Tags-Warning-Stroke: var(--Colors-Base-Accent-Yellow-50); 92 | --Colors-Tags-Warning-Hover: var(--Colors-Base-Accent-Yellow-200); 93 | --Colors-Tags-Warning-Focus: var(--Colors-Base-Neutral-1150); 94 | 95 | --Colors-Tags-Info: var(--Colors-Base-Accent-Sky-Blue-300); 96 | --Colors-Tags-Info-Text: var(--Colors-Base-Neutral-1300); 97 | --Colors-Tags-Info-Stroke: var(--Colors-Base-Accent-Sky-Blue-50); 98 | --Colors-Tags-Info-Hover: var(--Colors-Base-Accent-Sky-Blue-200); 99 | --Colors-Tags-Info-Focus: var(--Colors-Base-Neutral-1150); 100 | } 101 | } 102 | 103 | .tag, 104 | .tag.default { 105 | display: inline-flex; 106 | height: var(--UI-Input-sm); 107 | padding: var(--UI-Spacing-spacing-none) var(--UI-Spacing-spacing-ms); 108 | align-items: center; 109 | gap: var(--UI-Spacing-spacing-xs); 110 | flex-shrink: 0; 111 | border-radius: var(--UI-Radius-tag-medium); 112 | background: var(--Colors-Tags-Primary); 113 | border: 1.5px solid transparent; 114 | 115 | color: var(--Colors-Tags-Primary-Text); 116 | 117 | font-family: "Work Sans"; 118 | font-size: var(--Fonts-Body-Default-md); 119 | font-style: normal; 120 | font-weight: 500; 121 | line-height: 145%; /* 23.2px */ 122 | letter-spacing: -0.24px; 123 | } 124 | 125 | .tag:hover, 126 | .tag.hover, 127 | .tag.default:hover, 128 | .tag.default.hover { 129 | background: var(--Colors-Tags-Primary-Hover); 130 | } 131 | 132 | .tag:active, 133 | .tag.active, 134 | .tag.default:active, 135 | .tag.default.active { 136 | border: 1.5px solid var(--Colors-Tags-Primary-Stroke); 137 | } 138 | 139 | .tag:focus, 140 | .tag.focus, 141 | .tag.default:focus, 142 | .tag.default.focus { 143 | border: 1.5px solid var(--Colors-Tags-Primary-Stroke); 144 | box-shadow: 0 0 0 4px var(--Colors-Tags-Primary-Focus); 145 | } 146 | 147 | .tag.secondary { 148 | background: var(--Colors-Tags-Secondary); 149 | color: var(--Colors-Tags-Secondary-Text); 150 | } 151 | 152 | .tag.secondary:hover, 153 | .tag.secondary.hover { 154 | background: var(--Colors-Tags-Secondary-Hover); 155 | } 156 | 157 | .tag.secondary:active, 158 | .tag.secondary.active { 159 | border: 1.5px solid var(--Colors-Tags-Secondary-Stroke); 160 | } 161 | 162 | .tag.secondary:focus, 163 | .tag.secondary.focus { 164 | border: 1.5px solid var(--Colors-Tags-Secondary-Stroke); 165 | box-shadow: 0 0 0 4px var(--Colors-Tags-Secondary-Focus); 166 | } 167 | 168 | .tag.outline { 169 | background: var(--Colors-Tags-Outline); 170 | color: var(--Colors-Tags-Outline-Text); 171 | border: 1.5px solid var(--Colors-Tags-Outline-Stroke); 172 | } 173 | 174 | .tag.outline:hover, 175 | .tag.outline.hover { 176 | background: var(--Colors-Tags-Outline-Background-Hover); 177 | border: 1.5px solid var(--Colors-Tags-Outline-Hover); 178 | } 179 | 180 | .tag.outline:active, 181 | .tag.outline.active { 182 | border: 1.5px solid var(--Colors-Tags-Outline-Selected); 183 | } 184 | 185 | .tag.outline:focus, 186 | .tag.outline.focus { 187 | border: 1.5px solid var(--Colors-Tags-Outline-Selected); 188 | box-shadow: 0 0 0 4px var(--Colors-Tags-Outline-Focus); 189 | } 190 | 191 | .tag.success { 192 | background: var(--Colors-Tags-Success); 193 | color: var(--Colors-Tags-Success-Text); 194 | } 195 | 196 | .tag.success:hover, 197 | .tag.success.hover { 198 | background: var(--Colors-Tags-Success-Hover); 199 | } 200 | 201 | .tag.success:active, 202 | .tag.success.active { 203 | border: 1.5px solid var(--Colors-Tags-Success-Stroke); 204 | } 205 | 206 | .tag.success:focus, 207 | .tag.success.focus { 208 | border: 1.5px solid var(--Colors-Tags-Success-Stroke); 209 | box-shadow: 0 0 0 4px var(--Colors-Tags-Success-Focus); 210 | } 211 | 212 | .tag.error { 213 | background: var(--Colors-Tags-Error); 214 | color: var(--Colors-Tags-Error-Text); 215 | } 216 | 217 | .tag.error:hover, 218 | .tag.error.hover { 219 | background: var(--Colors-Tags-Error-Hover); 220 | } 221 | 222 | .tag.error:active, 223 | .tag.error.active { 224 | border: 1.5px solid var(--Colors-Tags-Error-Stroke); 225 | } 226 | 227 | .tag.error:focus, 228 | .tag.error.focus { 229 | border: 1.5px solid var(--Colors-Tags-Error-Stroke); 230 | box-shadow: 0 0 0 4px var(--Colors-Tags-Error-Focus); 231 | } 232 | 233 | .tag.warning { 234 | background: var(--Colors-Tags-Warning); 235 | color: var(--Colors-Tags-Warning-Text); 236 | } 237 | 238 | .tag.warning:hover, 239 | .tag.warning.hover { 240 | background: var(--Colors-Tags-Warning-Hover); 241 | } 242 | 243 | .tag.warning:active, 244 | .tag.warning.active { 245 | border: 1.5px solid var(--Colors-Tags-Warning-Stroke); 246 | } 247 | 248 | .tag.warning:focus, 249 | .tag.warning.focus { 250 | border: 1.5px solid var(--Colors-Tags-Warning-Stroke); 251 | box-shadow: 0 0 0 4px var(--Colors-Tags-Warning-Focus); 252 | } 253 | 254 | .tag.info { 255 | background: var(--Colors-Tags-Info); 256 | color: var(--Colors-Tags-Info-Text); 257 | } 258 | 259 | .tag.info:hover, 260 | .tag.info.hover { 261 | background: var(--Colors-Tags-Info-Hover); 262 | } 263 | 264 | .tag.info:active, 265 | .tag.info.active { 266 | border: 1.5px solid var(--Colors-Tags-Info-Stroke); 267 | } 268 | 269 | .tag.info:focus, 270 | .tag.info.focus { 271 | border: 1.5px solid var(--Colors-Tags-Info-Stroke); 272 | box-shadow: 0 0 0 4px var(--Colors-Tags-Info-Focus); 273 | } 274 | -------------------------------------------------------------------------------- /components/modal/modal.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Modal Component Styles 3 | * Matches CodeSignal Design System 4 | */ 5 | 6 | :root { 7 | /* Modal Variables - Light Mode */ 8 | --Colors-Modal-Overlay: rgba(29, 38, 62, 0.4); 9 | --Colors-Modal-Background: var(--Colors-Base-Neutral-00); 10 | --Colors-Modal-Shadow-Float: rgba(22, 44, 96, 0.12); 11 | --Colors-Modal-Close-Icon: var(--Colors-Base-Neutral-700); 12 | --Colors-Modal-Close-Icon-Hover: var(--Colors-Base-Neutral-900); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | /* Modal Variables - Dark Mode */ 18 | --Colors-Modal-Overlay: rgba(13, 21, 40, 0.94); 19 | --Colors-Modal-Background: var(--Colors-Base-Neutral-1300); 20 | --Colors-Modal-Shadow-Float: rgba(13, 21, 40, 0.3); 21 | --Colors-Modal-Close-Icon: var(--Colors-Base-Neutral-700); 22 | --Colors-Modal-Close-Icon-Hover: var(--Colors-Base-Neutral-500); 23 | } 24 | } 25 | 26 | /* Modal Overlay */ 27 | .modal-overlay { 28 | position: fixed; 29 | top: 0; 30 | left: 0; 31 | right: 0; 32 | bottom: 0; 33 | background: var(--Colors-Modal-Overlay); 34 | display: flex; 35 | align-items: center; 36 | justify-content: center; 37 | z-index: 10000; 38 | padding: var(--UI-Spacing-spacing-xxxl); 39 | opacity: 0; 40 | transition: opacity 0.2s ease; 41 | pointer-events: none; 42 | } 43 | 44 | .modal-overlay.open { 45 | opacity: 1; 46 | pointer-events: auto; 47 | } 48 | 49 | /* Modal Dialog */ 50 | .modal-dialog { 51 | position: relative; 52 | background: var(--Colors-Modal-Background); 53 | border-radius: var(--UI-Radius-radius-s); 54 | padding: var(--UI-Spacing-spacing-none); 55 | box-shadow: 0px 0px 48px 0px var(--Colors-Modal-Shadow-Float), 56 | -35px 30px 38px 0px var(--Colors-Modal-Shadow-Float); 57 | max-width: 100%; 58 | max-height: calc(100vh - 2 * var(--UI-Spacing-spacing-xxxl)); 59 | overflow: hidden; 60 | transform: scale(0.95); 61 | transition: transform 0.2s ease; 62 | 63 | display: flex; 64 | flex-direction: column; 65 | justify-content: flex-start; 66 | align-items: stretch; 67 | gap: var(--UI-Spacing-spacing-none); 68 | min-height: 0; 69 | } 70 | 71 | .modal-overlay.open .modal-dialog { 72 | transform: scale(1); 73 | } 74 | 75 | /* Modal Sizes */ 76 | .modal-dialog.size-small { 77 | width: 400px; 78 | } 79 | 80 | .modal-dialog.size-medium { 81 | width: 600px; 82 | } 83 | 84 | .modal-dialog.size-large { 85 | width: 900px; 86 | } 87 | 88 | .modal-dialog.size-xlarge { 89 | width: 1200px; 90 | } 91 | 92 | /* Modal Header */ 93 | .modal-header { 94 | display: flex; 95 | align-items: flex-end; 96 | height: 48px; 97 | position: relative; 98 | gap: var(--UI-Spacing-spacing-4xl); 99 | padding: var(--UI-Spacing-spacing-mxl) var(--UI-Spacing-spacing-xl) var(--UI-Spacing-spacing-mxl) var(--UI-Spacing-spacing-xl); 100 | align-self: stretch; 101 | } 102 | 103 | .modal-header:empty { 104 | display: none; 105 | } 106 | 107 | .modal-title { 108 | font-family: var(--heading-family); 109 | font-size: 20px; 110 | font-weight: 500; 111 | font-style: normal; 112 | line-height: 1.2; 113 | letter-spacing: -0.2px; 114 | color: var(--Colors-Text-Body-Strongest); 115 | margin: 0; 116 | flex: 1; 117 | } 118 | 119 | .modal-close-button { 120 | position: absolute; 121 | top: 4px; 122 | right: 4px; 123 | width: var(--UI-Input-md); 124 | height: var(--UI-Input-md); 125 | display: flex; 126 | align-items: center; 127 | justify-content: center; 128 | padding: var(--UI-Spacing-spacing-mxs); 129 | border: none; 130 | background: transparent; 131 | border-radius: var(--UI-Radius-radius-xs); 132 | cursor: pointer; 133 | transition: background-color 0.2s ease; 134 | } 135 | 136 | .modal-close-button:hover { 137 | background: var(--Colors-Backgrounds-Main-Medium); 138 | } 139 | 140 | .modal-close-button:focus { 141 | outline: 2px solid var(--Colors-Primary-Medium); 142 | outline-offset: 2px; 143 | } 144 | 145 | .modal-close-button:focus:not(:focus-visible) { 146 | outline: none; 147 | } 148 | 149 | .modal-close-icon { 150 | width: 24px; 151 | height: 24px; 152 | display: block; 153 | color: var(--Colors-Modal-Close-Icon); 154 | transition: color 0.2s ease; 155 | } 156 | 157 | .modal-close-button:hover .modal-close-icon { 158 | color: var(--Colors-Modal-Close-Icon-Hover); 159 | } 160 | 161 | .modal-close-icon svg { 162 | width: 100%; 163 | height: 100%; 164 | display: block; 165 | } 166 | 167 | /* Modal Content */ 168 | .modal-content { 169 | display: flex; 170 | flex: 1 1 auto; 171 | padding: var(--UI-Spacing-spacing-mxl) var(--UI-Spacing-spacing-xl) var(--UI-Spacing-spacing-xxxl) var(--UI-Spacing-spacing-xl); 172 | overflow-y: auto; 173 | min-height: 0; 174 | flex-direction: column; 175 | align-items: center; 176 | gap: var(--UI-Spacing-spacing-xl); 177 | align-self: stretch; 178 | scroll-behavior: smooth; 179 | } 180 | 181 | .modal-content:empty { 182 | min-height: 200px; 183 | } 184 | 185 | /* Modal Footer */ 186 | .modal-footer { 187 | padding: var(--UI-Spacing-spacing-mxl) var(--UI-Spacing-spacing-xl); 188 | min-height: 48px; 189 | display: flex; 190 | justify-content: flex-end; 191 | align-items: flex-start; 192 | align-self: stretch; 193 | gap: 12px; 194 | flex-shrink: 0; 195 | } 196 | 197 | .modal-footer-buttons { 198 | display: flex; 199 | flex: 1; 200 | gap: var(--UI-Spacing-spacing-mxs); 201 | align-items: flex-start; 202 | justify-content: flex-end; 203 | } 204 | 205 | .modal-footer-buttons .button { 206 | flex-shrink: 0; 207 | } 208 | 209 | /* Help Modal Styles */ 210 | .modal-content .toc { 211 | background: var(--Colors-Backgrounds-Main-Light); 212 | border-radius: var(--UI-Radius-radius-s); 213 | padding: var(--UI-Spacing-spacing-mxl); 214 | margin-bottom: var(--UI-Spacing-spacing-mxs); 215 | border-left: 3px solid var(--Colors-Primary-Default); 216 | align-self: flex-start; 217 | width: 100%; 218 | box-sizing: border-box; 219 | } 220 | 221 | .modal-content .toc strong { 222 | display: block; 223 | font-family: var(--heading-family); 224 | font-size: 24px; 225 | font-weight: 500; 226 | color: var(--Colors-Text-Body-Strongest); 227 | margin: 0 0 var(--UI-Spacing-spacing-mxl) 0; 228 | padding-bottom: var(--UI-Spacing-spacing-mxs); 229 | border-bottom: 1px solid var(--Colors-Stroke-Default); 230 | } 231 | 232 | .modal-content .toc ul { 233 | list-style: none; 234 | padding: 0; 235 | margin: var(--UI-Spacing-spacing-mxs) 0 0 0; 236 | } 237 | 238 | .modal-content .toc li { 239 | margin: var(--UI-Spacing-spacing-xs) 0; 240 | } 241 | 242 | .modal-content .toc a { 243 | color: var(--Colors-Primary-Default); 244 | text-decoration: none; 245 | font-size: 14px; 246 | transition: color 0.2s ease; 247 | } 248 | 249 | .modal-content .toc a:hover { 250 | color: var(--Colors-Primary-Strong); 251 | text-decoration: underline; 252 | } 253 | 254 | .modal-content section { 255 | margin-bottom: var(--UI-Spacing-spacing-mxs); 256 | scroll-margin-top: var(--UI-Spacing-spacing-mxl); 257 | align-self: flex-start; 258 | width: 100%; 259 | } 260 | 261 | .modal-content section:last-child { 262 | margin-bottom: 0; 263 | } 264 | 265 | .modal-content section h2 { 266 | font-family: var(--heading-family); 267 | font-size: 24px; 268 | font-weight: 500; 269 | color: var(--Colors-Text-Body-Strongest); 270 | margin: 0 0 var(--UI-Spacing-spacing-mxl) 0; 271 | padding-bottom: var(--UI-Spacing-spacing-mxs); 272 | border-bottom: 1px solid var(--Colors-Stroke-Default); 273 | } 274 | 275 | .modal-content section h3 { 276 | font-family: var(--heading-family); 277 | font-size: 18px; 278 | font-weight: 500; 279 | color: var(--Colors-Text-Body-Strongest); 280 | margin: var(--UI-Spacing-spacing-xl) 0 var(--UI-Spacing-spacing-mxs) 0; 281 | } 282 | 283 | .modal-content section p { 284 | color: var(--Colors-Text-Body-Default); 285 | margin: var(--UI-Spacing-spacing-mxs) 0; 286 | line-height: 1.6; 287 | } 288 | 289 | .modal-content section ol, 290 | .modal-content section ul { 291 | margin: var(--UI-Spacing-spacing-mxs) 0; 292 | padding-left: var(--UI-Spacing-spacing-xl); 293 | color: var(--Colors-Text-Body-Default); 294 | } 295 | 296 | .modal-content section li { 297 | margin: var(--UI-Spacing-spacing-xs) 0; 298 | line-height: 1.6; 299 | } 300 | 301 | .modal-content section li strong { 302 | color: var(--Colors-Text-Body-Strongest); 303 | font-weight: 600; 304 | } 305 | 306 | .modal-content section details { 307 | margin: var(--UI-Spacing-spacing-mxs) 0; 308 | padding: var(--UI-Spacing-spacing-mxs); 309 | border-radius: var(--UI-Radius-radius-xs); 310 | transition: background-color 0.2s ease; 311 | } 312 | 313 | .modal-content section details:hover { 314 | background: var(--Colors-Backgrounds-Main-Light); 315 | } 316 | 317 | .modal-content section details summary { 318 | font-weight: 500; 319 | color: var(--Colors-Text-Body-Strong); 320 | cursor: pointer; 321 | padding: var(--UI-Spacing-spacing-xs); 322 | margin: calc(-1 * var(--UI-Spacing-spacing-xs)); 323 | user-select: none; 324 | } 325 | 326 | .modal-content section details summary:hover { 327 | color: var(--Colors-Primary-Default); 328 | } 329 | 330 | .modal-content section details[open] summary { 331 | margin-bottom: var(--UI-Spacing-spacing-mxs); 332 | } 333 | 334 | .modal-content section details p { 335 | margin: var(--UI-Spacing-spacing-mxs) 0 0 0; 336 | padding-left: var(--UI-Spacing-spacing-mxs); 337 | } 338 | 339 | .modal-content section code { 340 | font-family: 'JetBrains Mono', monospace; 341 | font-size: 13px; 342 | background: var(--Colors-Backgrounds-Main-Medium); 343 | padding: 2px 6px; 344 | border-radius: var(--UI-Radius-radius-xxs); 345 | color: var(--Colors-Text-Body-Strong); 346 | } 347 | 348 | .modal-content section img { 349 | max-width: 100%; 350 | height: auto; 351 | border-radius: var(--UI-Radius-radius-s); 352 | margin: var(--UI-Spacing-spacing-mxl) 0; 353 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 354 | } 355 | 356 | /* Responsive adjustments */ 357 | @media (max-width: 768px) { 358 | .modal-overlay { 359 | padding: var(--UI-Spacing-spacing-mxl); 360 | } 361 | 362 | .modal-dialog.size-small, 363 | .modal-dialog.size-medium, 364 | .modal-dialog.size-large, 365 | .modal-dialog.size-xlarge { 366 | width: 100%; 367 | max-width: 100%; 368 | } 369 | 370 | .modal-header, 371 | .modal-content, 372 | .modal-footer { 373 | padding-left: var(--UI-Spacing-spacing-mxl); 374 | padding-right: var(--UI-Spacing-spacing-mxl); 375 | } 376 | 377 | .modal-content section h2 { 378 | font-size: 20px; 379 | } 380 | 381 | .modal-content section h3 { 382 | font-size: 16px; 383 | } 384 | 385 | .modal-content .toc strong { 386 | font-size: 20px; 387 | } 388 | } 389 | 390 | -------------------------------------------------------------------------------- /components/dropdown/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Component Tests - Dropdown 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 116 | 117 | 118 |
119 |

120 | Component Tests - Dropdown 121 |

122 | 123 | 124 |
125 |

Basic Dropdown

126 |
127 |
128 | 129 | 130 |
131 |
132 |
133 | Selected Value: None 134 |
135 |
136 | 137 | 138 |
139 |

Dropdown with Pre-selected Value

140 |
141 |
142 | 143 | 144 |
145 |
146 |
147 | Selected Value: option-2 148 |
149 |
150 | 151 | 152 |
153 |

Different Widths

154 |
155 |
156 | 157 | 158 |
159 |
160 | 161 | 162 |
163 |
164 | 165 | 166 |
167 |
168 |
169 | 170 | 171 |
172 |

Custom Placeholder

173 |
174 |
175 | 176 | 177 |
178 |
179 |
180 | Selected Value: None 181 |
182 |
183 | 184 | 185 |
186 |

Many Options

187 |
188 |
189 | 190 | 191 |
192 |
193 |
194 | Selected Value: None 195 |
196 |
197 | 198 | 199 |
200 |

Grow to Fit Selected Value

201 |
202 |
203 | 204 | 205 |
206 |
207 |
208 | Selected Value: None 209 |
210 | 211 | Select a long option to see the dropdown grow to fit the text. 212 | 213 |
214 |
215 |
216 | 217 | 315 | 316 | 317 | 318 | -------------------------------------------------------------------------------- /components/modal/README.md: -------------------------------------------------------------------------------- 1 | # Modal Component 2 | 3 | A customizable modal component matching the CodeSignal Design System. Supports declarative content insertion and flexible configuration. 4 | 5 | ## Usage 6 | 7 | ### 1. Import Assets 8 | 9 | Include the CSS and JS files: 10 | 11 | ```html 12 | 13 | 17 | ``` 18 | 19 | ### 2. Initialize Component 20 | 21 | ```javascript 22 | import Modal from './modal.js'; 23 | 24 | const modal = new Modal({ 25 | size: 'medium', 26 | title: 'Modal Title', 27 | content: '

Modal content goes here

', 28 | footerButtons: [ 29 | { label: 'Cancel', type: 'secondary' }, 30 | { label: 'Save', type: 'primary', onClick: () => console.log('Saved!') } 31 | ] 32 | }); 33 | 34 | // Open the modal 35 | modal.open(); 36 | ``` 37 | 38 | ## Configuration Options 39 | 40 | | Option | Type | Default | Description | 41 | | :--- | :--- | :--- | :--- | 42 | | `size` | String | `'medium'` | Modal size: `'small'` (400px), `'medium'` (600px), `'large'` (900px), `'xlarge'` (1200px). | 43 | | `title` | String | `null` | Modal title text. If `null`, header is hidden. | 44 | | `content` | String/Element | `null` | Modal content. Can be HTML string, DOM element, or CSS selector. | 45 | | `showCloseButton` | Boolean | `true` | If `true`, displays close button (X) in header. | 46 | | `footerButtons` | Array | `null` | Array of button configs `[{label, type, onClick}]`. If `null`, footer is hidden. | 47 | | `closeOnOverlayClick` | Boolean | `true` | If `true`, clicking overlay closes modal. | 48 | | `closeOnEscape` | Boolean | `true` | If `true`, pressing Escape closes modal. | 49 | | `onOpen` | Function | `null` | Callback triggered when modal opens. Receives `(modal)` instance. | 50 | | `onClose` | Function | `null` | Callback triggered when modal closes. Receives `(modal)` instance. | 51 | 52 | ## Declarative Content 53 | 54 | The modal supports multiple ways to set content declaratively: 55 | 56 | ### HTML String 57 | 58 | ```javascript 59 | const modal = new Modal({ 60 | title: 'Simple Modal', 61 | content: '

This is simple HTML content.

' 62 | }); 63 | modal.open(); 64 | ``` 65 | 66 | ### DOM Element 67 | 68 | ```javascript 69 | const contentDiv = document.createElement('div'); 70 | contentDiv.innerHTML = '

Content from DOM element

'; 71 | 72 | const modal = new Modal({ 73 | title: 'DOM Element Modal', 74 | content: contentDiv 75 | }); 76 | modal.open(); 77 | ``` 78 | 79 | ### CSS Selector 80 | 81 | ```javascript 82 | // Use existing element in the page (cloned) 83 | const modal = new Modal({ 84 | title: 'Selector Modal', 85 | content: '#my-content-element' // or '.my-content-class' 86 | }); 87 | modal.open(); 88 | ``` 89 | 90 | ### Template Element 91 | 92 | ```html 93 | 94 | 100 | 101 | 111 | ``` 112 | 113 | ## API Methods 114 | 115 | ### `open()` 116 | 117 | Opens the modal and locks body scroll. 118 | 119 | ```javascript 120 | modal.open(); 121 | ``` 122 | 123 | ### `close()` 124 | 125 | Closes the modal and restores body scroll. 126 | 127 | ```javascript 128 | modal.close(); 129 | ``` 130 | 131 | ### `updateContent(content)` 132 | 133 | Updates the modal content dynamically. Accepts same content types as constructor. 134 | 135 | ```javascript 136 | modal.updateContent('

New content

'); 137 | modal.updateContent(document.querySelector('#new-content')); 138 | ``` 139 | 140 | ### `updateTitle(title)` 141 | 142 | Updates the modal title. Creates title element if it doesn't exist. 143 | 144 | ```javascript 145 | modal.updateTitle('New Title'); 146 | ``` 147 | 148 | ### `destroy()` 149 | 150 | Removes the modal from DOM and cleans up event listeners. 151 | 152 | ```javascript 153 | modal.destroy(); 154 | ``` 155 | 156 | ## Examples 157 | 158 | ### Basic Modal 159 | 160 | ```javascript 161 | const modal = new Modal({ 162 | title: 'Basic Modal', 163 | content: '

This is a basic modal with just content.

' 164 | }); 165 | modal.open(); 166 | ``` 167 | 168 | ### Modal with Footer Buttons 169 | 170 | ```javascript 171 | const modal = new Modal({ 172 | size: 'large', 173 | title: 'Confirm Action', 174 | content: '

Are you sure you want to proceed?

', 175 | footerButtons: [ 176 | { 177 | label: 'Cancel', 178 | type: 'secondary', 179 | onClick: () => { 180 | console.log('Cancelled'); 181 | modal.close(); 182 | } 183 | }, 184 | { 185 | label: 'Confirm', 186 | type: 'primary', 187 | onClick: () => { 188 | console.log('Confirmed'); 189 | modal.close(); 190 | } 191 | } 192 | ] 193 | }); 194 | modal.open(); 195 | ``` 196 | 197 | ### Modal with Form Content 198 | 199 | ```javascript 200 | const formHTML = ` 201 |
202 |
203 | 204 | 205 |
206 |
207 | 208 | 209 |
210 |
211 | `; 212 | 213 | const modal = new Modal({ 214 | title: 'User Information', 215 | content: formHTML, 216 | footerButtons: [ 217 | { label: 'Cancel', type: 'secondary' }, 218 | { label: 'Save', type: 'primary' } 219 | ] 220 | }); 221 | modal.open(); 222 | ``` 223 | 224 | ### Modal Without Footer 225 | 226 | ```javascript 227 | const modal = new Modal({ 228 | title: 'Information', 229 | content: '

This modal has no footer buttons.

', 230 | footerButtons: null 231 | }); 232 | modal.open(); 233 | ``` 234 | 235 | ### Modal Without Title 236 | 237 | ```javascript 238 | const modal = new Modal({ 239 | title: null, 240 | content: '

This modal has no title.

', 241 | footerButtons: [ 242 | { label: 'Close', type: 'primary' } 243 | ] 244 | }); 245 | modal.open(); 246 | ``` 247 | 248 | ### Dynamic Content Update 249 | 250 | ```javascript 251 | const modal = new Modal({ 252 | title: 'Dynamic Content', 253 | content: '

Initial content

' 254 | }); 255 | modal.open(); 256 | 257 | // Update content after 2 seconds 258 | setTimeout(() => { 259 | modal.updateContent('

Updated content!

'); 260 | }, 2000); 261 | ``` 262 | 263 | ### Help Modal 264 | 265 | The Modal component includes a specialized `createHelpModal()` static method for creating help/documentation modals. It's a convenience method that sets sensible defaults (xlarge size, footer with close button) and applies appropriate styling for help content. 266 | 267 | **Use HTML `