@felloinnovations/brand-kit 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/AGENTS.md +198 -24
  2. package/CHANGELOG.md +197 -0
  3. package/README.md +34 -7
  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 +134 -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 +357 -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 +52 -16
  249. package/design-system/components/Skeleton/preview.html +35 -23
  250. package/design-system/components/Skeleton/style.css +22 -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 +4091 -744
  335. package/design-system/tokens.json +1314 -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 +110 -11
  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 +174 -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 +1292 -62
  361. package/tokens/tokens.md +588 -61
  362. package/tokens/tokens.root.css +1298 -62
  363. package/tokens/tokens.ts +1677 -64
@@ -15,10 +15,9 @@ A set of mutually exclusive choices, each a native radio under a skin, grouped b
15
15
 
16
16
  ## Anatomy
17
17
 
18
- - Dot: 16px (`--ws-icon`) circle, `radius-full` corners, 1px (`--ws-line`) edge in `--ws-edge-card` at rest.
19
- - Chosen: the ring turns `--color-text-heading` (navy) and a smaller navy disc fills its centre; the outer ring is never
20
- fully filled (that look belongs to `Checkbox`).
21
- - Label: 14px medium, `--color-text-heading`, beside the dot; an optional description line in `.ws-muted` (`--color-text-muted`) under it.
18
+ - Dot: 18 px circle with a 1.5 px navy edge at rest, in `Checkbox`'s language (Seth's action box), drawn inside.
19
+ - Chosen: an 8 px navy disc in its centre; the ring is never filled (that look belongs to `Checkbox`).
20
+ - Label: 14 / 20 medium navy, 10 px from the dot; an optional description line in `.ws-muted` under it, 2 apart.
22
21
  - Group: a `fieldset` with no border of its own, a `legend` at 12px medium `--color-text-body` above the options.
23
22
 
24
23
  ## Variants and sizes
@@ -28,33 +27,33 @@ A set of mutually exclusive choices, each a native radio under a skin, grouped b
28
27
  | Plain | label only | a short, self-explaining option ("Easy", "5 more") |
29
28
  | With description | label plus a muted line under it | an option that needs one more clause ("Needs a higher plan") |
30
29
 
31
- There is one size.
30
+ There is one size (18). A choice among two to four short options may instead be a `Segmented` of native radios.
32
31
 
33
32
  ## States
34
33
 
35
- - **Default (not chosen):** the cream dot with the fine grey edge.
36
- - **Hover:** no change to the dot itself at rest; a surrounding row may take a faint tint where it sits in a list.
37
- - **Focus:** the kit's focus halo around the dot, from the native input's own focus.
34
+ - **Default (not chosen):** the cream dot with its navy ring.
35
+ - **Hover (a mouse only):** the dot takes the navy wash while it is not chosen.
36
+ - **Focus:** the kit's ring, 2 px navy, 2 px out, round the dot.
38
37
  - **Pressed:** the native input's own press.
39
- - **Selected (chosen):** a navy ring and a navy inner disc. **The system's rule is navy, not the kit's salmon**: the
40
- kit's `Radio` draws its chosen state in salmon, which the reference implementation overrides, since navy matches the
41
- rest of the chosen states.
38
+ - **Selected (chosen):** the navy ring and the 8 px navy disc, popping in (`--ws-ease-pop`). Navy, never the kit's
39
+ salmon.
42
40
  - **Pending / loading:** not applicable.
43
41
  - **Disabled:** 50% opacity on the row, pointer events off; a disabled, chosen dot still shows its navy centre.
44
- - **Error:** the kit's `RadioGroup` defines none of its own; a group needing one draws a message below it, as a field
45
- does.
42
+ - **Error:** a group that needs a choice takes the salmon's AA step on each ring (`aria-invalid="true"`) and a message
43
+ below the group.
46
44
  - **Empty:** a group may start with nothing chosen, in which case every dot is at rest.
47
45
  - **Long label:** the label wraps to a second line; the dot stays aligned to the first line.
48
46
  - **Narrow:** the group stacks in one column regardless of width; options never wrap into a row of their own.
49
- - **Touch:** the whole label is the tap target, not the 16px dot alone.
47
+ - **Touch:** one line is a 40 px row; the whole label is the target.
48
+ - **Width:** each label is as wide as its words, never the whole column, so a click far from them does nothing.
49
+ - **Dark:** the ring and the disc become the light ink.
50
50
 
51
51
  ## Behaviour
52
52
 
53
53
  - Keyboard: a native `<input type="radio">` group; Tab reaches the group once, arrow keys move between its options and
54
54
  choose as they go (the platform's own radio-group behaviour).
55
55
  - Pointer / touch: clicking or tapping anywhere in the label chooses it.
56
- - Motion: the ring and inner disc's colour and opacity transition over `--ws-duration-instant` on `--ws-ease-soft`;
57
- nothing moves under `prefers-reduced-motion: reduce`.
56
+ - Motion: the disc pops in over the settle beat on `--ws-ease-pop`; still under reduced motion.
58
57
 
59
58
  ## Content
60
59
 
@@ -94,5 +93,29 @@ There is one size.
94
93
 
95
94
  ## Source
96
95
 
97
- **Derived from**: the kit's own `Radio`/`RadioGroup` contracts, overridden for the system's navy-when-chosen rule.
96
+ **Derived from**: the kit's own `Radio`/`RadioGroup` contracts, overridden for the system's navy-when-chosen rule;
97
+ 0.5.0 draws it in `Checkbox`'s language (Seth's 18 px box with a 1.5 px navy edge; Seth draws no radio).
98
98
  **Precedent**: the first product built on the system.
99
+
100
+ ## Markup
101
+
102
+ ```html
103
+ <fieldset class="ws-radio-group">
104
+ <legend class="ws-radio-group__legend">Lead temperature</legend>
105
+ <label class="ws-radio">
106
+ <input type="radio" name="temperature" checked>
107
+ <span class="ws-radio__dot" aria-hidden="true"></span>
108
+ <span class="ws-radio__words"><span class="ws-radio__label">Cold</span></span>
109
+ </label>
110
+ <label class="ws-radio">
111
+ <input type="radio" name="temperature">
112
+ <span class="ws-radio__dot" aria-hidden="true"></span>
113
+ <span class="ws-radio__words"><span class="ws-radio__label">Warm</span><span class="ws-muted">Replied once, then gone quiet</span></span>
114
+ </label>
115
+ </fieldset>
116
+ ```
117
+
118
+ ## Styles
119
+
120
+ `design-system/components/Radio/style.css`: `ws-radio-group` and `__legend`; `ws-radio` with `--disabled`; `__dot`,
121
+ `__words`, `__label`.
@@ -1,4 +1,4 @@
1
- <!-- @dsCard group="Fields" height=300 -->
1
+ <!-- @dsCard group="Fields" height=440 -->
2
2
  <!doctype html>
