@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
@@ -1,5 +1,6 @@
1
- A table variant that ranks people against one number: a rank, a face and a name, a thin bar for the score, the score
2
- itself, with the person's own row lit so they find themselves at once.
1
+ A list that ranks people against one number: a rank, a face and a name, a thin bar for the score, the score itself,
2
+ with the person's own row lit so they find themselves at once. Every row shares one grid, so the bars start and end on
3
+ the same lines and no number is ever cut.
3
4
 
4
5
  ## Use it when
5
6
 
@@ -15,80 +16,106 @@ itself, with the person's own row lit so they find themselves at once.
15
16
 
16
17
  ## Anatomy
17
18
 
18
- - Built on `Table`'s row rhythm: `--ws-row-h` (48 px, a row with a face) rather than the dense 40 px, since every row
19
- carries an avatar; 20 px at the sides (`--ws-card-inset`), the card's one content edge.
20
- - Rank: 16 px semibold navy numerals (`--ws-text-value` size, 600 weight), right-aligned in a 20 px column so every digit
21
- lines up.
22
- - Person: a 28 px `Avatar` (`ws-avatar--sm`: neutral disc, navy initials) then their name, 14 px medium navy, truncating rather than
23
- wrapping.
24
- - Score: the system's thin bar, 8 px tall (`--space-xs`) with 1 px corners (`--ws-radius-bar`) on a
25
- `--ws-chart-cell-edge` track, as long as the score against the leader's. Its fill takes the tone of the row's share
26
- of the goal, by meaning, never by rank: teal (`--ws-chart-finished`) at or over the goal, purple
27
- (`--ws-chart-past-half`) past half, salmon (`--ws-chart-under-half`) under half, the same tones `GoalBars` and
28
- `TickRow` use. The number sits at the right, 14 px semibold navy, tabular numerals.
29
- - The person's own row: `--ws-surface-sunk`, the same wash a selected or hovered row takes elsewhere, so it reads as
30
- "you" without a second colour being invented for it.
19
+ - `ol.ws-leaderboard` in a `Card`: one grid (rank, face, name, bar, score) that every row takes as a subgrid, 12 between
20
+ columns; rows at least 48 (`--ws-row-h`, a row with a face), the card's 20 px edge at both sides, a 0.5 px line between
21
+ rows (`--ws-divider`).
22
+ - Rank: 16 / 20 semibold navy numerals, right-aligned in a column at least 20 wide, tabular.
23
+ - Person: a 28 px `Avatar` (`ws-avatar--sm`) then the name, 14 / 20 medium navy. The name column is as wide as the
24
+ longest name, up to 40 % of the list and never under 108; a longer name truncates with an ellipsis and carries a
25
+ `title` (K13).
26
+ - Score: the system's thin bar, 8 tall, 1 px corners, on the empty track (`--ws-track`), as long as the score against
27
+ the leader's. Its fill (`__fill`) takes the tone of the row's share of the goal, by meaning, never by rank: teal
28
+ (`__fill--finished`, `--ws-chart-finished`) at or over the goal, purple (`__fill--past-half`,
29
+ `--ws-chart-past-half`) past half, salmon (`__fill--under-half`, `--ws-chart-under-half`, also the fill's default)
30
+ under half. The number sits at the right, 14 / 20 semibold navy, tabular, in a column as wide
31
+ as the widest score: it never truncates.
32
+ - The person's own row: `--ws-surface-sunk`, also when the row is a link or a button.
33
+ - Narrow (the list under 24rem): the bar drops under the name, from the name's edge to the score's, so every bar is the
34
+ same length; the row grows to fit. The name has its own line there and wraps instead of truncating (a phone has no
35
+ hover to show a `title`); the rank, face and score centre on its line or lines.
31
36
 
32
37
  ## Variants and sizes
33
38
 
34
39
  | Part | Choice | Why |
35
40
  | --- | --- | --- |
36
41
  | Score mark | `TickRow` | When the number is itself a count out of a small whole ("6 of 10 goals") |
37
- | Score mark | A thin bar (this preview) | When the number is a raw count or a percentage with no natural "whole" to tick against |
42
+ | Score mark | A thin bar (this preview) | When the number is a raw count or a percentage with no natural "whole" |
43
+ | Row | `li.ws-leaderboard__row` | A static row |
44
+ | Row | `li > a.ws-leaderboard__row` | A row that opens the person; the `li` passes the grid on |
38
45
 
39
46
  ## States
40
47
 
41
48
  - **Default**: as above.
42
- - **Your row**: `--ws-surface-sunk`, no extra border or icon, so it matches every other "this one is yours" wash in the
43
- system rather than adding a new signal.
44
- - **Tie**: two rows share a rank number; the system does not invent a visual tie-break (no "T-3"), since the number
45
- itself already says so.
46
- - **Hover**: the row tints `--ws-surface-sunk` like any list row, stacking visually with "your row" when they coincide
47
- (a slightly heavier tone is acceptable since both already share the same token).
49
+ - **Your row**: `--ws-surface-sunk`, no extra border or icon.
50
+ - **Tie**: two rows share a rank number; no invented tie-break ("T-3").
51
+ - **Hover** (a row that opens): North's faint navy wash (`--ws-hover-faint`), laid over the row's ground, so your own
52
+ row stays lit under it; its focus ring is drawn inside the row.
48
53
  - **Empty** (nobody has a score yet): the same card, a sentence ("Nobody has logged a call yet") and the act that starts
49
- it, in the band, per the system's empty-state rule; never a leaderboard drawn with every bar at zero.
50
- - **Long name**: truncates with an ellipsis, as any row title does; the full name is never the one piece of information
51
- worth clipping silently, so it carries a `title` attribute.
54
+ it, in the band; never a leaderboard drawn with every bar at zero.
55
+ - **Long name**: truncates past 40 % with a `title`; under 24rem it wraps instead. A short list of short names takes
56
+ only the room it needs.
57
+ - **Narrow**: the bar under the name.
52
58
 
53
59
  ## Behaviour
54
60
 
55
- - Keyboard: each row is a static list item unless it opens something, in which case it is a link `Card` row in the
56
- `Table` pattern, reachable in document order.
57
- - Pointer: hover tints the row; a click (if the row opens a person) behaves like any link row.
58
- - Motion: bars grow from the left on arrival (`ws-grow`, staggered per row up to the six-item cap), matching `GoalBars`'
59
- own arrival motion; reduced motion shows every bar at its final length.
61
+ - Keyboard: a static list unless a row opens something; then each row is a link in document order.
62
+ - Pointer: hover tints a row that opens.
63
+ - Motion: each bar fills from the left on arrival (North's fill, 700 ms on `--ws-ease-settle`), a row's worth apart
64
+ (`--ws-stagger-row`, 45 ms; set `--ws-i` on each fill). Under reduced motion every bar shows at its length.
60
65
 
61
66
  ## Content
62
67
 
63
- - A name is the person's own name when they have consented to be named; otherwise a role ("Team best", "Team average"),
64
- never a name the viewer did not choose to show (the system's rule for any teammate comparison).
65
- - No em dashes; the score is a bare number with its unit implied by the card's title, not repeated in every row.
68
+ - A name is the person's own name when they have consented to be named; otherwise a role ("Team best", "Team average").
69
+ - No em dashes; the score is a bare number with its unit in the card's title, not repeated in every row.
66
70
 
67
71
  ## Props
68
72
 
69
73
  | Name | Type | Meaning |
70
74
  | --- | --- | --- |
71
- | `rows` | `{ rank, name, initials, score, max, isYou? }[]` | Every ranked row, already sorted |
72
- | `scoreOf` | `number` | The value a full-width bar represents, for the fill's percentage |
75
+ | `rows` | `{ rank, name, initials, score, isYou?, href? }[]` | Every ranked row, already sorted |
76
+ | `scoreOf` | `number` | The value a full-width bar represents |
73
77
  | `goal` | `number` | The goal each row's share is measured against, for its bar's tone |
74
78
  | `markKind` | `'bar' \| 'ticks'` | Which mark draws the score |
75
79
 
76
80
  ## Rules
77
81
 
78
- - **The person's row is `--ws-surface-sunk`**, the same token as a hovered or selected row everywhere else, never a new
79
- colour invented to mean "you" (`02-principles.md` 5, "Colour means something").
80
- - **A comparison with a teammate is a role, not a name, when the name is not the person's to share** (`02-principles.md`
81
- 10, "Honest data"; `09-charts.md` "Data rules"), carried over from `GoalBars`' own "you beside others" row.
82
- - **A bar's tone is the row's share of the goal**: finished, past half or under half, never its rank and never an
83
- arbitrary colour per row, so the same tone means the same thing here as in `GoalBars` and `TickRow`.
84
- - **A new shape for rankings**: rankings are a different reading task from a record table (`Table`) or a category
85
- comparison (`GoalBars`), needing a rank column and a highlighted "you" row neither already provides. Derived from
86
- `Table`'s row heights and hairlines, the sunk hover used everywhere for "the current one", and the goal's tones.
82
+ - **The person's row is `--ws-surface-sunk`**, never a new colour invented to mean "you".
83
+ - **A comparison with a teammate is a role, not a name, when the name is not the person's to share.**
84
+ - **A bar's tone is the row's share of the goal**, never its rank.
85
+ - **A number never truncates** (K13), and every bar has the same length (the shared grid).
87
86
 
88
87
  ## Source
89
88
 
90
- - **Derived from**: the system's row heights and hairlines (`Table`'s `--ws-row-h`, 48 px, "a list row with an
91
- avatar"), the sunk-wash "this one is you" rule, the goal's tones by meaning (`GoalBars`), and `GoalTicks`'
92
- `TickRow` for the tick alternative.
93
- - **Precedent**: an intentional addition decided from the system, 26 Oct 2026, since no single screen in the first
94
- product built on the system ranks a team against one number as a table this way.
89
+ - **Derived from**: the row heights and hairlines of `Table`, the sunk "this one is you" wash, the goal's tones
90
+ (`GoalBars`), North's hover wash and fill motion, and the K13 audit (names cut to 108 of 125 to 139 px in a 556 px
91
+ card, a fixed name box).
92
+ - **Precedent**: an intentional addition decided from the system, 26 Oct 2026; one shared grid in 0.5.0 (4 Nov 2026).
93
+
94
+ ## Markup
95
+
96
+ ```html
97
+ <section class="ws-card" aria-label="Cold calls this month, team ranking, goal 50">
98
+ <ol class="ws-leaderboard">
99
+ <li class="ws-leaderboard__row">
100
+ <span class="ws-leaderboard__rank">1</span>
101
+ <span class="ws-avatar ws-avatar--sm" aria-hidden="true">PN</span>
102
+ <span class="ws-leaderboard__name" title="Priya Nair">Priya Nair</span>
103
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--finished" style="width: 100%;"></span></span>
104
+ <span class="ws-leaderboard__score">58</span>
105
+ </li>
106
+ <li class="ws-leaderboard__row ws-leaderboard__row--you">
107
+ <span class="ws-leaderboard__rank">2</span>
108
+ <span class="ws-avatar ws-avatar--sm" aria-hidden="true">RY</span>
109
+ <span class="ws-leaderboard__name" title="You">You</span>
110
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half" style="width: 62%; --ws-i: 1;"></span></span>
111
+ <span class="ws-leaderboard__score">36</span>
112
+ </li>
113
+ </ol>
114
+ </section>
115
+ ```
116
+
117
+ ## Styles
118
+
119
+ `design-system/components/Leaderboard/style.css`, in `bundle.css` and `fello.css`. Dark comes from the tokens
120
+ (`--ws-track`, `--ws-surface-sunk`, the chart tones). The list is a size container named `ws-leaderboard`, so it needs
121
+ its card's width (a card in a shrink-to-fit row collapses: give it a width).
@@ -1,4 +1,4 @@
1
- <!-- @dsCard group="Data" height=508 -->
1
+ <!-- @dsCard group="Data" height=640 -->
2
2
  <!doctype html>
3
3
  <html>
4
4
  <head>
@@ -10,74 +10,109 @@
10
10
  <body>
11
11
  <div class="ws-stage ws-stage--grey">
12
12
 
13
- <figure class="ws-stack" style="width: 420px; max-width: 100%;">
14
- <span class="ws-muted">8 rows against a goal of 50 calls: rank, face and name, a thin bar toned by the row's share of the goal (teal at or over it, purple past half, salmon under half), the number at the right, your own row lit</span>
15
- <section class="ws-card" aria-label="Cold calls this month, team ranking, goal 50">
13
+ <figure class="ws-stack" style="width: 556px;">
14
+ <span class="ws-muted">8 rows against a goal of 900 calls: one shared grid, so every bar starts and ends on the same line; names as wide as the longest up to 40 %, the score never cut; your own row lit</span>
15
+ <section class="ws-card" aria-label="Cold calls this month, team ranking, goal 900">
16
16
  <ol class="ws-leaderboard">
17
17
  <li class="ws-leaderboard__row">
18
18
  <span class="ws-leaderboard__rank">1</span>
19
19
  <span class="ws-avatar ws-avatar--sm" aria-hidden="true">PN</span>
20
20
  <span class="ws-leaderboard__name" title="Priya Nair">Priya Nair</span>
21
- <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--finished ws-grow" style="width: 100%;"></span></span>
22
- <span class="ws-leaderboard__score">58</span>
21
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--finished" style="width: 100%;"></span></span>
22
+ <span class="ws-leaderboard__score">1,058</span>
23
23
  </li>
24
24
  <li class="ws-leaderboard__row">
25
25
  <span class="ws-leaderboard__rank">2</span>
26
26
  <span class="ws-avatar ws-avatar--sm" aria-hidden="true">DA</span>
27
- <span class="ws-leaderboard__name" title="Dana Alvarez">Dana Alvarez</span>
28
- <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--finished ws-grow" style="width: 91%; animation-delay: calc(var(--ws-stagger) * 1);"></span></span>
29
- <span class="ws-leaderboard__score">53</span>
27
+ <span class="ws-leaderboard__name" title="Dana Alvarez-Whitcombe">Dana Alvarez-Whitcombe</span>
28
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--finished" style="width: 91%; --ws-i: 1;"></span></span>
29
+ <span class="ws-leaderboard__score">963</span>
30
30
  </li>
31
31
  <li class="ws-leaderboard__row">
32
32
  <span class="ws-leaderboard__rank">3</span>
33
33
  <span class="ws-avatar ws-avatar--sm" aria-hidden="true">MD</span>
34
- <span class="ws-leaderboard__name" title="Marco Diaz">Marco Diaz</span>
35
- <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half ws-grow" style="width: 78%; animation-delay: calc(var(--ws-stagger) * 2);"></span></span>
36
- <span class="ws-leaderboard__score">45</span>
34
+ <span class="ws-leaderboard__name" title="Marco Diaz-Fernandez de la Vega">Marco Diaz-Fernandez de la Vega</span>
35
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half" style="width: 78%; --ws-i: 2;"></span></span>
36
+ <span class="ws-leaderboard__score">825</span>
37
37
  </li>
38
38
  <li class="ws-leaderboard__row">
39
39
  <span class="ws-leaderboard__rank">4</span>
40
40
  <span class="ws-avatar ws-avatar--sm" aria-hidden="true">SO</span>
41
41
  <span class="ws-leaderboard__name" title="Sam Okafor">Sam Okafor</span>
42
- <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half ws-grow" style="width: 69%; animation-delay: calc(var(--ws-stagger) * 3);"></span></span>
43
- <span class="ws-leaderboard__score">40</span>
42
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half" style="width: 69%; --ws-i: 3;"></span></span>
43
+ <span class="ws-leaderboard__score">730</span>
44
44
  </li>
45
45
  <li class="ws-leaderboard__row ws-leaderboard__row--you">
46
46
  <span class="ws-leaderboard__rank">5</span>
47
47
  <span class="ws-avatar ws-avatar--sm" aria-hidden="true">RY</span>
48
48
  <span class="ws-leaderboard__name" title="You">You</span>
49
- <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half ws-grow" style="width: 62%; animation-delay: calc(var(--ws-stagger) * 4);"></span></span>
50
- <span class="ws-leaderboard__score">36</span>
49
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half" style="width: 62%; --ws-i: 4;"></span></span>
50
+ <span class="ws-leaderboard__score">656</span>
51
51
  </li>
52
52
  <li class="ws-leaderboard__row">
53
53
  <span class="ws-leaderboard__rank">6</span>
54
54
  <span class="ws-avatar ws-avatar--sm" aria-hidden="true">NP</span>
55
55
  <span class="ws-leaderboard__name" title="Nellie Park">Nellie Park</span>
56
- <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half ws-grow" style="width: 50%; animation-delay: calc(var(--ws-stagger) * 5);"></span></span>
57
- <span class="ws-leaderboard__score">29</span>
56
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half" style="width: 50%; --ws-i: 5;"></span></span>
57
+ <span class="ws-leaderboard__score">529</span>
58
58
  </li>
59
59
  <li class="ws-leaderboard__row">
60
60
  <span class="ws-leaderboard__rank">7</span>
61
61
  <span class="ws-avatar ws-avatar--sm" aria-hidden="true">JW</span>
62
62
  <span class="ws-leaderboard__name" title="Jo Whitfield">Jo Whitfield</span>
63
- <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--under-half ws-grow" style="width: 38%; animation-delay: calc(var(--ws-stagger) * 5);"></span></span>
64
- <span class="ws-leaderboard__score">22</span>
63
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--under-half" style="width: 38%; --ws-i: 6;"></span></span>
64
+ <span class="ws-leaderboard__score">402</span>
65
65
  </li>
66
66
  <li class="ws-leaderboard__row">
67
67
  <span class="ws-leaderboard__rank">8</span>
68
68
  <span class="ws-avatar ws-avatar--sm" aria-hidden="true">TL</span>
69
69
  <span class="ws-leaderboard__name" title="Theo Lang">Theo Lang</span>
70
- <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--under-half ws-grow" style="width: 24%; animation-delay: calc(var(--ws-stagger) * 5);"></span></span>
71
- <span class="ws-leaderboard__score">14</span>
70
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--under-half" style="width: 24%; --ws-i: 7;"></span></span>
71
+ <span class="ws-leaderboard__score">254</span>
72
72
  </li>
73
73
  </ol>
74
74
  </section>
75
- <figcaption class="ws-muted">
76
- When names are not the leader's to share, a row beside "You" reads as a role, "Team best" or "Team average", never
77
- a name the person did not consent to show.
78
- </figcaption>
75
+ <figcaption class="ws-muted">When names are not the leader's to share, a row beside "You" reads as a role, "Team best" or "Team average", never a name the person did not consent to show.</figcaption>
79
76
  </figure>
80
77
 
78
+ <div class="ws-stack ws-stack--component" style="width: 320px;">
79
+ <figure class="ws-stack">
80
+ <span class="ws-muted">Under 24rem the bar drops under the name; rows that open a person tint on hover</span>
81
+ <section class="ws-card" aria-label="Deals closed this quarter, goal 10">
82
+ <ol class="ws-leaderboard">
83
+ <li><a class="ws-leaderboard__row" href="#dana">
84
+ <span class="ws-leaderboard__rank">1</span>
85
+ <span class="ws-avatar ws-avatar--sm" aria-hidden="true">DA</span>
86
+ <span class="ws-leaderboard__name" title="Dana Alvarez">Dana Alvarez</span>
87
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--finished" style="width: 100%;"></span></span>
88
+ <span class="ws-leaderboard__score">12</span>
89
+ </a></li>
90
+ <li><a class="ws-leaderboard__row ws-leaderboard__row--you" href="#you">
91
+ <span class="ws-leaderboard__rank">2</span>
92
+ <span class="ws-avatar ws-avatar--sm" aria-hidden="true">RY</span>
93
+ <span class="ws-leaderboard__name" title="You">You</span>
94
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--past-half" style="width: 58%; --ws-i: 1;"></span></span>
95
+ <span class="ws-leaderboard__score">7</span>
96
+ </a></li>
97
+ <li><a class="ws-leaderboard__row" href="#team">
98
+ <span class="ws-leaderboard__rank">3</span>
99
+ <span class="ws-avatar ws-avatar--sm" aria-hidden="true">TA</span>
100
+ <span class="ws-leaderboard__name" title="Team average">Team average</span>
101
+ <span class="ws-leaderboard__bar" aria-hidden="true"><span class="ws-leaderboard__fill ws-leaderboard__fill--under-half" style="width: 33%; --ws-i: 2;"></span></span>
102
+ <span class="ws-leaderboard__score">4</span>
103
+ </a></li>
104
+ </ol>
105
+ </section>
106
+ </figure>
107
+ <figure class="ws-stack">
108
+ <span class="ws-muted">Empty: a sentence and the act that starts it, never bars at zero</span>
109
+ <section class="ws-card ws-card--compound">
110
+ <header class="ws-head"><div><h3 class="ws-title">Nobody has logged a call yet</h3><p class="ws-body ws-card__line">The board fills in as the team calls this month.</p></div></header>
111
+ <div class="ws-band"><div class="ws-band__words"><span class="ws-band__title">Start with your first call</span></div><button type="button" class="ws-btn ws-btn--primary">Log a call</button></div>
112
+ </section>
113
+ </figure>
114
+ </div>
115
+
81
116
  </div>
82
117
  </body>
83
118
  </html>
@@ -0,0 +1,44 @@
1
+ /* ---- Leaderboard: a team ranked by one number, a thin bar toned by each row's share of the goal, your row lit. One grid
2
+ for every row (subgrid), so ranks, bars and scores sit in shared columns: the name column is as wide as the longest
3
+ name up to 40 % (at least 108), and a number never truncates (K13). Under 24rem the bar drops under the name. ---- */
4
+ .ws-leaderboard { container: ws-leaderboard / inline-size; display: grid; grid-template-columns: [rank] minmax(calc(var(--space-md) + var(--space-xxs)), auto) [face] auto [name] fit-content(40%) [bar] minmax(0, 1fr) [score] auto; column-gap: var(--space-sm); border-radius: inherit; }
5
+ /* Each item spans the grid and passes its columns on (an li holding a link row does too), a 0.5 px line between. */
6
+ .ws-leaderboard > *, .ws-leaderboard__row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
7
+ .ws-leaderboard > * + * { border-top: var(--ws-divider); }
8
+ .ws-leaderboard > :first-child { border-top-left-radius: inherit; border-top-right-radius: inherit; }
9
+ .ws-leaderboard > :last-child { border-bottom-left-radius: inherit; border-bottom-right-radius: inherit; }
10
+ .ws-leaderboard__row { align-items: center; min-height: var(--ws-row-h); padding: 0 var(--ws-card-inset); }
11
+ /* A link row takes its li's corners (only the first and last li have any), so a lit row or a line mid-list stays square. */
12
+ .ws-leaderboard > * > .ws-leaderboard__row { border-radius: inherit; }
13
+ /* Two classes, so the lit row stays lit when it is a link or a button (their reset is transparent). */
14
+ .ws-leaderboard .ws-leaderboard__row--you { background-color: var(--ws-surface-sunk); }
15
+ .ws-leaderboard__rank { text-align: right; font-size: var(--ws-text-value); line-height: calc(var(--space-md) + var(--space-xxs)); font-weight: 600; color: var(--color-text-heading); font-variant-numeric: tabular-nums; }
16
+ /* The name: truncates only past 40 % of the list (its title then holds it whole); never under 108. */
17
+ .ws-leaderboard__name { min-width: calc(var(--space-6xl) + var(--space-sm)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); font-weight: 500; color: var(--color-text-heading); }
18
+ /* The system's thin bar: 8 tall, 1 px corners, on the empty track. */
19
+ .ws-leaderboard__bar { position: relative; height: var(--space-xs); border-radius: var(--ws-radius-bar); background: var(--ws-track); overflow: hidden; }
20
+ .ws-leaderboard__fill { position: absolute; inset: 0 auto 0 0; border-radius: var(--ws-radius-bar); background: var(--ws-chart-under-half); transform-origin: left; }
21
+ .ws-leaderboard__fill--finished { background: var(--ws-chart-finished); }
22
+ .ws-leaderboard__fill--past-half { background: var(--ws-chart-past-half); }
23
+ .ws-leaderboard__fill--under-half { background: var(--ws-chart-under-half); }
24
+ .ws-leaderboard__score { text-align: right; white-space: nowrap; font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); font-weight: 600; color: var(--color-text-heading); font-variant-numeric: tabular-nums; }
25
+ /* A row that opens the person: North's faint wash on hover, laid over the row's own ground (so your lit row stays lit),
26
+ nothing moves. */
27
+ a.ws-leaderboard__row, button.ws-leaderboard__row { width: 100%; border: 0; background-color: transparent; text-align: left; text-decoration: none; color: inherit; cursor: pointer; transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
28
+ a.ws-leaderboard__row:focus-visible, button.ws-leaderboard__row:focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
29
+ @media (hover: hover) { a.ws-leaderboard__row:hover, button.ws-leaderboard__row:hover { background-image: linear-gradient(var(--ws-hover-faint), var(--ws-hover-faint)); } }
30
+ /* Arrival: each bar fills from the left (North's fill, settling), a row's worth apart; set --ws-i on each fill. */
31
+ @media (prefers-reduced-motion: no-preference) {
32
+ .ws-leaderboard__fill { animation: ws-grow var(--ws-duration-fill) var(--ws-ease-settle) backwards; animation-delay: calc(var(--ws-i, 0) * var(--ws-stagger-row)); }
33
+ }
34
+ /* Narrow: the bar under the name, from the name's edge to the score's, so every bar is the same length; the rank, the
35
+ face and the score centre on the name's line (or lines). */
36
+ @container ws-leaderboard (max-width: 24rem) {
37
+ .ws-leaderboard__row { grid-template-columns: [rank] minmax(calc(var(--space-md) + var(--space-xxs)), auto) [face] auto [name] minmax(0, 1fr) [score] auto; gap: var(--space-xs) var(--space-sm); padding-block: var(--space-sm); }
38
+ .ws-leaderboard__rank { grid-area: 1 / rank; }
39
+ .ws-leaderboard__row > .ws-avatar { grid-area: 1 / face; }
40
+ /* The name has its own line here, and a phone has no hover to show a title, so a long name wraps instead of cutting. */
41
+ .ws-leaderboard__name { grid-area: 1 / name; min-width: 0; overflow: visible; white-space: normal; overflow-wrap: anywhere; }
42
+ .ws-leaderboard__score { grid-area: 1 / score; }
43
+ .ws-leaderboard__bar { grid-area: 2 / name / 3 / -1; }
44
+ }
@@ -0,0 +1,222 @@
1
+ A list and the thing picked from it, side by side: the list one column wide, the open item two, on one three-column
2
+ grid. Seth's Review inbox (313:2671) and North's board (a chart and the list that drives it), with Tally's vendors.
3
+
4
+ ## Use it when
5
+
6
+ - People go through items one at a time and act on each (reviews to reply to, leads to work, tools to cut), with the
7
+ list in view so they keep their place.
8
+ - Picking a row in a short list changes a chart or a detail beside it (North's board, Tally's "Vendor by vendor").
9
+
10
+ ## Not for
11
+
12
+ - Records to compare across columns: use `Table`.
13
+ - A detail that opens over the page: use `Panel` (a drawer) from a list.
14
+ - One detail at a time with no list: a `Card`.
15
+
16
+ ## Anatomy
17
+
18
+ - `.ws-list-detail` (a size container) holding `__grid`: three equal columns; the list (`__list`) takes one, the detail
19
+ (`__detail`) two. The list's right edge lines up with the first of three tiles above it on the same grid. A list that
20
+ needs a set width instead (Tally's vendor picker and North's board list, 362) sets `--ws-list-detail-list` on
21
+ `.ws-list-detail`; the detail takes the rest.
22
+ - Joined (default): one rim, the tray grey (`--ws-tray`), 1 px round and 1 px between the panes, so a single grey line
23
+ splits them; 8 px corners outside, each surface rounding only its outer corners (7), so the split stays clean.
24
+ - `--apart` (Seth's inbox): each pane its own rim, 8 apart.
25
+ - `--list-end` (North's board, Tally's vendors): the list at the end, the detail first. The list stays first in the DOM.
26
+ - A pane: its cream surface (`__surface`) and, under it on the rim, an optional foot (`__foot`: at least 32, 8 above and
27
+ below, 14 at the sides, 12 / 16 body words, a figure in 600, a needs-you figure in salmon's AA word; its stars are `--ws-chart-under-half`). Where its two
28
+ parts do not fit side by side (a phone) the second drops under the first, at the right, instead of both wrapping.
29
+ - The list's head (`__head`, Seth): 12 above and below, 16 in, 20 out, 14 apart: a select-all box (`Checkbox --quiet`,
30
+ Seth's status box) and a filter (`Segmented --clear --stretch`: no ground, the items sharing the width; buttons with
31
+ `aria-pressed`, since it filters rather than switching panes); a 0.5 px line under it.
32
+ - The list's scroller (`__scroll`): side by side, the list takes the detail's height and scrolls inside it. While rows
33
+ are hidden below, its last 24 px fade out; the fade lifts at the end (a scroll-driven fade, as `Table`'s far edge),
34
+ and a list that fits shows none.
35
+ - An inbox row (`__rows > li`): 12 above and below, 16 in, 20 out, 14 apart: a box (the skinned `Checkbox`, centred on
36
+ the meta line, a 24 target, 40 on touch), then the open button (`__open`, its hit area the whole row): a meta line
37
+ (`__meta`: 12 px stars 2 apart, the date taking the room, the source; 13 / 18 medium muted, 8 apart) and, 4 under, one
38
+ line of the thing (`__text`, 14 / 20 navy, an ellipsis and a `title`). A 0.5 px line between rows, drawn inside the
39
+ top of the row, so a row's 66 includes it as in Seth's frame.
40
+ - A picker list: `SideList`'s `__picks` on the list's surface.
41
+ - The detail's body (`__body`): 16 inside (North), `--roomy` 28 above and below and 32 at the sides (Seth); parts 24
42
+ apart. Its parts: a pager head (`__pager`: the item's meta, "4 of 10", and `__pager-acts`, two square steppers,
43
+ `IconButton --solid` at 32 with 6 px corners, up and down), a title and line (`__words`, 2 apart), a top row
44
+ (`__top`, Tally's vendor head: the words at the left taking the room, the controls in one row at the right on the
45
+ title's line, 16 between the two and 8 between controls, such as two `ws-filter-pick`s and a `Checkbox --sm`; where
46
+ both do not fit the controls drop under the words, 12 under), a quote (`__quote`, 22 / 30 navy), a sunk draft
47
+ (`Plate`), the acts (`__acts`, 8 apart, pinned to the foot of the detail: the primary first, then Seth's grey act,
48
+ `Button --well`, and a trailing check act at the end) and a last line (`__note`, 13 / 18 medium muted).
49
+ - A foot's needs-you figure (`__foot-needs`): salmon's AA word (`--ws-word-needs`), its figure in 600 (`<b>`).
50
+
51
+ ## Variants and sizes
52
+
53
+ | Variant | What changes |
54
+ | --- | --- |
55
+ | Joined (default) | One rim, a 1 px split |
56
+ | `--apart` | Two rims, 8 apart |
57
+ | `--list-end` | The list at the end |
58
+ | `--ws-list-detail-list` | The list's width in place of its third, the detail taking the rest (Tally and North: 362, `calc(var(--space-9xl) * 1.81)`); set on `.ws-list-detail`, either end |
59
+ | `--fill` | The pair is the rest of a height-bound column: the list scrolls, the detail's acts sit at its foot, the detail scrolls if it must |
60
+ | Narrow (under 48rem) | Stacked, 12 apart, each pane its own rim, the list first and as tall as its rows; a `--fill` pair scrolls as one |
61
+ | A detail under 30rem | The pager head takes two lines: the stars and the meta, then "4 of 10" and the steppers at the right, 8 under (the detail is a size container named `ws-list-detail-pane`) |
62
+
63
+ ## States
64
+
65
+ - **Default**: as above.
66
+ - **Current row** (`aria-current="true"` on the open button): the sunk cream (`--ws-surface-sunk`), the words semibold at
67
+ the same size, so no row moves (Seth's frame grew the row 2 px; the kit does not). No left bar.
68
+ - **Hover** (a row): North's faint wash (`--ws-hover-faint`), not on the current one.
69
+ - **Focus**: the 2 px navy ring drawn inside the row.
70
+ - **A row ticked**: its box only; the row stays where it is until the list is refreshed.
71
+ - **Empty list**: the list's surface says so in a sentence (`__empty`: 14 / 20 body words on the rows' edges, 24 above
72
+ and below: "No reviews need a reply"), the detail shows the next step or nothing.
73
+ - **Loading**: ghost lines (`__ghost ws-shimmer`) in the rows and the detail; still under reduced motion.
74
+ - **Long words**: a row's line truncates with a `title`; the detail's words wrap.
75
+
76
+ ## Behaviour
77
+
78
+ - Keyboard: each row's box and its open button are two stops; the steppers move to the previous and next item; the
79
+ scroller is a focusable region.
80
+ - Pointer: clicking a row anywhere but its box opens it.
81
+ - Motion: a picked row's fill slides to it and takes its height (North's "List highlight slide", `springs.highlight`
82
+ .45 / .14), plain CSS with no script and no new markup: the current row is a CSS anchor and `__rows`' `::before` is
83
+ the travelling fill, under the rows, their lines and their words, its four edges easing on motion's own
84
+ `spring(.45, .14)` written out as `linear()` (800 ms; `--ws-spring-highlight-duration` and `--ws-spring-highlight`). At
85
+ rest the current row paints its own fill (the 0.4 look); while the list is pointed at or holds focus the travelling
86
+ fill takes its place, so a pick made then slides. Rows that change places (a sort, a filter, a reply that files a
87
+ review away) glide with `reorder()` (see `KanbanBoard`). The old detail leaves and the new one arrives on
88
+ `springs.swap`, details settling in after it (`ws-settle`). Without CSS anchor positioning the fill moves at once;
89
+ under reduced motion the fill and the detail change at once. Try it in the preview: click a row.
90
+
91
+ ## Content
92
+
93
+ - A row's one line is the item's own words (the review, the lead's need), never a status alone.
94
+ - The detail's acts are verbs with their objects ("Copy reply", "Open your Zillow reviews"); the primary first.
95
+ - No em dashes.
96
+
97
+ ## Rules
98
+
99
+ - **The list is one column of the page's grid**, so it lines up with tiles above it; it takes a set width
100
+ (`--ws-list-detail-list`) only where the design sets one (Tally's and North's 362 picker beside a chart).
101
+ - **The list never pushes the page**: side by side it scrolls in the detail's height.
102
+ - **Reading order is the DOM order** (list, then detail) at every width; `--list-end` only moves the list visually.
103
+ - **A current row never changes size.**
104
+
105
+ ## Source
106
+
107
+ **Derived from**: Seth's Review inbox (313:2671: the 3-column grid 8 apart, the list head and rows, the open review),
108
+ Tally's vendor board (319:3445: two trays joined by one grey line) and North's board (style1.css 173 to 206: detail and
109
+ list joined, 16 inside, a 31 px foot, list first on a phone), on the kit's rim (D5) and type steps (D6). **Precedent**:
110
+ new in 0.5.0 (4 Nov 2026).
111
+
112
+ ## Markup
113
+
114
+ The inbox (Seth's Review inbox, `--apart --fill`: the list with its head, the open review with its pager, draft, acts
115
+ and last line):
116
+
117
+ ```html
118
+ <div class="ws-list-detail ws-list-detail--apart ws-list-detail--fill">
119
+ <div class="ws-list-detail__grid">
120
+ <section class="ws-list-detail__list" aria-label="Reviews">
121
+ <div class="ws-list-detail__surface">
122
+ <div class="ws-list-detail__head">
123
+ <label class="ws-check ws-check--quiet"><input type="checkbox" aria-label="Select every review"><span class="ws-check__box" aria-hidden="true"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 6.25L4.75 8.5L9.5 3.75"/></svg></span></label>
124
+ <div class="ws-segmented ws-segmented--clear ws-segmented--stretch" role="group" aria-label="Show">
125
+ <button type="button" class="ws-segmented__item" aria-pressed="true">Needs a reply <span class="ws-tnum">10</span></button>
126
+ <button type="button" class="ws-segmented__item" aria-pressed="false">All <span class="ws-tnum">100</span></button>
127
+ </div>
128
+ </div>
129
+ <div class="ws-list-detail__scroll" role="region" aria-label="Reviews, scrolls" tabindex="0">
130
+ <ul class="ws-list-detail__rows">
131
+ <li>
132
+ <label class="ws-check"><input type="checkbox" aria-label="Replied to the Oct 2 review"><span class="ws-check__box" aria-hidden="true"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 6.25L4.75 8.5L9.5 3.75"/></svg></span></label>
133
+ <button type="button" class="ws-list-detail__open" aria-current="true">
134
+ <span class="ws-list-detail__meta"><span class="ws-list-detail__stars" role="img" aria-label="5 of 5 stars"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.9 4.1 4.4.5-3.3 3 .9 4.4L8 11.3l-3.9 2.2.9-4.4-3.3-3 4.4-.5z"/></svg><!-- five in all --></span><span>Oct 2</span><span>Zillow</span></span>
135
+ <span class="ws-list-detail__text" title="Knew the market inside out and got us a great price.">Knew the market inside out and got us a great price.</span>
136
+ </button>
137
+ </li>
138
+ </ul>
139
+ </div>
140
+ </div>
141
+ </section>
142
+ <section class="ws-list-detail__detail" aria-label="Review from Oct 2">
143
+ <div class="ws-list-detail__surface">
144
+ <div class="ws-list-detail__body ws-list-detail__body--roomy">
145
+ <div class="ws-list-detail__pager">
146
+ <span class="ws-list-detail__stars ws-list-detail__stars--lg" role="img" aria-label="5 of 5 stars"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.9 4.1 4.4.5-3.3 3 .9 4.4L8 11.3l-3.9 2.2.9-4.4-3.3-3 4.4-.5z"/></svg><!-- five in all --></span>
147
+ <span>Zillow, Oct 2, 2026</span>
148
+ <span class="ws-tnum">4 of 10</span>
149
+ <span class="ws-list-detail__pager-acts">
150
+ <button type="button" class="ws-icon-btn ws-icon-btn--solid" aria-label="Previous review"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 10l4-4 4 4"/></svg></button>
151
+ <button type="button" class="ws-icon-btn ws-icon-btn--solid" aria-label="Next review"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button>
152
+ </span>
153
+ </div>
154
+ <p class="ws-list-detail__quote">“Knew the market inside out and got us a great price.”</p>
155
+ <div class="ws-plate ws-plate--draft">
156
+ <div class="ws-plate__head">
157
+ <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.5 16.5V9l6.5-5.5L16.5 9v7.5Z"/><path d="M8 16.5v-4h4v4"/></svg>
158
+ <p class="ws-section">Grow drafted a reply in your voice</p>
159
+ <button type="button" class="ws-btn ws-btn--text">Edit</button>
160
+ </div>
161
+ <p class="ws-body">Thank you both! It was a pleasure helping you price it right.</p>
162
+ </div>
163
+ <div class="ws-list-detail__acts">
164
+ <button type="button" class="ws-btn ws-btn--primary ws-btn--lg">Copy reply</button>
165
+ <button type="button" class="ws-btn ws-btn--well ws-btn--lg">Open your Zillow reviews</button>
166
+ <label class="ws-check"><input type="checkbox"><span class="ws-check__box" aria-hidden="true"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 6.25L4.75 8.5L9.5 3.75"/></svg></span><span class="ws-check__words"><span class="ws-check__label">Mark as replied</span></span></label>
167
+ </div>
168
+ <p class="ws-list-detail__note">Grow never posts for you. Copy the reply and paste it on Zillow.</p>
169
+ </div>
170
+ </div>
171
+ </section>
172
+ </div>
173
+ </div>
174
+ ```
175
+
176
+ The vendor board (Tally, `--list-end` joined: the list a set 362, a top row in the detail, its foot on the rim; the
177
+ picks are `SideList`'s):
178
+
179
+ ```html
180
+ <div class="ws-list-detail ws-list-detail--list-end" style="--ws-list-detail-list: calc(var(--space-9xl) * 1.81);">
181
+ <div class="ws-list-detail__grid">
182
+ <section class="ws-list-detail__list" aria-label="Tools">
183
+ <div class="ws-list-detail__surface">
184
+ <div class="ws-list-detail__scroll">
185
+ <ul class="ws-side-list__picks">
186
+ <li><button type="button" class="ws-side-list__pick" aria-current="true" style="--ws-dot: var(--ws-chart-past-half);"><span class="ws-side-list__dot" aria-hidden="true"></span><span class="ws-side-list__main"><span class="ws-side-list__pick-name">Luxury Presence</span><span class="ws-side-list__sub">Website and IDX, Q1 2027</span></span><span class="ws-side-list__end"><span class="ws-side-list__figure">$20,400</span><span class="ws-side-list__sub ws-side-list__sub--needs">saves $20,400</span></span></button></li>
187
+ </ul>
188
+ </div>
189
+ </div>
190
+ </section>
191
+ <section class="ws-list-detail__detail" aria-label="Luxury Presence">
192
+ <div class="ws-list-detail__surface">
193
+ <div class="ws-list-detail__body">
194
+ <div class="ws-list-detail__top">
195
+ <div class="ws-list-detail__words">
196
+ <h3 class="ws-title">Luxury Presence comes off in Q1 2027</h3>
197
+ <p class="ws-body">Fello websites and IDX home search replace it. Estimated from list prices.</p>
198
+ </div>
199
+ <div>
200
+ <button type="button" class="ws-filter-pick" aria-haspopup="menu" aria-expanded="false" aria-label="Kind: Website and IDX"><span class="ws-filter-pick__value">Website and IDX</span><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5l3 3 3-3"/></svg></button>
201
+ <label class="ws-check ws-check--sm"><input type="checkbox"><span class="ws-check__box" aria-hidden="true"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 6.25L4.75 8.5L9.5 3.75"/></svg></span><span class="ws-check__words"><span class="ws-check__label">Cancelled</span></span></label>
202
+ </div>
203
+ </div>
204
+ <p class="ws-cluster"><span class="ws-figure ws-figure--pane">$20,400</span><span class="ws-body">a year</span></p>
205
+ </div>
206
+ </div>
207
+ <div class="ws-list-detail__foot"><span>Website and IDX, estimate</span><span class="ws-list-detail__foot-needs"><b>Saves $20,400</b> a year</span></div>
208
+ </section>
209
+ </div>
210
+ </div>
211
+ ```
212
+
213
+ An empty list keeps its surface and says so: `<div class="ws-list-detail__surface"><p class="ws-list-detail__empty">No
214
+ reviews need a reply.</p></div>`.
215
+
216
+ ## Styles
217
+
218
+ `design-system/components/ListDetail/style.css`, in `bundle.css` and `fello.css`; a picker list also needs `SideList`'s
219
+ styles, a vendor head's pickers `FilterBar`'s. Dark and the navy ground come from the tokens (`--ws-tray`,
220
+ `--color-bg-page`, `--ws-divider`, `--ws-edge`,
221
+ `--ws-surface-sunk`). `.ws-list-detail` is a size container named `ws-list-detail`; with `--fill` its parent must be a
222
+ height-bound flex column.