@felloinnovations/brand-kit 0.4.0 → 0.5.1

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 (363) hide show
  1. package/AGENTS.md +198 -24
  2. package/CHANGELOG.md +221 -0
  3. package/README.md +38 -9
  4. package/design-system/01-how-claude-decides.md +26 -17
  5. package/design-system/02-principles.md +11 -6
  6. package/design-system/03-colour.md +135 -47
  7. package/design-system/04-type.md +45 -23
  8. package/design-system/05-space-and-layout.md +175 -52
  9. package/design-system/06-sizing-profiles.md +4 -4
  10. package/design-system/07-scrolling.md +66 -20
  11. package/design-system/08-choosing-components.md +50 -22
  12. package/design-system/09-charts.md +71 -28
  13. package/design-system/10-patterns.md +33 -10
  14. package/design-system/11-page-recipes.md +110 -23
  15. package/design-system/12-content.md +2 -1
  16. package/design-system/13-motion.md +330 -47
  17. package/design-system/14-accessibility.md +47 -24
  18. package/design-system/15-ai-experiences.md +11 -6
  19. package/design-system/16-creativity.md +1 -1
  20. package/design-system/17-decisions.md +367 -1
  21. package/design-system/18-checklist.md +79 -16
  22. package/design-system/19-public-pages.md +6 -4
  23. package/design-system/20-palettes.md +275 -0
  24. package/design-system/README.md +30 -17
  25. package/design-system/components/ActionList/README.md +96 -10
  26. package/design-system/components/ActionList/preview.html +52 -7
  27. package/design-system/components/ActionList/style.css +64 -0
  28. package/design-system/components/ActivityGrid/README.md +38 -2
  29. package/design-system/components/ActivityGrid/preview.html +1 -1
  30. package/design-system/components/ActivityGrid/style.css +35 -0
  31. package/design-system/components/AppShell/README.md +205 -75
  32. package/design-system/components/AppShell/preview.html +339 -58
  33. package/design-system/components/AppShell/style.css +154 -0
  34. package/design-system/components/Avatar/README.md +37 -10
  35. package/design-system/components/Avatar/preview.html +13 -4
  36. package/design-system/components/Avatar/style.css +14 -0
  37. package/design-system/components/Badge/README.md +41 -19
  38. package/design-system/components/Badge/preview.html +13 -10
  39. package/design-system/components/Badge/style.css +23 -0
  40. package/design-system/components/Banner/README.md +30 -9
  41. package/design-system/components/Banner/preview.html +1 -1
  42. package/design-system/components/Banner/style.css +19 -0
  43. package/design-system/components/Breadcrumb/README.md +58 -46
  44. package/design-system/components/Breadcrumb/preview.html +16 -16
  45. package/design-system/components/Breadcrumb/style.css +17 -0
  46. package/design-system/components/Button/README.md +113 -55
  47. package/design-system/components/Button/preview.html +98 -25
  48. package/design-system/components/Button/style.css +86 -0
  49. package/design-system/components/Card/README.md +158 -49
  50. package/design-system/components/Card/preview.html +141 -19
  51. package/design-system/components/Card/style.css +115 -0
  52. package/design-system/components/CardBoard/README.md +58 -37
  53. package/design-system/components/CardBoard/preview.html +26 -29
  54. package/design-system/components/ChatCard/README.md +115 -29
  55. package/design-system/components/ChatCard/preview.html +149 -32
  56. package/design-system/components/ChatCard/style.css +49 -0
  57. package/design-system/components/ChatShell/README.md +116 -0
  58. package/design-system/components/ChatShell/preview.html +201 -0
  59. package/design-system/components/ChatShell/style.css +86 -0
  60. package/design-system/components/ChatTurn/README.md +78 -35
  61. package/design-system/components/ChatTurn/preview.html +60 -28
  62. package/design-system/components/ChatTurn/style.css +50 -0
  63. package/design-system/components/CheckRow/README.md +141 -0
  64. package/design-system/components/CheckRow/preview.html +140 -0
  65. package/design-system/components/CheckRow/style.css +88 -0
  66. package/design-system/components/Checkbox/README.md +58 -22
  67. package/design-system/components/Checkbox/preview.html +112 -50
  68. package/design-system/components/Checkbox/style.css +45 -0
  69. package/design-system/components/Chip/README.md +78 -47
  70. package/design-system/components/Chip/preview.html +29 -4
  71. package/design-system/components/Chip/style.css +17 -0
  72. package/design-system/components/Combobox/README.md +44 -11
  73. package/design-system/components/Combobox/preview.html +35 -12
  74. package/design-system/components/Combobox/style.css +54 -0
  75. package/design-system/components/Composer/README.md +137 -35
  76. package/design-system/components/Composer/preview.html +115 -40
  77. package/design-system/components/Composer/style.css +81 -0
  78. package/design-system/components/ConfirmDialog/README.md +73 -52
  79. package/design-system/components/ConfirmDialog/preview.html +70 -26
  80. package/design-system/components/ConfirmDialog/style.css +5 -0
  81. package/design-system/components/Cover/preview.html +3 -3
  82. package/design-system/components/Cover/style.css +19 -0
  83. package/design-system/components/DateInput/README.md +77 -12
  84. package/design-system/components/DateInput/preview.html +27 -12
  85. package/design-system/components/DateInput/style.css +38 -0
  86. package/design-system/components/Dialog/README.md +112 -44
  87. package/design-system/components/Dialog/preview.html +141 -18
  88. package/design-system/components/Dialog/style.css +74 -0
  89. package/design-system/components/EmptyState/README.md +42 -12
  90. package/design-system/components/EmptyState/preview.html +11 -11
  91. package/design-system/components/EmptyState/style.css +9 -0
  92. package/design-system/components/EntryShell/README.md +73 -48
  93. package/design-system/components/EntryShell/preview.html +60 -27
  94. package/design-system/components/EntryShell/style.css +19 -0
  95. package/design-system/components/FeatureGrid/README.md +61 -39
  96. package/design-system/components/FeatureGrid/preview.html +2 -2
  97. package/design-system/components/FeatureGrid/style.css +20 -0
  98. package/design-system/components/FileUpload/README.md +33 -3
  99. package/design-system/components/FileUpload/preview.html +18 -4
  100. package/design-system/components/FileUpload/style.css +23 -0
  101. package/design-system/components/FilterBar/README.md +114 -0
  102. package/design-system/components/FilterBar/preview.html +82 -0
  103. package/design-system/components/FilterBar/style.css +22 -0
  104. package/design-system/components/Fold/README.md +84 -26
  105. package/design-system/components/Fold/preview.html +77 -34
  106. package/design-system/components/Fold/style.css +48 -0
  107. package/design-system/components/Footer/README.md +62 -39
  108. package/design-system/components/Footer/preview.html +5 -5
  109. package/design-system/components/Footer/style.css +30 -0
  110. package/design-system/components/FunnelBars/README.md +24 -3
  111. package/design-system/components/FunnelBars/preview.html +5 -5
  112. package/design-system/components/FunnelBars/style.css +13 -0
  113. package/design-system/components/GoalBars/README.md +54 -10
  114. package/design-system/components/GoalBars/preview.html +69 -28
  115. package/design-system/components/GoalBars/style.css +51 -0
  116. package/design-system/components/GoalTicks/README.md +74 -58
  117. package/design-system/components/GoalTicks/preview.html +41 -118
  118. package/design-system/components/GoalTicks/style.css +37 -0
  119. package/design-system/components/GoalTubes/README.md +29 -3
  120. package/design-system/components/GoalTubes/preview.html +6 -6
  121. package/design-system/components/GoalTubes/style.css +23 -0
  122. package/design-system/components/Heatmap/README.md +133 -0
  123. package/design-system/components/Heatmap/preview.html +168 -0
  124. package/design-system/components/Heatmap/style.css +119 -0
  125. package/design-system/components/Hero/README.md +58 -40
  126. package/design-system/components/Hero/preview.html +1 -1
  127. package/design-system/components/Hero/style.css +19 -0
  128. package/design-system/components/HoverCard/README.md +114 -0
  129. package/design-system/components/HoverCard/preview.html +107 -0
  130. package/design-system/components/HoverCard/style.css +59 -0
  131. package/design-system/components/IconButton/README.md +69 -38
  132. package/design-system/components/IconButton/preview.html +77 -43
  133. package/design-system/components/IconButton/style.css +45 -0
  134. package/design-system/components/Input/README.md +36 -11
  135. package/design-system/components/Input/preview.html +1 -1
  136. package/design-system/components/Input/style.css +23 -0
  137. package/design-system/components/KanbanBoard/README.md +93 -45
  138. package/design-system/components/KanbanBoard/preview.html +159 -43
  139. package/design-system/components/KanbanBoard/style.css +53 -0
  140. package/design-system/components/Kbd/README.md +25 -9
  141. package/design-system/components/Kbd/preview.html +6 -2
  142. package/design-system/components/Kbd/style.css +9 -0
  143. package/design-system/components/KitButton/README.md +81 -47
  144. package/design-system/components/KitButton/preview.html +35 -13
  145. package/design-system/components/KitButton/style.css +46 -0
  146. package/design-system/components/KitCard/README.md +75 -44
  147. package/design-system/components/KitCard/preview.html +18 -10
  148. package/design-system/components/KitCard/style.css +15 -0
  149. package/design-system/components/KitInput/README.md +54 -39
  150. package/design-system/components/KitInput/preview.html +7 -7
  151. package/design-system/components/KitInput/style.css +23 -0
  152. package/design-system/components/Leaderboard/README.md +76 -49
  153. package/design-system/components/Leaderboard/preview.html +61 -26
  154. package/design-system/components/Leaderboard/style.css +44 -0
  155. package/design-system/components/ListDetail/README.md +222 -0
  156. package/design-system/components/ListDetail/preview.html +213 -0
  157. package/design-system/components/ListDetail/style.css +148 -0
  158. package/design-system/components/LoadBar/README.md +79 -0
  159. package/design-system/components/LoadBar/preview.html +40 -0
  160. package/design-system/components/LoadBar/style.css +17 -0
  161. package/design-system/components/MarketingNav/README.md +76 -42
  162. package/design-system/components/MarketingNav/preview.html +37 -8
  163. package/design-system/components/MarketingNav/style.css +45 -0
  164. package/design-system/components/Matrix/README.md +129 -0
  165. package/design-system/components/Matrix/preview.html +99 -0
  166. package/design-system/components/Matrix/style.css +84 -0
  167. package/design-system/components/Menu/README.md +117 -41
  168. package/design-system/components/Menu/preview.html +85 -23
  169. package/design-system/components/Menu/style.css +61 -0
  170. package/design-system/components/Meter/README.md +39 -4
  171. package/design-system/components/Meter/preview.html +18 -1
  172. package/design-system/components/Meter/style.css +23 -0
  173. package/design-system/components/MetricTile/README.md +237 -0
  174. package/design-system/components/MetricTile/preview.html +241 -0
  175. package/design-system/components/MetricTile/style.css +129 -0
  176. package/design-system/components/Morph/README.md +106 -0
  177. package/design-system/components/Morph/preview.html +103 -0
  178. package/design-system/components/Morph/style.css +31 -0
  179. package/design-system/components/Motion/README.md +164 -0
  180. package/design-system/components/Motion/preview.html +2537 -0
  181. package/design-system/components/NumberInput/README.md +27 -8
  182. package/design-system/components/NumberInput/preview.html +21 -5
  183. package/design-system/components/NumberInput/style.css +34 -0
  184. package/design-system/components/PaceBar/README.md +75 -0
  185. package/design-system/components/PaceBar/preview.html +67 -0
  186. package/design-system/components/PaceBar/style.css +25 -0
  187. package/design-system/components/Page/README.md +57 -18
  188. package/design-system/components/Page/preview.html +15 -17
  189. package/design-system/components/Page/style.css +7 -0
  190. package/design-system/components/PageBar/README.md +90 -34
  191. package/design-system/components/PageBar/preview.html +60 -19
  192. package/design-system/components/PageBar/style.css +35 -0
  193. package/design-system/components/PageHero/README.md +87 -51
  194. package/design-system/components/PageHero/preview.html +35 -6
  195. package/design-system/components/PageHero/style.css +31 -0
  196. package/design-system/components/Pagination/README.md +70 -49
  197. package/design-system/components/Pagination/preview.html +24 -15
  198. package/design-system/components/Pagination/style.css +12 -0
  199. package/design-system/components/Panel/README.md +109 -51
  200. package/design-system/components/Panel/preview.html +118 -20
  201. package/design-system/components/Panel/style.css +51 -0
  202. package/design-system/components/PasswordInput/README.md +50 -18
  203. package/design-system/components/PasswordInput/preview.html +71 -21
  204. package/design-system/components/PasswordInput/style.css +31 -0
  205. package/design-system/components/Plate/README.md +75 -17
  206. package/design-system/components/Plate/preview.html +40 -7
  207. package/design-system/components/Plate/style.css +37 -0
  208. package/design-system/components/Popover/README.md +70 -49
  209. package/design-system/components/Popover/preview.html +44 -22
  210. package/design-system/components/Popover/style.css +41 -0
  211. package/design-system/components/PricingTable/README.md +69 -49
  212. package/design-system/components/PricingTable/preview.html +21 -21
  213. package/design-system/components/PricingTable/style.css +21 -0
  214. package/design-system/components/ProgressPetals/README.md +31 -3
  215. package/design-system/components/ProgressPetals/preview.html +14 -15
  216. package/design-system/components/ProgressPetals/style.css +27 -0
  217. package/design-system/components/README.md +151 -0
  218. package/design-system/components/Radio/README.md +40 -17
  219. package/design-system/components/Radio/preview.html +16 -1
  220. package/design-system/components/Radio/style.css +21 -0
  221. package/design-system/components/RankBar/README.md +29 -2
  222. package/design-system/components/RankBar/preview.html +14 -12
  223. package/design-system/components/RankBar/style.css +18 -0
  224. package/design-system/components/Ring/README.md +61 -11
  225. package/design-system/components/Ring/preview.html +55 -10
  226. package/design-system/components/Ring/style.css +49 -0
  227. package/design-system/components/ScoreHero/README.md +91 -0
  228. package/design-system/components/ScoreHero/preview.html +104 -0
  229. package/design-system/components/ScoreHero/style.css +32 -0
  230. package/design-system/components/Section/README.md +72 -33
  231. package/design-system/components/Section/preview.html +46 -16
  232. package/design-system/components/Section/style.css +18 -0
  233. package/design-system/components/Segmented/README.md +100 -55
  234. package/design-system/components/Segmented/preview.html +107 -26
  235. package/design-system/components/Segmented/style.css +94 -0
  236. package/design-system/components/SegmentedProgress/README.md +64 -0
  237. package/design-system/components/SegmentedProgress/preview.html +69 -0
  238. package/design-system/components/SegmentedProgress/style.css +11 -0
  239. package/design-system/components/Select/README.md +47 -12
  240. package/design-system/components/Select/preview.html +28 -2
  241. package/design-system/components/Select/style.css +22 -0
  242. package/design-system/components/ShareBar/README.md +69 -0
  243. package/design-system/components/ShareBar/preview.html +72 -0
  244. package/design-system/components/ShareBar/style.css +25 -0
  245. package/design-system/components/SideList/README.md +147 -0
  246. package/design-system/components/SideList/preview.html +139 -0
  247. package/design-system/components/SideList/style.css +94 -0
  248. package/design-system/components/Skeleton/README.md +53 -16
  249. package/design-system/components/Skeleton/preview.html +35 -23
  250. package/design-system/components/Skeleton/style.css +17 -0
  251. package/design-system/components/Slider/README.md +95 -0
  252. package/design-system/components/Slider/preview.html +49 -0
  253. package/design-system/components/Slider/style.css +13 -0
  254. package/design-system/components/Split/README.md +112 -0
  255. package/design-system/components/Split/preview.html +105 -0
  256. package/design-system/components/Split/style.css +30 -0
  257. package/design-system/components/Starter/README.md +21 -9
  258. package/design-system/components/Starter/preview.html +5 -5
  259. package/design-system/components/Stat/README.md +64 -43
  260. package/design-system/components/Stat/preview.html +3 -3
  261. package/design-system/components/Stat/style.css +15 -0
  262. package/design-system/components/StatTiles/README.md +88 -44
  263. package/design-system/components/StatTiles/preview.html +142 -71
  264. package/design-system/components/StatTiles/style.css +64 -0
  265. package/design-system/components/StatusLine/README.md +85 -0
  266. package/design-system/components/StatusLine/preview.html +49 -0
  267. package/design-system/components/StatusLine/style.css +14 -0
  268. package/design-system/components/StatusScreen/README.md +49 -35
  269. package/design-system/components/StatusScreen/preview.html +20 -5
  270. package/design-system/components/StatusScreen/style.css +9 -0
  271. package/design-system/components/StepLine/README.md +97 -0
  272. package/design-system/components/StepLine/preview.html +108 -0
  273. package/design-system/components/StepLine/style.css +62 -0
  274. package/design-system/components/Stepper/README.md +35 -13
  275. package/design-system/components/Stepper/style.css +13 -0
  276. package/design-system/components/StreakCalendar/README.md +22 -1
  277. package/design-system/components/StreakCalendar/preview.html +10 -10
  278. package/design-system/components/StreakCalendar/style.css +12 -0
  279. package/design-system/components/SuggestionChip/README.md +87 -0
  280. package/design-system/components/SuggestionChip/preview.html +42 -0
  281. package/design-system/components/SuggestionChip/style.css +20 -0
  282. package/design-system/components/Switch/README.md +44 -24
  283. package/design-system/components/Switch/preview.html +23 -8
  284. package/design-system/components/Switch/style.css +31 -0
  285. package/design-system/components/Table/README.md +129 -58
  286. package/design-system/components/Table/preview.html +106 -72
  287. package/design-system/components/Table/style.css +104 -0
  288. package/design-system/components/Tabs/README.md +71 -46
  289. package/design-system/components/Tabs/preview.html +56 -24
  290. package/design-system/components/Tabs/style.css +44 -0
  291. package/design-system/components/Testimonial/README.md +48 -32
  292. package/design-system/components/Testimonial/preview.html +14 -14
  293. package/design-system/components/Testimonial/style.css +12 -0
  294. package/design-system/components/TextArea/README.md +27 -11
  295. package/design-system/components/TextArea/preview.html +8 -2
  296. package/design-system/components/TextArea/style.css +12 -0
  297. package/design-system/components/ThemeSwitch/README.md +116 -0
  298. package/design-system/components/ThemeSwitch/preview.html +57 -0
  299. package/design-system/components/ThemeSwitch/style.css +6 -0
  300. package/design-system/components/TickRow/README.md +52 -36
  301. package/design-system/components/TickRow/preview.html +64 -41
  302. package/design-system/components/TickRow/style.css +23 -0
  303. package/design-system/components/TickStrip/README.md +95 -0
  304. package/design-system/components/TickStrip/preview.html +88 -0
  305. package/design-system/components/TickStrip/style.css +42 -0
  306. package/design-system/components/Tile/README.md +79 -18
  307. package/design-system/components/Tile/preview.html +91 -40
  308. package/design-system/components/Tile/style.css +36 -0
  309. package/design-system/components/Timeline/README.md +68 -48
  310. package/design-system/components/Timeline/preview.html +27 -15
  311. package/design-system/components/Timeline/style.css +25 -0
  312. package/design-system/components/Toast/README.md +70 -38
  313. package/design-system/components/Toast/preview.html +43 -8
  314. package/design-system/components/Toast/style.css +25 -0
  315. package/design-system/components/Tooltip/README.md +93 -35
  316. package/design-system/components/Tooltip/preview.html +69 -15
  317. package/design-system/components/Tooltip/style.css +78 -0
  318. package/design-system/components/TopBar/README.md +67 -53
  319. package/design-system/components/TopBar/preview.html +14 -27
  320. package/design-system/components/TopBar/style.css +35 -0
  321. package/design-system/components/TopicRail/README.md +108 -0
  322. package/design-system/components/TopicRail/preview.html +58 -0
  323. package/design-system/components/TopicRail/style.css +48 -0
  324. package/design-system/components/TrendBars/README.md +52 -14
  325. package/design-system/components/TrendBars/preview.html +65 -32
  326. package/design-system/components/TrendBars/style.css +25 -0
  327. package/design-system/components/TrendLine/README.md +164 -0
  328. package/design-system/components/TrendLine/preview.html +373 -0
  329. package/design-system/components/TrendLine/style.css +155 -0
  330. package/design-system/components/Well/README.md +64 -22
  331. package/design-system/components/Well/preview.html +14 -14
  332. package/design-system/components/Well/style.css +22 -0
  333. package/design-system/components/_house.css +324 -0
  334. package/design-system/components/bundle.css +4086 -744
  335. package/design-system/tokens.json +1310 -124
  336. package/dist/fello-core.css +2 -2
  337. package/dist/fello.css +6 -1369
  338. package/dist/fello.scoped.css +7 -0
  339. package/dist/index.d.ts +109 -10
  340. package/dist/index.js +781 -96
  341. package/dist/palettes.d.ts +464 -0
  342. package/dist/palettes.js +497 -0
  343. package/dist/scoped.d.ts +3 -0
  344. package/dist/scoped.js +16 -0
  345. package/dist/toast.d.ts +14 -0
  346. package/dist/toast.js +33 -29
  347. package/dist/tokens.d.ts +1686 -62
  348. package/dist/tokens.js +1668 -61
  349. package/package.json +20 -4
  350. package/scripts/check-palette.mjs +295 -0
  351. package/scripts/token-model.mjs +552 -0
  352. package/tokens/_dark.css +173 -0
  353. package/tokens/_palettes.css +46 -0
  354. package/tokens/_product.css +266 -53
  355. package/tokens/_source.hubl.css +39 -19
  356. package/tokens/design-tokens.json +319 -0
  357. package/tokens/palettes.ts +504 -0
  358. package/tokens/shadcn-compat.css +19 -19
  359. package/tokens/shadcn-compat.hsl.css +76 -0
  360. package/tokens/tokens.css +1287 -62
  361. package/tokens/tokens.md +587 -61
  362. package/tokens/tokens.root.css +1293 -62
  363. package/tokens/tokens.ts +1677 -64
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@felloinnovations/brand-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Fello brand tokens, the house stylesheet (fello.css), React components and the design system guide. One source of truth for every GTM app.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -18,6 +18,8 @@
18
18
  ],