3
3
  <html>
4
4
  <head>
@@ -32,6 +32,21 @@
32
32
  </label>
33
33
  </fieldset>
34
34
 
35
+ <fieldset class="ws-radio-group" style="max-width: 320px;">
36
+ <legend class="ws-radio-group__legend">When Grow checks again (one is required)</legend>
37
+ <label class="ws-radio">
38
+ <input type="radio" name="when" aria-invalid="true" aria-describedby="r-err">
39
+ <span class="ws-radio__dot" aria-hidden="true"></span>
40
+ <span class="ws-radio__words"><span class="ws-radio__label">Every week, on the morning after the team's Monday call</span></span>
41
+ </label>
42
+ <label class="ws-radio">
43
+ <input type="radio" name="when" aria-invalid="true" aria-describedby="r-err">
44
+ <span class="ws-radio__dot" aria-hidden="true"></span>
45
+ <span class="ws-radio__words"><span class="ws-radio__label">Once a month</span></span>
46
+ </label>
47
+ <p class="ws-field-msg ws-field-msg--error" id="r-err">Choose when Grow checks again</p>
48
+ </fieldset>
49
+
35
50
  <fieldset class="ws-radio-group">
36
51
  <legend class="ws-radio-group__legend">How many more leads</legend>
37
52
  <label class="ws-radio">
@@ -0,0 +1,21 @@
1
+ /* ---- Radio: a native radio under a skin, in Checkbox's language (Seth's 18 px, 1.5 px navy edge at rest): a ring, an
2
+ 8 px navy dot when chosen; a group is a fieldset with its legend. ---- */
3
+ .ws-radio-group { display: flex; flex-direction: column; gap: var(--space-xxs); margin: 0; padding: 0; border: 0; min-width: 0; }
4
+ .ws-radio-group__legend { padding: 0 0 var(--space-xxs); font-size: var(--ws-text-dense); line-height: var(--space-md); font-weight: 500; color: var(--color-text-body); }
5
+ .ws-radio { --ws-radio: calc(var(--space-md) + var(--space-3xs)); --ws-radio-line: calc(var(--space-md) + var(--space-xxs)); --ws-radio-edge: var(--color-text-heading); position: relative; display: flex; align-items: flex-start; gap: var(--space-xs-plus); width: fit-content; max-width: 100%; padding: var(--space-xxs) 0; cursor: pointer; }
6
+ .ws-radio input { position: absolute; width: var(--ws-line); height: var(--ws-line); margin: 0; opacity: 0; }
7
+ .ws-radio__dot { position: relative; flex-shrink: 0; width: var(--ws-radio); height: var(--ws-radio); margin-top: calc((var(--ws-radio-line) - var(--ws-radio)) / 2); border: 0; border-radius: var(--radius-full); background: var(--color-bg-page); box-shadow: inset 0 0 0 calc(var(--ws-line) * 1.5) var(--ws-radio-edge); }
8
+ .ws-radio__dot::after { content: ""; position: absolute; inset: 0; width: var(--space-xs); height: var(--space-xs); margin: auto; border-radius: var(--radius-full); background: var(--color-text-heading); opacity: 0; transform: scale(var(--ws-pop-scale)); }
9
+ .ws-radio input:checked + .ws-radio__dot::after { opacity: 1; transform: none; }
10
+ .ws-radio input:focus-visible + .ws-radio__dot { outline: var(--ws-focus-width) solid var(--ws-focus-color); outline-offset: var(--ws-focus-offset); }
11
+ .ws-radio input[aria-invalid="true"]:not(:checked) + .ws-radio__dot { --ws-radio-edge: var(--ws-word-needs); }
12
+ @media (hover: hover) {
13
+ .ws-radio:hover input:not(:checked, :disabled) + .ws-radio__dot { background-image: linear-gradient(var(--ws-hover-strong), var(--ws-hover-strong)); }
14
+ }
15
+ @media (prefers-reduced-motion: no-preference) {
16
+ .ws-radio__dot::after { transition: opacity var(--ws-duration-instant) var(--ws-ease-soft), transform var(--ws-duration-settle) var(--ws-ease-pop); }
17
+ }
18
+ .ws-radio__words { display: flex; min-width: 0; flex-direction: column; gap: var(--space-3xs); }
19
+ .ws-radio__label { font-size: var(--ws-text-body); line-height: var(--ws-radio-line); color: var(--color-text-heading); font-weight: 500; }
20
+ .ws-radio--disabled, .ws-radio:has(input:disabled) { opacity: 0.5; cursor: not-allowed; }
21
+ @media (pointer: coarse) { .ws-radio { padding-block: calc((var(--ws-touch) - var(--ws-radio-line)) / 2); } }
@@ -14,9 +14,11 @@ The person against their team on one skill: a strip of fine ticks from 0 to 100%
14
14
 
15
15
  **Anatomy**
16
16
 
17
- - Strip: `--space-2xl` (40 px) tall, the unreached part a fine repeating line every `--space-xxs` (4 px) in `--ws-edge-slot`; the reached part twice as wide, salmon, from `--ws-chart-salmon-fade` to `--color-brand-salmon`, uncovered left to right when the card first shows.
17
+ - Strip: `--space-2xl` (40 px) tall, the unreached part a fine repeating line every `--space-xxs` (4 px) in `--ws-edge-slot`; the reached part twice as wide, salmon, from `--ws-chart-salmon-fade` to `--ws-chart-under-half`, uncovered left to right when the card first shows.
18
18
  - Mark: a line `calc(--ws-hairline * 3)` wide standing from the strip's foot up to `calc(--space-2xl + --space-md)` (56 px), one label row (14 px) higher for each row its label is raised, in the mark's own colour.
