@omni-design/components 0.7.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 (223) hide show
  1. package/.claude/commands/ScanComponents.md +26 -0
  2. package/.claude/commands/ShareComponent.md +128 -0
  3. package/.cursorrules +21 -0
  4. package/CLAUDE.md +1046 -0
  5. package/README.md +70 -0
  6. package/dist/OmniDesignProvider.esm.js +2 -0
  7. package/dist/OmniDesignProvider.esm.js.map +1 -0
  8. package/dist/OmniDesignProvider.js +2 -0
  9. package/dist/OmniDesignProvider.js.map +1 -0
  10. package/dist/index.esm.js +42 -0
  11. package/dist/index.esm.js.map +1 -0
  12. package/dist/index.js +42 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/types/OmniDesignProvider.d.ts +158 -0
  15. package/dist/types/components/Accordion/Accordion.d.ts +42 -0
  16. package/dist/types/components/Accordion/AccordionShowcase.d.ts +2 -0
  17. package/dist/types/components/Accordion/index.d.ts +2 -0
  18. package/dist/types/components/Alert/Alert.d.ts +14 -0
  19. package/dist/types/components/Alert/AlertShowcase.d.ts +2 -0
  20. package/dist/types/components/Alert/index.d.ts +2 -0
  21. package/dist/types/components/AppBar/AppBar.d.ts +43 -0
  22. package/dist/types/components/AppBar/AppBarShowcase.d.ts +2 -0
  23. package/dist/types/components/AppBar/index.d.ts +2 -0
  24. package/dist/types/components/Autocomplete/Autocomplete.d.ts +26 -0
  25. package/dist/types/components/Autocomplete/AutocompleteShowcase.d.ts +2 -0
  26. package/dist/types/components/Autocomplete/index.d.ts +2 -0
  27. package/dist/types/components/Avatar/Avatar.d.ts +29 -0
  28. package/dist/types/components/Avatar/AvatarShowcase.d.ts +2 -0
  29. package/dist/types/components/Avatar/defaultAvatar.d.ts +2 -0
  30. package/dist/types/components/Avatar/index.d.ts +3 -0
  31. package/dist/types/components/BackgroundPicker.d.ts +12 -0
  32. package/dist/types/components/Badge/Badge.d.ts +43 -0
  33. package/dist/types/components/Badge/BadgeShowcase.d.ts +1 -0
  34. package/dist/types/components/Badge/index.d.ts +2 -0
  35. package/dist/types/components/BevelText/BevelText.d.ts +15 -0
  36. package/dist/types/components/BevelText/BevelTextShowcase.d.ts +2 -0
  37. package/dist/types/components/BevelText/index.d.ts +2 -0
  38. package/dist/types/components/BottomNavigation/BottomNavigation.d.ts +15 -0
  39. package/dist/types/components/BottomNavigation/BottomNavigationShowcase.d.ts +2 -0
  40. package/dist/types/components/BottomNavigation/index.d.ts +2 -0
  41. package/dist/types/components/Box/Box.d.ts +36 -0
  42. package/dist/types/components/Box/BoxShowcase.d.ts +2 -0
  43. package/dist/types/components/Box/index.d.ts +2 -0
  44. package/dist/types/components/Breadcrumbs/Breadcrumbs.d.ts +22 -0
  45. package/dist/types/components/Breadcrumbs/BreadcrumbsShowcase.d.ts +2 -0
  46. package/dist/types/components/Breadcrumbs/index.d.ts +2 -0
  47. package/dist/types/components/Button/Button.d.ts +66 -0
  48. package/dist/types/components/Button/index.d.ts +2 -0
  49. package/dist/types/components/ButtonGroup/ButtonGroup.d.ts +41 -0
  50. package/dist/types/components/ButtonGroup/index.d.ts +2 -0
  51. package/dist/types/components/Card/Card.d.ts +54 -0
  52. package/dist/types/components/Card/CardShowcase.d.ts +2 -0
  53. package/dist/types/components/Card/index.d.ts +2 -0
  54. package/dist/types/components/Checkbox/Checkbox.d.ts +51 -0
  55. package/dist/types/components/Checkbox/index.d.ts +2 -0
  56. package/dist/types/components/Chip/Chip.d.ts +42 -0
  57. package/dist/types/components/Chip/ChipShowcase.d.ts +2 -0
  58. package/dist/types/components/Chip/index.d.ts +2 -0
  59. package/dist/types/components/CircularProgress/CircularProgress.d.ts +12 -0
  60. package/dist/types/components/CircularProgress/CircularProgressShowcase.d.ts +2 -0
  61. package/dist/types/components/CircularProgress/index.d.ts +2 -0
  62. package/dist/types/components/CodeBlock/CodeBlock.d.ts +35 -0
  63. package/dist/types/components/Colors/Colors.d.ts +7 -0
  64. package/dist/types/components/Colors/index.d.ts +1 -0
  65. package/dist/types/components/Container/Container.d.ts +70 -0
  66. package/dist/types/components/Container/index.d.ts +1 -0
  67. package/dist/types/components/Copyright/Copyright.d.ts +11 -0
  68. package/dist/types/components/Copyright/CopyrightShowcase.d.ts +2 -0
  69. package/dist/types/components/Copyright/index.d.ts +2 -0
  70. package/dist/types/components/CurvedText/CurvedText.d.ts +23 -0
  71. package/dist/types/components/CurvedText/CurvedTextShowcase.d.ts +2 -0
  72. package/dist/types/components/CurvedText/index.d.ts +2 -0
  73. package/dist/types/components/Dialog/Dialog.d.ts +82 -0
  74. package/dist/types/components/Dialog/DialogShowcase.d.ts +2 -0
  75. package/dist/types/components/Dialog/index.d.ts +2 -0
  76. package/dist/types/components/Divider/Divider.d.ts +22 -0
  77. package/dist/types/components/Divider/DividerShowcase.d.ts +2 -0
  78. package/dist/types/components/Divider/index.d.ts +2 -0
  79. package/dist/types/components/Drawer/Drawer.d.ts +30 -0
  80. package/dist/types/components/Drawer/DrawerShowcase.d.ts +2 -0
  81. package/dist/types/components/Drawer/index.d.ts +2 -0
  82. package/dist/types/components/DropZone/DropZone.d.ts +15 -0
  83. package/dist/types/components/DropZone/index.d.ts +2 -0
  84. package/dist/types/components/Fab/Fab.d.ts +17 -0
  85. package/dist/types/components/Fab/FabShowcase.d.ts +2 -0
  86. package/dist/types/components/Fab/index.d.ts +2 -0
  87. package/dist/types/components/Footer/Footer.d.ts +14 -0
  88. package/dist/types/components/Footer/FooterShowcase.d.ts +2 -0
  89. package/dist/types/components/Footer/index.d.ts +2 -0
  90. package/dist/types/components/Gradient/Gradient.d.ts +38 -0
  91. package/dist/types/components/Gradient/GradientShowcase.d.ts +2 -0
  92. package/dist/types/components/Gradient/gradientUtils.d.ts +185 -0
  93. package/dist/types/components/Gradient/index.d.ts +3 -0
  94. package/dist/types/components/Grid/Grid.d.ts +112 -0
  95. package/dist/types/components/Grid/index.d.ts +1 -0
  96. package/dist/types/components/Header/Header.d.ts +53 -0
  97. package/dist/types/components/Header/index.d.ts +1 -0
  98. package/dist/types/components/Icon/Icon.d.ts +19 -0
  99. package/dist/types/components/Icon/IconShowcase.d.ts +2 -0
  100. package/dist/types/components/Icon/index.d.ts +2 -0
  101. package/dist/types/components/IconBadge/IconBadge.d.ts +10 -0
  102. package/dist/types/components/IconBadge/IconBadgeShowcase.d.ts +2 -0
  103. package/dist/types/components/IconBadge/index.d.ts +2 -0
  104. package/dist/types/components/Input/Input.d.ts +47 -0
  105. package/dist/types/components/Input/index.d.ts +2 -0
  106. package/dist/types/components/LinearProgress/LinearProgress.d.ts +9 -0
  107. package/dist/types/components/LinearProgress/LinearProgressShowcase.d.ts +2 -0
  108. package/dist/types/components/LinearProgress/index.d.ts +2 -0
  109. package/dist/types/components/Link/Link.d.ts +16 -0
  110. package/dist/types/components/Link/LinkShowcase.d.ts +2 -0
  111. package/dist/types/components/Link/index.d.ts +2 -0
  112. package/dist/types/components/List/List.d.ts +55 -0
  113. package/dist/types/components/List/ListShowcase.d.ts +2 -0
  114. package/dist/types/components/List/index.d.ts +2 -0
  115. package/dist/types/components/MainLayout/MainLayout.d.ts +28 -0
  116. package/dist/types/components/MainLayout/index.d.ts +1 -0
  117. package/dist/types/components/Menu/Menu.d.ts +37 -0
  118. package/dist/types/components/Menu/MenuShowcase.d.ts +2 -0
  119. package/dist/types/components/Menu/index.d.ts +2 -0
  120. package/dist/types/components/Modal/Modal.d.ts +18 -0
  121. package/dist/types/components/Modal/ModalShowcase.d.ts +2 -0
  122. package/dist/types/components/Modal/index.d.ts +2 -0
  123. package/dist/types/components/NumberField/NumberField.d.ts +22 -0
  124. package/dist/types/components/NumberField/NumberFieldShowcase.d.ts +2 -0
  125. package/dist/types/components/NumberField/index.d.ts +2 -0
  126. package/dist/types/components/Pagination/Pagination.d.ts +17 -0
  127. package/dist/types/components/Pagination/PaginationShowcase.d.ts +2 -0
  128. package/dist/types/components/Pagination/index.d.ts +2 -0
  129. package/dist/types/components/Paper/Paper.d.ts +111 -0
  130. package/dist/types/components/Paper/index.d.ts +1 -0
  131. package/dist/types/components/Popover/Popover.d.ts +31 -0
  132. package/dist/types/components/Popover/index.d.ts +1 -0
  133. package/dist/types/components/PreviewSurface.d.ts +18 -0
  134. package/dist/types/components/Radio/Radio.d.ts +55 -0
  135. package/dist/types/components/Radio/RadioShowcase.d.ts +2 -0
  136. package/dist/types/components/Radio/index.d.ts +2 -0
  137. package/dist/types/components/Rail/Rail.d.ts +15 -0
  138. package/dist/types/components/Rail/RailShowcase.d.ts +2 -0
  139. package/dist/types/components/Rail/index.d.ts +2 -0
  140. package/dist/types/components/Rating/Rating.d.ts +16 -0
  141. package/dist/types/components/Rating/RatingShowcase.d.ts +2 -0
  142. package/dist/types/components/Rating/index.d.ts +2 -0
  143. package/dist/types/components/Ratio/Ratio.d.ts +20 -0
  144. package/dist/types/components/Ratio/RatioShowcase.d.ts +2 -0
  145. package/dist/types/components/Ratio/index.d.ts +2 -0
  146. package/dist/types/components/SearchField/SearchField.d.ts +19 -0
  147. package/dist/types/components/SearchField/SearchFieldShowcase.d.ts +2 -0
  148. package/dist/types/components/SearchField/index.d.ts +2 -0
  149. package/dist/types/components/Section/Section.d.ts +44 -0
  150. package/dist/types/components/Section/SectionShowcase.d.ts +2 -0
  151. package/dist/types/components/Section/index.d.ts +2 -0
  152. package/dist/types/components/Select/Select.d.ts +23 -0
  153. package/dist/types/components/Select/SelectShowcase.d.ts +2 -0
  154. package/dist/types/components/Select/index.d.ts +2 -0
  155. package/dist/types/components/SettingsPanel.d.ts +2 -0
  156. package/dist/types/components/Sidebar/Sidebar.d.ts +53 -0
  157. package/dist/types/components/Sidebar/index.d.ts +1 -0
  158. package/dist/types/components/Slider/Slider.d.ts +79 -0
  159. package/dist/types/components/Slider/SliderShowcase.d.ts +2 -0
  160. package/dist/types/components/Slider/index.d.ts +2 -0
  161. package/dist/types/components/Snackbar/Snackbar.d.ts +17 -0
  162. package/dist/types/components/Snackbar/SnackbarShowcase.d.ts +2 -0
  163. package/dist/types/components/Snackbar/index.d.ts +2 -0
  164. package/dist/types/components/Spacing/Spacing.d.ts +16 -0
  165. package/dist/types/components/Spacing/index.d.ts +1 -0
  166. package/dist/types/components/SpeedDial/SpeedDial.d.ts +17 -0
  167. package/dist/types/components/SpeedDial/SpeedDialShowcase.d.ts +2 -0
  168. package/dist/types/components/SpeedDial/index.d.ts +2 -0
  169. package/dist/types/components/Stack/Stack.d.ts +52 -0
  170. package/dist/types/components/Stack/StackShowcase.d.ts +2 -0
  171. package/dist/types/components/Stack/index.d.ts +2 -0
  172. package/dist/types/components/Stepper/Stepper.d.ts +33 -0
  173. package/dist/types/components/Stepper/StepperShowcase.d.ts +2 -0
  174. package/dist/types/components/Stepper/index.d.ts +2 -0
  175. package/dist/types/components/Switch/Switch.d.ts +65 -0
  176. package/dist/types/components/Switch/SwitchShowcase.d.ts +2 -0
  177. package/dist/types/components/Switch/index.d.ts +2 -0
  178. package/dist/types/components/Table/Table.d.ts +20 -0
  179. package/dist/types/components/Table/TableShowcase.d.ts +2 -0
  180. package/dist/types/components/Table/index.d.ts +2 -0
  181. package/dist/types/components/Tabs/Tabs.d.ts +51 -0
  182. package/dist/types/components/Tabs/TabsShowcase.d.ts +2 -0
  183. package/dist/types/components/Tabs/index.d.ts +2 -0
  184. package/dist/types/components/Tag/Tag.d.ts +57 -0
  185. package/dist/types/components/Tag/TagShowcase.d.ts +2 -0
  186. package/dist/types/components/Tag/index.d.ts +2 -0
  187. package/dist/types/components/TextField/TextField.d.ts +105 -0
  188. package/dist/types/components/TextField/index.d.ts +1 -0
  189. package/dist/types/components/ToggleButton/ToggleButton.d.ts +94 -0
  190. package/dist/types/components/ToggleButton/index.d.ts +1 -0
  191. package/dist/types/components/ToggleButtonGroup/ToggleButtonGroup.d.ts +48 -0
  192. package/dist/types/components/Toolbar/Toolbar.d.ts +14 -0
  193. package/dist/types/components/Toolbar/ToolbarShowcase.d.ts +2 -0
  194. package/dist/types/components/Toolbar/index.d.ts +2 -0
  195. package/dist/types/components/Tooltip/Tooltip.d.ts +20 -0
  196. package/dist/types/components/Tooltip/TooltipShowcase.d.ts +2 -0
  197. package/dist/types/components/Tooltip/index.d.ts +2 -0
  198. package/dist/types/components/TreeView/TreeBranch.d.ts +14 -0
  199. package/dist/types/components/TreeView/TreeView.d.ts +82 -0
  200. package/dist/types/components/TreeView/TreeViewShowcase.d.ts +2 -0
  201. package/dist/types/components/TreeView/index.d.ts +3 -0
  202. package/dist/types/components/Typography/Typography.d.ts +492 -0
  203. package/dist/types/components/Typography/TypographyShowcase.d.ts +2 -0
  204. package/dist/types/components/Typography/index.d.ts +2 -0
  205. package/dist/types/components/_a11y/ContrastCheck.d.ts +15 -0
  206. package/dist/types/components/_bevelPresets.d.ts +92 -0
  207. package/dist/types/components/_shadows.d.ts +32 -0
  208. package/dist/types/components/index.d.ts +74 -0
  209. package/dist/types/theme/useThemeMode.d.ts +4 -0
  210. package/docs/components.md +797 -0
  211. package/docs/getting-started.md +409 -0
  212. package/docs/token-system.md +468 -0
  213. package/package.json +126 -0
  214. package/public/styles/Dark-Mode.css +13867 -0
  215. package/public/styles/Light-Mode.css +14091 -0
  216. package/public/styles/README.md +93 -0
  217. package/public/styles/base.css +261 -0
  218. package/public/styles/core.css +378 -0
  219. package/public/styles/foundation.css +72 -0
  220. package/public/styles/styles.css +6 -0
  221. package/public/styles/theme.js +234 -0
  222. package/public/styles/theme.json +12 -0
  223. package/public/styles/typography-tokens.css +720 -0