19
19
  "files": [
20
20
  "dist",
21
+ "scripts/check-palette.mjs",
22
+ "scripts/token-model.mjs",
21
23
  "tokens",
22
24
  "design-system",
23
25
  "assets",
@@ -36,31 +38,45 @@
36
38
  "types": "./dist/toast.d.ts",
37
39
  "import": "./dist/toast.js"
38
40
  },
41
+ "./scoped": {
42
+ "types": "./dist/scoped.d.ts",
43
+ "import": "./dist/scoped.js"
44
+ },
39
45
  "./fello.css": "./dist/fello.css",
46
+ "./fello.scoped.css": "./dist/fello.scoped.css",
40
47
  "./tokens.css": "./tokens/tokens.css",
41
48
  "./tokens.root.css": "./tokens/tokens.root.css",
42
49
  "./shadcn-compat.css": "./tokens/shadcn-compat.css",
50
+ "./shadcn-compat.hsl.css": "./tokens/shadcn-compat.hsl.css",
43
51
  "./tokens": {
44
52
  "types": "./dist/tokens.d.ts",
45
53
  "import": "./dist/tokens.js"
46
54
  },
55
+ "./palettes": {
56
+ "types": "./dist/palettes.d.ts",
57
+ "import": "./dist/palettes.js"
58
+ },
47
59
  "./design-tokens.json": "./tokens/design-tokens.json",