19
- - Label: 11 px semibold (`--ws-text-micro`) beside its mark's top, in the mark's AA-safe word colour; it sits right of the mark by default, or left where right would run off the card, on a row above the strip raised further when two would otherwise touch (`placeLabels`, pure and tested). In fello.css the row is data: `style="--lane: 1"` on a raised mark and its label, `style="--lanes: 1"` on `.ws-rank-bar__wrap` for its highest row.
19
+ - Label: 11 px semibold (`--ws-text-micro`) beside its mark's top, in the mark's AA-safe word colour; it sits right of the mark by default, or left where right would run off the card, on a row above the strip raised further when two would otherwise touch (`placeLabels`, pure and tested). In fello.css the row is data: `style="--lane: 1"` on a raised mark and its label, `style="--lanes: 1"` on `.ws-rank-bar__wrap` for its highest row. Fixed markup cannot measure, so raise a label
20
+ wherever it could touch its neighbour in the narrowest column the card meets (at 320, labels of two marks under about
21
+ 40% apart touch).
20
22
  - Quarters: 0, 25, 50, 75, 100%, in 11 px medium muted (`--ws-text-micro`, the floor; the first product drew them at 10), under the strip, their own ends held inside it.
21
23
  - Each mark's colour: you salmon (words `--ws-word-needs`), team average `--ws-chart-past-half` (words the same, it is AA itself), team best `--ws-chart-finished` (words `--ws-word-positive`).
22
24
 
@@ -27,6 +29,10 @@ The person against their team on one skill: a strip of fine ticks from 0 to 100%
27
29
  | Any mark `null` | That mark and its label are left out (no team best yet, or the person has not been scored) |
28
30
  | Labels collide | `placeLabels` turns the losing label to the strip's other side, or raises it a row, trying every arrangement and keeping the plainest: fewest turned, fewest rows, lowest |
29
31
 
32
+ **Parts and modifiers**
33
+
34
+ - `.ws-rank-bar__quarters`: the 0, 25, 50, 75, 100% row under the strip (11 px medium muted); each span placed with `left`.
35
+
30
36
  **States**
31
37
 
32
38
  - Default: the person's filled ticks uncover left to right (`ws-wipe`) over `--ws-duration-calm`; each mark's line grows up from the foot, its label fading in after.
@@ -63,8 +69,29 @@ The person against their team on one skill: a strip of fine ticks from 0 to 100%
63
69
  resolved, here.
64
70
  - Marks keep the system's exact colours; their labels take the AA step (`--ws-word-needs`, `--ws-word-positive`; the purple is AA on its own).
65
71
  - Never an axis or a grid beyond the quarters' own four marks; a tick strip, not a bar chart, carries the share.
72
+ - The CSS never moves a label on its own: the app places each one (its side with `left` or `right`, its row with
73
+ `--lane`, the wrap's `--lanes`) so no two touch at the narrowest width the card meets. With all three marks in a
74
+ 300 px card, the best's label goes up a row (the preview's narrow example).
66
75
 
67
76
  **Source**
68
77
 
69
78
  **Derived from**: the system's data-tone rules for a tick strip, and the privacy rule that a teammate shown beside the
70
79
  person is a role, never a name. **Precedent**: the first product built on the system.
80
+
81
+ **Markup**
82
+
83
+ ```html
84
+ <div class="ws-dots ws-rank-bar ws-rank-bar--in-card">
85
+ <figure role="img" aria-label="Team rankings: you 20%, team average 46%">
86
+ <div class="ws-rank-bar__caption-row" aria-hidden="true"><span class="ws-rank-bar__caption">Team rankings</span><span class="ws-rank-bar__goal">Goal is 40%</span></div>
87
+ <div class="ws-rank-bar__wrap" style="--lanes: 0;" aria-hidden="true">
88
+ <div class="ws-rank-bar__strip"><span class="ws-rank-bar__remaining" style="--you: 20%;"></span><span class="ws-rank-bar__filled ws-wipe" style="--you: 20%;"></span></div>
89
+ <span class="ws-rank-bar__mark" style="left: 20%; background: var(--ws-chart-under-half);"></span>
90
+ <span class="ws-rank-bar__label" style="left: calc(20% + var(--space-xxs)); color: var(--ws-word-needs);">You 20%</span>
91
+ </div>
92
+ </figure>
93
+ </div>
94
+ ```
95
+
96
+ **Styles**: `RankBar/style.css`, in `bundle.css` and `fello.css` as `.ws-rank-bar`. Every value is a token. `.ws-rank-bar` itself has no styles: it is the hook that
97
+ names the chart (and the root for its modifiers); its parts carry the styles.
@@ -26,17 +26,17 @@
26
26
  <span class="ws-rank-bar__caption">Team rankings</span>
27
27
  <span class="ws-rank-bar__goal">Goal is 40%</span>
28
28
  </div>
29
- <div class="ws-rank-bar__wrap" aria-hidden="true">
29
+ <div class="ws-rank-bar__wrap" style="--lanes: 1;" aria-hidden="true">
30
30
  <div class="ws-rank-bar__strip">
31
31
  <span class="ws-rank-bar__remaining" style="--you: 20%;"></span>
32
32
  <span class="ws-rank-bar__filled ws-wipe" style="--you: 20%;"></span>
33
33
  </div>
34
- <span class="ws-rank-bar__mark" style="left: 20%; background: var(--color-brand-salmon);"></span>
34
+ <span class="ws-rank-bar__mark" style="left: 20%; background: var(--ws-chart-under-half);"></span>
35
35
  <span class="ws-rank-bar__label" style="left: calc(20% + var(--space-xxs)); color: var(--ws-word-needs);">You 20%</span>
36
36
  <span class="ws-rank-bar__mark" style="left: 46%; background: var(--ws-chart-past-half);"></span>
37
- <span class="ws-rank-bar__label" style="left: calc(46% + var(--space-xxs)); color: var(--ws-chart-past-half);">Team avg 46%</span>
38
- <span class="ws-rank-bar__mark" style="left: 81%; background: var(--ws-chart-finished);"></span>
39
- <span class="ws-rank-bar__label" style="right: calc(19% + var(--space-xxs)); color: var(--ws-word-positive);">Team best 81%</span>
37
+ <span class="ws-rank-bar__label" style="left: calc(46% + var(--space-xxs)); color: var(--ws-word-past-half);">Team avg 46%</span>
38
+ <span class="ws-rank-bar__mark" style="--lane: 1; left: 81%; background: var(--ws-chart-finished);"></span>
39
+ <span class="ws-rank-bar__label" style="--lane: 1; right: calc(19% + var(--space-xxs)); color: var(--ws-word-positive);">Team best 81%</span>
40
40
  </div>
41
41
  <div class="ws-rank-bar__quarters" aria-hidden="true">
