defuss-shadcn 0.6.6 → 0.7.19

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 (711) hide show
  1. package/AGENTS.md +992 -0
  2. package/ARCH.md +133 -0
  3. package/LICENSE +22 -0
  4. package/Makefile +55 -0
  5. package/README.md +188 -41
  6. package/bun.lock +340 -0
  7. package/dist/SKILL.md +559 -0
  8. package/dist/SKILL_tpl.md +66 -0
  9. package/dist/components/accordion/accordion.css +82 -0
  10. package/dist/components/accordion/accordion.js +138 -0
  11. package/dist/components/accordion/component-skill.md +173 -0
  12. package/dist/components/alert/alert.css +67 -0
  13. package/dist/components/alert/component-skill.md +64 -0
  14. package/dist/components/alert-dialog/alert-dialog.css +136 -0
  15. package/dist/components/alert-dialog/alert-dialog.js +132 -0
  16. package/dist/components/alert-dialog/component-skill.md +64 -0
  17. package/dist/components/avatar/avatar.css +97 -0
  18. package/dist/components/avatar/avatar.js +126 -0
  19. package/dist/components/avatar/component-skill.md +80 -0
  20. package/dist/components/badge/badge.css +30 -0
  21. package/dist/components/badge/component-skill.md +48 -0
  22. package/dist/components/breadcrumb/breadcrumb.css +50 -0
  23. package/dist/components/breadcrumb/component-skill.md +119 -0
  24. package/dist/components/button/button.css +141 -0
  25. package/dist/components/button/component-skill.md +90 -0
  26. package/dist/components/button-group/button-group.css +98 -0
  27. package/dist/components/button-group/component-skill.md +75 -0
  28. package/dist/components/calendar/calendar.css +187 -0
  29. package/dist/components/calendar/calendar.js +261 -0
  30. package/dist/components/calendar/component-skill.md +83 -0
  31. package/dist/components/card/card.css +61 -0
  32. package/dist/components/card/component-skill.md +129 -0
  33. package/dist/components/carousel/carousel.css +153 -0
  34. package/dist/components/carousel/carousel.js +276 -0
  35. package/dist/components/carousel/component-skill.md +175 -0
  36. package/dist/components/checkbox/checkbox.css +121 -0
  37. package/dist/components/checkbox/component-skill.md +108 -0
  38. package/dist/components/collapsible/collapsible.css +109 -0
  39. package/dist/components/collapsible/component-skill.md +40 -0
  40. package/dist/components/color-picker/color-picker.css +42 -0
  41. package/dist/components/color-picker/color-picker.js +111 -0
  42. package/dist/components/color-picker/component-skill.md +51 -0
  43. package/dist/components/combobox/combobox.css +190 -0
  44. package/dist/components/combobox/combobox.js +231 -0
  45. package/dist/components/combobox/component-skill.md +176 -0
  46. package/dist/components/command/command.css +73 -0
  47. package/dist/components/command/command.js +221 -0
  48. package/dist/components/command/component-skill.md +111 -0
  49. package/dist/components/context-menu/component-skill.md +43 -0
  50. package/dist/components/context-menu/context-menu.css +75 -0
  51. package/dist/components/context-menu/context-menu.js +178 -0
  52. package/dist/components/date-picker/component-skill.md +48 -0
  53. package/dist/components/date-picker/date-picker.css +60 -0
  54. package/dist/components/dialog/component-skill.md +118 -0
  55. package/dist/components/dialog/dialog.css +73 -0
  56. package/dist/components/dialog/dialog.js +128 -0
  57. package/dist/components/dropdown/component-skill.md +170 -0
  58. package/dist/components/dropdown/dropdown.css +156 -0
  59. package/dist/components/dropdown/dropdown.js +206 -0
  60. package/dist/components/file-input/component-skill.md +45 -0
  61. package/dist/components/file-input/file-input.css +66 -0
  62. package/dist/components/form/component-skill.md +142 -0
  63. package/dist/components/form/form.css +135 -0
  64. package/dist/components/icon/component-skill.md +167 -0
  65. package/dist/components/icon/icon.css +53 -0
  66. package/dist/components/image/component-skill.md +192 -0
  67. package/dist/components/image/image.css +174 -0
  68. package/dist/components/image/image.js +218 -0
  69. package/dist/components/input/component-skill.md +145 -0
  70. package/dist/components/input/input.css +153 -0
  71. package/dist/components/label/component-skill.md +106 -0
  72. package/dist/components/label/label.css +56 -0
  73. package/dist/components/navigation-menu/component-skill.md +96 -0
  74. package/dist/components/navigation-menu/navigation-menu.css +59 -0
  75. package/dist/components/navigation-menu/navigation-menu.js +119 -0
  76. package/dist/components/number-input/component-skill.md +49 -0
  77. package/dist/components/number-input/number-input.css +86 -0
  78. package/dist/components/number-input/number-input.js +124 -0
  79. package/dist/components/pagination/component-skill.md +117 -0
  80. package/dist/components/pagination/pagination.css +51 -0
  81. package/dist/components/popover/component-skill.md +98 -0
  82. package/dist/components/popover/popover.css +87 -0
  83. package/dist/components/popover/popover.js +114 -0
  84. package/dist/components/progress/component-skill.md +36 -0
  85. package/dist/components/progress/progress.css +76 -0
  86. package/dist/components/radio/component-skill.md +148 -0
  87. package/dist/components/radio/radio.css +210 -0
  88. package/dist/components/scroll-area/component-skill.md +52 -0
  89. package/dist/components/scroll-area/scroll-area.css +21 -0
  90. package/dist/components/select/component-skill.md +77 -0
  91. package/dist/components/select/select.css +69 -0
  92. package/dist/components/separator/component-skill.md +101 -0
  93. package/dist/components/separator/separator.css +65 -0
  94. package/dist/components/sheet/component-skill.md +129 -0
  95. package/dist/components/sheet/sheet.css +233 -0
  96. package/dist/components/sheet/sheet.js +125 -0
  97. package/dist/components/sidebar/component-skill.md +196 -0
  98. package/dist/components/sidebar/sidebar.css +368 -0
  99. package/dist/components/sidebar/sidebar.js +144 -0
  100. package/dist/components/skeleton/component-skill.md +42 -0
  101. package/dist/components/skeleton/skeleton.css +43 -0
  102. package/dist/components/slider/component-skill.md +130 -0
  103. package/dist/components/slider/slider.css +178 -0
  104. package/dist/components/slider/slider.js +118 -0
  105. package/dist/components/sortable/component-skill.md +146 -0
  106. package/dist/components/sortable/sortable.css +131 -0
  107. package/dist/components/sortable/sortable.js +301 -0
  108. package/dist/components/spinner/component-skill.md +42 -0
  109. package/dist/components/spinner/spinner.css +48 -0
  110. package/dist/components/statistic/component-skill.md +53 -0
  111. package/dist/components/statistic/statistic.css +53 -0
  112. package/dist/components/steps/component-skill.md +191 -0
  113. package/dist/components/steps/steps.css +95 -0
  114. package/dist/components/switch/component-skill.md +85 -0
  115. package/dist/components/switch/switch.css +153 -0
  116. package/dist/components/table/component-skill.md +59 -0
  117. package/dist/components/table/table.css +135 -0
  118. package/dist/components/tabs/component-skill.md +174 -0
  119. package/dist/components/tabs/tabs.css +123 -0
  120. package/dist/components/tabs/tabs.js +177 -0
  121. package/dist/components/textarea/component-skill.md +49 -0
  122. package/dist/components/textarea/textarea.css +82 -0
  123. package/dist/components/timeline/component-skill.md +53 -0
  124. package/dist/components/timeline/timeline.css +78 -0
  125. package/dist/components/toast/component-skill.md +151 -0
  126. package/dist/components/toast/toast.css +235 -0
  127. package/dist/components/toast/toast.js +238 -0
  128. package/dist/components/toggle/component-skill.md +108 -0
  129. package/dist/components/toggle/toggle.css +116 -0
  130. package/dist/components/toggle/toggle.js +112 -0
  131. package/dist/components/toggle-group/component-skill.md +172 -0
  132. package/dist/components/toggle-group/toggle-group.css +125 -0
  133. package/dist/components/toggle-group/toggle-group.js +167 -0
  134. package/dist/components/toolbar/component-skill.md +91 -0
  135. package/dist/components/toolbar/toolbar.css +39 -0
  136. package/dist/components/toolbar/toolbar.js +146 -0
  137. package/dist/components/tooltip/component-skill.md +116 -0
  138. package/dist/components/tooltip/tooltip.css +123 -0
  139. package/dist/components/tooltip/tooltip.js +174 -0
  140. package/dist/components/tree-view/component-skill.md +71 -0
  141. package/dist/components/tree-view/tree-view.css +156 -0
  142. package/dist/components/tree-view/tree-view.js +164 -0
  143. package/dist/components/typography/component-skill.md +119 -0
  144. package/dist/components/typography/typography.css +152 -0
  145. package/dist/documentation/accessibility.html +455 -0
  146. package/dist/documentation/accordion.html +938 -0
  147. package/dist/documentation/alert-dialog.html +561 -0
  148. package/dist/documentation/alert.html +378 -0
  149. package/dist/documentation/animations.html +399 -0
  150. package/dist/documentation/avatar.html +605 -0
  151. package/dist/documentation/badge.html +381 -0
  152. package/dist/documentation/breadcrumb.html +424 -0
  153. package/dist/documentation/button-group.html +504 -0
  154. package/dist/documentation/button.html +630 -0
  155. package/dist/documentation/calendar.html +734 -0
  156. package/dist/documentation/card.html +563 -0
  157. package/dist/documentation/carousel.html +985 -0
  158. package/dist/documentation/cascade-layers.html +438 -0
  159. package/dist/documentation/changelog.html +506 -0
  160. package/dist/documentation/checkbox.html +566 -0
  161. package/dist/documentation/collapsible.html +408 -0
  162. package/dist/documentation/color-picker.html +385 -0
  163. package/dist/documentation/combobox.html +908 -0
  164. package/dist/documentation/command.html +582 -0
  165. package/dist/documentation/component-skills.html +317 -0
  166. package/dist/documentation/context-menu.html +450 -0
  167. package/dist/documentation/css/docs-theme.css +41 -0
  168. package/dist/documentation/css/docs-utilities.css +63 -0
  169. package/dist/documentation/css/layout.css +822 -0
  170. package/dist/documentation/dark-mode.html +335 -0
  171. package/dist/documentation/data-attribute-api.html +504 -0
  172. package/dist/documentation/date-picker.html +319 -0
  173. package/dist/documentation/dialog.html +785 -0
  174. package/dist/documentation/dropdown.html +936 -0
  175. package/dist/documentation/es-modules.html +397 -0
  176. package/dist/documentation/file-input.html +320 -0
  177. package/dist/documentation/fonts/fraunces-300.woff2 +0 -0
  178. package/dist/documentation/fonts/fraunces-300i.woff2 +0 -0
  179. package/dist/documentation/fonts/fraunces-400.woff2 +0 -0
  180. package/dist/documentation/fonts/fraunces-400i.woff2 +0 -0
  181. package/dist/documentation/fonts/fraunces-500.woff2 +0 -0
  182. package/dist/documentation/fonts/geist-300.woff2 +0 -0
  183. package/dist/documentation/fonts/geist-400.woff2 +0 -0
  184. package/dist/documentation/fonts/geist-500.woff2 +0 -0
  185. package/dist/documentation/fonts/geist-600.woff2 +0 -0
  186. package/dist/documentation/fonts/geist-mono-400.woff2 +0 -0
  187. package/dist/documentation/fonts/geist-mono-500.woff2 +0 -0
  188. package/dist/documentation/form.html +833 -0
  189. package/dist/documentation/icon.html +624 -0
  190. package/dist/documentation/image.html +911 -0
  191. package/dist/documentation/images/favicon.webp +0 -0
  192. package/dist/documentation/index.html +327 -0
  193. package/dist/documentation/input.html +743 -0
  194. package/dist/documentation/installation.html +231 -0
  195. package/dist/documentation/js/layout.js +736 -0
  196. package/dist/documentation/js/search-index.js +8 -0
  197. package/dist/documentation/js/shiki-highlight.js +69 -0
  198. package/dist/documentation/js/site.js +281 -0
  199. package/dist/documentation/js/theme-switcher.js +143 -0
  200. package/dist/documentation/js/themes.js +1747 -0
  201. package/dist/documentation/label.html +481 -0
  202. package/dist/documentation/native-web-apis.html +286 -0
  203. package/dist/documentation/navigation-menu.html +508 -0
  204. package/dist/documentation/number-input.html +430 -0
  205. package/dist/documentation/pagination.html +502 -0
  206. package/dist/documentation/popover.html +562 -0
  207. package/dist/documentation/progress.html +361 -0
  208. package/dist/documentation/radio.html +787 -0
  209. package/dist/documentation/scroll-area.html +249 -0
  210. package/dist/documentation/select.html +379 -0
  211. package/dist/documentation/separator.html +521 -0
  212. package/dist/documentation/sheet.html +960 -0
  213. package/dist/documentation/sidebar.html +1015 -0
  214. package/dist/documentation/skeleton.html +308 -0
  215. package/dist/documentation/slider.html +684 -0
  216. package/dist/documentation/sortable.html +898 -0
  217. package/dist/documentation/spinner.html +316 -0
  218. package/dist/documentation/statistic.html +417 -0
  219. package/dist/documentation/steps.html +749 -0
  220. package/dist/documentation/switch.html +539 -0
  221. package/dist/documentation/table.html +481 -0
  222. package/dist/documentation/tabs.html +941 -0
  223. package/dist/documentation/textarea.html +344 -0
  224. package/dist/documentation/theming.html +321 -0
  225. package/dist/documentation/timeline.html +402 -0
  226. package/dist/documentation/toast.html +880 -0
  227. package/dist/documentation/toggle-group.html +840 -0
  228. package/dist/documentation/toggle.html +612 -0
  229. package/dist/documentation/toolbar.html +488 -0
  230. package/dist/documentation/tooltip.html +807 -0
  231. package/dist/documentation/tree-view.html +611 -0
  232. package/dist/documentation/typography.html +646 -0
  233. package/dist/robots.txt +4 -0
  234. package/dist/sitemap.xml +72 -0
  235. package/dist/theme/default-semantic-tokens.css +139 -0
  236. package/docs/accessibility.html +455 -0
  237. package/docs/accordion.html +938 -0
  238. package/docs/alert-dialog.html +561 -0
  239. package/docs/alert.html +378 -0
  240. package/docs/animations.html +399 -0
  241. package/docs/avatar.html +605 -0
  242. package/docs/badge.html +381 -0
  243. package/docs/breadcrumb.html +424 -0
  244. package/docs/button-group.html +504 -0
  245. package/docs/button.html +630 -0
  246. package/docs/calendar.html +734 -0
  247. package/docs/card.html +563 -0
  248. package/docs/carousel.html +985 -0
  249. package/docs/cascade-layers.html +438 -0
  250. package/docs/changelog.html +506 -0
  251. package/docs/checkbox.html +566 -0
  252. package/docs/collapsible.html +408 -0
  253. package/docs/color-picker.html +385 -0
  254. package/docs/combobox.html +908 -0
  255. package/docs/command.html +582 -0
  256. package/docs/component-skills.html +317 -0
  257. package/docs/context-menu.html +450 -0
  258. package/docs/css/docs-theme.css +41 -0
  259. package/docs/css/docs-utilities.css +63 -0
  260. package/docs/css/layout.css +822 -0
  261. package/docs/dark-mode.html +335 -0
  262. package/docs/data-attribute-api.html +504 -0
  263. package/docs/date-picker.html +319 -0
  264. package/docs/dialog.html +785 -0
  265. package/docs/dropdown.html +936 -0
  266. package/docs/es-modules.html +397 -0
  267. package/docs/file-input.html +320 -0
  268. package/docs/fonts/fraunces-300.woff2 +0 -0
  269. package/docs/fonts/fraunces-300i.woff2 +0 -0
  270. package/docs/fonts/fraunces-400.woff2 +0 -0
  271. package/docs/fonts/fraunces-400i.woff2 +0 -0
  272. package/docs/fonts/fraunces-500.woff2 +0 -0
  273. package/docs/fonts/geist-300.woff2 +0 -0
  274. package/docs/fonts/geist-400.woff2 +0 -0
  275. package/docs/fonts/geist-500.woff2 +0 -0
  276. package/docs/fonts/geist-600.woff2 +0 -0
  277. package/docs/fonts/geist-mono-400.woff2 +0 -0
  278. package/docs/fonts/geist-mono-500.woff2 +0 -0
  279. package/docs/form.html +833 -0
  280. package/docs/icon.html +624 -0
  281. package/docs/image.html +911 -0
  282. package/docs/images/favicon.webp +0 -0
  283. package/docs/index.html +327 -0
  284. package/docs/input.html +743 -0
  285. package/docs/installation.html +231 -0
  286. package/docs/js/layout.js +736 -0
  287. package/docs/js/search-index.js +8 -0
  288. package/docs/js/shiki-highlight.js +69 -0
  289. package/docs/js/site.js +281 -0
  290. package/docs/js/theme-switcher.js +143 -0
  291. package/docs/js/themes.js +1747 -0
  292. package/docs/label.html +481 -0
  293. package/docs/native-web-apis.html +286 -0
  294. package/docs/navigation-menu.html +508 -0
  295. package/docs/number-input.html +430 -0
  296. package/docs/pagination.html +502 -0
  297. package/docs/popover.html +562 -0
  298. package/docs/progress.html +361 -0
  299. package/docs/radio.html +787 -0
  300. package/docs/robots.txt +4 -0
  301. package/docs/scroll-area.html +249 -0
  302. package/docs/select.html +379 -0
  303. package/docs/separator.html +521 -0
  304. package/docs/sheet.html +960 -0
  305. package/docs/sidebar.html +1015 -0
  306. package/docs/sitemap.xml +72 -0
  307. package/docs/skeleton.html +308 -0
  308. package/docs/slider.html +684 -0
  309. package/docs/sortable.html +898 -0
  310. package/docs/spinner.html +316 -0
  311. package/docs/statistic.html +417 -0
  312. package/docs/steps.html +749 -0
  313. package/docs/switch.html +539 -0
  314. package/docs/table.html +481 -0
  315. package/docs/tabs.html +941 -0
  316. package/docs/textarea.html +344 -0
  317. package/docs/theming.html +321 -0
  318. package/docs/timeline.html +402 -0
  319. package/docs/toast.html +880 -0
  320. package/docs/toggle-group.html +840 -0
  321. package/docs/toggle.html +612 -0
  322. package/docs/toolbar.html +488 -0
  323. package/docs/tooltip.html +807 -0
  324. package/docs/tree-view.html +611 -0
  325. package/docs/typography.html +646 -0
  326. package/package.json +33 -72
  327. package/scripts/build.ts +74 -0
  328. package/scripts/create-screenshots.ts +226 -0
  329. package/scripts/deploy.sh +198 -0
  330. package/scripts/lib/audit.ts +127 -0
  331. package/scripts/lib/changelog.ts +144 -0
  332. package/scripts/lib/contrast.ts +227 -0
  333. package/scripts/lib/inputs.ts +76 -0
  334. package/scripts/lib/mirror.ts +68 -0
  335. package/scripts/lib/readme.ts +33 -0
  336. package/scripts/lib/search-index.ts +99 -0
  337. package/scripts/lib/skill-files.ts +45 -0
  338. package/scripts/lib/skill.ts +83 -0
  339. package/scripts/lib/snippets.ts +57 -0
  340. package/scripts/push.sh +34 -0
  341. package/scripts/sync-css-snippets.ts +54 -0
  342. package/scripts/sync-docs.ts +38 -0
  343. package/scripts/sync-js-snippets.ts +47 -0
  344. package/scripts/verify.ts +917 -0
  345. package/src/SKILL.md +559 -0
  346. package/src/SKILL_tpl.md +66 -0
  347. package/src/components/accordion/accordion.css +82 -0
  348. package/src/components/accordion/accordion.ts +94 -0
  349. package/src/components/accordion/component-skill.md +173 -0
  350. package/src/components/alert/alert.css +67 -0
  351. package/src/components/alert/component-skill.md +64 -0
  352. package/src/components/alert-dialog/alert-dialog.css +136 -0
  353. package/src/components/alert-dialog/alert-dialog.ts +92 -0
  354. package/src/components/alert-dialog/component-skill.md +64 -0
  355. package/src/components/avatar/avatar.css +97 -0
  356. package/src/components/avatar/avatar.ts +84 -0
  357. package/src/components/avatar/component-skill.md +80 -0
  358. package/src/components/badge/badge.css +30 -0
  359. package/src/components/badge/component-skill.md +48 -0
  360. package/src/components/breadcrumb/breadcrumb.css +50 -0
  361. package/src/components/breadcrumb/component-skill.md +119 -0
  362. package/src/components/button/button.css +141 -0
  363. package/src/components/button/component-skill.md +90 -0
  364. package/src/components/button-group/button-group.css +98 -0
  365. package/src/components/button-group/component-skill.md +75 -0
  366. package/src/components/calendar/calendar.css +187 -0
  367. package/src/components/calendar/calendar.ts +219 -0
  368. package/src/components/calendar/component-skill.md +83 -0
  369. package/src/components/card/card.css +61 -0
  370. package/src/components/card/component-skill.md +129 -0
  371. package/src/components/carousel/carousel.css +153 -0
  372. package/src/components/carousel/carousel.ts +236 -0
  373. package/src/components/carousel/component-skill.md +175 -0
  374. package/src/components/checkbox/checkbox.css +121 -0
  375. package/src/components/checkbox/component-skill.md +108 -0
  376. package/src/components/collapsible/collapsible.css +109 -0
  377. package/src/components/collapsible/component-skill.md +40 -0
  378. package/src/components/color-picker/color-picker.css +42 -0
  379. package/src/components/color-picker/color-picker.ts +72 -0
  380. package/src/components/color-picker/component-skill.md +51 -0
  381. package/src/components/combobox/combobox.css +190 -0
  382. package/src/components/combobox/combobox.ts +149 -0
  383. package/src/components/combobox/component-skill.md +176 -0
  384. package/src/components/command/command.css +73 -0
  385. package/src/components/command/command.ts +163 -0
  386. package/src/components/command/component-skill.md +111 -0
  387. package/src/components/context-menu/component-skill.md +43 -0
  388. package/src/components/context-menu/context-menu.css +75 -0
  389. package/src/components/context-menu/context-menu.ts +138 -0
  390. package/src/components/date-picker/component-skill.md +48 -0
  391. package/src/components/date-picker/date-picker.css +60 -0
  392. package/src/components/dialog/component-skill.md +118 -0
  393. package/src/components/dialog/dialog.css +73 -0
  394. package/src/components/dialog/dialog.ts +84 -0
  395. package/src/components/dropdown/component-skill.md +170 -0
  396. package/src/components/dropdown/dropdown.css +156 -0
  397. package/src/components/dropdown/dropdown.ts +132 -0
  398. package/src/components/file-input/component-skill.md +45 -0
  399. package/src/components/file-input/file-input.css +66 -0
  400. package/src/components/form/component-skill.md +142 -0
  401. package/src/components/form/form.css +135 -0
  402. package/src/components/icon/component-skill.md +167 -0
  403. package/src/components/icon/icon.css +53 -0
  404. package/src/components/image/component-skill.md +192 -0
  405. package/src/components/image/image.css +174 -0
  406. package/src/components/image/image.ts +181 -0
  407. package/src/components/input/component-skill.md +145 -0
  408. package/src/components/input/input.css +153 -0
  409. package/src/components/label/component-skill.md +106 -0
  410. package/src/components/label/label.css +56 -0
  411. package/src/components/navigation-menu/component-skill.md +96 -0
  412. package/src/components/navigation-menu/navigation-menu.css +59 -0
  413. package/src/components/navigation-menu/navigation-menu.ts +78 -0
  414. package/src/components/number-input/component-skill.md +49 -0
  415. package/src/components/number-input/number-input.css +86 -0
  416. package/src/components/number-input/number-input.ts +81 -0
  417. package/src/components/pagination/component-skill.md +117 -0
  418. package/src/components/pagination/pagination.css +51 -0
  419. package/src/components/popover/component-skill.md +98 -0
  420. package/src/components/popover/popover.css +87 -0
  421. package/src/components/popover/popover.ts +73 -0
  422. package/src/components/progress/component-skill.md +36 -0
  423. package/src/components/progress/progress.css +76 -0
  424. package/src/components/radio/component-skill.md +148 -0
  425. package/src/components/radio/radio.css +210 -0
  426. package/src/components/scroll-area/component-skill.md +52 -0
  427. package/src/components/scroll-area/scroll-area.css +21 -0
  428. package/src/components/select/component-skill.md +77 -0
  429. package/src/components/select/select.css +69 -0
  430. package/src/components/separator/component-skill.md +101 -0
  431. package/src/components/separator/separator.css +65 -0
  432. package/src/components/sheet/component-skill.md +129 -0
  433. package/src/components/sheet/sheet.css +233 -0
  434. package/src/components/sheet/sheet.ts +81 -0
  435. package/src/components/sidebar/component-skill.md +196 -0
  436. package/src/components/sidebar/sidebar.css +368 -0
  437. package/src/components/sidebar/sidebar.ts +109 -0
  438. package/src/components/skeleton/component-skill.md +42 -0
  439. package/src/components/skeleton/skeleton.css +43 -0
  440. package/src/components/slider/component-skill.md +130 -0
  441. package/src/components/slider/slider.css +178 -0
  442. package/src/components/slider/slider.ts +79 -0
  443. package/src/components/sortable/component-skill.md +146 -0
  444. package/src/components/sortable/sortable.css +131 -0
  445. package/src/components/sortable/sortable.ts +269 -0
  446. package/src/components/spinner/component-skill.md +42 -0
  447. package/src/components/spinner/spinner.css +48 -0
  448. package/src/components/statistic/component-skill.md +53 -0
  449. package/src/components/statistic/statistic.css +53 -0
  450. package/src/components/steps/component-skill.md +191 -0
  451. package/src/components/steps/steps.css +95 -0
  452. package/src/components/switch/component-skill.md +85 -0
  453. package/src/components/switch/switch.css +153 -0
  454. package/src/components/table/component-skill.md +59 -0
  455. package/src/components/table/table.css +135 -0
  456. package/src/components/tabs/component-skill.md +174 -0
  457. package/src/components/tabs/tabs.css +123 -0
  458. package/src/components/tabs/tabs.ts +129 -0
  459. package/src/components/textarea/component-skill.md +49 -0
  460. package/src/components/textarea/textarea.css +82 -0
  461. package/src/components/timeline/component-skill.md +53 -0
  462. package/src/components/timeline/timeline.css +78 -0
  463. package/src/components/toast/component-skill.md +151 -0
  464. package/src/components/toast/toast.css +235 -0
  465. package/src/components/toast/toast.ts +173 -0
  466. package/src/components/toggle/component-skill.md +108 -0
  467. package/src/components/toggle/toggle.css +116 -0
  468. package/src/components/toggle/toggle.ts +73 -0
  469. package/src/components/toggle-group/component-skill.md +172 -0
  470. package/src/components/toggle-group/toggle-group.css +125 -0
  471. package/src/components/toggle-group/toggle-group.ts +131 -0
  472. package/src/components/toolbar/component-skill.md +91 -0
  473. package/src/components/toolbar/toolbar.css +39 -0
  474. package/src/components/toolbar/toolbar.ts +109 -0
  475. package/src/components/tooltip/component-skill.md +116 -0
  476. package/src/components/tooltip/tooltip.css +123 -0
  477. package/src/components/tooltip/tooltip.ts +135 -0
  478. package/src/components/tree-view/component-skill.md +71 -0
  479. package/src/components/tree-view/tree-view.css +156 -0
  480. package/src/components/tree-view/tree-view.ts +118 -0
  481. package/src/components/typography/component-skill.md +119 -0
  482. package/src/components/typography/typography.css +152 -0
  483. package/src/documentation/accessibility.html +455 -0
  484. package/src/documentation/accordion.html +938 -0
  485. package/src/documentation/alert-dialog.html +561 -0
  486. package/src/documentation/alert.html +378 -0
  487. package/src/documentation/animations.html +399 -0
  488. package/src/documentation/avatar.html +605 -0
  489. package/src/documentation/badge.html +381 -0
  490. package/src/documentation/breadcrumb.html +424 -0
  491. package/src/documentation/button-group.html +504 -0
  492. package/src/documentation/button.html +630 -0
  493. package/src/documentation/calendar.html +734 -0
  494. package/src/documentation/card.html +563 -0
  495. package/src/documentation/carousel.html +985 -0
  496. package/src/documentation/cascade-layers.html +438 -0
  497. package/src/documentation/changelog.html +506 -0
  498. package/src/documentation/checkbox.html +566 -0
  499. package/src/documentation/collapsible.html +408 -0
  500. package/src/documentation/color-picker.html +385 -0
  501. package/src/documentation/combobox.html +908 -0
  502. package/src/documentation/command.html +582 -0
  503. package/src/documentation/component-skills.html +317 -0
  504. package/src/documentation/context-menu.html +450 -0
  505. package/src/documentation/css/docs-theme.css +41 -0
  506. package/src/documentation/css/docs-utilities.css +63 -0
  507. package/src/documentation/css/layout.css +822 -0
  508. package/src/documentation/dark-mode.html +335 -0
  509. package/src/documentation/data-attribute-api.html +504 -0
  510. package/src/documentation/date-picker.html +319 -0
  511. package/src/documentation/dialog.html +785 -0
  512. package/src/documentation/dropdown.html +936 -0
  513. package/src/documentation/es-modules.html +397 -0
  514. package/src/documentation/file-input.html +320 -0
  515. package/src/documentation/fonts/fraunces-300.woff2 +0 -0
  516. package/src/documentation/fonts/fraunces-300i.woff2 +0 -0
  517. package/src/documentation/fonts/fraunces-400.woff2 +0 -0
  518. package/src/documentation/fonts/fraunces-400i.woff2 +0 -0
  519. package/src/documentation/fonts/fraunces-500.woff2 +0 -0
  520. package/src/documentation/fonts/geist-300.woff2 +0 -0
  521. package/src/documentation/fonts/geist-400.woff2 +0 -0
  522. package/src/documentation/fonts/geist-500.woff2 +0 -0
  523. package/src/documentation/fonts/geist-600.woff2 +0 -0
  524. package/src/documentation/fonts/geist-mono-400.woff2 +0 -0
  525. package/src/documentation/fonts/geist-mono-500.woff2 +0 -0
  526. package/src/documentation/form.html +833 -0
  527. package/src/documentation/icon.html +624 -0
  528. package/src/documentation/image.html +911 -0
  529. package/src/documentation/images/favicon.webp +0 -0
  530. package/src/documentation/index.html +327 -0
  531. package/src/documentation/input.html +743 -0
  532. package/src/documentation/installation.html +231 -0
  533. package/src/documentation/js/layout.ts +748 -0
  534. package/src/documentation/js/search-index.js +8 -0
  535. package/src/documentation/js/shiki-highlight.ts +83 -0
  536. package/src/documentation/js/site.ts +296 -0
  537. package/src/documentation/js/theme-switcher.ts +158 -0
  538. package/src/documentation/js/themes.ts +1748 -0
  539. package/src/documentation/label.html +481 -0
  540. package/src/documentation/native-web-apis.html +286 -0
  541. package/src/documentation/navigation-menu.html +508 -0
  542. package/src/documentation/number-input.html +430 -0
  543. package/src/documentation/pagination.html +502 -0
  544. package/src/documentation/popover.html +562 -0
  545. package/src/documentation/progress.html +361 -0
  546. package/src/documentation/radio.html +787 -0
  547. package/src/documentation/scroll-area.html +249 -0
  548. package/src/documentation/select.html +379 -0
  549. package/src/documentation/separator.html +521 -0
  550. package/src/documentation/sheet.html +960 -0
  551. package/src/documentation/sidebar.html +1015 -0
  552. package/src/documentation/skeleton.html +308 -0
  553. package/src/documentation/slider.html +684 -0
  554. package/src/documentation/sortable.html +898 -0
  555. package/src/documentation/spinner.html +316 -0
  556. package/src/documentation/statistic.html +417 -0
  557. package/src/documentation/steps.html +749 -0
  558. package/src/documentation/switch.html +539 -0
  559. package/src/documentation/table.html +481 -0
  560. package/src/documentation/tabs.html +941 -0
  561. package/src/documentation/textarea.html +344 -0
  562. package/src/documentation/theming.html +321 -0
  563. package/src/documentation/timeline.html +402 -0
  564. package/src/documentation/toast.html +880 -0
  565. package/src/documentation/toggle-group.html +840 -0
  566. package/src/documentation/toggle.html +612 -0
  567. package/src/documentation/toolbar.html +488 -0
  568. package/src/documentation/tooltip.html +807 -0
  569. package/src/documentation/tree-view.html +611 -0
  570. package/src/documentation/typography.html +646 -0
  571. package/src/robots.txt +4 -0
  572. package/src/shared/state-api.ts +43 -0
  573. package/src/sitemap.xml +72 -0
  574. package/src/theme/default-semantic-tokens.css +139 -0
  575. package/src/types/defuss-shadcn.d.ts +86 -0
  576. package/src/types/raw-import.d.ts +11 -0
  577. package/tests/changelog.test.ts +150 -0
  578. package/tests/contrast.test.ts +217 -0
  579. package/tests/e2e/accordion.e2e-fixture.html +152 -0
  580. package/tests/e2e/accordion.e2e.ts +186 -0
  581. package/tests/e2e/alert-dialog.e2e-fixture.html +28 -0
  582. package/tests/e2e/alert-dialog.e2e.ts +131 -0
  583. package/tests/e2e/alert.e2e-fixture.html +27 -0
  584. package/tests/e2e/alert.e2e.ts +37 -0
  585. package/tests/e2e/avatar.e2e-fixture.html +38 -0
  586. package/tests/e2e/avatar.e2e.ts +149 -0
  587. package/tests/e2e/badge.e2e-fixture.html +17 -0
  588. package/tests/e2e/badge.e2e.ts +35 -0
  589. package/tests/e2e/breadcrumb.e2e-fixture.html +29 -0
  590. package/tests/e2e/breadcrumb.e2e.ts +51 -0
  591. package/tests/e2e/button-group.e2e-fixture.html +28 -0
  592. package/tests/e2e/button-group.e2e.ts +51 -0
  593. package/tests/e2e/button.e2e-fixture.html +29 -0
  594. package/tests/e2e/button.e2e.ts +80 -0
  595. package/tests/e2e/calendar.e2e-fixture.html +22 -0
  596. package/tests/e2e/calendar.e2e.ts +193 -0
  597. package/tests/e2e/card.e2e-fixture.html +28 -0
  598. package/tests/e2e/card.e2e.ts +50 -0
  599. package/tests/e2e/carousel.e2e-fixture.html +34 -0
  600. package/tests/e2e/carousel.e2e.ts +160 -0
  601. package/tests/e2e/checkbox.e2e-fixture.html +41 -0
  602. package/tests/e2e/checkbox.e2e.ts +75 -0
  603. package/tests/e2e/collapsible.e2e-fixture.html +27 -0
  604. package/tests/e2e/collapsible.e2e.ts +36 -0
  605. package/tests/e2e/color-picker.e2e-fixture.html +24 -0
  606. package/tests/e2e/color-picker.e2e.ts +113 -0
  607. package/tests/e2e/combobox.e2e-fixture.html +58 -0
  608. package/tests/e2e/combobox.e2e.ts +245 -0
  609. package/tests/e2e/command.e2e-fixture.html +40 -0
  610. package/tests/e2e/command.e2e.ts +142 -0
  611. package/tests/e2e/context-menu.e2e-fixture.html +22 -0
  612. package/tests/e2e/context-menu.e2e.ts +158 -0
  613. package/tests/e2e/date-picker.e2e-fixture.html +14 -0
  614. package/tests/e2e/date-picker.e2e.ts +19 -0
  615. package/tests/e2e/dialog.e2e-fixture.html +39 -0
  616. package/tests/e2e/dialog.e2e.ts +141 -0
  617. package/tests/e2e/documentation.e2e.ts +289 -0
  618. package/tests/e2e/dropdown.e2e-fixture.html +42 -0
  619. package/tests/e2e/dropdown.e2e.ts +227 -0
  620. package/tests/e2e/file-input.e2e-fixture.html +14 -0
  621. package/tests/e2e/file-input.e2e.ts +33 -0
  622. package/tests/e2e/form.e2e-fixture.html +35 -0
  623. package/tests/e2e/form.e2e.ts +41 -0
  624. package/tests/e2e/icon.e2e-fixture.html +19 -0
  625. package/tests/e2e/icon.e2e.ts +44 -0
  626. package/tests/e2e/image.e2e-fixture.html +36 -0
  627. package/tests/e2e/image.e2e.ts +164 -0
  628. package/tests/e2e/input.e2e-fixture.html +18 -0
  629. package/tests/e2e/input.e2e.ts +61 -0
  630. package/tests/e2e/label.e2e-fixture.html +21 -0
  631. package/tests/e2e/label.e2e.ts +32 -0
  632. package/tests/e2e/lib/css-smoke.ts +91 -0
  633. package/tests/e2e/navigation-menu.e2e-fixture.html +37 -0
  634. package/tests/e2e/navigation-menu.e2e.ts +131 -0
  635. package/tests/e2e/number-input.e2e-fixture.html +25 -0
  636. package/tests/e2e/number-input.e2e.ts +139 -0
  637. package/tests/e2e/pagination.e2e-fixture.html +22 -0
  638. package/tests/e2e/pagination.e2e.ts +46 -0
  639. package/tests/e2e/popover.e2e-fixture.html +45 -0
  640. package/tests/e2e/popover.e2e.ts +141 -0
  641. package/tests/e2e/progress.e2e-fixture.html +15 -0
  642. package/tests/e2e/progress.e2e.ts +39 -0
  643. package/tests/e2e/radio.e2e-fixture.html +43 -0
  644. package/tests/e2e/radio.e2e.ts +75 -0
  645. package/tests/e2e/run.ts +32 -0
  646. package/tests/e2e/scroll-area.e2e-fixture.html +15 -0
  647. package/tests/e2e/scroll-area.e2e.ts +29 -0
  648. package/tests/e2e/select.e2e-fixture.html +28 -0
  649. package/tests/e2e/select.e2e.ts +51 -0
  650. package/tests/e2e/separator.e2e-fixture.html +24 -0
  651. package/tests/e2e/separator.e2e.ts +37 -0
  652. package/tests/e2e/server.ts +42 -0
  653. package/tests/e2e/sheet.e2e-fixture.html +62 -0
  654. package/tests/e2e/sheet.e2e.ts +231 -0
  655. package/tests/e2e/sidebar.e2e-fixture.html +62 -0
  656. package/tests/e2e/sidebar.e2e.ts +180 -0
  657. package/tests/e2e/skeleton.e2e-fixture.html +14 -0
  658. package/tests/e2e/skeleton.e2e.ts +30 -0
  659. package/tests/e2e/slider.e2e-fixture.html +23 -0
  660. package/tests/e2e/slider.e2e.ts +158 -0
  661. package/tests/e2e/sortable.e2e-fixture.html +25 -0
  662. package/tests/e2e/sortable.e2e.ts +191 -0
  663. package/tests/e2e/spinner.e2e-fixture.html +16 -0
  664. package/tests/e2e/spinner.e2e.ts +40 -0
  665. package/tests/e2e/statistic.e2e-fixture.html +25 -0
  666. package/tests/e2e/statistic.e2e.ts +38 -0
  667. package/tests/e2e/steps.e2e-fixture.html +40 -0
  668. package/tests/e2e/steps.e2e.ts +56 -0
  669. package/tests/e2e/switch.e2e-fixture.html +17 -0
  670. package/tests/e2e/switch.e2e.ts +68 -0
  671. package/tests/e2e/table.e2e-fixture.html +35 -0
  672. package/tests/e2e/table.e2e.ts +44 -0
  673. package/tests/e2e/tabs.e2e-fixture.html +34 -0
  674. package/tests/e2e/tabs.e2e.ts +142 -0
  675. package/tests/e2e/textarea.e2e-fixture.html +15 -0
  676. package/tests/e2e/textarea.e2e.ts +41 -0
  677. package/tests/e2e/timeline.e2e-fixture.html +27 -0
  678. package/tests/e2e/timeline.e2e.ts +39 -0
  679. package/tests/e2e/toast.e2e-fixture.html +29 -0
  680. package/tests/e2e/toast.e2e.ts +221 -0
  681. package/tests/e2e/toggle-group.e2e-fixture.html +45 -0
  682. package/tests/e2e/toggle-group.e2e.ts +169 -0
  683. package/tests/e2e/toggle.e2e-fixture.html +20 -0
  684. package/tests/e2e/toggle.e2e.ts +136 -0
  685. package/tests/e2e/toolbar.e2e-fixture.html +42 -0
  686. package/tests/e2e/toolbar.e2e.ts +161 -0
  687. package/tests/e2e/tooltip.e2e-fixture.html +48 -0
  688. package/tests/e2e/tooltip.e2e.ts +179 -0
  689. package/tests/e2e/tree-view.e2e-fixture.html +45 -0
  690. package/tests/e2e/tree-view.e2e.ts +206 -0
  691. package/tests/e2e/typography.e2e-fixture.html +24 -0
  692. package/tests/e2e/typography.e2e.ts +70 -0
  693. package/tests/helpers.ts +68 -0
  694. package/tests/readme.test.ts +42 -0
  695. package/tests/skill.test.ts +117 -0
  696. package/tests/ui.test.ts +108 -0
  697. package/tsconfig.json +25 -0
  698. package/tsconfig.scripts.json +18 -0
  699. package/tsconfig.test.json +14 -0
  700. package/vite.config.ts +22 -0
  701. package/vitest.config.ts +23 -0
  702. package/dist/css/shadcn.cdn.css +0 -4336
  703. package/dist/css/shadcn.cdn.min.css +0 -2
  704. package/dist/index.cjs +0 -6430
  705. package/dist/index.cjs.map +0 -1
  706. package/dist/index.d.cts +0 -533
  707. package/dist/index.d.cts.map +0 -1
  708. package/dist/index.d.mts +0 -533
  709. package/dist/index.d.mts.map +0 -1
  710. package/dist/index.mjs +0 -6306
  711. package/dist/index.mjs.map +0 -1
