open-ds 0.0.0-stage → 0.1.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 (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -2
  3. package/assets/schemes/accessible-first.json +3093 -0
  4. package/assets/schemes/brand-expressive.json +3124 -0
  5. package/assets/schemes/content-first.json +3110 -0
  6. package/assets/schemes/dense-data.json +3489 -0
  7. package/assets/schemes/grammar.json +3903 -0
  8. package/assets/schemes/multi-brand.json +4359 -0
  9. package/assets/skills/open-ds/SKILL.md +50 -0
  10. package/assets/skills/open-ds-build/SKILL.md +19 -0
  11. package/assets/skills/open-ds-change/SKILL.md +19 -0
  12. package/assets/skills/open-ds-choose/SKILL.md +18 -0
  13. package/assets/skills/open-ds-review/SKILL.md +22 -0
  14. package/assets/skills/open-ds-write/SKILL.md +13 -0
  15. package/assets/stories/accordion.stories.tsx +46 -0
  16. package/assets/stories/alert-dialog.stories.tsx +65 -0
  17. package/assets/stories/alert.stories.tsx +44 -0
  18. package/assets/stories/aspect-ratio.stories.tsx +37 -0
  19. package/assets/stories/avatar.stories.tsx +42 -0
  20. package/assets/stories/badge.stories.tsx +39 -0
  21. package/assets/stories/breadcrumb.stories.tsx +54 -0
  22. package/assets/stories/button.stories.tsx +46 -0
  23. package/assets/stories/calendar.stories.tsx +30 -0
  24. package/assets/stories/card.stories.tsx +50 -0
  25. package/assets/stories/carousel.stories.tsx +64 -0
  26. package/assets/stories/chart.stories.tsx +61 -0
  27. package/assets/stories/checkbox.stories.tsx +29 -0
  28. package/assets/stories/collapsible.stories.tsx +39 -0
  29. package/assets/stories/combobox.stories.tsx +57 -0
  30. package/assets/stories/command.stories.tsx +60 -0
  31. package/assets/stories/context-menu.stories.tsx +55 -0
  32. package/assets/stories/data-table.stories.tsx +93 -0
  33. package/assets/stories/date-picker.stories.tsx +27 -0
  34. package/assets/stories/dialog.stories.tsx +70 -0
  35. package/assets/stories/drawer.stories.tsx +62 -0
  36. package/assets/stories/dropdown-menu.stories.tsx +65 -0
  37. package/assets/stories/empty.stories.tsx +39 -0
  38. package/assets/stories/field.stories.tsx +42 -0
  39. package/assets/stories/form.stories.tsx +56 -0
  40. package/assets/stories/input-group.stories.tsx +41 -0
  41. package/assets/stories/input-otp.stories.tsx +48 -0
  42. package/assets/stories/input.stories.tsx +32 -0
  43. package/assets/stories/kbd.stories.tsx +30 -0
  44. package/assets/stories/label.stories.tsx +26 -0
  45. package/assets/stories/native-select.stories.tsx +39 -0
  46. package/assets/stories/navigation-menu.stories.tsx +41 -0
  47. package/assets/stories/pagination.stories.tsx +61 -0
  48. package/assets/stories/popover.stories.tsx +37 -0
  49. package/assets/stories/progress.stories.tsx +30 -0
  50. package/assets/stories/radio-group.stories.tsx +45 -0
  51. package/assets/stories/resizable.stories.tsx +54 -0
  52. package/assets/stories/scroll-area.stories.tsx +47 -0
  53. package/assets/stories/select.stories.tsx +57 -0
  54. package/assets/stories/separator.stories.tsx +28 -0
  55. package/assets/stories/sheet.stories.tsx +46 -0
  56. package/assets/stories/sidebar.stories.tsx +62 -0
  57. package/assets/stories/skeleton.stories.tsx +27 -0
  58. package/assets/stories/slider.stories.tsx +44 -0
  59. package/assets/stories/spinner.stories.tsx +25 -0
  60. package/assets/stories/switch.stories.tsx +29 -0
  61. package/assets/stories/table.stories.tsx +97 -0
  62. package/assets/stories/tabs.stories.tsx +55 -0
  63. package/assets/stories/textarea.stories.tsx +28 -0
  64. package/assets/stories/toast.stories.tsx +30 -0
  65. package/assets/stories/toggle-group.stories.tsx +41 -0
  66. package/assets/stories/toggle.stories.tsx +20 -0
  67. package/assets/stories/tooltip.stories.tsx +49 -0
  68. package/assets/tokens/accessible-first/content.json +32 -0
  69. package/assets/tokens/accessible-first/dark.tokens.json +581 -0
  70. package/assets/tokens/accessible-first/light.tokens.json +581 -0
  71. package/assets/tokens/accessible-first/shadcn.css +21 -0
  72. package/assets/tokens/accessible-first/theme.css +101 -0
  73. package/assets/tokens/accessible-first/tokens.css +331 -0
  74. package/assets/tokens/accessible-first/tokens.json +1616 -0
  75. package/assets/tokens/accessible-first/tokens.scoped.css +333 -0
  76. package/assets/tokens/brand-expressive/content.json +32 -0
  77. package/assets/tokens/brand-expressive/dark.tokens.json +585 -0
  78. package/assets/tokens/brand-expressive/light.tokens.json +585 -0
  79. package/assets/tokens/brand-expressive/shadcn.css +21 -0
  80. package/assets/tokens/brand-expressive/theme.css +101 -0
  81. package/assets/tokens/brand-expressive/tokens.css +331 -0
  82. package/assets/tokens/brand-expressive/tokens.json +1651 -0
  83. package/assets/tokens/brand-expressive/tokens.scoped.css +333 -0
  84. package/assets/tokens/content-first/content.json +32 -0
  85. package/assets/tokens/content-first/dark.tokens.json +582 -0
  86. package/assets/tokens/content-first/light.tokens.json +582 -0
  87. package/assets/tokens/content-first/shadcn.css +21 -0
  88. package/assets/tokens/content-first/theme.css +101 -0
  89. package/assets/tokens/content-first/tokens.css +331 -0
  90. package/assets/tokens/content-first/tokens.json +1631 -0
  91. package/assets/tokens/content-first/tokens.scoped.css +333 -0
  92. package/assets/tokens/dense-data/content.json +32 -0
  93. package/assets/tokens/dense-data/dark.tokens.json +580 -0
  94. package/assets/tokens/dense-data/light.tokens.json +580 -0
  95. package/assets/tokens/dense-data/shadcn.css +21 -0
  96. package/assets/tokens/dense-data/theme.css +101 -0
  97. package/assets/tokens/dense-data/tokens.css +331 -0
  98. package/assets/tokens/dense-data/tokens.json +1623 -0
  99. package/assets/tokens/dense-data/tokens.scoped.css +333 -0
  100. package/assets/tokens/index.json +143 -0
  101. package/assets/tokens/multi-brand/aster-dark.tokens.json +625 -0
  102. package/assets/tokens/multi-brand/aster-light.tokens.json +625 -0
  103. package/assets/tokens/multi-brand/birch-dark.tokens.json +625 -0
  104. package/assets/tokens/multi-brand/birch-light.tokens.json +625 -0
  105. package/assets/tokens/multi-brand/content.json +32 -0
  106. package/assets/tokens/multi-brand/coral-dark.tokens.json +625 -0
  107. package/assets/tokens/multi-brand/coral-light.tokens.json +625 -0
  108. package/assets/tokens/multi-brand/shadcn.css +21 -0
  109. package/assets/tokens/multi-brand/theme.css +101 -0
  110. package/assets/tokens/multi-brand/tokens.css +1002 -0
  111. package/assets/tokens/multi-brand/tokens.json +1754 -0
  112. package/assets/tokens/multi-brand/tokens.scoped.css +1022 -0
  113. package/assets/tokens/shadcn-map.json +34 -0
  114. package/assets/ui/components/accordion.tsx +60 -0
  115. package/assets/ui/components/alert-dialog.tsx +171 -0
  116. package/assets/ui/components/alert.tsx +56 -0
  117. package/assets/ui/components/aspect-ratio.tsx +17 -0
  118. package/assets/ui/components/avatar.tsx +96 -0
  119. package/assets/ui/components/badge.tsx +36 -0
  120. package/assets/ui/components/breadcrumb.tsx +99 -0
  121. package/assets/ui/components/button.tsx +53 -0
  122. package/assets/ui/components/calendar.tsx +169 -0
  123. package/assets/ui/components/card.tsx +72 -0
  124. package/assets/ui/components/carousel.tsx +224 -0
  125. package/assets/ui/components/chart.tsx +315 -0
  126. package/assets/ui/components/checkbox.tsx +29 -0
  127. package/assets/ui/components/collapsible.tsx +25 -0
  128. package/assets/ui/components/combobox.tsx +275 -0
  129. package/assets/ui/components/command.tsx +145 -0
  130. package/assets/ui/components/context-menu.tsx +218 -0
  131. package/assets/ui/components/data-table.tsx +307 -0
  132. package/assets/ui/components/date-picker.tsx +79 -0
  133. package/assets/ui/components/dialog.tsx +68 -0
  134. package/assets/ui/components/drawer.tsx +115 -0
  135. package/assets/ui/components/dropdown-menu.tsx +58 -0
  136. package/assets/ui/components/empty.tsx +92 -0
  137. package/assets/ui/components/field.tsx +234 -0
  138. package/assets/ui/components/form.tsx +146 -0
  139. package/assets/ui/components/input-group.tsx +154 -0
  140. package/assets/ui/components/input-otp.tsx +75 -0
  141. package/assets/ui/components/input.tsx +25 -0
  142. package/assets/ui/components/kbd.tsx +30 -0
  143. package/assets/ui/components/label.tsx +22 -0
  144. package/assets/ui/components/native-select.tsx +59 -0
  145. package/assets/ui/components/navigation-menu.tsx +149 -0
  146. package/assets/ui/components/pagination.tsx +108 -0
  147. package/assets/ui/components/popover.tsx +61 -0
  148. package/assets/ui/components/progress.tsx +31 -0
  149. package/assets/ui/components/radio-group.tsx +40 -0
  150. package/assets/ui/components/resizable.tsx +54 -0
  151. package/assets/ui/components/scroll-area.tsx +55 -0
  152. package/assets/ui/components/select.tsx +81 -0
  153. package/assets/ui/components/separator.tsx +35 -0
  154. package/assets/ui/components/sheet.tsx +114 -0
  155. package/assets/ui/components/sidebar.tsx +685 -0
  156. package/assets/ui/components/skeleton.tsx +15 -0
  157. package/assets/ui/components/slider.tsx +70 -0
  158. package/assets/ui/components/spinner.tsx +16 -0
  159. package/assets/ui/components/switch.tsx +32 -0
  160. package/assets/ui/components/table.tsx +78 -0
  161. package/assets/ui/components/tabs.tsx +49 -0
  162. package/assets/ui/components/textarea.tsx +25 -0
  163. package/assets/ui/components/toast.tsx +54 -0
  164. package/assets/ui/components/toggle-group.tsx +88 -0
  165. package/assets/ui/components/toggle.tsx +48 -0
  166. package/assets/ui/components/tooltip.tsx +34 -0
  167. package/assets/ui/lib/use-mobile.ts +20 -0
  168. package/assets/ui/lib/utils.ts +7 -0
  169. package/assets/ui/styles.css +27 -0
  170. package/bin/open-ds.js +6 -0
  171. package/package.json +56 -4
  172. package/src/agents.mjs +150 -0
  173. package/src/cli.mjs +445 -0
  174. package/src/components.mjs +69 -0
  175. package/src/context.mjs +143 -0
  176. package/src/create.mjs +377 -0
  177. package/src/docs.mjs +570 -0
  178. package/src/guidance.mjs +63 -0
  179. package/src/index.mjs +20 -0
  180. package/src/mcp.mjs +660 -0
  181. package/src/readmes.mjs +106 -0
  182. package/src/schemes.mjs +188 -0
  183. package/src/tokens.mjs +127 -0
  184. package/templates/project/.github/workflows/ci.yml +15 -0
  185. package/templates/project/.storybook/component-docs.mdx +26 -0
  186. package/templates/project/.storybook/main.ts +17 -0
  187. package/templates/project/.storybook/manager-head.html +3 -0
  188. package/templates/project/.storybook/manager.ts +37 -0
  189. package/templates/project/.storybook/preview-head.html +3 -0
  190. package/templates/project/.storybook/preview.tsx +82 -0
  191. package/templates/project/docs/docusaurus.config.ts +64 -0
  192. package/templates/project/docs/sidebars.ts +8 -0
  193. package/templates/project/docs/src/css/custom.css +275 -0
  194. package/templates/project/gitignore +6 -0
  195. package/templates/project/src/styles/index.css +16 -0
  196. package/templates/project/src/vite-env.d.ts +7 -0
  197. package/templates/project/stories/docs-blocks.tsx +190 -0
  198. package/templates/project/stories/samples.tsx +102 -0
  199. package/templates/project/stories/usage.tsx +31 -0
  200. package/templates/project/tokens/build.mjs +468 -0
  201. package/templates/project/tsconfig.json +15 -0
@@ -0,0 +1,8 @@
1
+ import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
2
+
3
+ // The page order is in each page's front matter.
4
+ const sidebars: SidebarsConfig = {
5
+ docs: [{ type: "autogenerated", dirName: "." }],
6
+ };
7
+
8
+ export default sidebars;
@@ -0,0 +1,275 @@
1
+ /* The docs wear the design system. build/docusaurus.css (npm run tokens) holds its tokens and
2
+ points Docusaurus's own variables at them. The rules below dress the parts Docusaurus has no
3
+ variable for in the same tokens and component keys. */
4
+ @import url("https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:wght@400;700&family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,600;12..96,800&family=DM+Mono:wght@400;500&family=DM+Sans:wght@400;700&family=IBM+Plex+Mono&family=IBM+Plex+Sans:wght@400;500;600&family=Inter:wght@400;600;700&family=Plus+Jakarta+Sans:wght@400;600;700&family=Poppins:wght@400;600;700&family=Source+Serif+4:wght@400;600&display=swap");
5
+ @import "../../../build/docusaurus.css";
6
+
7
+ body {
8
+ font-size: var(--type-body-font-size);
9
+ -webkit-font-smoothing: antialiased;
10
+ }
11
+
12
+ /* Headings follow the type scale, the first one included. */
13
+ .markdown h1:first-child,
14
+ .markdown h1 {
15
+ --ifm-h1-font-size: var(--type-display-font-size);
16
+ line-height: var(--type-display-line-height);
17
+ font-weight: var(--type-display-font-weight);
18
+ }
19
+ .markdown h2 {
20
+ line-height: var(--type-title-line-height);
21
+ font-weight: var(--type-title-font-weight);
22
+ margin-top: var(--space-gap-section);
23
+ }
24
+ .markdown h3 {
25
+ line-height: var(--type-heading-line-height);
26
+ font-weight: var(--type-heading-font-weight);
27
+ }
28
+ .markdown > h2 {
29
+ --ifm-h2-font-size: var(--type-title-font-size);
30
+ --ifm-h2-vertical-rhythm-top: 1;
31
+ }
32
+ .markdown > h3 {
33
+ --ifm-h3-font-size: var(--type-heading-font-size);
34
+ --ifm-h3-vertical-rhythm-top: 1;
35
+ }
36
+
37
+ /* Navigation: label type, control density. */
38
+ .menu__link,
39
+ .table-of-contents__link,
40
+ .breadcrumbs__link,
41
+ .navbar__link {
42
+ font-size: var(--type-label-font-size);
43
+ line-height: var(--type-label-line-height);
44
+ font-weight: var(--type-label-font-weight);
45
+ }
46
+ .menu__link--active:not(.menu__link--sublist) {
47
+ border-radius: var(--radius-control);
48
+ }
49
+ .breadcrumbs__item--active .breadcrumbs__link {
50
+ background: var(--color-surface-selected);
51
+ border-radius: var(--radius-control);
52
+ }
53
+ .navbar {
54
+ border-bottom: var(--border-width-default) solid var(--color-border-default);
55
+ box-shadow: none;
56
+ }
57
+ .footer {
58
+ border-top: var(--border-width-default) solid var(--color-border-default);
59
+ }
60
+
61
+ /* Tables read the table component keys. */
62
+ .markdown table {
63
+ display: table;
64
+ width: 100%;
65
+ border-collapse: collapse;
66
+ font-size: var(--table-font-size);
67
+ }
68
+ .markdown table thead tr {
69
+ background: var(--table-header-background);
70
+ }
71
+ .markdown table th,
72
+ .markdown table td {
73
+ border: 0;
74
+ border-bottom: var(--border-width-default) solid var(--table-row-border);
75
+ padding: 0 var(--table-cell-padding-x);
76
+ height: var(--table-row-height);
77
+ text-align: left;
78
+ }
79
+ .markdown table th {
80
+ font-size: var(--type-label-font-size);
81
+ font-weight: var(--type-label-font-weight);
82
+ color: var(--color-text-secondary);
83
+ }
84
+ .markdown table tr:nth-child(2n) {
85
+ background: transparent;
86
+ }
87
+
88
+ /* Code uses the data type. */
89
+ code {
90
+ font-family: var(--type-data-font-family);
91
+ border: var(--border-width-default) solid var(--color-border-default);
92
+ padding: 0 var(--space-gap-tight);
93
+ }
94
+
95
+ /* Cards, buttons, badges, tabs and callouts read their component keys. */
96
+ .card,
97
+ .pagination-nav__link {
98
+ background: var(--card-background);
99
+ border: var(--border-width-default) solid var(--card-border);
100
+ border-radius: var(--card-radius);
101
+ box-shadow: var(--card-shadow);
102
+ }
103
+ .button {
104
+ height: var(--button-height);
105
+ font-size: var(--button-font-size);
106
+ font-weight: var(--button-font-weight);
107
+ line-height: var(--button-height);
108
+ padding-top: 0;
109
+ padding-bottom: 0;
110
+ }
111
+ .button--primary {
112
+ background: var(--button-primary-background);
113
+ border-color: var(--button-primary-background);
114
+ color: var(--button-primary-label);
115
+ }
116
+ .button--primary:hover {
117
+ background: var(--button-primary-background-hover);
118
+ }
119
+ .button--secondary {
120
+ background: var(--button-secondary-background);
121
+ border-color: var(--button-secondary-border);
122
+ color: var(--button-secondary-label);
123
+ }
124
+ .badge {
125
+ font-size: var(--badge-font-size);
126
+ padding: 0 var(--badge-padding-x);
127
+ background: var(--badge-neutral-background);
128
+ color: var(--badge-neutral-label);
129
+ border: 0;
130
+ }
131
+ .tabs__item {
132
+ color: var(--tabs-label);
133
+ font-size: var(--type-label-font-size);
134
+ }
135
+ .tabs__item--active {
136
+ color: var(--tabs-label-active);
137
+ border-bottom-color: var(--tabs-indicator);
138
+ }
139
+ .alert,
140
+ .theme-admonition {
141
+ border-radius: var(--radius-container);
142
+ border-width: var(--border-width-default);
143
+ box-shadow: none;
144
+ }
145
+
146
+ /* Specimens: small live samples on the foundation pages, drawn from the token variables. */
147
+ .ods-specimen {
148
+ display: flex;
149
+ flex-wrap: wrap;
150
+ gap: var(--space-gap-default, 0.5rem) var(--space-gap-group, 1rem);
151
+ padding: var(--space-inset-container, 1rem);
152
+ margin-bottom: var(--space-gap-section, 1.5rem);
153
+ border: var(--border-width-default, 1px) solid var(--color-border-default);
154
+ border-radius: var(--radius-container, 0.375rem);
155
+ background: var(--color-surface-default);
156
+ }
157
+ .ods-item {
158
+ display: inline-flex;
159
+ align-items: center;
160
+ gap: var(--space-gap-default, 0.5rem);
161
+ min-width: 13rem;
162
+ }
163
+ .ods-type {
164
+ display: flex;
165
+ align-items: baseline;
166
+ gap: var(--space-gap-group, 1rem);
167
+ width: 100%;
168
+ }
169
+ .ods-type .ods-label {
170
+ flex: 0 0 5rem;
171
+ }
172
+ .ods-label {
173
+ font-family: var(--type-data-font-family, monospace);
174
+ font-size: var(--type-caption-font-size, 0.75rem);
175
+ color: var(--color-text-muted);
176
+ }
177
+ .ods-swatch,
178
+ .ods-tile,
179
+ .ods-card {
180
+ display: inline-block;
181
+ width: 1.5rem;
182
+ height: 1.5rem;
183
+ flex: none;
184
+ border-radius: var(--radius-control, 0.25rem);
185
+ border: var(--border-width-default, 1px) solid var(--color-border-default);
186
+ }
187
+ .ods-swatch {
188
+ background: var(--color-text-primary);
189
+ }
190
+ .ods-tile {
191
+ background: var(--color-surface-sunken);
192
+ border-color: var(--color-border-strong);
193
+ }
194
+ .ods-card {
195
+ width: 2.5rem;
196
+ background: var(--color-surface-raised);
197
+ }
198
+ .ods-bar {
199
+ display: inline-block;
200
+ height: 0.5rem;
201
+ min-width: 1px;
202
+ background: var(--color-action-primary);
203
+ border-radius: 1px;
204
+ }
205
+ .ods-line {
206
+ display: inline-block;
207
+ width: 1.5rem;
208
+ background: var(--color-text-primary);
209
+ }
210
+ .ods-font {
211
+ font-size: 1.125rem;
212
+ }
213
+ .ods-motion {
214
+ display: inline-block;
215
+ width: 3rem;
216
+ height: 0.75rem;
217
+ border-radius: 9999px;
218
+ background: var(--color-surface-sunken);
219
+ position: relative;
220
+ }
221
+ .ods-motion > span {
222
+ position: absolute;
223
+ left: 0;
224
+ top: 0;
225
+ width: 0.75rem;
226
+ height: 0.75rem;
227
+ border-radius: 9999px;
228
+ background: var(--color-action-primary);
229
+ transition: left var(--ods-d) var(--motion-easing-standard, ease);
230
+ }
231
+ .ods-specimen:hover .ods-motion > span {
232
+ left: 2.25rem;
233
+ }
234
+ @media (prefers-reduced-motion: reduce) {
235
+ .ods-motion > span {
236
+ transition: none;
237
+ }
238
+ }
239
+ .markdown table .ods-swatch,
240
+ .markdown table .ods-tile,
241
+ .markdown table .ods-card {
242
+ vertical-align: middle;
243
+ }
244
+
245
+ /* The brand: the Open DS mark in this system's colours, its name in the title type, and a tag
246
+ that says these are its docs. */
247
+ .navbar__brand {
248
+ gap: var(--space-gap-default);
249
+ }
250
+ .navbar__logo {
251
+ height: 1.25rem;
252
+ margin-right: 0;
253
+ }
254
+ .navbar__logo img {
255
+ height: 100%;
256
+ }
257
+ .navbar__title {
258
+ font-family: var(--type-title-font-family);
259
+ font-weight: var(--type-title-font-weight);
260
+ font-size: var(--type-heading-font-size);
261
+ }
262
+ .ods-docs-tag {
263
+ display: inline-flex;
264
+ align-items: center;
265
+ height: 1.375rem;
266
+ padding: 0 var(--badge-padding-x);
267
+ border-radius: var(--badge-radius);
268
+ background: var(--color-surface-selected);
269
+ color: var(--color-text-link);
270
+ font-family: var(--type-label-font-family);
271
+ font-size: var(--type-caption-font-size);
272
+ font-weight: var(--type-label-font-weight);
273
+ letter-spacing: 0.02em;
274
+ text-transform: uppercase;
275
+ }
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ build/
3
+ storybook-static/
4
+ docs/build/
5
+ docs/.docusaurus/
6
+ .DS_Store
@@ -0,0 +1,16 @@
1
+ /* The design system's styles: Tailwind, the tokens built by Style Dictionary (npm run tokens),
2
+ the shadcn and Tailwind theme, and the base styles. Import this once in an app. */
3
+ @import "tailwindcss";
4
+ @import "../../build/tokens.css";
5
+ @import "../../build/theme.css";
6
+ @import "../../build/shadcn.css";
7
+ @import "./base.css";
8
+
9
+ @source "../";
10
+ @source "../../stories";
11
+
12
+ /* Portalled overlays sit on body, so body carries the scheme font too. */
13
+ body {
14
+ font-family: var(--type-body-font-family);
15
+ color: var(--color-text-primary);
16
+ }
@@ -0,0 +1,7 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ // The docs page template is MDX, loaded by Storybook.
4
+ declare module "*.mdx" {
5
+ const Page: () => JSX.Element;
6
+ export default Page;
7
+ }
@@ -0,0 +1,190 @@
1
+ // The blocks of every component's docs page. Each reads the component id and situations from the
2
+ // stories' parameters (openDs), and this design system's scheme.json.
3
+ import { useOf } from "@storybook/addon-docs/blocks";
4
+ import scheme from "../scheme.json";
5
+ import grammar from "../.open-ds/grammar.json";
6
+ import type { Samples } from "./samples";
7
+
8
+ interface SchemeData {
9
+ name: string;
10
+ inventory: {
11
+ in: { id: string; why: string }[];
12
+ out: { id: string; why: string }[];
13
+ };
14
+ samples: Samples;
15
+ usage: { situation: string; use: string; not: string; why: string }[];
16
+ }
17
+
18
+ type Meta = { component: string; situations: string[] };
19
+ type Tree = Record<string, unknown>;
20
+
21
+ // The component keys each built component reads.
22
+ const KEY_GROUP: Record<string, string> = {
23
+ button: "button",
24
+ input: "input",
25
+ select: "input",
26
+ label: "input",
27
+ checkbox: "control",
28
+ switch: "control",
29
+ badge: "badge",
30
+ card: "card",
31
+ table: "table",
32
+ tabs: "tabs",
33
+ dialog: "overlay",
34
+ "dropdown-menu": "overlay",
35
+ toast: "overlay",
36
+ tooltip: "overlay",
37
+ };
38
+
39
+ /** This design system's scheme. */
40
+ function useActiveScheme(): SchemeData {
41
+ return scheme as unknown as SchemeData;
42
+ }
43
+
44
+ function useOpenDs(): Meta {
45
+ const resolved = useOf("meta");
46
+ const meta = resolved.type === "meta" ? resolved.preparedMeta : undefined;
47
+ return (meta?.parameters?.openDs ?? {
48
+ component: "",
49
+ situations: [],
50
+ }) as Meta;
51
+ }
52
+
53
+ const get = (tree: unknown, path: string) =>
54
+ path
55
+ .split(".")
56
+ .reduce<unknown>(
57
+ (n, p) => (n && typeof n === "object" ? (n as Tree)[p] : undefined),
58
+ tree,
59
+ );
60
+
61
+ /** The aliases a key points through, light mode, default brand. */
62
+ function chain(s: SchemeData, path: string) {
63
+ const t = (s as unknown as { tokens: Tree }).tokens as {
64
+ primitive: Tree;
65
+ brands: Record<string, { theme: { base?: Tree; light: Tree } }>;
66
+ semantic: Tree;
67
+ component: Tree;
68
+ };
69
+ // Component key → semantic → the first brand's light theme pick → primitive.
70
+ const theme = Object.values(t.brands)[0].theme;
71
+ const trees = [
72
+ t.component,
73
+ t.semantic,
74
+ { theme: { ...theme.base, ...theme.light } },
75
+ t.primitive,
76
+ ];
77
+ const steps: string[] = [];
78
+ let at = path;
79
+ for (let i = 0; i < 8; i++) {
80
+ const node = trees
81
+ .map((x) => get(x, at))
82
+ .find((n) => n && typeof n === "object" && "$value" in (n as Tree)) as
83
+ | { $value: unknown }
84
+ | undefined;
85
+ const m =
86
+ typeof node?.$value === "string" && node.$value.match(/^\{(.+)\}$/);
87
+ if (!m) break;
88
+ steps.push(m[1]);
89
+ at = m[1];
90
+ }
91
+ return steps;
92
+ }
93
+
94
+ export function ComponentInfo() {
95
+ const { component } = useOpenDs();
96
+ const s = useActiveScheme();
97
+ const item = grammar.catalogue.items.find((i) => i.id === component);
98
+ const inv = s.inventory.in.find((i) => i.id === component);
99
+ const out = s.inventory.out.find((i) => i.id === component);
100
+ return (
101
+ <div className="sb-unstyled ods-info">
102
+ <p>{item?.description}</p>
103
+ <p>
104
+ <strong>
105
+ {inv ? "In" : "Not in"} the {s.name} inventory.
106
+ </strong>{" "}
107
+ {inv?.why ?? out?.why ?? "Its answers did not pick it."}{" "}
108
+ {s.inventory.in.length} of {grammar.catalogue.items.length} catalogue
109
+ components are in.
110
+ </p>
111
+ </div>
112
+ );
113
+ }
114
+
115
+ export function UsageRules() {
116
+ const { situations } = useOpenDs();
117
+ const s = useActiveScheme();
118
+ const titles = Object.fromEntries(
119
+ grammar.situations.map((x) => [x.id, x.title]),
120
+ );
121
+ const rules = s.usage.filter((u) => situations.includes(u.situation));
122
+ if (!rules.length) return null;
123
+ return (
124
+ <table className="ods-table">
125
+ <thead>
126
+ <tr>
127
+ <th>Situation</th>
128
+ <th>Use</th>
129
+ <th>Not</th>
130
+ <th>Why</th>
131
+ </tr>
132
+ </thead>
133
+ <tbody>
134
+ {rules.map((r) => (
135
+ <tr key={r.situation}>
136
+ <td>{titles[r.situation]}</td>
137
+ <td>{r.use}</td>
138
+ <td>{r.not}</td>
139
+ <td>{r.why}</td>
140
+ </tr>
141
+ ))}
142
+ </tbody>
143
+ </table>
144
+ );
145
+ }
146
+
147
+ export function ComponentKeys() {
148
+ const { component } = useOpenDs();
149
+ const s = useActiveScheme();
150
+ const keys = grammar.component_keys.filter(
151
+ (k) => k.component === KEY_GROUP[component],
152
+ );
153
+ if (!keys.length)
154
+ return (
155
+ <p>
156
+ From shadcn/ui. It reads shadcn&apos;s variables, which theme.css maps
157
+ onto the design system&apos;s semantic tokens, and the radius, shadow
158
+ and text scale in shadcn.css.
159
+ </p>
160
+ );
161
+ return (
162
+ <table className="ods-table">
163
+ <thead>
164
+ <tr>
165
+ <th>Key</th>
166
+ <th>What it controls</th>
167
+ <th>Points to</th>
168
+ </tr>
169
+ </thead>
170
+ <tbody>
171
+ {keys.map((k) => (
172
+ <tr key={k.id}>
173
+ <td>
174
+ <code>{k.id}</code>
175
+ </td>
176
+ <td>{k.description}</td>
177
+ <td>
178
+ {chain(s, k.id.replaceAll("/", ".")).map((x, i) => (
179
+ <span key={x}>
180
+ {i ? " → " : ""}
181
+ <code>{x}</code>
182
+ </span>
183
+ ))}
184
+ </td>
185
+ </tr>
186
+ ))}
187
+ </tbody>
188
+ </table>
189
+ );
190
+ }
@@ -0,0 +1,102 @@
1
+ // This design system's sample content and inventory (scheme.json). Every story shows its own words,
2
+ // data and status meanings, and says so when the system does not carry the component.
3
+ import type { ReactNode } from "react";
4
+ import scheme from "../scheme.json";
5
+
6
+ type Status = {
7
+ label: string;
8
+ meaning: "neutral" | "info" | "success" | "warning" | "danger";
9
+ why: string;
10
+ };
11
+ export interface Samples {
12
+ screen: string;
13
+ actions: { primary: string; secondary: string };
14
+ statuses: Status[];
15
+ table?: {
16
+ caption: string;
17
+ columns: { label: string; numeric?: boolean }[];
18
+ rows: { cells: string[]; status: string }[];
19
+ };
20
+ metrics: { label: string; value: string }[];
21
+ field: {
22
+ label: string;
23
+ placeholder: string;
24
+ hint: string;
25
+ error: string;
26
+ invalid: string;
27
+ };
28
+ checkbox?: string[];
29
+ select?: { label: string; options: string[]; value: string };
30
+ switches?: string[];
31
+ messages?: {
32
+ success: string;
33
+ error: { title: string; description: string };
34
+ neutral: string;
35
+ };
36
+ confirm?: {
37
+ trigger: string;
38
+ title: string;
39
+ body: string;
40
+ confirm: string;
41
+ cancel: string;
42
+ };
43
+ card?: {
44
+ title: string;
45
+ description: string;
46
+ status: string;
47
+ primary: string;
48
+ secondary: string;
49
+ };
50
+ tabs?: { label: string; content: string }[];
51
+ menu?: { trigger: string; items: string[]; destructive: string };
52
+ icon_action?: string;
53
+ }
54
+ const data = scheme as unknown as {
55
+ name: string;
56
+ inventory: { in: { id: string }[]; out: { id: string; why: string }[] };
57
+ samples: Samples;
58
+ };
59
+
60
+ /** The tone a status label maps to, from its meaning in the scheme. */
61
+ export const toneOf = (s: Samples, label: string) =>
62
+ s.statuses.find((x) => x.label === label)?.meaning ?? "neutral";
63
+
64
+ // The samples part each component's examples read.
65
+ const PART: Record<string, keyof Samples> = {
66
+ badge: "statuses",
67
+ card: "card",
68
+ checkbox: "checkbox",
69
+ dialog: "confirm",
70
+ "dropdown-menu": "menu",
71
+ select: "select",
72
+ switch: "switches",
73
+ table: "table",
74
+ tabs: "tabs",
75
+ toast: "messages",
76
+ tooltip: "icon_action",
77
+ };
78
+
79
+ export function Show({
80
+ component,
81
+ children,
82
+ }: {
83
+ component: string;
84
+ children: (s: Samples) => ReactNode;
85
+ }) {
86
+ if (!data.inventory.in.some((i) => i.id === component)) {
87
+ const out = data.inventory.out.find((i) => i.id === component);
88
+ return (
89
+ <p className="max-w-xl border-l-[length:var(--border-width-focus)] border-[var(--color-border-strong)] pl-[var(--space-gap-group)] text-[var(--color-text-secondary)]">
90
+ {data.name} does not carry this component.{" "}
91
+ {out?.why ?? "It is not in its inventory."}
92
+ </p>
93
+ );
94
+ }
95
+ if (PART[component] && !data.samples[PART[component]])
96
+ return (
97
+ <p className="text-[var(--color-text-secondary)]">
98
+ {data.name} has no sample content for this component yet.
99
+ </p>
100
+ );
101
+ return <>{children(data.samples)}</>;
102
+ }
@@ -0,0 +1,31 @@
1
+ // Shows this design system's when-to-use rules for the situations a component serves.
2
+ import scheme from "../scheme.json";
3
+ import situations from "../.open-ds/situations.json";
4
+
5
+ const TITLES: Record<string, string> = Object.fromEntries(
6
+ situations.map((s) => [s.id, s.title]),
7
+ );
8
+
9
+ export function Usage({ situations: ids }: { situations: string[] }) {
10
+ const rules = scheme.usage.filter((u) => ids.includes(u.situation));
11
+ if (!rules.length) return null;
12
+ return (
13
+ <section className="mt-[var(--space-gap-section)] max-w-3xl border-t-[length:var(--border-width-default)] border-[var(--color-border-default)] pt-[var(--space-gap-group)]">
14
+ <h2 className="mb-[var(--space-gap-default)] [font-family:var(--type-heading-font-family)] text-[length:var(--type-heading-font-size)] [font-weight:var(--type-heading-font-weight)]">
15
+ When to use
16
+ </h2>
17
+ <dl className="grid gap-[var(--space-gap-group)]">
18
+ {rules.map((r) => (
19
+ <div key={r.situation}>
20
+ <dt className="[font-weight:var(--type-label-font-weight)]">
21
+ {TITLES[r.situation]}
22
+ </dt>
23
+ <dd className="text-[var(--color-text-secondary)]">
24
+ Use {r.use}. Avoid: {r.not}. {r.why}
25
+ </dd>
26
+ </div>
27
+ ))}
28
+ </dl>
29
+ </section>
30
+ );
31
+ }