42
42
  <span style="left: 0;">0%</span>
@@ -69,10 +69,10 @@
69
69
  <span class="ws-rank-bar__filled ws-wipe" style="--you: 88%;"></span>
70
70
  </div>
71
71
  <span class="ws-rank-bar__mark" style="left: 46%; background: var(--ws-chart-past-half);"></span>
72
- <span class="ws-rank-bar__label" style="right: calc(54% + var(--space-xxs)); color: var(--ws-chart-past-half);">Team avg 46%</span>
72
+ <span class="ws-rank-bar__label" style="right: calc(54% + var(--space-xxs)); color: var(--ws-word-past-half);">Team avg 46%</span>
73
73
  <span class="ws-rank-bar__mark" style="left: 81%; background: var(--ws-chart-finished);"></span>
74
74
  <span class="ws-rank-bar__label" style="right: calc(19% + var(--space-xxs)); color: var(--ws-word-positive);">Team best 81%</span>
75
- <span class="ws-rank-bar__mark" style="--lane: 1; left: 88%; background: var(--color-brand-salmon);"></span>
75
+ <span class="ws-rank-bar__mark" style="--lane: 1; left: 88%; background: var(--ws-chart-under-half);"></span>
76
76
  <span class="ws-rank-bar__label" style="--lane: 1; right: calc(12% + var(--space-xxs)); color: var(--ws-word-needs);">You 88%</span>
77
77
  </div>
78
78
  <div class="ws-rank-bar__quarters" aria-hidden="true">
@@ -86,19 +86,21 @@
86
86
  </div>
87
87
 
88
88
  <div class="ws-stack">
89
- <span class="ws-muted">Narrow (300 px): the strip still carries every mark</span>
89
+ <span class="ws-muted">Narrow (300 px): every mark; the best's label is raised a row so it clears the average's</span>
90
90
  <div style="width: 300px; max-width: 100%;">
91
91
  <div class="ws-card ws-card--inset ws-rank-bar">
92
- <figure role="img" aria-label="Team rankings: You 20%, Team average 46%">
93
- <div class="ws-rank-bar__wrap" aria-hidden="true">
92
+ <figure role="img" aria-label="Team rankings: You 20%, Team average 46%, Team best 81%">
93
+ <div class="ws-rank-bar__wrap" style="--lanes: 1;" aria-hidden="true">
94
94
  <div class="ws-rank-bar__strip">
95
95
  <span class="ws-rank-bar__remaining" style="--you: 20%;"></span>
96
96
  <span class="ws-rank-bar__filled ws-wipe" style="--you: 20%;"></span>
97
97
  </div>
98
- <span class="ws-rank-bar__mark" style="left: 20%; background: var(--color-brand-salmon);"></span>
98
+ <span class="ws-rank-bar__mark" style="left: 20%; background: var(--ws-chart-under-half);"></span>
99
99
  <span class="ws-rank-bar__label" style="left: calc(20% + var(--space-xxs)); color: var(--ws-word-needs);">You 20%</span>
100
100
  <span class="ws-rank-bar__mark" style="left: 46%; background: var(--ws-chart-past-half);"></span>
101
- <span class="ws-rank-bar__label" style="left: calc(46% + var(--space-xxs)); color: var(--ws-chart-past-half);">Team avg 46%</span>
101
+ <span class="ws-rank-bar__label" style="left: calc(46% + var(--space-xxs)); color: var(--ws-word-past-half);">Team avg 46%</span>
102
+ <span class="ws-rank-bar__mark" style="--lane: 1; left: 81%; background: var(--ws-chart-finished);"></span>
103
+ <span class="ws-rank-bar__label" style="--lane: 1; right: calc(19% + var(--space-xxs)); color: var(--ws-word-positive);">Team best 81%</span>
102
104
  </div>
103
105
  <div class="ws-rank-bar__quarters" aria-hidden="true">
104
106
  <span style="left: 0;">0%</span>
@@ -0,0 +1,18 @@
1
+ /* ---- RankBar: the person against their team on one skill, a strip of fine ticks with a line for each mark ---- */
2
+ /* Each mark's line and words sit on a row (style="--lane: n", 0 just above the strip); the strip's wrap makes room for
3
+ its highest row (style="--lanes: n"). Positions and tones are data on the marks. */
4
+ .ws-rank-bar--in-card { padding: 0 var(--ws-card-inset) var(--space-xs); }
5
+ .ws-rank-bar__caption-row { display: flex; align-items: baseline; justify-content: space-between; padding-top: var(--space-lg); }
6
+ .ws-rank-bar__caption { font-size: var(--ws-text-meta); font-weight: 600; line-height: var(--space-md); color: var(--color-text-heading); }
7
+ .ws-rank-bar__goal { font-size: var(--ws-text-meta); color: var(--color-text-muted); }
8
+ .ws-rank-bar__wrap { --lanes: 0; position: relative; margin-top: calc(var(--space-lg) - var(--space-xxs) / 2); padding-top: calc(var(--space-md) + var(--lanes) * var(--space-md)); }
9
+ .ws-rank-bar__strip { position: relative; height: var(--space-2xl); background: var(--color-bg-page); }
10
+ /* "in srgb": a palette's stops are derived (color-mix), and a gradient blends those in Oklab unless told; sRGB is how the kit's own stops have always blended (D15). */
11
+ .ws-rank-bar__remaining { position: absolute; inset: 0; left: var(--you); background-image: repeating-linear-gradient(90deg in srgb, var(--ws-edge-slot) 0 var(--ws-line), transparent var(--ws-line) var(--space-xxs)); }
12
+ .ws-rank-bar__filled { position: absolute; inset: 0; width: var(--you); background: linear-gradient(90deg in srgb, var(--ws-chart-salmon-fade), var(--ws-chart-under-half)); -webkit-mask-image: repeating-linear-gradient(90deg, var(--color-text-heading) 0 calc(var(--space-xxs) / 2), transparent calc(var(--space-xxs) / 2) var(--space-xxs)); mask-image: repeating-linear-gradient(90deg, var(--color-text-heading) 0 calc(var(--space-xxs) / 2), transparent calc(var(--space-xxs) / 2) var(--space-xxs)); }
13
+ .ws-rank-bar__mark, .ws-rank-bar__label { --lane: 0; position: absolute; }
14
+ .ws-rank-bar__mark { bottom: 0; width: calc(var(--ws-hairline) * 3); height: calc(var(--space-2xl) + var(--space-md) + var(--lane) * var(--space-md)); transform: translateX(-50%); }
15
+ .ws-rank-bar__label { bottom: calc(var(--space-2xl) + var(--space-md) + var(--lane) * var(--space-md) - var(--space-xs-plus)); white-space: nowrap; font-size: var(--ws-text-micro); font-weight: 600; line-height: var(--space-sm); }
16
+ .ws-rank-bar__quarters { position: relative; height: var(--space-sm); margin-top: var(--space-xs); font-size: var(--ws-text-micro); font-weight: 500; color: var(--color-text-muted); }
17
+ .ws-rank-bar__quarters > span { position: absolute; top: 0; }
18
+
@@ -1,9 +1,9 @@
1
- A circle's own share, drawn as one arc or as a ring of fine ticks in up to three tones.
1
+ A circle's own share as one arc, or Seth's split ring: 96 radial ticks in each part's tone, the total in the hole, a key beside.
2
2
 