package/CLAUDE.md ADDED
@@ -0,0 +1,1046 @@
1
+ # CLAUDE.md — OmniDesign Component Library
2
+
3
+ This file tells AI coding tools (Claude Code, Cursor, Copilot, etc.) exactly how to use this design system correctly. Read this before generating any UI code.
4
+
5
+ ---
6
+
7
+ ## What This Is
8
+
9
+ A React component library where **all visual styling comes from CSS tokens**, not hardcoded values. Colors, spacing, border radius, shadows, and typography are all resolved at runtime from CSS custom properties set by `data-theme`, `data-style`, and `data-surface` attributes.
10
+
11
+ **Never write inline hex colors. Never hardcode border-radius. Never use MUI's `sx` prop for colors. Always use token variables.**
12
+
13
+ ---
14
+
15
+ ## Project Structure
16
+
17
+ ```
18
+ my-project/
19
+ ├── public/styles/
20
+ │ ├── foundation.css ← primitives, spacing, reset (loads 1st)
21
+ │ ├── core.css ← component base styles (loads 2nd)
22
+ │ ├── Light-Mode.css ← light color tokens + [data-theme] selectors (loads 3rd)
23
+ │ ├── Dark-Mode.css ← dark color tokens + [data-theme] selectors (loads 3rd, swaps with light)
24
+ │ ├── base.css ← data-style, data-surface rules (loads 4th)
25
+ │ └── styles.css ← overrides (loads last)
26
+ └── src/
27
+ ├── OmniDesignProvider.js ← CSS injector + context + hook
28
+ ├── App.js
29
+ ├── index.js
30
+ └── components/ ← 49 components, all token-driven
31
+ ```
32
+
33
+ ---
34
+
35
+ ## The Three Attributes — Learn These First
36
+
37
+ Every component's visual output is driven by three HTML attributes on ancestor elements:
38
+
39
+ ### `data-theme` — sets the color context
40
+ ```jsx
41
+ <div data-theme="Primary"> // primary context
42
+ <div data-theme="Neutral" data-surface="Surface-Dimmest"> // black context
43
+ <div data-theme="App-Bar"> // navigation bar
44
+ <div data-theme="Success"> // semantic success
45
+ ```
46
+
47
+ **Valid themes — there are nine:**
48
+
49
+ `Default` · `Primary` · `Secondary` · `Tertiary` · `Neutral` · `Info` ·
50
+ `Success` · `Warning` · `Error`
51
+
52
+ Plus three a component sets for itself rather than something you choose:
53
+ `App-Bar`, `Nav-Bar`, `Status`.
54
+
55
+ **That is the whole list.** Lightness and darkness are NOT themes — they are
56
+ the surface axis. These do not exist and resolve to nothing:
57
+
58
+ | you might reach for | write this instead |
59
+ | --- | --- |
60
+ | `data-theme="White"` | `data-theme="Neutral" data-surface="Surface-Brightest"` |
61
+ | `data-theme="Black"` | `data-theme="Neutral" data-surface="Surface-Dimmest"` |
62
+ | `data-theme="Primary-Light"` | `data-theme="Primary" data-surface="Surface-Brightest"` |
63
+ | `data-theme="Primary-Dark"` | `data-theme="Primary" data-surface="Surface-Dimmest"` |
64
+ | any `*-Light` / `*-Dark` | the theme, plus a bright or dim surface |
65
+
66
+ A `data-theme` with no matching block paints nothing and reports nothing —
67
+ the element simply inherits whatever surrounded it. Nothing errors, so this
68
+ is worth getting right the first time.
69
+
70
+ ### `data-style` — sets the shape language
71
+ ```jsx
72
+ <div data-style="Professional"> // 4px border-radius
73
+ <div data-style="Modern"> // 8px border-radius
74
+ <div data-style="Bold"> // 16px border-radius
75
+ <div data-style="Playful"> // 40px border-radius
76
+ ```
77
+ Set once on the root. Cascades to every component automatically.
78
+
79
+ ### `data-surface` — sets background depth AND lightness
80
+
81
+ Five page surfaces, darkest to brightest:
82
+
83
+ ```jsx
84
+ <div data-surface="Surface-Dimmest"> // darkest — black under Neutral
85
+ <div data-surface="Surface-Dim"> // slightly recessed
86
+ <div data-surface="Surface"> // base page background
87
+ <div data-surface="Surface-Bright"> // elevated (AppBar)
88
+ <div data-surface="Surface-Brightest"> // brightest — white under Neutral
89
+ ```
90
+
91
+ Five container levels for things that sit ON a surface:
92
+
93
+ ```jsx
94
+ <div data-surface="Container-Lowest"> // inputs, deepest
95
+ <div data-surface="Container-Low"> // inset card
96
+ <div data-surface="Container"> // card background
97
+ <div data-surface="Container-High"> // modal, highest card
98
+ <div data-surface="Container-Highest">
99
+ ```
100
+
101
+ This axis is where light and dark live. `Neutral` + `Surface-Dimmest` is a
102
+ pure-black region with white text; `Neutral` + `Surface-Brightest` is the
103
+ white one.
104
+
105
+ `data-surface` is the **only** way to change a region's background. Setting
106
+ it exposes the full set of paired tokens — `--Background`, `--Text`,
107
+ `--Quiet`, `--Header`, `--Border`, `--Border-Variant`, `--Hover`, `--Active`,
108
+ `--Hotlink`, and the per-palette `--Buttons-*-Border/Highlight/Lowlight`
109
+ overrides — all tuned for that surface's tone. Every nested component reads
110
+ those automatically.
111
+
112
+ ```jsx
113
+ // ✅ Right — declare the surface, every nested token auto-resolves
114
+ <section data-theme="Primary" data-surface="Surface-Bright">
115
+ <H2>Title</H2> {/* gets --Header */}
116
+ <Body>Body copy</Body> {/* gets --Text */}
117
+ <Button variant="primary">Save</Button> {/* gets --Buttons-Primary-* */}
118
+ </section>
119
+ ```
120
+
121
+ ```jsx
122
+ // ❌ Wrong — paints the box but leaves text/border/quiet on the parent's
123
+ // tone, so contrast breaks the moment the surface flips dark or moves to a
124
+ // different Color-N. Never write these:
125
+ <div style={{ background: 'var(--Surface)' }}>
126
+ <div style={{ background: 'var(--Container)' }}>
127
+ <div style={{ background: 'var(--Surface-Dim)' }}>
128
+ <div style={{ background: 'var(--Primary-Color-11)' }}>
129
+ <div style={{ background: '#f0ebe0' }}>
130
+ ```
131
+
132
+ Components that need to paint a background read `var(--Background)` (which
133
+ `data-surface` resolves for them). User code does **not** write
134
+ `var(--Background)` either — it sets `data-surface` on the element and lets
135
+ the cascade do the rest.
136
+
137
+ ---
138
+
139
+ ## MUI Integration
140
+
141
+ `@omni-design/components` is built on MUI. The `OmniDesignProvider` handles all MUI `ThemeProvider` wiring internally — **you never configure MUI directly**. OmniDesign's CSS custom properties drive all visual values; MUI is purely structural.
142
+
143
+ **MUI peer dependencies must be installed in the consuming app:**
144
+ ```bash
145
+ npm install @mui/material @mui/icons-material @emotion/react @emotion/styled
146
+ ```
147
+
148
+ **Never use MUI's theming system to set colors — always use OmniDesign tokens:**
149
+ ```jsx
150
+ // ✅ Correct — token-driven
151
+ style={{ background: 'var(--Buttons-Primary-Button)', color: 'var(--Buttons-Primary-Text)' }}
152
+
153
+ // ❌ Wrong — bypasses the token system
154
+ sx={{ bgcolor: 'primary.main', color: 'white' }}
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Provider Setup
160
+
161
+ The `OmniDesignProvider` must wrap your entire app. It injects CSS, configures MUI ThemeProvider, and sets the root attributes:
162
+
163
+ ```jsx
164
+ // src/App.js
165
+ import { OmniDesignProvider } from './OmniDesignProvider';
166
+
167
+ function App({ foundationCSS, coreCSS, lightModeCSS, darkModeCSS, baseCSS, stylesCSS }) {
168
+ return (
169
+ <OmniDesignProvider
170
+ foundationCSS={foundationCSS}
171
+ coreCSS={coreCSS}
172
+ lightModeCSS={lightModeCSS}
173
+ darkModeCSS={darkModeCSS}
174
+ baseCSS={baseCSS}
175
+ stylesCSS={stylesCSS}
176
+ defaultTheme="Default"
177
+ defaultSurface="Surface"
178
+ defaultStyle="Modern"
179
+ >
180
+ {/* your app */}
181
+ </OmniDesignProvider>
182
+ );
183
+ }
184
+ ```
185
+
186
+ ### Accessing theme state in any child component:
187
+ ```jsx
188
+ import { useOmniDesign } from './OmniDesignProvider';
189
+
190
+ function MyComponent() {
191
+ const { theme, style, isDark, toggleDarkMode, setTheme, setStyle } = useOmniDesign();
192
+ // ...
193
+ }
194
+ ```
195
+
196
+ ### Themed sub-zones (AppBar, alerts, nav):
197
+ ```jsx
198
+ import { ThemedZone, Surfaced } from './OmniDesignProvider';
199
+
200
+ // AppBar gets its own dark themed zone
201
+ <ThemedZone theme="App-Bar" surface="Surface-Bright" as="header">
202
+ <AppBar />
203
+ </ThemedZone>
204
+
205
+ // Semantic alert zones
206
+ <ThemedZone theme="Success" surface="Surface-Bright">
207
+ <Alert />
208
+ </ThemedZone>
209
+
210
+ // Card at a specific depth — no theme change, just surface
211
+ <Surfaced surface="Container">
212
+ <Card />
213
+ </Surfaced>
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Token Reference — Use These, Not Hex Values
219
+
220
+ ### Background & Surface
221
+ ```css
222
+ var(--Background) /* what data-surface resolves to. Components read this.
223
+ User code: prefer setting data-surface on the
224
+ element instead of writing background here. */
225
+
226
+ /* Source vars — DO NOT write these in styles. They exist so the cascade can
227
+ resolve --Background. Setting data-surface="Surface" exposes --Surface as
228
+ --Background; data-surface="Container" exposes --Container; etc. */
229
+ var(--Surface)
230
+ var(--Surface-Dim)
231
+ var(--Surface-Bright)
232
+ var(--Container)
233
+ var(--Container-Low)
234
+ var(--Container-Lowest) /* inputs live here */
235
+ var(--Container-High) /* modals live here */
236
+ ```
237
+
238
+ ### Text
239
+ ```css
240
+ var(--Text) /* primary body text */
241
+ var(--Text-Quiet) /* secondary / muted text */
242
+ var(--Header) /* Display and H1-H3 only - see below */
243
+ var(--Hotlink) /* links */
244
+ ```
245
+
246
+ `--Header` is a DISPLAY role, not a heading role. `<DisplayLarge>`,
247
+ `<DisplaySmall>` and `<H1>`-`<H3>` default to it; `<H4>`-`<H6>` default to
248
+ `--Text`. At H4 and below the type is body-sized and usually sits inline with
249
+ body copy, so a second tone reads as an inconsistency rather than as another
250
+ level of hierarchy. Both defaults are applied by the component - don't pass a
251
+ `color` prop to restate either one.
252
+
253
+ ### Borders & States
254
+ ```css
255
+ var(--Border)
256
+ var(--Border-Variant) /* subtle borders */
257
+ var(--Hover) /* hover overlay */
258
+ var(--Active) /* pressed state overlay */
259
+ var(--Focus-Visible) /* keyboard focus ring */
260
+ ```
261
+
262
+ ### Buttons — 9 color families
263
+ ```css
264
+ /* Replace {Color} with: Primary | Secondary | Tertiary | Neutral |
265
+ Info | Success | Warning | Error | Default */
266
+ var(--Buttons-{Color}-Button) /* fill color */
267
+ var(--Buttons-{Color}-Text) /* label color */
268
+ var(--Buttons-{Color}-Border) /* border + thumb color */
269
+ var(--Buttons-{Color}-Hover) /* hover fill */
270
+ var(--Buttons-{Color}-Active) /* pressed fill */
271
+
272
+ /* Light variants */
273
+ var(--Buttons-{Color}-Light-Button)
274
+ var(--Buttons-{Color}-Light-Text)
275
+ var(--Buttons-{Color}-Light-Hover)
276
+ var(--Buttons-{Color}-Light-Active)
277
+ ```
278
+
279
+ ### Icons
280
+ ```css
281
+ /* Replace {Family} with: Default | Primary | Secondary | Tertiary |
282
+ Neutral | Info | Success | Warning | Error */
283
+ var(--Icons-{Family}) /* icon fill */
284
+ var(--Icons-{Family}-Variant) /* two-tone secondary fill */
285
+ ```
286
+
287
+ ### Style & Shape
288
+ ```css
289
+ var(--Style-Border-Radius) /* set by data-style — use everywhere */
290
+ var(--Shadow-1) /* subtle shadow */
291
+ var(--Shadow-2) /* elevated shadow */
292
+ var(--Style-Gradient-Color-1)
293
+ var(--Style-Gradient-Color-2)
294
+ var(--Style-Gradient-Angle)
295
+ ```
296
+
297
+ ### Typography — the four faces
298
+
299
+ A design system publishes four faces, not two. Each has a `--Set-Font-Family-*`
300
+ input and a `--Font-Family-*` token that components actually read:
301
+
302
+ ```css
303
+ /* What components read */
304
+ var(--Font-Family-Display) /* the expressive face — display-large/medium/small */
305
+ var(--Font-Family-Header) /* h1–h6, and the only face carrying variable axes */
306
+ var(--Font-Family-Eyebrow) /* eyebrow labels; defaults to the OS UI stack */
307
+ var(--Font-Family-Body) /* everything else */
308
+
309
+ /* Per-face weights */
310
+ var(--Font-Weight-Display)
311
+ var(--Font-Weight-Header)
312
+ var(--Font-Weight-Eyebrow)
313
+ var(--Font-Weight-Body)
314
+
315
+ /* The generator's inputs — you set these, you don't read them */
316
+ var(--Set-Font-Family-Header)
317
+ var(--Set-Font-Family-Header-Weight)
318
+ var(--Set-Font-Family-Display)
319
+ var(--Set-Font-Family-Eyebrow)
320
+ var(--Set-Font-Family-Body)
321
+ var(--Set-Font-Family-Body-Weight)
322
+ var(--Set-Font-Family-Body-Semibold-Weight)
323
+ var(--Set-Font-Family-Body-Bold-Weight)
324
+ var(--Set-Font-Family-Decorative) /* legacy alias of the Display face */
325
+ ```
326
+
327
+ Every step of every ramp also publishes its own `--{Style}-Font-Family` and
328
+ `--{Style}-Text-Transform`, so a single style can be re-pointed without moving
329
+ the whole face.
330
+
331
+ ### Body has two weights — Subtitle is the bold one
332
+
333
+ The Body ramp publishes exactly two weights at each size, and no bold:
334
+
335
+ ```css
336
+ --Body-{Small,Medium,Large}-Font-Weight /* standard — var(--Font-Weight-Body) */
337
+ --Body-{Small,Medium,Large}-Semibold-Font-Weight /* 600 */
338
+ ```
339
+
340
+ **For bold at body sizes, use Subtitle.** It is Body at 700 — same face, same
341
+ sizes, same line height, same letter-spacing — so a third weight on Body would
342
+ publish the same three styles under two names:
343
+
344
+ | You want | Use | Reads |
345
+ | --- | --- | --- |
346
+ | body, normal | `<Body>` | `--Body-Medium-Font-Weight` |
347
+ | body, semibold | `<Typography variant="body-semibold">` | `--Body-Medium-Semibold-Font-Weight` |
348
+ | body, **bold** | `<Subtitle>` | `--Subtitle-Medium-Font-Weight` (700) |
349
+ | bold, one step down | `<Typography variant="subtitle-small">` | `--Subtitle-Small-Font-Weight` |
350
+ | bold, one step up | `<SubtitleLarge>` | `--Subtitle-Large-Font-Weight` |
351
+
352
+ Three things that bite when you make that swap:
353
+
354
+ **Subtitle defaults to `--Header`, Body defaults to `--Text`.** Swapping `<Body>`
355
+ for `<Subtitle>` to gain weight also changes the colour. Pass
356
+ `color="standard"` to keep `--Text`.
357
+
358
+ **`variant="body-bold"` is an alias, not a bold.** The `-bold` body keys still
359
+ resolve so old markup doesn't break, but they land on the *semibold* style. If
360
+ you asked for `body-large-bold` and got 600, that is why — reach for Subtitle.
361
+
362
+ **`--Body-Bold-Font-Weight` (700) exists, but it is not a type style.** It is a
363
+ face-level weight for bolding body-face text outside the ramp (`Link` uses it).
364
+ Don't build a Body-Bold ramp out of it — the sizes and leading that make
365
+ Subtitle a real style aren't there.
366
+
367
+ `SubtitleLarge` and `Subtitle` (the medium step) are named exports;
368
+ `subtitle-small` is reachable only through `<Typography variant>`.
369
+
370
+ ### Variable-font axes — Header only
371
+
372
+ When the Header face is a variable font (e.g. Google Sans Flex), the system
373
+ publishes its axes as tokens and composes them into one value:
374
+
375
+ ```css
376
+ var(--Font-Width-Header) /* wdth */
377
+ var(--Font-Optical-Size-Header) /* opsz */
378
+ var(--Font-Slant-Header) /* slnt — negative leans right */
379
+ var(--Font-Grade-Header) /* GRAD */
380
+ var(--Font-Roundness-Header) /* ROND — 100 is fully rounded */
381
+
382
+ var(--Font-Variation-Header) /* the composed font-variation-settings value */
383
+ ```
384
+
385
+ `H1`–`H6` apply `--Font-Variation-Header` themselves. If you hand-roll a
386
+ heading, set `font-variation-settings: var(--Font-Variation-Header)` — reaching
387
+ for the individual axis tokens one at a time just re-derives it.
388
+
389
+ **`wght` is deliberately absent from the composed value.** `font-weight`
390
+ already carries it, and declaring the weight in both places lets the two
391
+ disagree. Set weight with `font-weight` / `--Font-Weight-Header`.
392
+
393
+ Display, Eyebrow, and Body have no axis tokens — only the Header face does.
394
+
395
+ ### Page setup — the system styles NO bare elements
396
+
397
+ Loading the CSS changes nothing on its own. There is no `body`, `html`, `p`,
398
+ `h1` or `*` rule in any of the six files — only custom properties and opt-in
399
+ `.typography-*` classes. That is deliberate: a design system that restyled
400
+ `body` just by being imported would change a page nobody asked it to change.
401
+
402
+ So set the page defaults yourself, once:
403
+
404
+ ```css
405
+ body {
406
+ font-family: var(--Font-Family-Body);
407
+ font-size: var(--Body-Medium-Font-Size);
408
+ font-weight: var(--Font-Weight-Body);
409
+ line-height: var(--Body-Medium-Line-Height);
410
+ }
411
+ ```
412
+
413
+ Colour is separate. `--Text` and `--Background` are only defined under
414
+ `[data-theme]`, so a bare `body` cannot resolve them — either theme the body
415
+ (`<body data-theme="Default" data-surface="Surface">`) or leave colour to the
416
+ themed regions inside it. Type tokens have no such requirement; they resolve at
417
+ `:root`.
418
+
419
+ The alternative to a `body` rule is `class="typography-body"` on the elements
420
+ that want it — same tokens, explicit rather than global.
421
+
422
+ **`data-platform` is OPTIONAL.** `core.css` defines every `--Font-Family-*` at
423
+ `:root`, so text resolves without it. `typography-tokens.css` additionally
424
+ publishes per-platform ramps under `[data-platform="Desktop"|"IOS-Mobile"|
425
+ "IOS-Tablet"|"Android"]`; set the attribute only when you want a platform's
426
+ ramp instead of the default. Text rendering with the wrong family is almost
427
+ never a missing `data-platform` — check that `core.css` is loaded first.
428
+
429
+ ### Eyebrow — the style formerly called Overline
430
+
431
+ The component is `<Eyebrow>` (`textStyle="eyebrow"`), in three steps:
432
+ `eyebrow-small` / `eyebrow` / `eyebrow-large`. `<Overline>` and
433
+ `textStyle="overline"` still resolve to it, so existing code keeps working, but
434
+ Eyebrow is the name to write.
435
+
436
+ The rename now reaches the size tokens too, so an eyebrow is spelled one way
437
+ in every property:
438
+
439
+ ```css
440
+ color: var(--Eyebrow); /* per theme AND per surface */
441
+ font-family: var(--Font-Family-Eyebrow);
442
+ font-weight: var(--Font-Weight-Eyebrow);
443
+ font-size: var(--Eyebrow-Medium-Font-Size); /* NOT --Eyebrow-Font-Size */
444
+ letter-spacing: var(--Eyebrow-Medium-Letter-Spacing);
445
+ ```
446
+
447
+ `--Eyebrow-Font-Size` and `--Eyebrow-Letter-Spacing` — with no step — do not
448
+ exist and never have. Sizes are per STEP. Reaching for the bare name fails
449
+ silently: the declaration falls back to whatever literal you wrote, so the text
450
+ renders at the wrong size with no error.
451
+
452
+ Steps are `--Eyebrow-{Small,Medium,Large}-*` at 12 / 13 / 15px, with tracking
453
+ loosening as the size drops. Use `<Eyebrow>` and the tokens resolve themselves;
454
+ the token names only matter when you hand-roll one.
455
+
456
+ `--Overline-{Small,Medium,Large}-*` is still emitted as an alias pointing at
457
+ the Eyebrow token, because a design system's CSS is frozen once published and
458
+ consumers upgrade the lib on their own schedule. Read the Eyebrow name; the
459
+ components already fall back to the Overline one for systems generated before
460
+ the rename.
461
+
462
+ `--Eyebrow` is not a muted `--Text`. It is a rotation off the surface's own
463
+ palette — Primary borrows Secondary, Secondary borrows Tertiary, Tertiary and
464
+ Neutral borrow Primary, and the state themes borrow black or white — so
465
+ substituting `--Quiet` throws the rotation away and paints every eyebrow the
466
+ same grey.
467
+
468
+ ### Cap-height trim — Figma's "cap height to baseline"
469
+
470
+ Figma's vertical-trim setting has a CSS equivalent, and the lib ships it as
471
+ `CAP_HEIGHT_TRIM`:
472
+
473
+ ```jsx
474
+ import { CAP_HEIGHT_TRIM } from './components';
475
+
476
+ <Box sx={{ paddingTop: '2px', ...CAP_HEIGHT_TRIM }}>LABEL</Box>
477
+ ```
478
+
479
+ It cuts the text box to the cap height on top and the alphabetic baseline
480
+ underneath, so a label centres on its letterforms instead of on the font's
481
+ line box — which carries ascender and descender space that a button label or
482
+ an avatar's initials never use. `Button` and `Avatar` apply it already.
483
+
484
+ It is progressive enhancement: the rules sit inside
485
+ `@supports (text-box-edge: cap alphabetic)`, so a browser without `text-box`
486
+ renders exactly as it did before. That is why any optical padding you use to
487
+ fake the same effect goes OUTSIDE the spread — the block zeroes the padding
488
+ itself wherever the real trim applies.
489
+
490
+ Don't hand-write `text-box-trim` / `text-box-edge` in a component; spread the
491
+ constant so every trimmed surface trims to the same two edges.
492
+
493
+ ### Avatar initials wear the Eyebrow style
494
+
495
+ `Avatar` renders its initials in the Eyebrow face, weight and tracking — not
496
+ Legal or Number. The eyebrow ramp is three steps and the avatar ramp is eight,
497
+ so the step picked per size sets weight and tracking only; the size comes from
498
+ the avatar's own map. The eyebrow's trailing letter-spacing is cancelled with a
499
+ negative `margin-inline-end`, without which the initials sit visibly left of
500
+ centre in the circle.
501
+
502
+ ### Typography `color` prop — not `style={{ color }}`
503
+
504
+ All typography components (`H1`–`H6`, `DisplayLarge`/`DisplayMedium`/
505
+ `DisplaySmall`, `Body`, `BodySmall`, `BodyLarge`, `Caption`, `Eyebrow`,
506
+ `Label`, `Subtitle`) accept a `color` prop that maps
507
+ to the right brand token. **Do not write `style={{ color: 'var(--Text-…)' }}`
508
+ on typography** — set the prop instead:
509
+
510
+ ```jsx
511
+ <Body color="quiet">…</Body> // var(--Text-Quiet)
512
+ <Body color="primary">…</Body> // var(--Text-Primary)
513
+ <Body color="secondary">…</Body> // var(--Text-Secondary)
514
+ <Body color="success">…</Body> // semantic tokens
515
+ ```
516
+
517
+ Available values: `standard` (default), `quiet`, `primary`, `secondary`,
518
+ `tertiary`, `neutral`, `info`, `success`, `warning`, `error`. Same list for
519
+ headings.
520
+
521
+ ---
522
+
523
+ ## Component Imports
524
+
525
+ All 49 components import from `./components` (or `@omni-design/components` if installed as a package):
526
+
527
+ ```jsx
528
+ import {
529
+ // Typography
530
+ Typography, H1, H2, H3, H4, H5, H6,
531
+ DisplayLarge, DisplayMedium, DisplaySmall,
532
+ Body, BodySmall, BodyLarge, BodySemibold, Label, Caption,
533
+ Subtitle, SubtitleSmall, SubtitleLarge,
534
+ Eyebrow, EyebrowSmall, EyebrowLarge, // Overline* still exports as an alias
535
+ CAP_HEIGHT_TRIM, // sx spread — cap-height/baseline trim
536
+
537
+ // Buttons
538
+ Button, ButtonGroup, ButtonIcon, Fab, Rail, Toolbar,
539
+ ToggleButton, ToggleButtonGroup, NumberField,
540
+
541
+ // Inputs
542
+ TextField, TextInput, Select, Autocomplete,
543
+ Checkbox, RadioGroup, SwitchInput, SliderInput,
544
+ RatingInput, SearchField,
545
+
546
+ // Chips
547
+ Chip,
548
+
549
+ // Layout
550
+ Stack, HStack, VStack, Box, Container, Grid,
551
+
552
+ // Navigation
553
+ Tabs, TabList, Tab, TabPanel,
554
+ Breadcrumbs, Pagination, Dropdown, Menu, MenuItem,
555
+ BottomNavigation, Stepper, SpeedDial,
556
+
557
+ // Surfaces
558
+ Card, Paper,
559
+
560
+ // Dialogs
561
+ Dialog, Modal, Drawer,
562
+
563
+ // Feedback
564
+ Alert, Snackbar, CircularProgress, LinearProgress,
565
+
566
+ // Data Display
567
+ Avatar, AvatarGroup, Badge, Divider, List, Table, Tooltip,
568
+
569
+ // App Structure
570
+ AppBar, Header, Footer, Sidebar, MainLayout, Accordion,
571
+
572
+ // Utilities
573
+ Link, Skeleton, Backdrop,
574
+ } from './components';
575
+ ```
576
+
577
+ ---
578
+
579
+ ## Component Prop Patterns
580
+
581
+ ### Button
582
+ ```jsx
583
+ // variant: 'primary' | 'secondary' | 'tertiary' | 'neutral' |
584
+ // 'info' | 'success' | 'warning' | 'error' |
585
+ // 'black-white' |
586
+ // + '-outline' + '-light' variants | 'ghost' | 'text'
587
+ // ('black-white' has solid + outline only — see below)
588
+ // size: 'small' | 'medium' | 'large'
589
+ <Button variant="primary" size="medium" startIcon={<AddIcon />}>
590
+ Save Changes
591
+ </Button>
592
+ <Button variant="primary-outline">Cancel</Button>
593
+ <Button variant="primary-light">Secondary Action</Button>
594
+
595
+ // black-white resolves itself against whatever it is placed on: black on a
596
+ // light surface, white on a dark one, with a label that always clears 4.5:1.
597
+ // Works on every theme and surface with no prop change.
598
+ <Button variant="black-white">Works anywhere</Button>
599
+ <Button variant="black-white-outline">Also anywhere</Button>
600
+ // No 'black-white-light': that variant reads --<Color>-Color-11, and
601
+ // black-white is a resolved pair rather than a palette, so it has no tones.
602
+ ```
603
+
604
+ ### ButtonGroup
605
+ ```jsx
606
+ // Purely presentational — visually groups Buttons (shared border, spacing).
607
+ // Does NOT manage selection. Each child Button owns its own onClick + variant.
608
+ // Props: variant, size, disabled, orientation, spacing, fullWidth
609
+ // (variant/size on the group are inherited as defaults by children)
610
+ //
611
+ // For a segmented selector, drive the selected state in the parent and
612
+ // flip each child's variant between solid and -outline:
613
+ <ButtonGroup variant="primary" size="small">
614
+ <Button
615
+ variant={mode === 'light' ? 'primary' : 'primary-outline'}
616
+ onClick={() => setMode('light')}
617
+ >Light</Button>
618
+ <Button
619
+ variant={mode === 'dark' ? 'primary' : 'primary-outline'}
620
+ onClick={() => setMode('dark')}
621
+ >Dark</Button>
622
+ </ButtonGroup>
623
+
624
+ // ❌ Do NOT pass `value` / `onChange` to ButtonGroup — it has no controlled
625
+ // selection API. Use ToggleButtonGroup if you want managed selection.
626
+ ```
627
+
628
+ ### Card
629
+ ```jsx
630
+ // Always sets data-surface="Container" internally
631
+ // variant: 'default' | 'solid' | 'light'
632
+ <Card variant="default" color="primary">
633
+ <CardContent>...</CardContent>
634
+ <CardActions>...</CardActions>
635
+ </Card>
636
+ ```
637
+
638
+ ### Alert
639
+ ```jsx
640
+ // variant: 'standard' | 'outline' | 'light' | 'solid'
641
+ // color: 8 brand colors
642
+ // Wrap in ThemedZone for semantic coloring:
643
+ <ThemedZone theme="Success" surface="Surface-Bright">
644
+ <Alert variant="light" color="success">
645
+ Operation completed successfully.
646
+ </Alert>
647
+ </ThemedZone>
648
+ ```
649
+
650
+ ### Switch
651
+ ```jsx
652
+ // variant: '{color}' | '{color}-outline' | '{color}-light'
653
+ // size: 'small' | 'medium' | 'large'
654
+ <SwitchInput variant="primary" label="Enable notifications" />
655
+ ```
656
+
657
+ ### TextField / Input
658
+ ```jsx
659
+ // data-surface="Container-Lowest" set internally
660
+ <TextField
661
+ label="Email"
662
+ variant="primary-outline"
663
+ size="medium"
664
+ validation="error"
665
+ validationMessage="Invalid email address"
666
+ />
667
+ ```
668
+
669
+ ### AppBar
670
+ ```jsx
671
+ // Always wrap in ThemedZone — AppBar has its own themed zone
672
+ <ThemedZone theme="App-Bar" surface="Surface-Bright" as="header">
673
+ <AppBar
674
+ mode="desktop"
675
+ barColor="default"
676
+ companyName="My App"
677
+ navLinks={['Home', 'About', 'Contact']}
678
+ />
679
+ </ThemedZone>
680
+ ```
681
+
682
+ ### Section — paint a region with the design system
683
+ ```jsx
684
+ // Section bundles data-theme + data-surface + background paint into one
685
+ // component. Use it for page sections, footers, hero, sticky-nav wrappers.
686
+ <Section theme="Primary" surface="Surface" padding="80px 24px">
687
+ <H2>…</H2>
688
+ </Section>
689
+
690
+ // Inherit surface from parent (no theme switch):
691
+ <Section padding="48px 24px">…</Section>
692
+
693
+ // Render as a different element:
694
+ <Section as="footer" theme="Neutral" surface="Surface-Dimmest">…</Section>
695
+ ```
696
+
697
+ Use `<ThemedZone>` for the case where you want the attributes but NOT the
698
+ background paint (e.g., wrapping an `AppBar` whose own root paints itself).
699
+
700
+ ## The 60/30/10 pass — do this on any page you render
701
+
702
+ A page built entirely from `data-theme="Primary"` is *correct* and looks
703
+ monotonous. The system gives you three brand palettes and most generated pages
704
+ use one. So after the markup is right, do a second pass for colour BALANCE:
705
+
706
+ | share | role | where it lives |
707
+ | --- | --- | --- |
708
+ | ~60% | Primary | the dominant sections, the default surface |
709
+ | ~30% | Secondary | one or two whole sections, switched by `data-theme` |
710
+ | ~10% | Tertiary | accents only — avatars, a left border, a single stat block |
711
+
712
+ The ratio is of VISUAL AREA, not of element count. One full-bleed Secondary
713
+ section outweighs twenty Tertiary avatars, which is the point: Tertiary is a
714
+ seasoning and should never be a section background.
715
+
716
+ ### How to scan a rendered page
717
+
718
+ Read it as blocks of area, top to bottom, and count what each one paints:
719
+
720
+ 1. List every region that sets `data-theme` (or `<Section theme=…>`) and note
721
+ its rough height. That is the 60/30 split, and it is the only part that
722
+ moves the ratio much.
723
+ 2. If everything is one theme, convert ONE mid-page section to Secondary.
724
+ Prefer a section that is already conceptually a break — testimonials, stats,
725
+ a pricing band — over splitting a continuous narrative.
726
+ 3. Then place Tertiary in the small stuff, using the two moves below.
727
+
728
+ Do not chase exact percentages. 60/30/10 is a target, and anything in the
729
+ region of "mostly one, a clear second, a glint of a third" reads correctly.
730
+
731
+ ### Tertiary move 1 — avatars
732
+
733
+ Avatars are the highest-value Tertiary slot on most pages: they repeat, they
734
+ are small, and they are already visually separate from the text around them.
735
+
736
+ ```jsx
737
+ <Avatar initials={initials(name)} alt={name} color="tertiary" />
738
+ ```
739
+
740
+ Pass `color`, don't restyle. The component owns its size, shape and contrast,
741
+ and the initials wear the Eyebrow style (see *Avatar initials wear the Eyebrow
742
+ style* above).
743
+
744
+ ### Tertiary move 2 — a left border on NON-CLICKABLE cards
745
+
746
+ A card that the user can click already earns its emphasis from hover: it lifts,
747
+ it shadows, its border brightens. Adding a colour bar to it competes with that
748
+ and reads as a second affordance.
749
+
750
+ A card that is purely informational has no such signal, so it is the right
751
+ place for a Tertiary edge:
752
+
753
+ ```css
754
+ .stat {
755
+ background: var(--Container);
756
+ border: 1px solid var(--Border-Variant);
757
+ /* Declared AFTER the shorthand so it only replaces the left edge. */
758
+ border-left: 4px solid var(--Tertiary-Color-8);
759
+ border-radius: var(--Card-Radius);
760
+ }
761
+ ```
762
+
763
+ Two things that are easy to get wrong:
764
+
765
+ **Order matters.** `border-left` must come after the `border` shorthand or the
766
+ shorthand overwrites it and the accent silently disappears.
767
+
768
+ **Clickable cards get nothing.** If the card has an `onClick`, an `<a>` wrapper,
769
+ or a hover transform, leave its border alone.
770
+
771
+ ### Which cards get the accent — the standalone ones
772
+
773
+ Do not put a left border on every card in a grid. Repeating it across a
774
+ uniform set turns a 10% accent into a 30% one and flattens the hierarchy the
775
+ border was meant to create.
776
+
777
+ Apply it to cards that are already different from their neighbours:
778
+
779
+ - a lone card that isn't part of a set
780
+ - a summary or total that sits apart from the rows above it
781
+ - the one card in a group that differs in kind (a callout among plain items)
782
+
783
+ If every card in a region would qualify, that region does not need the accent —
784
+ switch the whole region's `data-theme` instead and let the surface carry it.
785
+
786
+ ### What NOT to reach for
787
+
788
+ - **Do not** paint a section `data-theme="Tertiary"` to hit the 10%. That makes
789
+ it 30%+ and there is no palette left for accents.
790
+ - **Do not** use `color="tertiary"` on body text to add colour. Text carries a
791
+ 4.5:1 requirement and a recoloured paragraph reads as a link.
792
+ - **Do not** hand-write a hex to "balance" a page. Every share of the ratio is
793
+ an existing token; if the colour you want isn't one, the ratio is not the
794
+ problem.
795
+
796
+ ### Footer & Copyright
797
+ ```jsx
798
+ // Footer = configurable 1–4 column footer with optional social row +
799
+ // subscribe area + auto Copyright strip. First column is always the
800
+ // company address / contact.
801
+ <Footer
802
+ brand={<Logo />}
803
+ address={{
804
+ company: 'My Company',
805
+ lines: ['123 Main St', 'San Francisco, CA'],
806
+ email: 'hi@example.com',
807
+ phone: '+1 555 555 5555',
808
+ }}
809
+ columns={[ // up to 3 more columns
810
+ { title: 'Product', links: [{ label: 'Features', href: '/features' }] },
811
+ { title: 'Company', links: [{ label: 'About', href: '/about' }] },
812
+ ]}
813
+ socialLinks={[ // optional
814
+ { icon: <TwitterIcon />, url: 'https://twitter.com/x', label: 'Twitter' },
815
+ ]}
816
+ subscribe={{ // optional
817
+ title: 'Stay in the loop',
818
+ onSubscribe: (email) => api.subscribe(email),
819
+ }}
820
+ copyrightName="My Company"
821
+ />
822
+
823
+ // Copyright = standalone copyright strip (used internally by Footer too).
824
+ <Copyright companyName="My Company" year={2026} />
825
+ ```
826
+
827
+ Footer/Copyright are hardcoded to `--Primary-Color-2` body / `--Primary-
828
+ Color-1` copyright strip for now. Long term they'll auto-derive from the
829
+ user's chosen background tone.
830
+
831
+ ---
832
+
833
+ ## Flagging Missing Components
834
+
835
+ The library has gaps. If a piece of UI you need isn't already in the import
836
+ list above — don't invent it inline. Surface it so it can be added to the
837
+ library for every consumer.
838
+
839
+ Use this exact comment format above the spot where the missing piece would
840
+ live (the literal `MISSING-LIB-COMPONENT` tag is greppable, so reviews and the
841
+ `/ScanComponents` slash command can find it):
842
+
843
+ ```tsx
844
+ // MISSING-LIB-COMPONENT: <ComponentName>
845
+ // Needed for: <one line on the use case>
846
+ // Proposed API: <props sketch>
847
+ // Lib-track: open an issue / PR against @omni-design/components
848
+ ```
849
+
850
+ Then either:
851
+
852
+ - **Trivial, one-off**: inline a minimal implementation under the tag so the
853
+ feature isn't blocked.
854
+ - **Reusable** (used in 2+ places, has its own state, portals, or needs
855
+ keyboard a11y): stop and tell the user. Confirm whether to add it to the
856
+ lib first, or inline with a tracked follow-up.
857
+
858
+ After tagging and implementing (either path), **ask the user** if they want
859
+ to share the component back with OmniDesign for review:
860
+
861
+ > "I've tagged `<ComponentName>` as a missing lib component. Want me to
862
+ > submit a proposal to the OmniDesign admin dashboard for review? If they
863
+ > accept it, your inline copy can be replaced in a future package update."
864
+
865
+ If the user says yes, run `/ShareComponent <ComponentName>` (see below). If
866
+ no, leave the tag in place — the next contributor or a future
867
+ `/ScanComponents` pass will surface it again.
868
+
869
+ ### Scanning for outstanding gaps
870
+
871
+ Two Claude Code slash commands ship with this package:
872
+
873
+ - `/ScanComponents` — runs `grep -rn "MISSING-LIB-COMPONENT" src/` and groups
874
+ the results by file, so you can see what's been tagged across the codebase
875
+ at a glance.
876
+ - `/ShareComponent [Name]` — generates a submission package (proposed API +
877
+ inline implementation + source pointer) for a tagged component, asks the
878
+ user to confirm, then POSTs it to the OmniDesign admin dashboard for
879
+ review.
880
+
881
+ To install them in your project after `npm install @omni-design/components`:
882
+
883
+ ```bash
884
+ mkdir -p .claude/commands
885
+ cp node_modules/@omni-design/components/.claude/commands/*.md \
886
+ .claude/commands/
887
+ ```
888
+
889
+ Then restart Claude Code and type `/ScanComponents` or `/ShareComponent`.
890
+ (The same scan works from a plain shell without Claude Code:
891
+ `grep -rn "MISSING-LIB-COMPONENT" src/`.)
892
+
893
+ Empty `/ScanComponents` result → codebase is fully covered. Matches → those
894
+ are the components the library still needs to grow.
895
+
896
+ ---
897
+
898
+ ## Rules for AI-Generated Code
899
+
900
+ ### ✅ DO
901
+ ```jsx
902
+ // Always use OmniDesign components for layout and interaction — never raw HTML
903
+ <HStack spacing={2}> // instead of <div style={{ display: 'flex', gap: 16 }}>
904
+ <Button variant="outline" color="default"> // instead of <button style={{ border: '1px solid var(--Border)' }}>
905
+ <Body> // instead of <p> or <span>
906
+ <BodySmall> // instead of <span style={{ fontSize: 14 }}>
907
+ <Caption> // instead of <small> or <span style={{ fontSize: 12 }}>
908
+ <H2> // instead of <h2>
909
+
910
+ // Use token variables for all colors
911
+ style={{ background: 'var(--Buttons-Primary-Button)', color: 'var(--Buttons-Primary-Text)' }}
912
+
913
+ // Use data-theme for color context zones
914
+ <div data-theme="Success" data-surface="Surface-Bright">
915
+
916
+ // After the markup is right, run the 60/30/10 pass — a page built entirely
917
+ // from one palette is correct and monotonous. See "The 60/30/10 pass" above.
918
+ <Section theme="Secondary" surface="Surface"> // one section carries the 30
919
+ <Avatar initials="LN" color="tertiary" /> // accents carry the 10
920
+ style={{ borderLeft: '4px solid var(--Tertiary-Color-8)' }} // non-clickable cards only
921
+
922
+ // Use data-surface for background depth
923
+ <div data-surface="Container">
924
+
925
+ // Use var(--Style-Border-Radius) for all border radius
926
+ style={{ borderRadius: 'var(--Style-Border-Radius)' }}
927
+
928
+ // Use var(--Background) as the background in components
929
+ style={{ background: 'var(--Background)' }}
930
+
931
+ // Import components from the library
932
+ import { Button, Card, Alert, H2, Body, HStack } from './components';
933
+ ```
934
+
935
+ ### ❌ NEVER DO
936
+ ```jsx
937
+ // Never use raw HTML elements when a OmniDesign component exists
938
+ <button style={...}> // ← use <Button>
939
+ <div style={{ display: 'flex' }}> // ← use <HStack> or <VStack>
940
+ <span style={{ fontSize: 14 }}> // ← use <BodySmall>, <Label>, <Caption>
941
+ <p> // ← use <Body>
942
+ <h2> // ← use <H2>
943
+
944
+ // Never override a component's colors with style — change the variant instead
945
+ <Button style={{ background: '...', color: '...' }}> // ← use variant="primary"
946
+ <H2 style={{ color: '...' }}> // ← H2 sets --Header itself
947
+ <Body style={{ color: 'var(--Quiet)' }}> // ← use quiet variant or <Caption>
948
+ <Card style={{ background: 'var(--Container-High)' }}> // ← Card sets data-surface itself
949
+
950
+ // Never paint a background with a surface var directly — use data-surface
951
+ style={{ background: 'var(--Surface)' }} // ← <div data-surface="Surface">
952
+ style={{ background: 'var(--Container)' }} // ← <div data-surface="Container">
953
+ style={{ background: 'var(--Surface-Dim)' }} // ← <div data-surface="Surface-Dim">
954
+
955
+ // Never hardcode hex colors
956
+ style={{ background: '#006b5a', color: '#ffffff' }}
957
+
958
+ // Never hardcode border-radius
959
+ style={{ borderRadius: '8px' }}
960
+
961
+ // Never use MUI sx prop for colors
962
+ sx={{ bgcolor: 'primary.main', color: 'white' }}
963
+
964
+ // Never use Tailwind color classes
965
+ className="bg-teal-600 text-white"
966
+
967
+ // Never import MUI components directly without wrapping in OmniDesign tokens
968
+ import Button from '@mui/material/Button'; // ← bypasses the token system
969
+ ```
970
+
971
+ ---
972
+
973
+ ## Correct Page Structure
974
+
975
+ ```jsx
976
+ function MyPage() {
977
+ return (
978
+ // Root: set by OmniDesignProvider — don't add data-theme here again
979
+ <main data-surface="Surface-Dim" style={{ minHeight: '100vh' }}>
980
+
981
+ {/* AppBar always gets its own themed zone */}
982
+ <ThemedZone theme="App-Bar" surface="Surface-Bright" as="header">
983
+ <AppBar mode="desktop" companyName="My App" />
984
+ </ThemedZone>
985
+
986
+ <div style={{ padding: '32px', maxWidth: 1200, margin: '0 auto' }}>
987
+
988
+ {/* Cards use Container surface */}
989
+ <Surfaced surface="Container">
990
+ <Card>
991
+ <CardContent>
992
+ <H2>Page Title</H2>
993
+ <Body>Content goes here.</Body>
994
+ </CardContent>
995
+ <CardActions>
996
+ <Button variant="primary">Save</Button>
997
+ <Button variant="primary-outline">Cancel</Button>
998
+ </CardActions>
999
+ </Card>
1000
+ </Surfaced>
1001
+
1002
+ {/* Semantic alert zones */}
1003
+ <ThemedZone theme="Warning" surface="Surface-Bright">
1004
+ <Alert variant="light" color="warning">
1005
+ Please review before continuing.
1006
+ </Alert>
1007
+ </ThemedZone>
1008
+
1009
+ </div>
1010
+ </main>
1011
+ );
1012
+ }
1013
+ ```
1014
+
1015
+ ---
1016
+
1017
+ ## Dark Mode
1018
+
1019
+ ```jsx
1020
+ // Toggle dark mode from any component inside the Provider
1021
+ const { isDark, toggleDarkMode } = useOmniDesign();
1022
+
1023
+ <button onClick={toggleDarkMode}>
1024
+ {isDark ? 'Switch to Light' : 'Switch to Dark'}
1025
+ </button>
1026
+ ```
1027
+
1028
+ Dark mode swaps `Light-Mode.css` for `Dark-Mode.css` — all token values update automatically. No component code changes needed.
1029
+
1030
+ ---
1031
+
1032
+ ## CSS Load Order (do not change)
1033
+
1034
+ ```
1035
+ 1. foundation.css — primitives
1036
+ 2. core.css — component base styles
1037
+ 3. Light-Mode.css — OR Dark-Mode.css (never both)
1038
+ 4. base.css — data-style / data-surface rules
1039
+ 5. styles.css — final overrides
1040
+ ```
1041
+
1042
+ The `OmniDesignProvider` manages this automatically. Do not add `<link>` tags for these files in `index.html` — the Provider handles injection.
1043
+
1044
+ ---
1045
+
1046
+ *Repo: https://github.com/lwnoble/omni-design*