48
60
  "./AGENTS.md": "./AGENTS.md",
49
61
  "./tokens.md": "./tokens/tokens.md",
50
62
  "./design-system/*": "./design-system/*"
51
63
  },
52
64
  "scripts": {
53
- "tokens": "node scripts/generate-tokens.mjs",
65
+ "bundle": "node scripts/build-bundle.mjs",
66
+ "tokens": "node scripts/build-bundle.mjs && node scripts/generate-tokens.mjs",
54
67
  "build": "yarn tokens && tsup",
55
68
  "dev": "tsup --watch",
56
69
  "typecheck": "tsc --noEmit",
57
70
  "prepublishOnly": "yarn build",
58
71
  "site": "vite",
59
- "build:site": "node scripts/build-skill.mjs && node scripts/build-assets.mjs && vite build && node scripts/build-hosted.mjs",
72
+ "build:site": "node scripts/build-bundle.mjs && node scripts/build-skill.mjs && node scripts/build-assets.mjs && vite build && node scripts/build-hosted.mjs",
60
73
  "verify": "yarn tokens && yarn typecheck && bunx tsup && bunx vite build",
61
74
  "skill": "node scripts/build-skill.mjs",
62
75
  "assets": "node scripts/build-assets.mjs",
63
- "hosted": "node scripts/build-hosted.mjs"
76
+ "hosted": "node scripts/build-hosted.mjs",
77
+ "check:geometry": "node scripts/check-geometry.mjs",
78
+ "check:scope": "node scripts/check-scope.mjs",
79
+ "check:palette": "node scripts/check-palette.mjs"
64
80
  },