3
3
  **Use it when**
4
4
 
5
5
  - One share needs to read as a circle rather than a bar: a score, a seal, a review still running.
6
- - A share of 360 degrees splits into named parts that add up to the whole (a week's time between calls, admin and the rest).
6
+ - A whole splits into a few named parts that add up to it (where a team's savings come from, a week's time between calls, admin and the rest).
7
7
  - A live process needs a ring that keeps an even pace while it runs, not one that draws in and stops.
8
8
 
9
9
  **Not for**
@@ -16,9 +16,21 @@ A circle's own share, drawn as one arc or as a ring of fine ticks in up to three
16
16
  **Anatomy**
17
17
 
18
18
  - `Arc`: a plain SVG `<circle>`, `pathLength={1}` so its `stroke-dasharray` is simply `"{share} 1"`; the caller sets its place, radius, stroke width, colour and caps, so one Arc draws every ring (a score, a seal, a timer).
19
- - A final figure: draws in once over `--ws-duration-calm` on the soft curve (`ws-draw`, from nothing to its share), then eases to any new value the same way.
20
- - A live ring: never draws in; it simply follows each new value at a steady, even pace (`--ws-ease-steady`, linear), because it is keeping time with something still running, not announcing a result.
21
- - The week-split ring: a ring of fine radial ticks (about 70 a full turn), each tone's run proportional to its share of the week, the rest of the circle (not on a call) left the empty chart tone; a plate under the ring keeps the card's dots from showing through its hole. A legend beside it names each tone's count and label.
19
+ - A final figure draws in once over 900 ms (`--ws-duration-draw`) on the soft curve (`ws-draw`), then eases to a new
20
+ value. A live ring never draws in; it follows each new value at a steady pace (`--ws-ease-steady`).
21
+ - An arc at 0 draws nothing (no round-cap dot): `stroke-dasharray="0 1"` hides the value circle.
22
+ - The split ring (Seth's v7 Tally, issue #6):
23
+ - Plate: a cream disc 240 across (`.ws-ring__plate`) that keeps the card's dots out from between the ticks.
24
+ - Ticks: 232 across, a 44 px band (the hole is 144), 96 radial ticks with 0.72 degree gaps, the first centred on
25
+ twelve o'clock. The parts' tones are the ticks' `conic-gradient` background, starting `from -1.875deg` with each
26
+ boundary on a whole tick (`n / 96` of the turn), so no tick is split between two tones. A rest of the turn that is
27
+ no part is `--ws-track`.
28
+ - Hole: the total at 36/44 semibold navy and a caption at 13/16 medium navy, centred.
29
+ - Key, 64 beside the ring, or under it when the card is too narrow: rows 24 tall, 6 apart; an 8 px dot, 8, the
30
+ figure (13/16 semibold navy), 12, a 0.5 x 14 rule (`--ws-edge-slot`), 6, the name (13/16 regular navy, wraps).
31
+ The rows share one grid, so the figures sit in one column and the rules line up down the key. A name that wraps
32
+ keeps the dot, the figure and the rule on its first line.
33
+ - In a card: 24 above, 32 below, 20 at the sides (`.ws-ring__split`).
22
34
 
23
35
  **Variants and sizes**
24
36
 
@@ -26,20 +38,35 @@ A circle's own share, drawn as one arc or as a ring of fine ticks in up to three
26
38
  | --- | --- |
27
39
  | `Arc` size | The caller's own radius and stroke width; shown here at 48 px (a tile's small score) and 120 px (a card's larger one) |
28
40
  | `live` | Swaps the draw-in entrance and soft curve for a steady linear follow; never plays the entrance again |
29
- | Ring of ticks | Several `Arc`-style shares drawn as adjoining stretches of one ring instead of one circle's single share |
41
+ | Split ring | Parts drawn as adjoining runs of 96 ticks round one ring, the total in the hole, the key beside |
42
+
43
+ **Parts and modifiers**
44
+
45
+ - `.ws-ring__arc`: the arc's frame: an SVG turned so the share starts at twelve, the centre words over it.
46
+ - `.ws-ring__track`: the empty circle, in `--ws-track` (the kit's one empty-track grey, so a ring at 0 still shows).
47
+ - `.ws-ring__value`: the share: a circle with `pathLength="1"` and `stroke-dasharray="<share> 1"`, round caps; at "0 " it is hidden.
48
+ - `.ws-ring__value--draw`: draws in once on arrival (`--ws-duration-draw`).
49
+ - `.ws-ring__value--live`: follows its value as it changes, at an even pace.
50
+ - `.ws-ring__center`: the words in the arc: semibold navy, tabular.
51
+ - `.ws-ring__center--sm`: centre words at 12 px (a 36 to 48 px arc).
52
+ - `.ws-ring__center--lg`: centre words at 22 px (a 120 px arc).
30
53
 
31
54
  **States**
32
55
 
33
- - Default (a final figure): draws in from nought to its share once, then eases to a changed value over `--duration-calm`.
56
+ - Default (a final figure): draws in from nought to its share once, then eases to a changed value.
57
+ - Split ring: the ticks sweep round once over the 900 ms draw (a registered `--ws-ring-sweep` angle), and the total
58
+ settles in half way through.
34
59
  - Live: no entrance; the arc simply tracks its value at an even pace while the thing it measures keeps running.
35
60
  - Sending: paired with a small working chip (`ws-working`) beside the ring to say it is still counting.
36
61
  - Disabled / error / hover / focus / pressed / selected / empty: do not apply; a ring is always some share, including zero, and never a control.
37
- - Reduced motion: the arc simply sits at its final share; a live ring simply sits at its latest value.
62
+ - Reduced motion: the arc sits at its final share, a live ring at its latest value, the split ring whole.
38
63
 
39
64
  **Behaviour**
40
65
 
41
66
  - `Arc` itself is undecorated SVG: the caller supplies the `aria-label` on an outer `<figure>` or `role="img"`, and keeps the arc itself `aria-hidden`.
42
- - Motion: `transition: stroke-dasharray` over `--ws-duration-calm`; a final figure also plays `ws-draw` once on arrival; a live ring's transition runs on `--ws-ease-steady` instead of the soft curve, and never replays its entrance.
67
+ - Motion: a final figure plays `ws-draw` once on arrival; a live ring's `stroke-dasharray` transition runs on
68
+ `--ws-ease-steady` and never replays its entrance.
69
+ - The split ring's `role="img"` label says the total and every part in one sentence; the plate and key are `aria-hidden`.
43
70
  - No pointer or keyboard behaviour.
44
71
 
45
72
  **Content**
@@ -57,12 +84,35 @@ A circle's own share, drawn as one arc or as a ring of fine ticks in up to three
57
84
 
58
85
  **Rules**
59
86
 
60
- - `Arc` draws nothing at a value of 0 (no stray round-cap dot); a kit gap reported to its owners, not patched here.
87
+ - `Arc` draws nothing at a value of 0 (no stray round-cap dot).
88
+ - At most five parts in a split ring; past that, use `ShareBar`.
89
+ - The total is short: six characters at most ("$46.8K", "40%"), since the hole is 144 across and the total never
90
+ wraps. Round a longer figure ("$146.8K" becomes "$147K").
91
+ - A part in the mid blue takes `--color-text-interactive` (the mid blue in light, lifted in dark); the raw
92
+ `--color-brand-mid-blue` falls under 3:1 on the dark grounds.
61
93
  - The system's exact tones stay on the ring; a legend's words beside a tone take the AA step where the tone itself would fail contrast.
62
94
  - The data behind a week-split ring (profile views, a dated time split) does not exist in every product yet; the card is
63
95
  left out entirely where it has nothing real to draw, never filled with invented shares.
64
96
 
65
97
  **Source**
66
98
 
67
- **Derived from**: the system's rules for a final figure versus a live ring, and the data-tone and legend rules for a
99
+ **Seth**: the split ring, v7 Tally first open (282:1202): 232 / 44 / 96 ticks, read from the frame's own vectors (issue
100
+ #6 said 144 ticks; 144 is the hole's diameter). **Derived from**: the system's rules for a final figure versus a live ring, and the data-tone and legend rules for a
68
101
  split chart. **Precedent**: the first product built on the system.
102
+
103
+ **Markup**
104
+
105
+ ```html
106
+ <div class="ws-dots ws-ring__split" role="img" aria-label="$46.8K of savings: duplicate software $14.2K, unconverted leads $11.6K, the rest $21K">
107
+ <span class="ws-ring__plate" aria-hidden="true">
108
+ <span class="ws-ring__ticks" style="background: conic-gradient(from -1.875deg, var(--ws-chart-under-half) 0 30.21%, var(--ws-chart-past-half) 0 55.21%, var(--ws-track) 0 100%);"></span>
109
+ <span class="ws-ring__hole"><span class="ws-ring__total">$46.8K</span><span class="ws-ring__caption">Potential savings</span></span>
110
+ </span>
111
+ <ul class="ws-ring__legend" aria-hidden="true">
112
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-under-half);"></span><span class="ws-ring__count">$14.2K</span><span class="ws-ring__label">Duplicate software</span></li>
113
+ </ul>
114
+ </div>
115
+ ```
116
+
117
+ **Styles**: `Ring/style.css`, in `bundle.css` and `fello.css` as `.ws-ring`. Every value is a token, except the ticks'
118
+ geometry, which is Seth's (96 ticks, 0.72 degree gaps) drawn as an SVG mask.
@@ -1,4 +1,4 @@
1
- <!-- @dsCard group="Progress" height=607 -->
1
+ <!-- @dsCard group="Progress" height=1060 -->
2
2
  <!doctype html>
3
3
  <html>
4
4
  <head>
@@ -16,13 +16,26 @@
16
16
  <span class="ws-ring__arc" aria-hidden="true">
17
17
  <svg width="48" height="48" viewBox="0 0 48 48">
18
18
  <circle class="ws-ring__track" cx="24" cy="24" r="21.5" stroke-width="5" />
19
- <circle class="ws-ring__value ws-ring__value--draw" cx="24" cy="24" r="21.5" stroke-width="5" stroke="var(--color-brand-navy)" pathLength="1" stroke-dasharray="0.72 1" />
19
+ <circle class="ws-ring__value ws-ring__value--draw" cx="24" cy="24" r="21.5" stroke-width="5" stroke="var(--color-text-heading)" pathLength="1" stroke-dasharray="0.72 1" />
20
20
  </svg>
21
21
  <span class="ws-ring__center ws-ring__center--sm">72%</span>
22
22
  </span>
23
23
  </figure>
24
24
  </div>
25
25
 
26
+ <div class="ws-stack">
27
+ <span class="ws-muted">Arc at 0: the track alone, never a dot</span>
28
+ <figure class="ws-ring" aria-label="0 percent">
29
+ <span class="ws-ring__arc" aria-hidden="true">
30
+ <svg width="48" height="48" viewBox="0 0 48 48">
31
+ <circle class="ws-ring__track" cx="24" cy="24" r="21.5" stroke-width="5" />
32
+ <circle class="ws-ring__value" cx="24" cy="24" r="21.5" stroke-width="5" stroke="var(--color-text-heading)" pathLength="1" stroke-dasharray="0 1" />
33
+ </svg>
34
+ <span class="ws-ring__center ws-ring__center--sm">0%</span>
35
+ </span>
36
+ </figure>
37
+ </div>
38
+
26
39
  <div class="ws-stack">
27
40
  <span class="ws-muted">Arc at 120 px, live: keeps pace on `--ws-ease-steady`, never draws in</span>
28
41
  <figure class="ws-ring" aria-label="A text campaign sending, 40 percent">
@@ -33,12 +46,43 @@
33
46
  </svg>
34
47
  <span class="ws-ring__center ws-ring__center--lg">40%</span>
35
48
  </span>
36
- <span class="ws-chip ws-chip--live" aria-hidden="true"><span class="ws-dot ws-working"></span>Sending live</span>
37
49
  </figure>
38
50
  </div>
39
51
 
52
+ <div class="ws-stack" style="width: 656px; max-width: 100%;">
53
+ <span class="ws-muted">Seth's split ring (v7 Tally): 96 radial ticks, the total in the hole, the key beside</span>
54
+ <section class="ws-card ws-card--compound">
55
+ <header class="ws-head">
56
+ <div>
57
+ <h3 class="ws-title">What a team like yours could save</h3>
58
+ <p class="ws-body">Where a typical 15-agent team's yearly savings come from.</p>
59
+ </div>
60
+ </header>
61
+ <div class="ws-dots ws-ring__split" role="img" aria-label="$46.8K of potential savings: duplicate software $14.2K, unconverted leads $11.6K, admin per deal $8.4K, tools Fia replaces $7.1K, paid lead overlap $5.5K">
62
+ <span class="ws-ring__plate" aria-hidden="true">
63
+ <span class="ws-ring__ticks" style="background: conic-gradient(from -1.875deg, var(--ws-chart-under-half) 0 30.21%, var(--ws-chart-past-half) 0 55.21%, var(--ws-chart-finished) 0 72.92%, var(--color-text-interactive) 0 88.54%, var(--color-text-heading) 0 100%);"></span>
64
+ <span class="ws-ring__hole"><span class="ws-ring__total">$46.8K</span><span class="ws-ring__caption">Potential savings</span></span>
65
+ </span>
66
+ <ul class="ws-ring__legend" aria-hidden="true">
67
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-under-half);"></span><span class="ws-ring__count">$14.2K</span><span class="ws-ring__label">Duplicate software</span></li>
68
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-past-half);"></span><span class="ws-ring__count">$11.6K</span><span class="ws-ring__label">Unconverted leads</span></li>
69
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-finished);"></span><span class="ws-ring__count">$8.4K</span><span class="ws-ring__label">Admin per deal</span></li>
70
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--color-text-interactive);"></span><span class="ws-ring__count">$7.1K</span><span class="ws-ring__label">Tools Fia replaces</span></li>
71
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--color-text-heading);"></span><span class="ws-ring__count">$5.5K</span><span class="ws-ring__label">Paid lead overlap</span></li>
72
+ </ul>
73
+ </div>
74
+ <div class="ws-band">
75
+ <div class="ws-band__words">
76
+ <span class="ws-band__title">Start here: see your real numbers</span>
77
+ <span class="ws-band__how">10 quick questions, about 3 minutes.</span>
78
+ </div>
79
+ <button type="button" class="ws-btn ws-btn--primary ws-btn--lg">Start questions</button>
80
+ </div>
81
+ </section>
82
+ </div>
83
+
40
84
  <div class="ws-stack" style="width: var(--ws-chart-card-w); max-width: 100%;">
