@codecademy/gamut 68.6.1-alpha.e6c390.0 → 68.6.1-alpha.f6b2ce.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/agent-tools/.cursor-plugin/plugin.json +1 -1
  2. package/agent-tools/DESIGN.Codecademy.md +239 -191
  3. package/agent-tools/DESIGN.LXStudio.md +236 -184
  4. package/agent-tools/DESIGN.Percipio.md +232 -182
  5. package/agent-tools/DESIGN.md +1 -1
  6. package/agent-tools/commands/gamut-review.md +176 -87
  7. package/agent-tools/guidelines/components/animations.md +74 -0
  8. package/agent-tools/guidelines/components/buttons.md +74 -23
  9. package/agent-tools/guidelines/components/card.md +19 -0
  10. package/agent-tools/guidelines/components/coachmark.md +21 -0
  11. package/agent-tools/guidelines/components/data-table.md +79 -0
  12. package/agent-tools/guidelines/components/forms.md +106 -0
  13. package/agent-tools/guidelines/components/loading-states.md +17 -0
  14. package/agent-tools/guidelines/components/menu.md +58 -0
  15. package/agent-tools/guidelines/components/overview.md +97 -17
  16. package/agent-tools/guidelines/components/radial-progress.md +13 -0
  17. package/agent-tools/guidelines/components/select.md +23 -0
  18. package/agent-tools/guidelines/components/tooltips.md +22 -0
  19. package/agent-tools/guidelines/components/video.md +29 -0
  20. package/agent-tools/guidelines/foundations/color.md +140 -58
  21. package/agent-tools/guidelines/foundations/modes.md +39 -17
  22. package/agent-tools/guidelines/foundations/spacing.md +78 -37
  23. package/agent-tools/guidelines/foundations/typography.md +69 -37
  24. package/agent-tools/guidelines/overview-icons.md +19 -0
  25. package/agent-tools/guidelines/overview-illustrations.md +7 -0
  26. package/agent-tools/guidelines/overview-patterns.md +7 -0
  27. package/agent-tools/guidelines/overview.md +69 -23
  28. package/agent-tools/guidelines/setup.md +59 -18
  29. package/agent-tools/rules/accessibility.mdc +22 -13
  30. package/agent-tools/skills/gamut-accessibility/SKILL.md +97 -112
  31. package/agent-tools/skills/gamut-color-mode/SKILL.md +79 -29
  32. package/agent-tools/skills/gamut-components/SKILL.md +46 -0
  33. package/agent-tools/skills/gamut-forms/SKILL.md +101 -0
  34. package/agent-tools/skills/gamut-style-utilities/SKILL.md +111 -0
  35. package/agent-tools/skills/gamut-system-props/SKILL.md +70 -26
  36. package/agent-tools/skills/gamut-testing/SKILL.md +106 -62
  37. package/agent-tools/skills/gamut-theming/SKILL.md +34 -86
  38. package/agent-tools/skills/gamut-typography/SKILL.md +36 -80
  39. package/bin/commands/plugin/install.mjs +96 -56
  40. package/bin/commands/plugin/list.mjs +11 -43
  41. package/bin/commands/plugin/remove.mjs +30 -38
  42. package/bin/commands/plugin/update.mjs +15 -5
  43. package/bin/gamut.mjs +17 -13
  44. package/bin/lib/design.mjs +71 -0
  45. package/bin/lib/io.mjs +14 -0
  46. package/package.json +6 -6
  47. package/bin/lib/figma.mjs +0 -49