package/AGENTS.md ADDED
@@ -0,0 +1,992 @@
1
+ # defuss-shadcn — Maintainer Instructions
2
+
3
+ Scratch space: throwaway scripts, probes and scaffolds go in the repo-local
4
+ `tmp/` (gitignored) — never `/tmp` or other machine-specific locations. This
5
+ keeps everything relative to the repo root (see the `portable paths` verify
6
+ check) and inspectable. **Write the file with the file-writing tool and run it
7
+ with `bun tmp/<script>.ts` from the repo root** — do not build scripts through
8
+ `python3 - <<'PY'`/heredoc rewrites, which get permission-blocked and are
9
+ harder to iterate on than editing the file directly.
10
+
11
+ Long-running commands: always run e2e tests, screenshot generation, builds and
12
+ browser tooling with a **timeout** (execute_command's `timeout` parameter, or
13
+ `--timeout`/`PAGE_TIMEOUT_MS` inside the scripts). A stalled Chromium session
14
+ must surface as a failure with output, never as an agent hanging forever.
15
+
16
+ You are working on the **defuss-shadcn** design system repo.
17
+ The consumer-facing system lives in `dist/` — **it is generated**: edit sources in
18
+ `src/` (`bun run build` compiles `.ts` → `.js` and copies everything else 1:1).
19
+ `docs/` is also generated: **only the documentation site** (`dist/documentation/*` +
20
+ `robots.txt`/`sitemap.xml`) published by GitHub Pages — its pages' `../components/…` and
21
+ `../theme/…` references are rewritten to the jsDelivr GitHub CDN (shared transform in
22
+ `scripts/lib/mirror.ts`), so the mirror carries no copies of the component assets.
23
+ Refresh it with `bun run docs` (`make build` does this automatically and `verify`
24
+ fails if the mirror drifts). Never edit `docs/` directly — like `dist/`, it is
25
+ deleted and rebuilt on every `bun run docs`.
26
+ Never edit `dist/` directly; it is deleted and rebuilt on every build.
27
+
28
+ ---
29
+
30
+ ## Project structure
31
+
32
+ ```
33
+ defuss-shadcn/
34
+ ├── dist/ ← the distributable (drop into any project)
35
+ │ ├── SKILL.md ← agent entry point (generated from src/SKILL_tpl.md + skill frontmatter)
36
+ │ ├── theme/default-semantic-tokens.css ← design tokens (source of truth for colors, radius, shadows)
37
+ │ ├── components/ ← self-contained component folders
38
+ │ │ └── {name}/
39
+ │ │ ├── component-skill.md ← component skill (frontmatter + HTML structure & ARIA reference)
40
+ │ │ ├── {name}.css ← component stylesheet (edit directly)
41
+ │ │ └── {name}.js ← interaction JS (only for interactive components)
42
+ │ └── documentation/ ← reference implementations + public website
43
+ │ ├── *.html ← one page per component + overview pages
44
+ │ ├── css/docs-utilities.css ← hand-written utility classes for doc pages
45
+ │ ├── css/docs-theme.css ← doc-site font overrides (not part of the system)
46
+ │ ├── css/layout.css ← doc-site layout (not part of the system)
47
+ │ ├── js/layout.js ← SPA router, <site-header>/<site-nav> web components
48
+ │ ├── js/site.js ← doc-site-only JS (tabs, copy buttons, skill modal, code collapse)
49
+ │ ├── js/shiki-highlight.js ← Shiki-based syntax highlighting (ES module, CDN)
50
+ │ ├── js/themes.js ← tweakcn color theme presets (global THEMES array)
51
+ │ └── js/theme-switcher.js ← applies theme overrides to CSS custom properties
52
+ │
53
+ ├── .github/
54
+ │ ├── instructions/ ← auto-attached instruction files for Copilot
55
+ │ │ ├── documentation.instructions.md
56
+ │ │ ├── specifications.instructions.md
57
+ │ │ └── tokens.instructions.md
58
+ │ └── prompts/ ← reusable prompt files
59
+ │ └── component-review.prompt.md
60
+ │
61
+ ├── screenshots/ ← generated PNGs per component AND per declared state, light/ + dark/ (`bun run screenshots`, gitignored) — SKILL.md maps state names to these files
62
+ ├── scripts/ ← build & maintenance scripts (no one-shot migrations)
63
+ │ ├── build.ts ← src/ → dist/ (tsc type-strip + copy everything else 1:1)
64
+ │ ├── verify.ts ← static consistency gate (runs at end of build; `bun run verify`)
65
+ │ ├── sync-docs.ts ← mirror dist/documentation → docs/ (CDN-rewritten; `bun run docs`)
66
+ │ ├── lib/mirror.ts ← shared docs/ mirror transform (sync-docs + verify compare against it)
67
+ │ ├── lib/search-index.ts ← docs search index generator (build.ts regenerates it every build)
68
+ │ ├── lib/skill.ts ← SKILL.md generation core: frontmatter parser + index renderer (pure)
69
+ │ ├── lib/skill-files.ts ← dist/SKILL.md index generator from src/SKILL_tpl.md + skill frontmatter (build.ts regenerates every build)
70
+ │ ├── create-screenshots.ts ← parallel default-state screenshots for agent inspection
71
+ │ ├── lib/audit.ts ← undefined-utility audit (used by verify)
72
+ │ ├── lib/snippets.ts ← shared snippet drift/replace logic (syncers + verify)
73
+ │ ├── sync-css-snippets.ts ← re-embed component CSS into doc pages after edits
74
+ │ ├── sync-js-snippets.ts ← re-embed component JS into doc pages after edits
75
+ │ ├── push.sh ← commit + push dev → main (non-release)
76
+ │ └── deploy.sh ← release: version bump, changelog, tag, GitHub release
77
+ ├── tests/ ← UI tests (Vitest browser mode + Playwright)
78
+ │ ├── helpers.ts ← loads real doc pages in a same-origin iframe
79
+ │ ├── ui.test.ts ← end-to-end tests of the actual site UI
80
+ │ └── e2e/ ← per-component smoke tests (plain Playwright)
81
+ │ ├── run.ts ← `bun run e2e` runner: every *.e2e.ts file
82
+ │ ├── server.ts ← Bun static server exposing /dist and /tests/e2e
83
+ │ └── accordion.e2e-{fixture.html,ts} ← fixture + test for one component
84
+ ├── vitest.config.ts ← browser-mode test config (root = repo root)
85
+ ├── Makefile ← setup / dev / test / e2e / build shortcuts (wrappers for bun scripts)
86
+ │
87
+ └── AGENTS.md ← this file (maintainer instructions)
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Critical rules
93
+
94
+ ### The verifier is authoritative
95
+
96
+ `bun run verify` is the single source of truth for "is this done?". Its
97
+ output — including every `fix:` line it prints on failure — overrides any
98
+ interpretation of this file: when prose and verifier disagree, the verifier
99
+ wins and its `fix:` line is the work to do. A build is only ever as good as
100
+ its last green `verify` run; there is no flag that skips the gate. (The
101
+ warn-ratchets it still reports, e.g. `STATE_API_LEGACY`, are named migration
102
+ debt with an explicit removal path — not permission to ignore them.) The
103
+ full rationale lives in [ARCH.md](ARCH.md).
104
+
105
+ ### Each component owns its dialog
106
+
107
+ `dialog.js`'s init claims plain `<dialog>` elements for backdrop-click close —
108
+ via a `dialog:not(.alert-dialog):not(.sheet):not(.command):not([data-init])`
109
+ selector. **Any component that ships its own `<dialog class="…">` with custom
110
+ behavior must be `:not()`-excluded there**, or dialog.js (loaded before every
111
+ component script on all doc pages) stamps `data-init` first and the real
112
+ owner's init silently skips the element — the docs search palette was dead
113
+ exactly this way once. `verify`'s `dialog ownership boundary` gate enforces
114
+ the exclusion list for the three dialog owners (alert-dialog, sheet, command);
115
+ extend the list in both places when a fourth appears.
116
+
117
+ The docs header search is the shipped command component itself: clicking the
118
+ input (or ⌘/Ctrl+K) opens `<dialog class="command" id="docs-palette">`, fed by
119
+ the build-time index (`scripts/lib/search-index.ts` → `js/search-index.js`,
120
+ NAV entries + every page `<h2>` with TOC-compatible ids). The trigger is
121
+ click/Enter only — never `focus`: `dialog.close()` restores focus to the
122
+ opener synchronously, which would bounce the palette open again.
123
+
124
+ ### README ↔ index parity
125
+
126
+ `README.md` and `src/documentation/index.html` describe the same system to
127
+ humans and to browser users — **when you update either, update the other in
128
+ the same commit.** They share two claims that drift independently if you
129
+ forget:
130
+
131
+ - **Pillar set** — the README intro bullets (`- **Name** — …`, under "What
132
+ this is" and before "Quick start") and the index pillar cards
133
+ (`<h3 class="card-title">`) must list the same set of pillars.
134
+ - **Hero sentence** — the intro paragraph wording must match
135
+ (`"No build step for consumers — dist/ is committed and ready to use
136
+ as-is."` once diverged to a bare `"No build step."`).
137
+ - **CDN dogfooding** — the index carries the "this site loads its assets
138
+ from the jsDelivr CDN, exactly as README's Via CDN quick start shows — if
139
+ it renders, the CDN install works" note, which must stay true if the CDN
140
+ strategy in [`scripts/lib/mirror.ts`](scripts/lib/mirror.ts) ever changes.
141
+ - **CSS-only count** — the "**N of M components need no JavaScript**" line
142
+ appears in both README.md and the index; `verify`'s
143
+ `README CSS-only stat` / `index CSS-only stat` gates compare it against
144
+ the actual `src/components/` tree (a component `.ts` = ships a `.js`).
145
+ Update both files together whenever a component gains or loses behavior.
146
+
147
+ `verify` enforces the pillar set (`README ↔ index parity`, hard gate) and the
148
+ **`README ↔ index commit window`** gate: if the two files' last-touch commits
149
+ are neither identical nor within **15 minutes** of each other, the build fails
150
+ as "un-synced edit" — forcing you to land them together. Wording parity itself
151
+ is yours to maintain (no automated text diff; the window is the backstop).
152
+
153
+ ### Changelog (two-commit rule)
154
+
155
+ Every version in `package.json` needs an entry in
156
+ [`src/documentation/changelog.html`](src/documentation/changelog.html) listing
157
+ **all commit messages** of that release (deploy.sh generates them since the
158
+ last tag; v0.7.14 was the first release after the fork from
159
+ codylindley/shadcn-html, so its entry documents the fork). Each entry shows
160
+ either the release date badge or — once the version is actually **committed** —
161
+ the short git hash of the commit that added the entry, embedded as
162
+ `<code class="changelog-hash">abc1234</code>`. The hash is unknowable before
163
+ that commit exists, so authoring an entry is inherently **two commits**:
164
+
165
+ 1. commit the entry (with date badge),
166
+ 2. commit that commit's short hash into the entry.
167
+
168
+ `verify`'s `changelog ↔ version` gate fails when the committed `package.json`
169
+ version has no entry (or a hash-less / message-less entry for that version);
170
+ its `fix:` line states the two-commit instruction. `scripts/deploy.sh`
171
+ automates the whole flow. Legacy entries (v0.7.13-alpha and earlier) keep
172
+ date-only badges — the hash requirement applies from this rule onward.
173
+
174
+ ### Native web platform first
175
+
176
+ Every component starts from a native HTML element or browser API. If the browser
177
+ can do it, we don't write JavaScript for it.
178
+
179
+ **HTML elements & attributes**
180
+
181
+ - Use `<dialog>` for modals — not divs with JS show/hide
182
+ - Use `popover` API for dropdowns, tooltips, toasts — not JS positioning
183
+ - Use `popover="hint"` for tooltips — not `popover="auto"` (hints don't
184
+ close other popovers)
185
+ - Use `<details>/<summary>` for accordions — not JS toggle logic
186
+ - Use `<details name="group">` for exclusive (single-open) accordions — not
187
+ JS that closes siblings
188
+ - Use `commandfor` / `command` attributes for declarative button→dialog/popover
189
+ triggers — not JS click handlers that call `showModal()` or `togglePopover()`
190
+ - Use `<progress>` for completion indicators — not div-based progress bars
191
+ - Use `<meter>` for scalar values in a range — not custom gauge components
192
+ - Use `<output>` for computed/live results — not manual `aria-live` regions
193
+ - Use `inert` attribute to disable interaction on background content — not
194
+ JS focus traps or `aria-hidden` toggling
195
+ - Use `loading="lazy"` for images/iframes — not JS lazy load libraries
196
+ - Use `autofocus` in dialogs/popovers — not JS `.focus()` calls
197
+ - Use `inputmode` for mobile keyboard hints — not separate input types
198
+ - Use `enterkeyhint` for mobile Enter key labels (`search`, `send`, `go`)
199
+ - Use `autocomplete` with proper field names — not custom autofill
200
+ - Use `<datalist>` for native type-ahead suggestions — not custom dropdowns
201
+ - Use `fetchpriority` for resource priority hints (`high`/`low`)
202
+ - Use `disabled` / `readonly` for native form states — not JS class toggling
203
+
204
+ **CSS**
205
+
206
+ - Use `@starting-style` + `transition-behavior: allow-discrete` for
207
+ enter/exit animations on `display: none` elements — not JS class toggling
208
+ - Use CSS anchor positioning for popover placement — not Floating UI / Popper
209
+ - Use `::backdrop` + `backdrop-filter` for dialog/sheet overlays — not
210
+ JS-managed overlay divs or canvas blur
211
+ - Use `:has()` for parent-state reactions — not JS class propagation
212
+ - Use `:focus-visible` for keyboard-only focus rings — not JS focus detection
213
+ - Use `:user-valid` / `:user-invalid` for post-interaction validation
214
+ styling — not JS blur listeners with class toggling
215
+ - Use `field-sizing: content` for auto-growing textareas — not JS resize
216
+ - Use `oklch()` and relative color syntax for wide-gamut, derived colors — not
217
+ hardcoded hex/hsl palettes
218
+ - Use `color-mix(in oklch, ...)` for hover/disabled color derivation — not
219
+ Sass `darken()`/`lighten()` or hardcoded variants
220
+ - Use `light-dark()` for inline dark mode values — not media queries or
221
+ class toggles when `color-scheme` is already set
222
+ - Use `color-scheme` property for dark mode browser defaults — not all-manual
223
+ dark overrides on every native element
224
+ - Use `accent-color` for theming native form controls — not custom replacements
225
+ - Use `text-wrap: balance` for headings and labels — not JS text-balancing
226
+ - Use `text-wrap: pretty` for body text orphan prevention — not manual `&nbsp;`
227
+ - Use `overscroll-behavior: contain` on scroll containers inside overlays — not
228
+ JS scroll-lock libraries
229
+ - Use `scroll-snap` for carousel/slider snap points — not JS snap calculations
230
+ - Use `scrollbar-gutter: stable` to prevent layout shift from scrollbars — not
231
+ padding hacks
232
+ - Use individual transform properties (`rotate`, `scale`, `translate`) — not
233
+ compound `transform` strings
234
+ - Use CSS nesting, `@layer`, container queries — not preprocessors
235
+ - Use `aspect-ratio` for intrinsic ratios — not padding-bottom hacks
236
+ - Use `content-visibility` for expand/collapse transitions — not JS lazy rendering
237
+ - Use `interpolate-size: allow-keywords` for animating to `auto` height — not
238
+ JS measurement or `max-height` hacks
239
+ - Use `@property` for typed, animatable custom properties — not JS animation
240
+ of CSS values
241
+ - Use scroll-driven animations (`animation-timeline: scroll()` / `view()`) for
242
+ scroll-linked effects — not scroll listeners or IntersectionObserver
243
+ - Use View Transitions API for smooth DOM state changes — not JS crossfades
244
+ - Use `@supports` for CSS feature detection — not Modernizr or JS detection
245
+ - Use logical properties (`margin-inline`, `padding-block`) for RTL support — not
246
+ separate LTR/RTL stylesheets
247
+ - Use subgrid for aligned child layouts — not manually synchronized columns
248
+ - Use dynamic viewport units (`dvh`, `svh`, `lvh`) — not JS `innerHeight` hacks
249
+ - Use CSS math functions (`clamp()`, `min()`, `max()`) for responsive sizing — not
250
+ JS resize calculations
251
+ - Use `:is()` / `:where()` for selector grouping — not repeated selectors
252
+ - Use `hanging-punctuation` for optical quote alignment — not negative text-indent
253
+ - Use `@layer` + descriptive prefixed class names (`card-header`, `slider-track`) for
254
+ style scoping — not `@scope` (generic class names lose context for AI generation)
255
+ or Shadow DOM
256
+ - Use `@media (scripting)` for no-JS progressive enhancement — not `<noscript>` alone
257
+
258
+ **Accessibility (REQUIRED)**
259
+
260
+ - Use `prefers-reduced-motion: reduce` to suppress/simplify all animations — not
261
+ ignoring motion preferences (this is an accessibility requirement, not optional)
262
+ - Use `prefers-contrast: more` to increase contrast when requested
263
+ - Use `forced-colors: active` to support Windows High Contrast Mode with system colors
264
+ - Use `prefers-color-scheme` for automatic dark mode defaults
265
+
266
+ **JavaScript (only when HTML/CSS cannot express it)**
267
+
268
+ - Use Web Animations API (`el.animate()`) for imperative animations — not CSS
269
+ class toggling when JS needs to coordinate timing
270
+ - Use `Intl` APIs (`DateTimeFormat`, `NumberFormat`, `RelativeTimeFormat`,
271
+ `ListFormat`) for locale-aware formatting — not moment.js or date-fns
272
+ - Use native Drag and Drop API for reordering — not SortableJS or drag libraries
273
+ - Use `CustomEvent` for component-to-component communication — not framework
274
+ event systems
275
+ - Use `element.checkVisibility()` for visibility detection — not manual
276
+ offset calculations
277
+ - Use `IntersectionObserver` for viewport-entry detection — not scroll listeners
278
+ with `getBoundingClientRect()`
279
+ - Use `ResizeObserver` for element size changes — not window resize listeners
280
+ - Use `MutationObserver` for DOM change reactions — not polling loops
281
+ - Use `navigator.clipboard` for clipboard access — not `document.execCommand('copy')`
282
+ - Use `CloseWatcher` for platform close signals in custom UI — not manual
283
+ Escape key listeners
284
+ - Use `AbortController` for canceling fetches/listeners — not boolean flags
285
+ - Use `FormData` for form serialization — not manual value collection loops
286
+ - Use `structuredClone()` for deep cloning — not `JSON.parse(JSON.stringify())`
287
+ - Use `ElementInternals` for custom form elements — not hidden input proxies
288
+ - Use Navigation API for SPA routing — not History API hacks
289
+
290
+ JavaScript is only for behavior that HTML and CSS cannot express: keyboard
291
+ navigation patterns, focus management, and state coordination between elements.
292
+ Use modern ECMAScript (ES modules, arrow functions, `const`/`let`, etc.) —
293
+ no libraries, no frameworks.
294
+
295
+ All `querySelectorAll` loops that add event listeners **must** guard against
296
+ double-initialization using `:not([data-init])` in the selector and setting
297
+ `element.dataset.init = ''` as the first line inside the loop.
298
+
299
+ Component JS files wrap initialization in an `init()` function, call it once,
300
+ then use a `MutationObserver` to auto-initialize new elements after SPA
301
+ navigation or dynamic DOM changes:
302
+
303
+ ```js
304
+ function init() {
305
+ document.querySelectorAll('.my-component:not([data-init])').forEach((el) => {
306
+ el.dataset.init = '';
307
+ el.addEventListener('click', () => { /* … */ });
308
+ });
309
+ }
310
+
311
+ init();
312
+ new MutationObserver(init).observe(document, { childList: true, subtree: true });
313
+ ```
314
+
315
+ For document-level event delegation (no per-element loop), use a global flag:
316
+ ```js
317
+ if (!document.__myComponentInit) {
318
+ document.__myComponentInit = true;
319
+ document.addEventListener('click', (e) => { /* … */ });
320
+ }
321
+ ```
322
+
323
+ ### No window globals (REQUIRED)
324
+
325
+ Never define or read application globals on `window` — use `globalThis`, and keep
326
+ every name under the single namespace `globalThis._defussShadcn`:
327
+
328
+ ```js
329
+ globalThis._defussShadcn = globalThis._defussShadcn || {}; // idempotent bootstrap
330
+ globalThis._defussShadcn.toast = { show, success, dismiss }; // public imperative API
331
+ ```
332
+
333
+ - Components expose their State API as `_defussShadcn.{name}Api` / `{name}States`
334
+ (see "State API" below); a component's additional public imperative API lives
335
+ under `_defussShadcn.{name}` — never as a bare global. Example: the toast
336
+ factory is `_defussShadcn.toast.show(...)`, **not** `window.toast = { … }`.
337
+ - Doc-site-only scripts (not shipped) share the same discipline under
338
+ `_defussShadcn.docs` (theme registry, SPA hooks: `THEMES`, `applyTheme`,
339
+ `onPageReady`, …).
340
+ - Third-party CDN globals (`lucide`, `marked`) are owned by their vendors — read
341
+ them via `globalThis.*`; never assign to `window`.
342
+
343
+ **Why:** `window` is a browser-only alias; `globalThis` is the one canonical
344
+ global object and works unchanged in Workers and other runtimes (see the
345
+ isomorphic rule). Scoping everything under `_defussShadcn` keeps a
346
+ copy-paste/CDN-shipped system collision-free on hosts we do not control.
347
+ `scripts/verify.ts` fails the build on any `window.x =` assignment in `src/`.
348
+
349
+ ### Each component is a self-contained folder
350
+
351
+ Each component at `dist/components/{name}/` contains:
352
+ - `component-skill.md` — component skill: HTML structure, attributes, ARIA, and usage notes
353
+ - `{name}.css` — the component stylesheet (edit directly)
354
+ - `{name}.js` — interaction JS (only for interactive components, edit directly)
355
+
356
+ The component skill `.md` file documents **how to build the HTML**. The `.css` and `.js` files
357
+ are the actual implementation — edit them directly, no build step needed.
358
+
359
+ ### State API (REQUIRED for every JS component)
360
+
361
+ Components with a `.js` file have observable UI states (open/closed, collapsed,
362
+ selected…). Every such component must expose its states **by name** so agents and
363
+ tests can drive them without knowing the implementation:
364
+
365
+ ```js
366
+ document.querySelector('#x').api.setState('open', { /* config */ });
367
+ document.querySelector('#x').api.getState(); // → { name: 'open', config: { … } }
368
+ ```
369
+
370
+ **Required shape of `{name}.js`** (verify.ts enforces these markers by regex;
371
+ `accordion.ts` and `dialog.ts` are the reference implementations):
372
+
373
+ 1. **Preamble** — import the shared helper (single source in
374
+ `src/shared/state-api.ts`; `build.ts` inlines it into the shipped `.js`,
375
+ so dist components stay isolated single files):
376
+ ```js
377
+ import { defussGlobals } from '../../shared/state-api.js';
378
+ const _defussShadcn = defussGlobals();
379
+ ```
380
+ 2. **State list** — `const {name}States = ['default', …]` — `'default'` must be
381
+ the first entry and always be one of the declared states. Every component
382
+ starts in its `default` state unless markup declares otherwise.
383
+ 3. **`triggerStateChange(el, stateName, config)`** — the only function that
384
+ touches the DOM for a state change; `switch`/dispatch over the declared states.
385
+ 4. **Registry API** — `export const {name}Api = { setState(el, name, config), getState(el) }`
386
+ with the element passed explicitly; reject unknown state names by throwing.
387
+ Register both globals:
388
+ ```js
389
+ globalThis._defussShadcn.{name}Api = {name}Api;
390
+ globalThis._defussShadcn.{name}States = {name}States;
391
+ ```
392
+ 5. **Per-instance binding** inside `init()`, on each element the component
393
+ initializes (state lives **on the element** — `dataset.stateName` +
394
+ `_stateConfig` — never in module scope; 26 components share one page and each
395
+ instance may hold a different state):
396
+ ```js
397
+ el.api = {
398
+ setState: (stateName, config) => {name}Api.setState(el, stateName, config),
399
+ getState: () => {name}Api.getState(el),
400
+ };
401
+ ```
402
+ 6. **Document the states** in the component skill (`component-skill.md` →
403
+ `## States` section): state names, meaning, and one `api.setState(...)` example.
404
+ 7. **Every state must be visually verifiable** — four artifacts cover each
405
+ declared state (verify.ts parses `{name}States = [...]` and checks all four):
406
+ - **Doc page**: `src/documentation/{name}.html` has a `States` section with
407
+ `<code>{state}</code>` per state, AND the state-bearing demo element
408
+ carries `data-state-demo` (the anchor `create-screenshots.ts` drives via
409
+ `api.setState()` to capture `screenshots/{mode}/{name}-{state}.png`)
410
+ - **Screenshots**: one PNG per state per mode (light + dark), default state
411
+ as `{name}.png` from the first `.preview`
412
+ - **Skill**: the state name appears in the `## States` section
413
+ - **E2E**: the state name string appears in `{name}.e2e.ts` (a
414
+ `setState(name)` assertion)
415
+ Adding a state without all four fails the build.
416
+
417
+ Types for the globals live in `src/types/defuss-shadcn.d.ts` — extend it when the
418
+ contract grows; never re-declare the globals inside a component file.
419
+
420
+ `scripts/verify.ts` checks all markers for every new JS component (hard fail).
421
+ Legacy components in its `STATE_API_LEGACY` list warn only until migrated —
422
+ remove a name from the list in the same commit that migrates the component.
423
+
424
+ ### Theme radius consistency (REQUIRED)
425
+
426
+ A theme is **one identity, two palettes** — light and dark are the same
427
+ theme, so its shape (`radius`) must be **identical in both modes**. If Doom64
428
+ is hard-square in dark mode it is hard-square in light mode; if ChatGPT is
429
+ pill-round in dark, light is pill-round too. A theme that only declares
430
+ `radius` in one mode silently reverts the other to the default rounding, so
431
+ toggling dark mode "changes the theme's shape" — reported (Doom64, Retro
432
+ Arcade) and now gated.
433
+
434
+ - In `src/documentation/js/themes.ts`, declare `radius` in **both** the
435
+ `light` and `dark` block of every theme, with the same value.
436
+ - `verify`'s `theme radius consistency` gate fails the build otherwise.
437
+ - Same principle for **contrast**: every theme's sidebar text pairs must
438
+ reach WCAG AA (≥ 4.5) in *both* modes — see the `theme sidebar contrast`
439
+ gate. Re-tune one mode's color and you own both.
440
+
441
+ ### Tokens are the source of truth for design values
442
+
443
+ `dist/theme/default-semantic-tokens.css` defines all CSS custom properties. These must match
444
+ the shape of tweakcn.com theme exports so themes are drop-in compatible.
445
+
446
+ The token file provides:
447
+ - Color pairs (surface + foreground) for light and dark modes
448
+ - `--radius` (base) — derived values (`--radius-sm/md/lg/xl`) are computed in the token file
449
+ - Shadow scale and composition tokens
450
+ - Font stacks (generic — overridden by doc site)
451
+ - Spacing and tracking
452
+
453
+ #### Tokens & Theme
454
+
455
+ ##### Token boundary rule (CRITICAL)
456
+
457
+ **Components must only use the tokens that exist in the TweakCN export shape.**
458
+
459
+ - `default-semantic-tokens.css` defines the complete set of available CSS custom properties.
460
+ - No new custom properties may be added to `default-semantic-tokens.css` beyond what TweakCN provides.
461
+ - Components CSS (`dist/components/{name}/{name}.css`)
462
+ must reference only these tokens via `var(--*)`.
463
+ - If a component needs a color that has no token (e.g., status colors like
464
+ green/amber/blue), use a hardcoded CSS value directly in the component CSS.
465
+ Do NOT invent a new `--*` token for it.
466
+
467
+ ###### Why
468
+ TweakCN themes are drop-in replacements. If we add tokens that TweakCN doesn't
469
+ export, swapping a theme will leave those tokens undefined and break components.
470
+ The token file must be a pure subset of what TweakCN produces.
471
+
472
+ ###### Allowed token list (exhaustive)
473
+
474
+ **Color pairs** (surface + foreground):
475
+ `--background`, `--foreground`, `--card`, `--card-foreground`, `--popover`,
476
+ `--popover-foreground`, `--primary`, `--primary-foreground`, `--secondary`,
477
+ `--secondary-foreground`, `--muted`, `--muted-foreground`, `--accent`,
478
+ `--accent-foreground`, `--destructive`, `--destructive-foreground`
479
+
480
+ **Utility tokens**: `--border`, `--input`, `--ring`
481
+
482
+ **Sidebar tokens**: `--sidebar`, `--sidebar-foreground`, `--sidebar-primary`,
483
+ `--sidebar-primary-foreground`, `--sidebar-accent`, `--sidebar-accent-foreground`,
484
+ `--sidebar-border`, `--sidebar-ring`
485
+
486
+ **Chart tokens**: `--chart-1` through `--chart-5`
487
+
488
+ **Typography**: `--font-sans`, `--font-serif`, `--font-mono`
489
+
490
+ **Radius**: `--radius` (base), `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl` (derived)
491
+
492
+ **Shadow scale**: `--shadow-2xs` through `--shadow-2xl`
493
+
494
+ **Spacing**: `--spacing`
495
+
496
+ **Tracking**: `--tracking-normal`
497
+
498
+ **Nothing else.** If a value is not on this list, it cannot be a `var(--*)` reference
499
+ in component CSS. Use a literal CSS value instead.
500
+
501
+ ###### Token shape (tweakcn compatible)
502
+
503
+ `dist/theme/default-semantic-tokens.css` must match the shape of theme exports from tweakcn.com:
504
+
505
+ ###### `:root` block provides:
506
+ - Color pairs: `--primary` / `--primary-foreground` (and all other semantic pairs)
507
+ - Utility tokens: `--border`, `--input`, `--ring`
508
+ - Sidebar tokens: `--sidebar`, `--sidebar-foreground`, etc.
509
+ - Chart tokens: `--chart-1` through `--chart-5`
510
+ - Font stacks: `--font-sans`, `--font-serif`, `--font-mono` (generic defaults)
511
+ - Base radius: `--radius`
512
+ - Derived radius: `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl` (computed from `var(--radius)`)
513
+ - Shadow scale: `--shadow-2xs` through `--shadow-2xl`
514
+ - Spacing: `--spacing`
515
+ - Tracking: `--tracking-normal`
516
+
517
+ ###### `.dark` block overrides:
518
+ - All color pairs for dark mode
519
+ - Same tokens, different values
520
+
521
+ ###### Derived radius values
522
+
523
+ Tweakcn themes provide `--radius` and derived values. The derived values use additive offsets:
524
+ ```css
525
+ --radius-sm: calc(var(--radius) - 4px);
526
+ --radius-md: calc(var(--radius) - 2px);
527
+ --radius-lg: var(--radius);
528
+ --radius-xl: calc(var(--radius) + 4px);
529
+ ```
530
+
531
+ These exist as real CSS custom properties because component CSS uses `var(--radius-md)` directly.
532
+
533
+ ### Documentation site architecture
534
+
535
+ The doc site dev server runs on `http://localhost:3000/` via `bun run dev` (Vite).
536
+ When testing, use the existing dev server — don't start a new one.
537
+
538
+ The doc site is a **SPA-style multi-page app**. `layout.js` loads synchronously in
539
+ `<head>` and provides:
540
+
541
+ - `<site-header>` — renders the fixed header (logo, GitHub link, dark mode toggle)
542
+ - `<site-nav>` — renders the sidebar from a centralized `NAV` array, auto-detecting the active page
543
+ - **SPA router** — intercepts nav clicks, fetches HTML, swaps `<main>` content
544
+ without full-page reloads (uses View Transitions API for smooth crossfade)
545
+
546
+ **Sidebar nav is centralized in `layout.js`.** To add or reorder nav links, edit
547
+ the `NAV` array and the `BUILT` set in that one file — individual HTML pages
548
+ do not contain nav markup.
549
+
550
+ Each HTML page duplicates the full list of component CSS `<link>` tags in `<head>`
551
+ and component JS `<script>` tags at end of `<body>`. When adding a new component,
552
+ these imports must be added to **every** HTML file.
553
+
554
+ ---
555
+
556
+ ## Adding a new component
557
+
558
+ ### Reference sites (REQUIRED)
559
+
560
+ Before writing any component skill or documentation page, **fetch and review** the component on these sites:
561
+
562
+ #### Feature checklist (what to build)
563
+ 1. **shadcn/ui** → `https://ui.shadcn.com/docs/components/{name}`
564
+ 2. **Basecoat UI** → `https://basecoatui.com/components/{name}/`
565
+
566
+ These define the completeness bar. Every variant, size, state, and composition pattern
567
+ shown on those pages must be accounted for in the component skill and doc page — adapted
568
+ to our semantic HTML / CSS custom property / vanilla JS model. Do not copy their markup;
569
+ use them as a feature checklist.
570
+
571
+ #### Native implementation (how to build it)
572
+ 3. **WAI-ARIA APG** → `https://www.w3.org/WAI/ARIA/apg/patterns/{name}/` — canonical keyboard navigation and ARIA patterns
573
+ 4. **MDN Web Docs** → `https://developer.mozilla.org/` — authoritative reference for HTML elements, CSS properties, and JS APIs
574
+ 5. **Open UI** → `https://open-ui.org` — W3C community group defining native component standards
575
+ 6. **Base UI** → `https://base-ui.com/react/components/{name}` — headless component architecture (closest to our approach in spirit)
576
+
577
+ Always prefer native browser APIs over JS workarounds. Check MDN for the latest
578
+ support status of newer APIs (`popover`, anchor positioning, `@starting-style`, etc.).
579
+
580
+ ### Steps
581
+
582
+ 1. **Create the component folder** → `dist/components/{name}/`
583
+
584
+ 2. **Write the component skill** → `dist/components/{name}/component-skill.md`
585
+ - Follow the template: Native basis → Native Web APIs → Structure → Variants → Sizes → ARIA → Notes
586
+ - Documents the HTML pattern, not CSS/JS (those are the actual files)
587
+ - Cross-check variants, sizes, and states against the reference sites above
588
+
589
+ 3. **Write the CSS** → `dist/components/{name}/{name}.css`
590
+ - Edit directly — no build step
591
+
592
+ 4. **Write the JS** (if interactive) → `dist/components/{name}/{name}.js`
593
+ - Plain ES module — wrap initialization in an `init()` function
594
+ - Call `init()` immediately, then add `new MutationObserver(init).observe(document, { childList: true, subtree: true });`
595
+ - This auto-initializes new elements after SPA navigation or dynamic DOM changes
596
+ - No `export`, no `window.onPageReady` — just the init function + MutationObserver
597
+
598
+ 5. **Create the doc page** → `dist/documentation/{name}.html`
599
+ - Copy an existing component page as template (e.g., badge.html)
600
+ - Add `<link rel="stylesheet" href="../components/{name}/{name}.css">` to the head
601
+ - Add `<script type="module" src="../components/{name}/{name}.js"></script>` if interactive
602
+ - Replace demo content with working examples
603
+
604
+ 6. **Update layout.js** → add the component to the `NAV` array and `BUILT` set
605
+ in `dist/documentation/js/layout.js` (this is the single source of truth for sidebar nav)
606
+
607
+ 7. **Add CSS/JS imports to all HTML pages** → add the new component's `<link>` and
608
+ `<script>` tags to every HTML file in `dist/documentation/`
609
+
610
+ 8. **Sync inline source snippets** → run `bun run sync-snippets` to replace the inline
611
+ `<pre><code>` blocks in every doc page with the actual contents of each component's
612
+ `.css` and `.ts` files. This must be done after any change to a component's CSS or
613
+ JS — not just for new components.
614
+
615
+
616
+ # Component Skill Editing
617
+
618
+ ## Reference sites (REQUIRED)
619
+
620
+ Before writing or updating any component skill, fetch and review the component:
621
+
622
+ ### Feature checklist (what to build)
623
+ 1. **shadcn/ui** → `https://ui.shadcn.com/docs/components/{name}`
624
+ 2. **Basecoat UI** → `https://basecoatui.com/components/{name}/`
625
+
626
+ Every variant, size, state, and composition pattern shown on those pages must be
627
+ accounted for in the component skill — adapted to our semantic HTML / CSS custom property /
628
+ vanilla JS model.
629
+
630
+ ### Native implementation (how to build it)
631
+ 3. **WAI-ARIA APG** → `https://www.w3.org/WAI/ARIA/apg/patterns/{name}/` — canonical keyboard navigation and ARIA patterns
632
+ 4. **MDN Web Docs** → `https://developer.mozilla.org/` — authoritative reference for HTML elements, CSS properties, and JS APIs
633
+ 5. **Open UI** → `https://open-ui.org` — W3C community group defining native component standards
634
+ 6. **Base UI** → `https://base-ui.com/react/components/{name}` — headless component architecture reference
635
+
636
+ Always prefer native browser APIs over JS workarounds. Check MDN for support
637
+ status of newer APIs (`popover`, anchor positioning, `@starting-style`, etc.).
638
+
639
+ ## Component skill template
640
+
641
+ Component skills document **how to build the HTML** for a component. CSS and JS live in
642
+ their own files alongside the skill — edit `.css` and `.js` directly.
643
+
644
+ Every component skill **must open with a YAML frontmatter block** — it is the machine-readable
645
+ discovery layer `dist/SKILL.md` (the agent entry point) is generated from. `verify`'s
646
+ `skill frontmatter` gate fails without it, and `SKILL.md ↔ skills` fails when the generated
647
+ index lags (rebuild with `bun run build`):
648
+
649
+ ```markdown
650
+ ---
651
+ name: Dialog
652
+ why: Native <dialog> + showModal(): focus trap, Escape, ::backdrop, and inert background are browser-provided.
653
+ when: Modals for forms, detail views, or previews — unless the answer is mandatory (then alert-dialog).
654
+ where: dist/components/dialog/dialog.css + dist/components/dialog/dialog.js
655
+ supportedStates: default, open
656
+ ---
657
+ ```
658
+
659
+ - **name** — display name (Title Case) · **why** — what its native basis buys · **when** — which
660
+ use it for, and when to pick a sibling instead · **where** — the shipped files
661
+ (`dist/components/{name}/{name}.css` + `.js` if interactive) · **supportedStates** — the exact
662
+ State API names (matches `{name}States`, `default` first; CSS-only components: `default`).
663
+
664
+ Every component skill must include these sections in order:
665
+
666
+ 0. **Frontmatter** — `name` / `why` / `when` / `where` / `supportedStates` (see above — REQUIRED)
667
+ 1. **Native basis** — which HTML element/API it builds on
668
+ 2. **Native Web APIs** — bulleted list of significant platform APIs with MDN links (see format below)
669
+ 3. **Structure** — complete HTML markup with all attributes
670
+ 4. **Variants** — variant table mapping `data-variant` values to visual behavior
671
+ 5. **Sizes** — size table (if applicable)
672
+ 6. **ARIA** — accessibility attributes table
673
+ 7. **Notes** — edge cases, composition tips, caveats
674
+
675
+ Do NOT include CSS or JavaScript code blocks in the component skill. The `.css` and `.js` files
676
+ in the same folder are the source of truth for styles and behavior.
677
+
678
+ ## Native Web APIs section format
679
+
680
+ Every component skill must include a `## Native Web APIs` section immediately after
681
+ `## Native basis`. This section lists the significant web platform APIs the component
682
+ relies on, with MDN links. Use this format:
683
+
684
+ ```markdown
685
+ ## Native Web APIs
686
+ - [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) — native modal with focus trap and Escape-to-close
687
+ - [`@starting-style`](https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style) — entry animation starting values
688
+ ```
689
+
690
+ ### What to include
691
+ - HTML elements that provide core behavior (`<dialog>`, `<details>`, `<summary>`, `<progress>`, `<meter>`, `<output>`)
692
+ - HTML attributes that replace JS (`popover`, `popover="hint"`, `commandfor`/`command`, `inert`, `autofocus`, `loading="lazy"`)
693
+ - Browser APIs (`Popover API`, `showModal()`, `View Transitions API`, `Navigation API`)
694
+ - Significant CSS features (`CSS Anchor Positioning`, `@starting-style`, `::backdrop`, `::details-content`, Container Queries, `:has()`, `field-sizing: content`, `interpolate-size`, `content-visibility`, `@property`, `scroll-driven animations`, `light-dark()`, `color-mix()`)
695
+ - Accessibility features (`prefers-reduced-motion`, `prefers-contrast`, `forced-colors`)
696
+ - JS APIs used (`IntersectionObserver`, `ResizeObserver`, `MutationObserver`, `Clipboard API`, `Intl.*`, `CloseWatcher`, `AbortController`, `FormData`, `structuredClone()`)
697
+ - WAI-ARIA patterns when the component follows a specific APG pattern
698
+
699
+ ### What to exclude
700
+ - Basic DOM methods (`querySelector`, `classList`, `addEventListener`)
701
+ - Standard CSS layout (`flexbox`, `grid` unless using subgrid/container queries)
702
+ - Common pseudo-classes (`:hover`, `:disabled`) unless component-defining (`:focus-visible`, `:has()`)
703
+
704
+ ## CSS authoring conventions
705
+
706
+ ### `@layer components`
707
+ All component CSS must be wrapped in `@layer components { ... }`. This establishes
708
+ explicit cascade priority: tokens → components → utilities. Never write component CSS
709
+ outside a layer.
710
+
711
+ ### Native CSS nesting
712
+ Use `&` nesting for all related selectors. Group variants, sizes, states, and child
713
+ element styles inside the base selector:
714
+ ```css
715
+ @layer components {
716
+ .btn {
717
+ /* base styles */
718
+ &[data-variant="outline"] { /* ... */ }
719
+ &[data-size="sm"] { /* ... */ }
720
+ &:hover { /* ... */ }
721
+ &:disabled { /* ... */ }
722
+ & svg { /* child styles */ }
723
+ }
724
+ }
725
+ ```
726
+
727
+ ### Modern CSS features (use where applicable)
728
+ - **CSS anchor positioning** — for popover/dropdown/combobox placement (`position-anchor`, `anchor()`, `position-try-fallbacks: flip-block`). No JS positioning code needed.
729
+ - **`:has()` selector** — for parent/sibling state reactions (e.g., label styling when input is focused)
730
+ - **`field-sizing: content`** — for auto-growing textareas with zero JS
731
+ - **Container queries** — for components that adapt to their container width (`container-type: inline-size`, `@container`)
732
+ - **`@starting-style`** — for enter animations on elements added to DOM or moving to top layer
733
+ - **`interpolate-size: allow-keywords`** — for smooth height-to-`auto` transitions (accordion, collapsible)
734
+ - **`content-visibility`** — for expand/collapse transitions with `allow-discrete`
735
+ - **`@property`** — for typed, animatable custom properties (progress rings, gradient transitions)
736
+ - **`color-mix(in oklch, ...)`** — for deriving hover/disabled states from token colors
737
+ - **`light-dark()`** — for inline dark mode values when `color-scheme` is set
738
+ - **`accent-color`** — for theming native form controls (checkbox, radio, range, progress)
739
+ - **Scroll-driven animations** — `animation-timeline: scroll()` / `view()` for scroll-linked effects
740
+ - **View Transitions API** — `startViewTransition()` for smooth DOM state changes
741
+ - **Logical properties** — `margin-inline`, `padding-block`, `inset-inline-start` for RTL support
742
+ - **Subgrid** — `grid-template-columns: subgrid` for child alignment to parent grid tracks
743
+ - **`:is()` / `:where()`** — selector grouping; `:where()` has zero specificity (ideal for resets)
744
+ - **`@scope`** — bounded style scoping with upper and lower boundaries (available but
745
+ **not used in this project** — we use `@layer` + descriptive prefixed class names instead;
746
+ see cascade-layers.html for rationale)
747
+ - **Dynamic viewport units** — `dvh`, `svh`, `lvh` for mobile browser chrome awareness
748
+ - **CSS math** — `clamp()`, `min()`, `max()`, `round()` for responsive sizing
749
+
750
+ ### Accessibility CSS (REQUIRED for all components)
751
+ - **`prefers-reduced-motion: reduce`** — suppress/simplify all transitions and animations
752
+ - **`prefers-contrast: more`** — increase contrast when requested by the user
753
+ - **`forced-colors: active`** — support Windows High Contrast Mode with system colors
754
+ - **`prefers-color-scheme`** — automatic dark mode defaults from OS preference
755
+
756
+ ### After editing component CSS or JS
757
+
758
+ Doc pages display each component's CSS and JS in inline `<pre><code>` blocks.
759
+ After changing any `.css` or `.js` file, run the sync scripts to keep doc pages accurate:
760
+
761
+ ```
762
+ bun run sync-snippets
763
+ ```
764
+
765
+ ## Accuracy requirements
766
+
767
+ - All CSS values must match what the documentation site actually renders
768
+ - All class names must match the CSS selectors exactly
769
+ - Token references must use `var(--*)` — never raw color values (see token boundary rule in tokens.instructions.md)
770
+ - `<dialog>` components must include `margin: auto; position: fixed; inset: 0;` for centering (some browsers need this explicitly)
771
+
772
+ ## Variant and size API (data attributes)
773
+
774
+ Components use `data-*` attributes — never CSS class modifiers — for variants and sizes.
775
+
776
+ ### Rules
777
+ - **`data-variant`** for visual variations (e.g., `default`, `outline`, `ghost`, `destructive`)
778
+ - **`data-size`** for size variations (e.g., `sm`, `lg`, `icon`)
779
+ - **`data-side`**, **`data-position`**, etc. for structural variations where applicable
780
+ - **One base class** per component (`.btn`, `.badge`, `.card`) — this identifies *what* it is
781
+ - **Data attributes** express *which version* — never add modifier classes like `.btn-primary` or `.btn--ghost`
782
+ - CSS selectors combine the base class with the attribute: `.btn[data-variant="outline"]`
783
+
784
+ ### Why
785
+ - Flat specificity — all selectors have equal weight, no conflicts
786
+ - Uniform API — every component follows the same pattern, easy for AI to learn
787
+ - Independent axes — variant and size combine freely without combinatorial class names
788
+ - Clean `class` attribute — no long modifier chains
789
+
790
+ ### Correct
791
+ ```html
792
+ <button class="btn" data-variant="destructive" data-size="lg">Delete</button>
793
+ <span class="badge" data-variant="outline">Status</span>
794
+ ```
795
+
796
+ ### Wrong
797
+ ```html
798
+ <button class="btn btn-destructive btn-lg">Delete</button>
799
+ <span class="badge badge-outline">Status</span>
800
+ ```
801
+
802
+ ## Reference
803
+
804
+ Check shadcn/ui (ui.shadcn.com) for the expected behavior and API of each component.
805
+ Translate React/Radix patterns into semantic HTML + vanilla JS.
806
+
807
+ ---
808
+
809
+ ## Linting
810
+
811
+ `make lint` (oxlint over `src/`, `tests/`, `scripts/`) reports warnings but must stay
812
+ **non-blocking** — do not configure it to fail on warnings (`--deny-warnings` is banned);
813
+ only real errors should gate CI.
814
+
815
+ Keep the log clean: fix every warning an edit introduces. For a variable that is
816
+ **known to be intentionally unused**, prefix it with `_` (e.g. `var _copyBtn = …`) —
817
+ oxlint's `no-unused-vars` accepts that convention. For intentionally unused
818
+ **caught errors**, omit the parameter entirely (`catch { … }`, ES2019) — oxlint flags
819
+ `catch (_e)` too. Do not silence warnings with ignore comments, and do not delete
820
+ code that linters flag without checking why it exists (e.g. `window.THEMES` in
821
+ `themes.ts` is a cross-file global contract — make the contract explicit instead
822
+ of removing it).
823
+
824
+ ## Testing
825
+
826
+ `make help` lists the shortcuts (`setup`, `dev`, `test`, `test-run`, `coverage`, `e2e`, `lint`,
827
+ `verify`, `screenshots`, `build`) — they wrap the equivalent `bun run <script>` commands;
828
+ package.json stays the single source of truth. `make build` is the full pipeline:
829
+ lint → compile → screenshots → docs-mirror → verify → tests → e2e (it calls `scripts/build.ts` directly,
830
+ since `bun run build` runs `verify` before screenshots could be refreshed).
831
+
832
+ `bun run test:run` runs the UI suite in headless Chromium (Vitest browser mode + Playwright).
833
+ First run needs `make setup` (or `bunx playwright install`).
834
+
835
+ Tests load the real pages from `dist/documentation/` inside a **same-origin iframe**
836
+ (`openDocPage()` in `tests/helpers.ts`) — Vitest browser mode has no `page.goto()`.
837
+ Interactions go through `userEvent.click()` on elements queried from the iframe's
838
+ `document` (trusted Playwright input); state is asserted by reading that same
839
+ same-origin `document` directly. `frame.getBy*()` locators work too, but Vitest's
840
+ ARIA-tree queries are slow/flaky on these very large doc pages, so prefer
841
+ `doc.querySelector` + `expect` for assertions.
842
+
843
+ When adding a component, add at least one interaction test in `tests/ui.test.ts`
844
+ covering its JS behavior (see the dialog/accordion tests as templates).
845
+
846
+ ### Component E2E smoke tests (`bun run e2e`)
847
+
848
+ Per-component smoke tests live in `tests/e2e/` and run with **plain Playwright**
849
+ (no Vitest). Each component gets two files:
850
+
851
+ - `{name}.e2e-fixture.html` — uses the component **in every configuration** the
852
+ component skill documents (all variants/sizes/states/compositions), linking the
853
+ real files by absolute path (`/dist/theme/default-semantic-tokens.css`,
854
+ `/dist/components/{name}/{name}.css` + `.js`). The fixture is served by
855
+ `server.ts`, a static Bun server rooted at the repo root that exposes only
856
+ `/dist/` and `/tests/e2e/`.
857
+ - `{name}.e2e.ts` — a standalone script (exits non-zero on failure) that launches
858
+ Chromium, serves the fixture, and asserts behavior: init markers, initial state,
859
+ applied CSS (via computed styles), each interaction, and keyboard behavior.
860
+ `accordion.e2e.ts` is the reference template.
861
+
862
+ `tests/e2e/run.ts` globs and runs every `*.e2e.ts` in isolated child processes.
863
+ When adding a component, add both files. Interactive components follow the
864
+ `accordion.e2e.{ts,fixture.html}` template (drive `api.setState` + interactions);
865
+ CSS-only components use the shared `tests/e2e/lib/css-smoke.ts` runner — the
866
+ assertions are data (literal computed px values, pairwise-distinct token colors,
867
+ `run` escape hatch for pseudo-states), see `tests/e2e/badge.e2e.ts`.
868
+
869
+ ### Legacy rollouts (complete)
870
+
871
+ Every component now ships the State API (interactive ones) and an e2e pair —
872
+ the `STATE_API_LEGACY` / e2e warn-ratchets in `scripts/verify.ts` are empty and
873
+ the checks are hard gates. New components must land compliant from day one:
874
+ source → skill `## States` + doc page (`<code>` per state + `data-state-demo`
875
+ anchor) → `bun run screenshots` → e2e fixture + test, ideally in one commit.
876
+
877
+ ### Docs ↔ E2E parity (REQUIRED)
878
+
879
+ The e2e fixture and the documentation must describe the **same** component
880
+ surface, so a green pipeline means "documented == tested == shipped":
881
+
882
+ | Feature exists in… | Then… |
883
+ | --- | --- |
884
+ | code only | add it to the doc page **and** the e2e fixture/test |
885
+ | docs only | add an e2e check for it (it must work in the shipped files) |
886
+ | fixture only | it is undocumented — document it |
887
+ | all three | ✅ |
888
+
889
+ - The fixture `{name}.e2e-fixture.html` instantiates every configuration the
890
+ doc page demonstrates (variants, sizes, states, compositions) — same `data-*`
891
+ attributes, same nesting. If the doc page shows a variant, the fixture has it.
892
+ - Every State API state gets an assertion via `el.api.setState(name)` plus an
893
+ observation of the resulting UI (computed styles / DOM flags).
894
+ - When you add a feature to a component, update **all three artifacts in the
895
+ same commit**: source, doc page, fixture + e2e assertions. Reviewers should
896
+ reject any of the three landing alone.
897
+
898
+ ## Common pitfalls
899
+
900
+ - **Dialog/Sheet centering**: Always set `margin: auto; position: fixed; inset: 0;`
901
+ explicitly for centered dialogs.
902
+ - **CSS drift**: If the component skill's variant/size tables don't match the `.css` file,
903
+ update the component skill to stay in sync — the `.css` file is the source of truth for styles.
904
+ - **CSS/JS import drift**: When adding a component, you must add its `<link>` and
905
+ `<script>` tags to ALL HTML pages. Missing imports cause components in cross-page
906
+ demos to break silently.
907
+ - **SPA re-initialization**: Component JS modules use `MutationObserver` to
908
+ auto-initialize new elements when the DOM changes — no manual re-import needed.
909
+ Doc-site-only scripts (site.js) use `window.onPageReady(fn)` for their own re-init.
910
+ - **Font stacks**: The system tokens use generic font stacks. The doc site overrides
911
+ them in `css/docs-theme.css`. Don't put custom fonts in `default-semantic-tokens.css`.
912
+ - **Inline source snippet drift**: Doc pages show the component's CSS and JS in
913
+ `<pre><code>` blocks. These must always match the actual files. After editing any
914
+ component `.css` or `.ts`, run `bun run sync-snippets` to update all doc pages
915
+ automatically (the syncers edit `src/`, never `dist/`).
916
+
917
+ # Documentation Pages
918
+
919
+ ## Shared layout (Web Components)
920
+
921
+ The header and sidebar navigation are centralized in `dist/documentation/js/layout.js`
922
+ using two custom elements:
923
+
924
+ - `<site-header>` — renders the fixed header (logo, GitHub link, dark mode toggle)
925
+ - `<site-nav>` — renders the sidebar with navigation links, auto-detecting the active page
926
+
927
+ **To add/remove/reorder nav links or change the header, edit `layout.js` only.**
928
+ No need to touch individual HTML files for navigation changes.
929
+
930
+ `layout.js` is loaded **synchronously** in `<head>` (no `defer`) so the custom
931
+ elements render without FOUC when the parser encounters them in `<body>`.
932
+
933
+ ### layout.js data structures
934
+
935
+ - `NAV` — array of `{ heading, items: [{ label, href }] }` defining the sidebar sections
936
+ - `BUILT` — `Set` of page filenames that have real doc pages (non-built pages render as disabled links)
937
+
938
+ ## Adding a component page
939
+
940
+ 1. Copy an existing component page (e.g., `badge.html`) as the template
941
+ 2. Change the `<title>`, `<h1>`, breadcrumb, and main content
942
+ 3. Add `<link rel="stylesheet" href="../components/{name}/{name}.css">` to the head
943
+ 4. Add `<script type="module" src="../components/{name}/{name}.js"></script>` if interactive
944
+ 5. In `layout.js`: add the page to the `NAV` array and the `BUILT` set
945
+
946
+ No need to update sidebar nav links in other files — `<site-nav>` handles it globally.
947
+
948
+ ## Sidebar nav order
949
+
950
+ The sidebar is ordered by dependency (primitives first):
951
+ 1. Overview (Introduction, Installation, Theming, Dark Mode, Data Attribute API, Cascade Layers, ES Modules, Native Web APIs, Animations, Accessibility, Component Skills, Changelog)
952
+ 2. Primitives (Typography, Separator, Icon)
953
+ 3. Layout (Scroll Area, Carousel, Sortable)
954
+ 4. Actions (Button, Toggle, Toggle Group, Button Group, Toolbar)
955
+ 5. Forms & Inputs (Label, Input, Textarea, Checkbox, Radio Group, Switch, Slider, Select, Number Input, File Input, Color Picker, Date Picker, Combobox, Form)
956
+ 6. Data Display (Badge, Avatar, Card, Image, Statistic, Table, Collapsible, Timeline, Tree View, Calendar)
957
+ 7. Feedback & Status (Spinner, Skeleton, Progress, Alert, Alert Dialog, Toast)
958
+ 8. Overlays (Popover, Tooltip, Context Menu, Dialog, Sheet, Accordion, Command)
959
+ 9. Navigation (Breadcrumb, Pagination, Steps, Tabs, Dropdown Menu, Navigation Menu)
960
+ 10. Application (Sidebar)
961
+
962
+ To reorder, edit the `NAV` array in `layout.js`.
963
+
964
+ ## CSS and JS imports
965
+
966
+ Every page imports ALL component CSS and JS files (not just its own). This ensures
967
+ components used in demos on other pages render correctly. CSS and JS files live in
968
+ `../components/{name}/{name}.css` and `../components/{name}/{name}.js` respectively.
969
+
970
+ ## Inline source code snippets
971
+
972
+ Each component doc page displays the component's CSS and JS in `<pre><code>` blocks.
973
+ These inline snippets must always match the actual files in `dist/components/`.
974
+ After editing any component `.css` or `.js` file, run:
975
+
976
+ ```
977
+ bun run sync-snippets
978
+ ```
979
+
980
+ These scripts replace every inline snippet with the current file content.
981
+ Do NOT manually edit the `<pre><code>` blocks — they will be overwritten by the sync scripts.
982
+
983
+ ## Doc-site utility classes
984
+
985
+ The doc site uses a small, hand-written set of utility classes for layout and
986
+ spacing inside doc pages (`css/docs-utilities.css`). The utilities are plain
987
+ class rules — they only affect elements that explicitly opt in by using the
988
+ class name, so they cannot leak into component styles.
989
+
990
+ If you need a new utility (e.g. `mt-4`, `gap-5`), add it directly to
991
+ `css/docs-utilities.css`. Keep the utility set minimal — prefer inline `style`
992
+ attributes for one-off layout tweaks in demo wrappers.