41
- <span class="ws-muted">The week-split ring: a ring of fine radial ticks, three tones and a legend</span>
85
+ <span class="ws-muted">The week split: three parts and the rest of the turn empty</span>
42
86
  <section class="ws-card">
43
87
  <header class="ws-head">
44
88
  <div>
@@ -46,14 +90,15 @@
46
90
  <p class="ws-body">Up from 32%. Admin is the easiest thing to trim next.</p>
47
91
  </div>
48
92
  </header>
49
- <div class="ws-dots ws-ring__split" role="img" aria-label="40 percent of the week on calls: Cold calls 14, Live transfers 8, Follow-ups 4">
50
- <span class="ws-ring__ticks" style="background: conic-gradient(var(--ws-chart-under-half) 0% 21.5%, var(--ws-chart-past-half) 21.5% 33.9%, var(--ws-chart-finished) 33.9% 40%, var(--ws-chart-empty) 40% 100%);" aria-hidden="true">
51
- <span class="ws-ring__hole"></span>
93
+ <div class="ws-dots ws-ring__split" role="img" aria-label="40 percent of the week on calls: cold calls 14 hours, live transfers 8, follow-ups 4">
94
+ <span class="ws-ring__plate" aria-hidden="true">
95
+ <span class="ws-ring__ticks" style="background: conic-gradient(from -1.875deg, var(--ws-chart-under-half) 0 21.88%, var(--ws-chart-past-half) 0 34.38%, var(--ws-chart-finished) 0 39.58%, var(--ws-track) 0 100%);"></span>
96
+ <span class="ws-ring__hole"><span class="ws-ring__total">40%</span><span class="ws-ring__caption">of your week</span></span>
52
97
  </span>