@@ -0,0 +1,79 @@
1
+ # DataTable & DataList sorting
2
+
3
+ `DataTable` and `DataList` do not sort data automatically. `sortable: true` on a column only renders sort UI. You must implement sorting via `query` and `onQueryChange`, and pass pre-sorted `rows`.
4
+
5
+ ## How sorting works
6
+
7
+ 1. `sortable: true` — enables sort toggle on the column header. Without `query` / `onQueryChange`, clicks do nothing.
8
+ 2. `query` — `Query<Row>` holding current sort (and filter) state. `sort` maps column keys to `'asc' | 'desc'`.
9
+ 3. `onQueryChange` — receives `QueryChangeEvent<Row>` when the user clicks a sortable header. Update `query` in your handler.
10
+ 4. Sorted rows — sort the `rows` array yourself from `query.sort` before passing to the component.
11
+
12
+ ## Required pattern
13
+
14
+ ```tsx
15
+ import { useMemo, useReducer } from 'react';
16
+ import { DataTable } from '@codecademy/gamut';
17
+ import type { Query, QueryChangeEvent } from '@codecademy/gamut';
18
+
19
+ function queryReducer<Row>(
20
+ state: Query<Row>,
21
+ event: QueryChangeEvent<Row>
22
+ ): Query<Row> {
23
+ switch (event.type) {
24
+ case 'sort': {
25
+ const { dimension, value } = event.payload;
26
+ if (value === 'none') {
27
+ const { [dimension]: _, ...rest } = state.sort ?? {};
28
+ return { ...state, sort: rest };
29
+ }
30
+ return { ...state, sort: { [dimension]: value } };
31
+ }
32
+ case 'filter': {
33
+ const { dimension, value } = event.payload;
34
+ return { ...state, filter: { ...state.filter, [dimension]: value } };
35
+ }
36
+ case 'reset':
37
+ return {};
38
+ default:
39
+ return state;
40
+ }
41
+ }
42
+
43
+ const [query, onQueryChange] = useReducer(queryReducer, {});
44
+
45
+ const sortedRows = useMemo(() => {
46
+ const sorted = [...rows];
47
+ const sortEntries = Object.entries(query.sort ?? {});
48
+ if (sortEntries.length > 0) {
49
+ const [key, direction] = sortEntries[0];
50
+ sorted.sort((a, b) => {
51
+ const aVal = String(a[key]).toLowerCase();
52
+ const bVal = String(b[key]).toLowerCase();
53
+ if (aVal < bVal) return direction === 'asc' ? -1 : 1;
54
+ if (aVal > bVal) return direction === 'asc' ? 1 : -1;
55
+ return 0;
56
+ });
57
+ }
58
+ return sorted;
59
+ }, [query.sort, rows]);
60
+
61
+ <DataTable
62
+ id="my-table"
63
+ idKey="id"
64
+ rows={sortedRows}
65
+ columns={columns}
66
+ query={query}
67
+ onQueryChange={onQueryChange}
68
+ />;
69
+ ```
70
+
71
+ ## Rules
72
+
73
+ 1. Never use `sortable: true` without `query` and `onQueryChange`.
74
+ 2. Always sort rows client-side (or server-side before passing) from `query.sort`.
75
+ 3. Handle `'sort'`, `'filter'`, and `'reset'` in the reducer even if you only use sorting.
76
+ 4. One active sort column in standard usage — replace the previous sort entry when the user picks a new column.
77
+ 5. Import `Query` and `QueryChangeEvent` from `@codecademy/gamut` for type safety.
78
+
79
+ Storybook: [Organisms / DataTable](https://gamut.codecademy.com/?path=/docs-organisms-datatable--docs)
@@ -0,0 +1,106 @@
1
+ # Forms
2
+
3
+ Gamut provides two form organisms — `GridForm` and `ConnectedForm` — that handle layout, validation, submission state, and field registration. Always use one of these organisms for functional forms. Do not compose forms from individual form atoms when the interface needs submit, validation, reset, or dirty tracking.
4
+
5
+ Related: [`skills/gamut-forms/SKILL.md`](../../skills/gamut-forms/SKILL.md) (accessibility wiring) · [`skills/gamut-accessibility/SKILL.md`](../../skills/gamut-accessibility/SKILL.md) (universal ARIA)
6
+
7
+ ## When to use each
8
+
9
+ | Component | Use when |
10
+ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `GridForm` | Declarative, config-driven form. Pass `fields` (and optional `sections`) plus `submit` — layout, labels, validation, and submit button are automatic. Best for settings pages, profile forms, and CRUD dialogs. |
12
+ | `ConnectedForm` | Full layout control with managed form state. Provides `react-hook-form` context; compose with `ConnectedFormGroup` and connected inputs. Best for tabs, custom columns, or content interleaved with fields. |
13
+
14
+ ## Rules
15
+
16
+ 1. Never use bare form atoms for functional forms. `Input`, `Select`, `Checkbox`, `Radio`, `TextArea`, `FormGroup`, and `FormGroupLabel` are building blocks _inside_ organisms. Use them standalone only when there is no submit step (e.g. live search filter, immediate callback toggle).
17
+ 2. Prefer `GridForm` over `ConnectedForm` when the layout fits a grid.
18
+ 3. Always provide `defaultValues`. Omitting defaults causes uncontrolled-to-controlled warnings and broken resets.
19
+ 4. Use `validation="onChange"` when the submit button should stay disabled until required fields are valid. Default `"onSubmit"` validates only on submit.
20
+ 5. `GridForm` sections — use `GridFormSectionProps` (`title`, `as`, `fields`) to group fields under headings in one form, not multiple separate forms.
21
+ 6. Import connected inputs from `@codecademy/gamut`. `ConnectedInput`, `ConnectedSelect`, `ConnectedCheckbox`, `ConnectedRadioGroup`, `ConnectedTextArea`, `ConnectedFormGroup`, `SubmitButton`.
22
+ 7. `hideLabel: true` on checkbox/radio fields with no `label`. Without it, `FormGroupLabel` renders a stray "(optional)" or "\*" row above the control.
23
+ 8. `hideLabel: true` on toggle (`custom` type) fields. `Toggle` renders its own inline label; a `FormGroupLabel` above is redundant.
24
+
25
+ ## GridForm example
26
+
27
+ ```tsx
28
+ import { GridForm } from '@codecademy/gamut';
29
+
30
+ <GridForm
31
+ fields={[
32
+ {
33
+ title: 'General',
34
+ as: 'h2',
35
+ variant: 'title-sm',
36
+ fields: [
37
+ {
38
+ name: 'orgName',
39
+ label: 'Organization Name',
40
+ type: 'text',
41
+ size: 6,
42
+ defaultValue: 'Acme Corp',
43
+ },
44
+ {
45
+ name: 'emailNotifs',
46
+ description: 'Receive email notifications',
47
+ type: 'checkbox',
48
+ size: 12,
49
+ defaultValue: true,
50
+ hideLabel: true,
51
+ },
52
+ ],
53
+ },
54
+ ]}
55
+ submit={{ contents: 'Save Settings', size: 12 }}
56
+ onSubmit={(values) => console.log(values)}
57
+ validation="onChange"
58
+ />;
59
+ ```
60
+
61
+ ## ConnectedForm example (`useConnectedForm`)
62
+
63
+ Prefer `useConnectedForm` for custom layouts — it types `ConnectedForm` / `ConnectedFormGroup` from `defaultValues` and spreads `connectedFormProps` (defaults + `validationRules`).
64
+
65
+ ```tsx
66
+ import {
67
+ ConnectedForm,
68
+ ConnectedFormGroup,
69
+ ConnectedInput,
70
+ SubmitButton,
71
+ useConnectedForm,
72
+ } from '@codecademy/gamut';
73
+
74
+ export const ProfileForm = () => {
75
+ const { ConnectedFormGroup, ConnectedForm, connectedFormProps } =
76
+ useConnectedForm({
77
+ defaultValues: { displayName: '' },
78
+ validationRules: {
79
+ displayName: { required: 'Display name is required' },
80
+ },
81
+ });
82
+
83
+ return (
84
+ <ConnectedForm
85
+ onSubmit={(values) => console.log(values)}
86
+ validation="onChange"
87
+ {...connectedFormProps}
88
+ >
89
+ <ConnectedFormGroup
90
+ name="displayName"
91
+ label="Display Name"
92
+ field={{ component: ConnectedInput }}
93
+ />
94
+ <SubmitButton>Save</SubmitButton>
95
+ </ConnectedForm>
96
+ );
97
+ };
98
+ ```
99
+
100
+ ## Quick reference
101
+
102
+ | Layer | Components | When to use directly |
103
+ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------- |
104
+ | Organisms | `GridForm`, `ConnectedForm` | Always for functional forms |
105
+ | Connected inputs | `ConnectedInput`, `ConnectedSelect`, `ConnectedCheckbox`, `ConnectedRadioGroup`, `ConnectedTextArea`, `ConnectedFormGroup`, `SubmitButton` | Only inside `ConnectedForm` |
106
+ | Atoms | `Input`, `Select`, `Checkbox`, `Radio`, `TextArea`, `FormGroup`, `FormGroupLabel`, `FormError` | Standalone display / live filters only |
@@ -0,0 +1,17 @@
1
+ # Loading states
2
+
3
+ Use `Shimmer` or `Spinner` for loading states. Do not use `FeatureShimmer` as a loading indicator.
4
+
5
+ Storybook: [Atoms / Shimmer](https://gamut.codecademy.com/?path=/docs-atoms-shimmer--docs) · [Spinner](https://gamut.codecademy.com/?path=/docs-atoms-spinner--docs)
6
+
7
+ ## Components
8
+
9
+ | Component | Use for |
10
+ | ---------------- | --------------------------------------------------------------------------------------------------------------------- |
11
+ | `Shimmer` | Skeleton placeholders for content being loaded |
12
+ | `Spinner` | Indeterminate spinner for active loading |
13
+ | `FeatureShimmer` | Not a loader. Draws attention to secondary new features on a page. Do not use as a general-purpose loading indicator. |
14
+
15
+ ## Rule
16
+
17
+ `Shimmer` and `Spinner` are the only true loading indicators. `FeatureShimmer` is for new-feature surfacing only.
@@ -0,0 +1,58 @@
1
+ # Menu
2
+
3
+ `Menu` and `MenuItem` from `@codecademy/gamut` are stateless — the consumer manages `active` and `disabled` state.
4
+
5
+ Related: [`skills/gamut-accessibility/SKILL.md`](../../skills/gamut-accessibility/SKILL.md)
6
+
7
+ Storybook: [Molecules / Menu](https://gamut.codecademy.com/?path=/docs-molecules-menu--docs)
8
+
9
+ ## Variants — always set explicitly
10
+
11
+ Always pass the `variant` prop. Relying on the default produces the wrong variant for persistent navigation.
12
+
13
+ | Variant | Use for | Rendering |
14
+ | --------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
15
+ | `fixed` | Persistent inline navigation — sidebars, primary nav, footer nav, any menu that does not open/close on interaction. | Must use `as="nav"` for accessible navigation. |
16
+ | `popover` | Temporary surfaces — overflow menus, action menus, kebab menus, dismiss on outside click. | Default popover surface. |
17
+
18
+ ### Fixed (sidebar / persistent navigation)
19
+
20
+ ```tsx
21
+ <Menu variant="fixed" as="nav">
22
+ <MenuItem
23
+ active={activeId === 'overview'}
24
+ onClick={() => setActive('overview')}
25
+ >
26
+ Overview
27
+ </MenuItem>
28
+ <MenuItem
29
+ active={activeId === 'settings'}
30
+ onClick={() => setActive('settings')}
31
+ >
32
+ Settings
33
+ </MenuItem>
34
+ </Menu>
35
+ ```
36
+
37
+ ### Popover (overflow / action menu)
38
+
39
+ ```tsx
40
+ <Menu variant="popover">
41
+ <MenuItem onClick={handleEdit}>Edit</MenuItem>
42
+ <MenuItem disabled label="Owner permissions required" onClick={handleDelete}>
43
+ Delete
44
+ </MenuItem>
45
+ </Menu>
46
+ ```
47
+
48
+ ## `MenuItem` state
49
+
50
+ - `active: boolean` — currently selected item; drive from your state.
51
+ - `disabled: boolean` — unavailable for interaction.
52
+ - `label: string` — when `disabled`, renders a `ToolTip` explaining why. Provide for any disabled item users might question.
53
+
54
+ ## Do not stretch `Menu` in flex layouts
55
+
56
+ `Menu` is a flex column sized to its content. If an ancestor is a flex child that stretches (`flex={1}`, `height: 100%`, `alignSelf: stretch`), the menu expands and items spread apart.
57
+
58
+ Rule: Wrap `Menu` in a block-level container with intrinsic height (`as="nav"` on a fixed menu, `Box`, or `div`). Do not use `FlexBox` as the immediate wrapper, and do not apply stretch on ancestors between `Menu` and the sidebar scroll container.
@@ -2,43 +2,123 @@
2
2
 
3
3
  52 components have Figma ↔ code mappings via Figma Code Connect (`packages/code-connect/`). Live code snippets appear in Figma's inspect panel when you select a component.
4
4
 
5
- ## Atoms — foundational, single-purpose
5
+ ## Core rules
6
6
 
7
- Badge, Button (FillButton, StrokeButton, CTAButton, TextButton, IconButton), ButtonBase, Card, Checkbox, CodeBlock, ColorMode, Drawer, FlexBox, FormGroup, GridBox, HiddenText, Icon, Input, Label, Loader, Radio, Select, Spinner, Tag, TextArea, Toggle, Tooltip
7
+ - Always use existing Gamut components from `packages/gamut/src` rather than one-off equivalents. See the quick reference table below.
8
+ - Apply exact sizing, variant, and props from the source. Never rely on defaults when the design or prompt specifies a value (e.g. `size="small"` on `Input`, `sizeVariant="small"` on `Select`).
9
+ - When unsure, reference `Badge`, `Tag`, or button atoms in `packages/gamut/src`.
8
10
 
9
- ## Molecules composed of atoms
11
+ ## Forms vs. standalone inputs
12
+
13
+ Functional forms — submit/save bundling multiple fields, validation, errors, or dirty tracking — must use `GridForm` or `ConnectedForm`. Read [forms.md](forms.md) before building any functional form.
14
+
15
+ Standalone inputs — live search, real-time filters, single controls that update state immediately — use bare atoms (`Input`, `Select`, `Checkbox`, `Radio`, `TextArea`, `FormGroup`). No organism when there is no submit step.
16
+
17
+ The test: submit/save bundled values → organism. Real-time state with no submit → atom.
18
+
19
+ ## Component discovery
20
+
21
+ Before custom markup for any UI pattern:
22
+
23
+ 1. Enumerate exports — check `@codecademy/gamut` public API or `index.d.ts`.
24
+ 2. Prefer Gamut over raw HTML — e.g. `Menu` for nav, `DataTable` for tables, `Tabs` for tabs. Do not rebuild from `<ul>` / `<motion.div>` when a primitive exists.
25
+ 3. Read type definitions for props before custom wrappers.
26
+ 4. Never build custom media players — use `Video` ([video.md](video.md)).
27
+ 5. When no Gamut component exists, comment: `{/* No Gamut component for [pattern] — custom markup */}`
28
+
29
+ ### Quick reference
30
+
31
+ | UI Pattern | Gamut component(s) | Guide |
32
+ | --------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------ |
33
+ | Buttons | `FillButton`, `StrokeButton`, `TextButton`, `IconButton`, `CTAButton` | [buttons.md](buttons.md) |
34
+ | Links | `Anchor` | — |
35
+ | Typography | `Text` | [`gamut-typography` skill](../../skills/gamut-typography/SKILL.md) |
36
+ | Markdown | `Markdown` | — |
37
+ | Layout | `Box`, `FlexBox`, `GridBox`, `Layout`, `LayoutGrid` | [spacing.md](../foundations/spacing.md) |
38
+ | Animations | `Rotation`, `ExpandInCollapseOut`, `FadeInSlideOut` | [animations.md](animations.md) |
39
+ | Page containers | `ContentContainer`, `GridContainer` | — |
40
+ | Navigation / menus | `Menu`, `MenuItem`, `MenuSeparator` | [menu.md](menu.md) |
41
+ | Breadcrumbs | `Breadcrumbs` | — |
42
+ | Pagination | `Pagination` | — |
43
+ | Tabs | `Tabs` | [`gamut-accessibility` skill](../../skills/gamut-accessibility/SKILL.md) |
44
+ | Accordions | `Disclosure` | — |
45
+ | Data tables | `DataTable`, `DataList` | [data-table.md](data-table.md) |
46
+ | Lists | `List` | — |
47
+ | Cards | `Card` | [card.md](card.md) |
48
+ | Charts | `BarChart` | — |
49
+ | Modals | `Dialog` (binary confirm/cancel); `Modal` (multi-view, complex) | [`gamut-accessibility` skill](../../skills/gamut-accessibility/SKILL.md) |
50
+ | Drawers / flyouts | `Drawer`, `Flyout` | [`gamut-accessibility` skill](../../skills/gamut-accessibility/SKILL.md) |
51
+ | Overlays / focus trap | `Overlay`, `FocusTrap` | [`gamut-accessibility` skill](../../skills/gamut-accessibility/SKILL.md) |
52
+ | Popovers | `Popover`, `PopoverContainer` | — |
53
+ | Tooltips | `ToolTip`, `InfoTip`, `PreviewTip` | [tooltips.md](tooltips.md) |
54
+ | Onboarding | `Coachmark` | [coachmark.md](coachmark.md) |
55
+ | Alerts / toasts | `Alert`, `Toast`, `Toaster` | — |
56
+ | Badges / tags | `Badge`, `Tag` | — |
57
+ | Progress | `ProgressBar`, `RadialProgress` | [radial-progress.md](radial-progress.md) |
58
+ | Loading | `Shimmer`, `Spinner` | [loading-states.md](loading-states.md) |
59
+ | Video | `Video` | [video.md](video.md) |
60
+ | Forms | `GridForm`, `ConnectedForm` | [forms.md](forms.md) |
61
+ | Standalone inputs | `Input`, `Select`, `Checkbox`, `Radio`, `TextArea`, `FormGroup` | Forms breadcrumb above |
62
+ | Rich select | `SelectDropdown` | [select.md](select.md) |
63
+ | Date | `DatePicker` | [`gamut-forms` skill](../../skills/gamut-forms/SKILL.md) |
64
+ | Toggle (standalone) | `Toggle` | — |
65
+ | Screen reader text | `HiddenText` | — |
66
+ | Skip link | `SkipToContent` | — |
67
+ | Dark / light regions | `ColorMode`, `Background` | [modes.md](../foundations/modes.md) |
68
+
69
+ ## Validate variant props
70
+
71
+ `Card`, `Badge`, `Tag`, and `Alert` accept specific `variant` values. Invalid strings (e.g. `"navy-on-white"`) crash `parseToHsl()` at runtime. Inspect prop types in `@codecademy/gamut` before using variant/color props. See [card.md](card.md) for `Card` variants.
72
+
73
+ ## Catalog by layer
74
+
75
+ ### Atoms
76
+
77
+ Badge, FillButton, StrokeButton, CTAButton, TextButton, IconButton, Card, Checkbox, CodeBlock, Drawer, FlexBox, FormGroup, GridBox, HiddenText, Icon, Input, Label, Loader, Radio, Select, Spinner, Tag, TextArea, Toggle, Tooltip
78
+
79
+ ### Molecules
10
80
 
11
81
  Alert, Anchor, Breadcrumbs, Coachmark, Disclosure, GridForm, Markdown, Menu, Modal, Pagination, Popover, ProgressBar, Table, Tabs, Toast, Toaster, Video
12
82
 
13
- ## Organisms — page-level compositions
83
+ ### Organisms
14
84
 
15
85
  ContentContainer, GridContainer, Layout, LayoutGrid
16
86
 
17
87
  ## Key patterns
18
88
 
19
89
  ### Buttons
20
- See [buttons.md](buttons.md) for full reference. Use `FillButton` for primary actions, `StrokeButton` for secondary.
90
+
91
+ [buttons.md](buttons.md) — `FillButton` primary, `StrokeButton` secondary.
92
+
93
+ ### Forms and accessibility
94
+
95
+ [forms.md](forms.md) · [`gamut-forms` skill](../../skills/gamut-forms/SKILL.md) · [`gamut-accessibility` skill](../../skills/gamut-accessibility/SKILL.md)
21
96
 
22
97
  ### Cards
23
- - **Background variants**: `default` (ColorMode-responsive), `white`, `yellow`, `beige`, `navy`, `hyper`
24
- - **Shadow variants**: `none` (default), `outline`, `patternLeft`, `patternRight`
98
+
99
+ See [card.md](card.md). Variants include `default`, `white`, `yellow`, `hyper`, `navy`. Confirm against `DESIGN.md` for theme-specific surfaces.
100
+ `FormGroup`, `GridForm`, `ConnectedForm`, tips, dialogs, composite widgets: [`skills/gamut-forms/SKILL.md`](../../skills/gamut-forms/SKILL.md) (forms) · [`skills/gamut-accessibility/SKILL.md`](../../skills/gamut-accessibility/SKILL.md) (overlays, composites, checklists) · Storybook [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page).
101
+
102
+ - Background variants: `default` (ColorMode-responsive), `white`, `yellow`, `beige`, `navy`, `hyper`
103
+ - Shadow variants: `none` (default), `outline`, `patternLeft`, `patternRight`
25
104
  - Add `isInteractive` when wrapping in `<Anchor>` — enables hover shadow + `borderRadius: md`
26
105
  - Default `borderRadius` is `none`; override with `borderRadius` prop
27
106
 
28
- ### Color-aware components
29
- - `<ColorMode mode="light|dark|system">` — scopes a subtree to an explicit color mode
30
- - `<Background bg="<color>">` — applies background color + auto-switches inner color mode for contrast
107
+ ### Color-aware regions
108
+
109
+ [foundations/modes.md](../foundations/modes.md) `<ColorMode mode="light|dark|system">`, `<Background bg="">`.
31
110
 
32
111
  ### Alerts
33
- | Variant | Tokens |
34
- |---|---|
35
- | Error | `feedback-error` + `background-error` |
112
+
113
+ | Variant | Tokens |
114
+ | ------- | ----------------------------------------- |
115
+ | Error | `feedback-error` + `background-error` |
36
116
  | Success | `feedback-success` + `background-success` |
37
117
  | Warning | `feedback-warning` + `background-warning` |
38
118
 
39
119
  ## Global tokens
40
120
 
41
- | Token | Value | Use |
42
- |---|---|---|
43
- | `headerHeight` | 64px (base), 80px (md+) | Global page header height |
44
- | `headerZ` | 15 | Z-index for global page header |
121
+ | Token | Value | Use |
122
+ | -------------- | ----------------------- | ------------------ |
123
+ | `headerHeight` | 64px (base), 80px (md+) | Global page header |
124
+ | `headerZ` | 15 | Header z-index |
@@ -0,0 +1,13 @@
1
+ # RadialProgress
2
+
3
+ Storybook: [Molecules / RadialProgress](https://gamut.codecademy.com/?path=/docs-molecules-radialprogress--docs)
4
+
5
+ ## Labels as `children` by default
6
+
7
+ `RadialProgress` renders children in a centered overlay inside the ring. Place the label (e.g. percentage text) as a child, not as a sibling below the component, unless the design explicitly places it outside the ring.
8
+
9
+ ```tsx
10
+ <RadialProgress percent={75}>
11
+ <Text>75%</Text>
12
+ </RadialProgress>
13
+ ```
@@ -0,0 +1,23 @@
1
+ # Select vs. SelectDropdown
2
+
3
+ `Select` is a native select suitable for most dropdown needs. Prefer `Select` over `SelectDropdown` for simple lists.
4
+
5
+ Storybook: [Atoms / Select](https://gamut.codecademy.com/?path=/docs-atoms-select--docs) · [Organisms / SelectDropdown](https://gamut.codecademy.com/?path=/docs-organisms-selectdropdown--docs)
6
+
7
+ ## When to use `SelectDropdown`
8
+
9
+ Only when you need:
10
+
11
+ - Searchable/filterable options (`isSearchable`)
12
+ - Multi-select (`multiple`)
13
+ - Option subtitles, right labels, icons, abbreviations
14
+ - Grouped options with dividers and group labels
15
+ - Custom option styling
16
+
17
+ ## When to use `Select`
18
+
19
+ Plain option lists with no enrichment — simpler, more performant, and more accessible.
20
+
21
+ ## Sizing
22
+
23
+ Apply exact sizing from the design (e.g. `sizeVariant="small"` on `Select`) — do not rely on defaults when the source specifies a value.
@@ -0,0 +1,22 @@
1
+ # ToolTip and InfoTip
2
+
3
+ Related: [`skills/gamut-accessibility/SKILL.md`](../../skills/gamut-accessibility/SKILL.md) (`aria-describedby`, `InfoTip` labeling)
4
+
5
+ Storybook: [Molecules / ToolTip](https://gamut.codecademy.com/?path=/docs-molecules-tips-tooltip--docs) · [InfoTip](https://gamut.codecademy.com/?path=/docs-molecules-tips-infotip--docs)
6
+
7
+ ## Use `placement: 'floating'` inside overflow containers
8
+
9
+ `ToolTip` defaults to `placement: 'inline'`, which is clipped by ancestors with `overflow: hidden` (e.g. `DataTable` rows).
10
+
11
+ When an `IconButton` or `ToolTip` is inside a table, card, or clipping container:
12
+
13
+ - `tipProps={{ placement: 'floating' }}` on `IconButton`
14
+ - `placement="floating"` on `ToolTip` directly
15
+
16
+ This renders the tooltip in a floating layer above surrounding content.
17
+
18
+ ## Use `alignment="bottom-*"` near the top of the viewport
19
+
20
+ Tips default to opening above the trigger. Near the page top (e.g. in a heading), the tip can clip.
21
+
22
+ Pass `alignment="bottom-right"` or `"bottom-left"` so the tip opens downward.
@@ -0,0 +1,29 @@
1
+ # Video
2
+
3
+ Never build custom media players. Gamut exports `Video` (built on `@vidstack/react`) with controls, poster support, text tracks, and accessibility.
4
+
5
+ Storybook: [Molecules / Video](https://gamut.codecademy.com/?path=/docs-molecules-video--docs)
6
+
7
+ ## Rule
8
+
9
+ Before constructing `<video>`, image + play-button overlays, or third-party embed wrappers, use `Video`.
10
+
11
+ ## Common props
12
+
13
+ | Prop | Purpose |
14
+ | ------------------ | ------------------- |
15
+ | `videoUrl` | Video source |
16
+ | `placeholderImage` | Poster thumbnail |
17
+ | `videoTitle` | Accessibility title |
18
+ | `controls` | Standard player UI |
19
+
20
+ ```tsx
21
+ import { Video } from '@codecademy/gamut';
22
+
23
+ <Video
24
+ videoUrl="https://example.com/video.mp4"
25
+ placeholderImage="https://example.com/poster.jpg"
26
+ videoTitle="Course introduction"
27
+ controls
28
+ />;
29
+ ```