65
81
  "dependencies": {
66
82
  "@radix-ui/react-tabs": "^1.1.21",
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * check-palette: proves a palette keeps the kit's promises (D15). A palette is a set of values for the contract
4
+ * (tokens/design-tokens.json "palette"); this applies it over the kit, resolves every token in light, night and the
5
+ * navy ground, and checks what the kit checks for itself.
6
+ *
7
+ * node scripts/check-palette.mjs the built-ins (tokens/_palettes.css), and that every colour a
8
+ * component reads follows the contract; generate-tokens runs the first
9
+ * node scripts/check-palette.mjs my-palette.css an app's own palette file(s), written as
10
+ * [data-palette="name"] { ... } for light and
11
+ * [data-palette="name"][data-theme="dark"] { ... } for dark
12
+ * node scripts/check-palette.mjs --emit my-palette.css > my-palette.shipped.css
13
+ * checks it, then writes the CSS to ship: the built-ins' own form, so
14
+ * the palette works on <html> or any container, nested either way, in
15
+ * light and dark, in fello.css and fello.scoped.css, before or after the
16
+ * kit; a dark value it leaves out is the kit's, derived from the dark
17
+ * inputs (the report goes to stderr, and it writes nothing on an error)
18
+ * node scripts/check-palette.mjs --selftest planted bad palettes and a planted leftover, each caught for its reason
19
+ *
20
+ * Errors (exit 1):
21
+ * contract a token outside the contract (the nearest contract token is suggested)
22
+ * word a word token under 4.5:1 on a ground it can sit on, or a word under 4.5:1 on its own tint
23
+ * focus the focus ring under 3:1 on any ground
24
+ * act the act fill (the heading colour) under 3:1 on a ground a button sits on
25
+ * chart-distinct two chart tones closer than CIEDE2000 10
26
+ * chart-contrast a chart tone under 3:1 on the chart ground (a tone drawn with its word is reported instead)
27
+ * leftover (the kit's own check) a colour a component reads that no contract token moves, in light or dark
28
+ * Reports (no exit): a dark value the palette leaves out, and what dark paints instead; a file that is not in the
29
+ * shipped form.
30
+ */
31
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
32
+ import { join } from 'node:path';
33
+ import { fileURLToPath } from 'node:url';
34
+ import {
35
+ root, tokens, night, known, contract, CONTRACT, resolved, ratio, deltaE, GROUNDS, WORDS, PAIRS, parsePalettes, builtIns,
36
+ paletteCss, parseColour,
37
+ } from './token-model.mjs';
38
+
39
+ const LIGHT = Object.fromEntries(tokens);
40
+ const NIGHT = Object.fromEntries(night);
41
+ const KIT = resolved();
42
+ const CHART = contract.filter((t) => t.group === 'chart');
43
+ const ACT_GROUNDS = ['--color-bg-page', '--color-surface', '--ws-raised', '--ws-tray'];
44
+ const CHART_GROUND = '--color-bg-page';
45
+ const THEMES = ['light', 'dark', 'navy'];
46
+ const fx = (r) => r.toFixed(2);
47
+
48
+ const refs = (v = '') => [...v.matchAll(/var\(\s*(--[a-z0-9-]+)/g)].map(([, n]) => n);
49
+ /** The contract tokens a kit token derives from in one theme's map, through every var() it reads. */
50
+ function inputsOf(name, map = LIGHT, seen = new Set()) {
51
+ if (CONTRACT.has(name)) return [name];
52
+ if (seen.has(name)) return [];
53
+ seen.add(name);
54
+ return [...new Set(refs(map[name]).flatMap((r) => inputsOf(r, map, seen)))];
55
+ }
56
+ const distance = (a, b) => {
57
+ const d = Array.from({ length: b.length + 1 }, (_, i) => i);
58
+ for (let i = 1; i <= a.length; i++) {
59
+ let prev = d[0]++;
60
+ for (let j = 1; j <= b.length; j++) [prev, d[j]] = [d[j], Math.min(d[j] + 1, d[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1))];
61
+ }
62
+ return d[b.length];
63
+ };
64
+ function suggest(name) {
65
+ const from = known.has(name) ? inputsOf(name) : [];
66
+ if (from.length) return `it derives from ${from.join(', ')}: set ${from.length > 1 ? 'those' : 'that'} instead`;
67
+ const words = (n) => new Set(n.slice(2).split('-'));
68
+ const shared = (a, b) => [...words(a)].filter((w) => words(b).has(w)).length;
69
+ const near = [...CONTRACT].sort((a, b) => shared(name, b) - shared(name, a) || distance(name, a) - distance(name, b))[0];
70
+ return `the nearest contract token is ${near}`;
71
+ }
72
+
73
+ /**
74
+ * Checks palettes ({ name: { light, dark } } of token -> value) as they paint once shipped in the built-ins' form (the
75
+ * generator for a built-in, `--emit` for an app): a dark value left out takes the kit's dark expression, and a contract
76
+ * token the kit derives (the focus ring from the heading) recomputes from the palette's inputs unless it sets it.
77
+ * `shipped: false` (an app's file not written by --emit) adds a report that it should be.
78
+ */
79
+ export function checkPalettes(palettes, { shipped = true } = {}) {
80
+ const out = { palettes: {}, errors: [], reports: [] };
81
+ for (const [id, p] of Object.entries(palettes)) {
82
+ const errors = [], reports = [];
83
+ const err = (kind, text) => errors.push({ kind, text });
84
+ const set = new Set([...Object.keys(p.light), ...Object.keys(p.dark)]);
85
+ for (const n of set) if (!CONTRACT.has(n)) err('contract', `${n} is not in the palette contract (${known.has(n) ? 'a derived token' : 'not a token'}); ${suggest(n)}`);
86
+ const light = Object.fromEntries(Object.entries(p.light).filter(([n]) => CONTRACT.has(n)));
87
+ const dark = Object.fromEntries(Object.entries(p.dark).filter(([n]) => CONTRACT.has(n)));
88
+ const R = resolved({ light, dark });
89
+ for (const n of Object.keys(light).filter((n) => !(n in dark)))
90
+ reports.push(`dark: no ${n}, so it takes the kit's dark ${n} (${R.dark[n]}), derived from the dark inputs`);
91
+ if (!shipped && set.size)
92
+ reports.push(`not in the shipped form: as written, its light values also paint in dark wherever the theme sits on another element than the palette; ship the output of \`check-palette --emit\``);
93
+
94
+ for (const theme of THEMES) {
95
+ const v = R[theme];
96
+ const at = (n) => `${n} ${v[n]}`;
97
+ /** One line per colour that fails: on how many grounds, and the worst. */
98
+ const onGrounds = (kind, what, fg, grounds, min) => {
99
+ const low = grounds.map(([g, label]) => [label, g, ratio(v[fg], v[g])]).filter(([, , r]) => r < min).sort((a, b) => a[2] - b[2]);
100
+ if (low.length)
101
+ err(kind, `${theme}: ${what} ${v[fg]} is ${low.length === 1 ? '' : `under ${min}:1 on ${low.length} grounds, lowest `}${fx(low[0][2])}:1 on the ${low[0][0]} (${at(low[0][1])})${low.length === 1 ? `, under ${min}` : ''}`);
102
+ };
103
+ for (const w of WORDS) onGrounds('word', w, w, Object.entries(GROUNDS), 4.5);
104
+ for (const [w, g] of PAIRS) {
105
+ const r = ratio(v[w], v[g]);
106
+ if (r < 4.5) err('word', `${theme}: ${at(w)} on ${at(g)} is ${fx(r)}:1, under 4.5`);
107
+ }
108
+ onGrounds('focus', 'the focus ring', '--ws-focus-color', Object.entries(GROUNDS), 3);
109
+ onGrounds('act', 'the act fill', '--color-text-heading', ACT_GROUNDS.map((g) => [g, GROUNDS[g]]), 3);
110
+ for (let i = 0; i < CHART.length; i++) {
111
+ const a = CHART[i];
112
+ const r = ratio(v[a.name], v[CHART_GROUND]);
113
+ if (r < 3) {
114
+ // a tone drawn beside its word: reported when the palette moves it, the kit's own two are said once below
115
+ if (a.withWords) v[a.name] !== KIT[theme][a.name] && reports.push(`${theme}: ${at(a.name)} is ${fx(r)}:1 on the chart ground, drawn with its word (reported, not required)`);
116
+ else err('chart-contrast', `${theme}: ${at(a.name)} on the chart ground (${at(CHART_GROUND)}) is ${fx(r)}:1, under 3`);
117
+ }
118
+ for (const b of CHART.slice(i + 1)) {
119
+ const d = deltaE(v[a.name], v[b.name], v[CHART_GROUND]);
120
+ if (d < 10) err('chart-distinct', `${theme}: ${at(a.name)} and ${at(b.name)} are CIEDE2000 ${d.toFixed(1)} apart, under 10`);
121
+ }
122
+ }
123
+ }
124
+ out.palettes[id] = { set: [...set], errors, reports };
125
+ out.errors.push(...errors.map((e) => ({ ...e, palette: id })));
126
+ out.reports.push(...reports.map((text) => ({ palette: id, text })));
127
+ }
128
+ return out;
129
+ }
130
+
131
+ /** The built-ins plus the kit itself as "default" (no attribute), held to the same checks. */
132
+ export const withDefault = (palettes) => ({ default: { light: {}, dark: {} }, ...palettes });
133
+
134
+ export function formatReport(result) {
135
+ const lines = [];
136
+ for (const [id, r] of Object.entries(result.palettes)) {
137
+ lines.push(`${id}: ${r.errors.length ? `${r.errors.length} errors` : 'passes'} (sets ${r.set.length}: ${r.set.join(', ') || 'nothing'})`);
138
+ for (const e of r.errors) lines.push(` error ${e.kind}: ${e.text}`);
139
+ for (const t of r.reports) lines.push(` report: ${t}`);
140
+ }
141
+ const n = Object.keys(result.palettes).length;
142
+ const withWords = CHART.filter((t) => t.withWords).map((t) => `${t.name} ${fx(ratio(KIT.light[t.name], KIT.light[CHART_GROUND]))}:1`);
143
+ lines.push(`${n} palette${n === 1 ? '' : 's'}: ${result.errors.length} errors, ${result.reports.length} reports. Checked in light, night and navy: every word AA on every ground, the focus ring and the act fill 3:1, the chart tones CIEDE2000 10 apart and 3:1 on the chart ground (the kit's ${withWords.join(' and ')} are drawn beside their words, so their ratio is reported, not required).`);
144
+ return lines.join('\n');
145
+ }
146
+
147
+ /** The CSS to ship for a file's palettes (what --emit writes): each in the built-ins' own form. */
148
+ export function emitPalettes(palettes, file = 'a palette file') {
149
+ return [
150
+ `/* Generated by check-palette --emit from ${file}. Ship this file, not the source: it works on <html> or on any`,
151
+ ` container, nested either way, in light and dark, in fello.css and fello.scoped.css, loaded before or after the kit. */`,
152
+ ...Object.entries(palettes).map(([id, p]) => paletteCss(id, p, 'root', { navy: true })),
153
+ '',
154
+ ].join('\n');
155
+ }
156
+
157
+ /* ---- leftovers: every colour a component reads follows the contract, in light and dark (D15) ----
158
+ For each token a component's style.css (or _house.css) reads, in light and in night: if it paints a colour, the
159
+ contract tokens it derives from, through every var(). One with none is a kit constant no palette can move. The
160
+ deliberate ones are ruled below: brand marks that keep the brand, and pairs whose words would lose AA if their
161
+ ground moved. A transparent value, or black at any alpha (a drop), is not a colour here. */
162
+ const KEEP = {
163
+ '--ws-mask': [null, 'the opaque stop of a mask: only its alpha is read, it never paints'],
164
+ // brand marks, read only by the marketing set
165
+ '--color-brand-navy': [['KitButton', 'Cover'], 'the brand navy: the logo, and the words on the salmon act'],
166
+ '--color-brand-salmon': [['Cover'], 'the brand salmon logo mark'],
167
+ '--color-brand-mid-blue': [['KitButton'], "the brand navy act's gradient"],
168
+ '--color-button-border': [['KitButton'], "the salmon act's foot"],
169
+ '--gradient-btn-primary': [['KitButton'], 'the brand salmon act'],
170
+ '--color-white': [['KitButton'], 'white words on the brand navy act'],
171
+ // the navy ground's fields: white with navy words, a brand pair (read under the navy ground only)
172
+ '--color-gray-2': [['Combobox'], "a chip inside the navy ground's white field, a brand pair with its navy words"],
173
+ };
174
+ const KEEP_DARK = {
175
+ '--ws-inverse': [null, 'the toast, note and footer navy in dark: a brand pair with white words (D12)'],
176
+ '--ws-on-inverse': [null, 'white words on that navy, the other half of the pair'],
177
+ };
178
+ const kept = (rules, n, file) => rules[n] && (!rules[n][0] || rules[n][0].some((c) => file.startsWith(`${c}/`)));
179
+ const paints = (value) => {
180
+ const cols = [...(value || '').matchAll(/#[0-9a-f]{3,8}\b|rgba?\([^()]*\)/gi)].map(([c]) => parseColour(c)).filter(Boolean);
181
+ return cols.some(([r, g, b, a]) => a > 0 && (r || g || b));
182
+ };
183
+ export function leftovers(parts) {
184
+ const DARK = { ...LIGHT, ...NIGHT };
185
+ const found = [];
186
+ for (const [file, css] of parts) {
187
+ const read = new Set(refs(css.replace(/\/\*[\s\S]*?\*\//g, '')).filter((n) => known.has(n)));
188
+ for (const n of read)
189
+ for (const [theme, map] of [['light', LIGHT], ['dark', DARK]]) {
190
+ if (CONTRACT.has(n) || kept(KEEP, n, file) || (theme === 'dark' && kept(KEEP_DARK, n, file))) continue;
191
+ if (!paints(KIT[theme][n])) continue;
192
+ if (!inputsOf(n, map).length) found.push(`${file}: ${n} paints ${KIT[theme][n].slice(0, 60)} in ${theme} and no contract token moves it`);
193
+ }
194
+ }
195
+ return [...new Set(found)];
196
+ }
197
+ /** The kit's own parts: _house.css and every component's style.css, as files (the package ships them). */
198
+ export function componentParts() {
199
+ const dir = join(root, 'design-system/components');
200
+ return [['_house.css', readFileSync(join(dir, '_house.css'), 'utf8')],
201
+ ...readdirSync(dir, { withFileTypes: true }).filter((d) => d.isDirectory() && existsSync(join(dir, d.name, 'style.css')))
202
+ .map((d) => [`${d.name}/style.css`, readFileSync(join(dir, d.name, 'style.css'), 'utf8')])];
203
+ }
204
+
205
+ /* ---- the self-test: planted bad palettes, each caught for the reason it was planted ---- */
206
+ function selftest() {
207
+ const planted = `
208
+ [data-palette="outside"] { --ws-surface-sunk: #f0f0f0; --ws-trey: #dddddd; }
209
+ [data-palette="muted"] { --color-text-muted: #9a9a9a; }
210
+ [data-palette="muted"][data-theme="dark"] { --color-text-muted: #8494a5; }
211
+ [data-palette="focus"] { --ws-focus-color: #d0d0d0; }
212
+ [data-palette="focus"][data-theme="dark"] { --ws-focus-color: #1a2631; }
213
+ [data-palette="act"] { --color-text-heading: #c8c8c8; }
214
+ [data-palette="act"][data-theme="dark"] { --color-text-heading: #e8edf3; }
215
+ [data-palette="close"] { --ws-chart-past-half: #2f6189; }
216
+ [data-palette="close"][data-theme="dark"] { --ws-chart-past-half: #9a84ee; }
217
+ [data-palette="faint"] { --ws-chart-navy-light: #d4e2ee; }
218
+ [data-palette="faint"][data-theme="dark"] { --ws-chart-navy-light: #6e9cc4; }
219
+ [data-palette="nodark"] { --ws-edge: #e0e0e0; }
220
+ [data-palette="clean"] { --color-text-interactive: #093555; }
221
+ [data-palette="clean"][data-theme="dark"] { --color-text-interactive: #e8edf3; }
222
+ `;
223
+ const { palettes } = parsePalettes(planted);
224
+ const r = checkPalettes(palettes);
225
+ const kinds = (id) => new Set(r.palettes[id].errors.map((e) => e.kind));
226
+ const fails = [];
227
+ const expect = (cond, what) => cond || fails.push(what);
228
+ const outside = r.palettes.outside.errors.map((e) => e.text).join(' ');
229
+ expect(kinds('outside').has('contract') && /--color-bg-page/.test(outside), 'a derived token is refused, pointing at the contract token it derives from');
230
+ expect(/nearest contract token is --ws-tray\b/.test(outside), 'an unknown token gets the nearest contract name');
231
+ expect(kinds('muted').has('word') && !kinds('muted').has('contract'), 'a muted word under AA is an error');
232
+ expect(kinds('focus').has('focus') && r.palettes.focus.errors.some((e) => e.text.startsWith('dark:')), 'a weak focus ring is caught in light and in dark');
233
+ expect(kinds('act').has('act'), 'a pale act fill is caught');
234
+ expect(kinds('close').has('chart-distinct') && !kinds('close').has('chart-contrast'), 'two chart tones too close are caught');
235
+ expect(kinds('faint').has('chart-contrast'), 'a faint chart tone is caught');
236
+ expect(!r.palettes.nodark.errors.length && r.palettes.nodark.reports.some((t) => /^dark: no --ws-edge, so it takes the kit's dark --ws-edge \(#1e2a36\)/.test(t)), 'a missing dark value is reported and falls back to the kit\'s dark, not the light one');
237
+ expect(!r.palettes.clean.errors.length, 'a sound palette passes');
238
+ // what --emit writes reads back as the same palette, and nests on the theme around it and inside it
239
+ const emitted = emitPalettes(palettes, 'the self-test');
240
+ const back = parsePalettes(emitted).palettes;
241
+ expect(JSON.stringify(back.clean) === JSON.stringify({ light: palettes.clean.light, dark: palettes.clean.dark }), 'an emitted palette parses back to itself');
242
+ expect(back.nodark.dark['--ws-edge'] === NIGHT['--ws-edge'], 'an emitted palette fills a missing dark value with the kit\'s dark one');
243
+ expect(/@scope \(\[data-theme="dark"\]\) \{\n {2}\[data-palette="clean"\]/.test(emitted) && /@scope \(\[data-palette="clean"\]\) \{\n {2}\[data-theme="dark"\]/.test(emitted), 'an emitted palette carries the @scope rules that let it nest');
244
+ const built = checkPalettes(withDefault(builtIns()));
245
+ expect(!built.errors.length, 'the built-in palettes pass');
246
+ // the leftover check: a planted part that reads two kit constants is caught, a part that reads the contract is not
247
+ const plantedLeft = leftovers([['Planted/style.css', '.ws-planted { background: var(--color-gray-1); border-color: var(--color-ai-cyan); color: var(--color-text-body); box-shadow: var(--ws-shadow-pop); }']]);
248
+ expect(['--color-gray-1', '--color-ai-cyan'].every((n) => plantedLeft.some((l) => l.includes(n))) && plantedLeft.every((l) => /--color-gray-1|--color-ai-cyan/.test(l)), `a planted leftover is caught, and only it (got ${JSON.stringify(plantedLeft)})`);
249
+ expect(leftovers([['Planted/style.css', '.ws-planted { color: var(--color-brand-salmon) }']]).length === 2, 'a brand constant ruled for one component is a leftover in another');
250
+ const kit = leftovers(componentParts());
251
+ expect(!kit.length, `the kit has no leftover (got ${kit.length}: ${kit.slice(0, 3).join('; ')})`);
252
+ if (fails.length) {
253
+ console.error(`check-palette self-test failed:\n ${fails.join('\n ')}\n\n${formatReport(r)}`);
254
+ process.exit(1);
255
+ }
256
+ console.log(`check-palette self-test: ${Object.keys(palettes).length} planted palettes, each caught for its reason; emitted palettes parse back and carry their @scope rules; a planted leftover is caught; the built-ins pass and the kit has no leftover`);
257
+ }
258
+
259
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
260
+ const args = process.argv.slice(2);
261
+ if (args.includes('--selftest')) selftest();
262
+ else if (args.includes('--emit')) {
263
+ const files = args.filter((a) => a !== '--emit');
264
+ if (!files.length) { console.error('--emit takes a palette file'); process.exit(2); }
265
+ const all = {};
266
+ for (const file of files) Object.assign(all, parsePalettes(readFileSync(file, 'utf8')).palettes);
267
+ const r = checkPalettes(all);
268
+ console.error(formatReport(r));
269
+ if (r.errors.length || !Object.keys(all).length) process.exit(1);
270
+ process.stdout.write(emitPalettes(all, files.join(', ')));
271
+ } else if (!args.length) {
272
+ const r = checkPalettes(withDefault(builtIns()));
273
+ console.log(formatReport(r));
274
+ const left = leftovers(componentParts());
275
+ console.log(left.length ? `${left.length} leftovers:\n ${left.join('\n ')}` : `leftovers: 0 (every colour a component reads follows a contract token in light and dark; ${Object.keys(KEEP).length + Object.keys(KEEP_DARK).length} brand constants ruled)`);
276
+ if (r.errors.length || left.length) process.exit(1);
277
+ } else {
278
+ let failed = false;
279
+ for (const file of args) {
280
+ const css = readFileSync(file, 'utf8');
281
+ const { palettes, other } = parsePalettes(css);
282
+ console.log(`== ${file}`);
283
+ if (!Object.keys(palettes).length) {
284
+ console.log(' error: no [data-palette="name"] block');
285
+ failed = true;
286
+ continue;
287
+ }
288
+ for (const sel of other) console.log(` report: "${sel}" is not a palette block, so it is not checked`);
289
+ const r = checkPalettes(palettes, { shipped: /@scope\s*\(\s*\[data-palette/.test(css) });
290
+ console.log(formatReport(r));
291
+ failed ||= r.errors.length > 0;
292
+ }
293
+ if (failed) process.exit(1);
294
+ }
295
+ }