53
98
  <ul class="ws-ring__legend" aria-hidden="true">
54
- <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-under-half);"></span><span class="ws-ring__count">14</span><span class="ws-ring__label">Cold calls</span></li>
55
- <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-past-half);"></span><span class="ws-ring__count">8</span><span class="ws-ring__label">Live transfers</span></li>
56
- <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-finished);"></span><span class="ws-ring__count">4</span><span class="ws-ring__label">Follow-ups</span></li>
99
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-under-half);"></span><span class="ws-ring__count">14h</span><span class="ws-ring__label">Cold calls</span></li>
100
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-past-half);"></span><span class="ws-ring__count">8h</span><span class="ws-ring__label">Live transfers</span></li>
101
+ <li class="ws-ring__key"><span class="ws-ring__dot" style="background: var(--ws-chart-finished);"></span><span class="ws-ring__count">4h</span><span class="ws-ring__label">Follow-ups with past clients and their referrals</span></li>
57
102
  </ul>
58
103
  </div>
59
104
  </section>
@@ -0,0 +1,49 @@
1
+ /* ---- Ring: an arc for one share (drawn in once, or live), and Seth's split ring of radial ticks with its key ---- */
2
+ .ws-ring { display: flex; flex-direction: column; align-items: center; gap: var(--space-xs); }
3
+ .ws-ring__arc { position: relative; display: inline-flex; }
4
+ .ws-ring__arc svg { display: block; transform: rotate(-90deg); }
5
+ /* The empty track is the kit's one empty-track grey (as TickStrip, SegmentedProgress, PaceBar), so a ring at 0 shows. */
6
+ .ws-ring__track { fill: none; stroke: var(--ws-track); }
7
+ .ws-ring__value { fill: none; stroke-linecap: round; }
8
+ /* A share of nothing draws nothing: a round cap at 0 would leave a dot. */
9
+ .ws-ring__value[stroke-dasharray^="0 "] { visibility: hidden; }
10
+ @media (prefers-reduced-motion: no-preference) {
11
+ .ws-ring__value--draw { animation: ws-draw var(--ws-duration-draw) var(--ws-ease-soft) backwards; animation-delay: var(--ws-build-at, 0s); }
12
+ .ws-ring__value--live { transition: stroke-dasharray var(--ws-duration-calm) var(--ws-ease-steady); }
13
+ }
14
+ .ws-ring__center { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-weight: 600; color: var(--color-text-heading); font-variant-numeric: tabular-nums; }
15
+ .ws-ring__center--sm { font-size: var(--ws-text-dense); }
16
+ .ws-ring__center--lg { font-size: var(--ws-text-title); }
17
+ /* The split ring (Seth's v7 Tally, issue #6): 232 across, a 44 px band of 96 radial ticks (0.72 degree gaps), each
18
+ part's run its share of the turn (the tones as the band's conic-gradient background), the total in the hole, and
19
+ the key beside it; when the card is too narrow the key drops under the ring. A cream plate 4 px wider than the ring
20
+ keeps the card's dots out from between the ticks. */
21
+ /* The ticks are an SVG mask so their edges are smooth: a circle of radius 94 with a 44 stroke in a 232 box (the band
22
+ 72 to 116), dashed into 96 (pathLength 96, each dash 0.808 of its step), the first one centred on twelve o'clock. Put
23
+ each part's edge on a whole tick: the tones start at from -1.875deg, and part boundaries at n / 96 of the turn. */
24
+ .ws-ring__ticks { --ws-ring-ticks: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 232 232'%3E%3Ccircle cx='116' cy='116' r='94' fill='none' stroke='%23000' stroke-width='44' pathLength='96' stroke-dasharray='0.808 0.192' stroke-dashoffset='0.404' transform='rotate(-90 116 116)'/%3E%3C/svg%3E"); }
25
+ @property --ws-ring-sweep { syntax: "<angle>"; inherits: false; initial-value: 360deg; }
26
+ @keyframes ws-ring-sweep { from { --ws-ring-sweep: 0deg; } }
27
+ .ws-ring__split { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-lg) var(--space-4xl); padding: var(--space-lg) var(--ws-card-inset) var(--space-xl); }
28
+ .ws-ring__plate { position: relative; display: grid; flex-shrink: 0; place-items: center; width: calc(var(--space-9xl) + var(--space-xl) + var(--space-xs)); max-width: 100%; aspect-ratio: 1; border-radius: var(--radius-full); background: var(--color-bg-page); }
29
+ .ws-ring__ticks { position: relative; flex-shrink: 0; width: calc(var(--space-9xl) + var(--space-xl)); max-width: 100%; aspect-ratio: 1; border-radius: var(--radius-full); -webkit-mask-image: var(--ws-ring-ticks), conic-gradient(var(--ws-mask) var(--ws-ring-sweep, 360deg), transparent 0); -webkit-mask-composite: source-in; mask-image: var(--ws-ring-ticks), conic-gradient(var(--ws-mask) var(--ws-ring-sweep, 360deg), transparent 0); mask-composite: intersect; mask-size: 100% 100%; }
30
+ .ws-ring__plate > .ws-ring__ticks { grid-area: 1 / 1; }
31
+ /* The hole: the band is 44 of the 232, so the hole is 144 across; the total and its caption sit centred in it. */
32
+ .ws-ring__hole { position: absolute; inset: 15%; border-radius: var(--radius-full); background: var(--color-bg-page); }
33
+ .ws-ring__plate > .ws-ring__hole { position: relative; inset: auto; grid-area: 1 / 1; display: flex; width: calc(var(--space-7xl) + var(--space-lg)); max-width: 62%; aspect-ratio: 1; flex-direction: column; align-items: center; justify-content: center; gap: 0; text-align: center; }
34
+ .ws-ring__total { font-size: calc(var(--ws-text-figure) + var(--space-xxs)); font-weight: 600; line-height: calc(var(--space-2xl) + var(--space-xxs)); color: var(--color-text-heading); font-variant-numeric: tabular-nums; white-space: nowrap; }
35
+ .ws-ring__caption { font-size: var(--ws-text-control); font-weight: 500; line-height: var(--space-md); color: var(--color-text-heading); }
36
+ @media (prefers-reduced-motion: no-preference) {
37
+ /* The build-in: the ticks sweep round once over the draw (900 ms), the total settles in as they land. */
38
+ .ws-ring__plate > .ws-ring__ticks { animation: ws-ring-sweep var(--ws-duration-draw) var(--ws-ease-soft) backwards; animation-delay: var(--ws-build-at, 0s); }
39
+ .ws-ring__plate > .ws-ring__hole > * { animation: ws-settle var(--ws-duration-settle) var(--ws-ease-soft) backwards; animation-delay: calc(var(--ws-build-at, 0s) + var(--ws-duration-draw) / 2); }
40
+ }
41
+ /* The key: rows 24 tall, 6 apart; an 8 px dot, 8, the figure (13/16 semibold), 12, a 0.5 x 14 rule, 6, the name. Every
42
+ row sits on one grid (subgrid), so the figures share a column and the rules line up down the key. A name that wraps
43
+ keeps the dot, the figure and the rule on its first line. */
44
+ .ws-ring__legend { display: grid; flex: 1 1 0; min-width: min(100%, 11rem); grid-template-columns: auto auto minmax(0, 1fr); row-gap: var(--space-xxs-plus); }
45
+ .ws-ring__key { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; align-items: start; column-gap: var(--space-xs); min-height: var(--space-lg); padding-block: var(--space-xxs); font-size: var(--ws-text-control); line-height: var(--space-md); color: var(--color-text-heading); }
46
+ .ws-ring__dot { flex-shrink: 0; width: var(--space-xs); height: var(--space-xs); margin-top: var(--space-xxs); border-radius: var(--radius-full); }
47
+ .ws-ring__count { flex-shrink: 0; margin-right: var(--space-xxs); font-weight: 600; font-variant-numeric: tabular-nums; }
48
+ .ws-ring__label { display: flex; min-width: 0; align-items: flex-start; gap: var(--space-xxs-plus); }
49
+ .ws-ring__label::before { content: ""; flex-shrink: 0; width: var(--ws-hairline); height: var(--space-sm-plus); margin-top: var(--ws-line); background: var(--ws-edge-slot); }