@felloinnovations/brand-kit 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/AGENTS.md +198 -24
  2. package/CHANGELOG.md +221 -0
  3. package/README.md +38 -9
  4. package/design-system/01-how-claude-decides.md +26 -17
  5. package/design-system/02-principles.md +11 -6
  6. package/design-system/03-colour.md +135 -47
  7. package/design-system/04-type.md +45 -23
  8. package/design-system/05-space-and-layout.md +175 -52
  9. package/design-system/06-sizing-profiles.md +4 -4
  10. package/design-system/07-scrolling.md +66 -20
  11. package/design-system/08-choosing-components.md +50 -22
  12. package/design-system/09-charts.md +71 -28
  13. package/design-system/10-patterns.md +33 -10
  14. package/design-system/11-page-recipes.md +110 -23
  15. package/design-system/12-content.md +2 -1
  16. package/design-system/13-motion.md +330 -47
  17. package/design-system/14-accessibility.md +47 -24
  18. package/design-system/15-ai-experiences.md +11 -6
  19. package/design-system/16-creativity.md +1 -1
  20. package/design-system/17-decisions.md +367 -1
  21. package/design-system/18-checklist.md +79 -16
  22. package/design-system/19-public-pages.md +6 -4
  23. package/design-system/20-palettes.md +275 -0
  24. package/design-system/README.md +30 -17
  25. package/design-system/components/ActionList/README.md +96 -10
  26. package/design-system/components/ActionList/preview.html +52 -7
  27. package/design-system/components/ActionList/style.css +64 -0
  28. package/design-system/components/ActivityGrid/README.md +38 -2
  29. package/design-system/components/ActivityGrid/preview.html +1 -1
  30. package/design-system/components/ActivityGrid/style.css +35 -0
  31. package/design-system/components/AppShell/README.md +205 -75
  32. package/design-system/components/AppShell/preview.html +339 -58
  33. package/design-system/components/AppShell/style.css +154 -0
  34. package/design-system/components/Avatar/README.md +37 -10
  35. package/design-system/components/Avatar/preview.html +13 -4
  36. package/design-system/components/Avatar/style.css +14 -0
  37. package/design-system/components/Badge/README.md +41 -19
  38. package/design-system/components/Badge/preview.html +13 -10
  39. package/design-system/components/Badge/style.css +23 -0
  40. package/design-system/components/Banner/README.md +30 -9
  41. package/design-system/components/Banner/preview.html +1 -1
  42. package/design-system/components/Banner/style.css +19 -0
  43. package/design-system/components/Breadcrumb/README.md +58 -46
  44. package/design-system/components/Breadcrumb/preview.html +16 -16
  45. package/design-system/components/Breadcrumb/style.css +17 -0
  46. package/design-system/components/Button/README.md +113 -55
  47. package/design-system/components/Button/preview.html +98 -25
  48. package/design-system/components/Button/style.css +86 -0
  49. package/design-system/components/Card/README.md +158 -49
  50. package/design-system/components/Card/preview.html +141 -19
  51. package/design-system/components/Card/style.css +115 -0
  52. package/design-system/components/CardBoard/README.md +58 -37
  53. package/design-system/components/CardBoard/preview.html +26 -29
  54. package/design-system/components/ChatCard/README.md +115 -29
  55. package/design-system/components/ChatCard/preview.html +149 -32
  56. package/design-system/components/ChatCard/style.css +49 -0
  57. package/design-system/components/ChatShell/README.md +116 -0
  58. package/design-system/components/ChatShell/preview.html +201 -0
  59. package/design-system/components/ChatShell/style.css +86 -0
  60. package/design-system/components/ChatTurn/README.md +78 -35
  61. package/design-system/components/ChatTurn/preview.html +60 -28
  62. package/design-system/components/ChatTurn/style.css +50 -0
  63. package/design-system/components/CheckRow/README.md +141 -0
  64. package/design-system/components/CheckRow/preview.html +140 -0
  65. package/design-system/components/CheckRow/style.css +88 -0
  66. package/design-system/components/Checkbox/README.md +58 -22
  67. package/design-system/components/Checkbox/preview.html +112 -50
  68. package/design-system/components/Checkbox/style.css +45 -0
  69. package/design-system/components/Chip/README.md +78 -47
  70. package/design-system/components/Chip/preview.html +29 -4
  71. package/design-system/components/Chip/style.css +17 -0
  72. package/design-system/components/Combobox/README.md +44 -11
  73. package/design-system/components/Combobox/preview.html +35 -12
  74. package/design-system/components/Combobox/style.css +54 -0
  75. package/design-system/components/Composer/README.md +137 -35
  76. package/design-system/components/Composer/preview.html +115 -40
  77. package/design-system/components/Composer/style.css +81 -0
  78. package/design-system/components/ConfirmDialog/README.md +73 -52
  79. package/design-system/components/ConfirmDialog/preview.html +70 -26
  80. package/design-system/components/ConfirmDialog/style.css +5 -0
  81. package/design-system/components/Cover/preview.html +3 -3
  82. package/design-system/components/Cover/style.css +19 -0
  83. package/design-system/components/DateInput/README.md +77 -12
  84. package/design-system/components/DateInput/preview.html +27 -12
  85. package/design-system/components/DateInput/style.css +38 -0
  86. package/design-system/components/Dialog/README.md +112 -44
  87. package/design-system/components/Dialog/preview.html +141 -18
  88. package/design-system/components/Dialog/style.css +74 -0
  89. package/design-system/components/EmptyState/README.md +42 -12
  90. package/design-system/components/EmptyState/preview.html +11 -11
  91. package/design-system/components/EmptyState/style.css +9 -0
  92. package/design-system/components/EntryShell/README.md +73 -48
  93. package/design-system/components/EntryShell/preview.html +60 -27
  94. package/design-system/components/EntryShell/style.css +19 -0
  95. package/design-system/components/FeatureGrid/README.md +61 -39
  96. package/design-system/components/FeatureGrid/preview.html +2 -2
  97. package/design-system/components/FeatureGrid/style.css +20 -0
  98. package/design-system/components/FileUpload/README.md +33 -3
  99. package/design-system/components/FileUpload/preview.html +18 -4
  100. package/design-system/components/FileUpload/style.css +23 -0
  101. package/design-system/components/FilterBar/README.md +114 -0
  102. package/design-system/components/FilterBar/preview.html +82 -0
  103. package/design-system/components/FilterBar/style.css +22 -0
  104. package/design-system/components/Fold/README.md +84 -26
  105. package/design-system/components/Fold/preview.html +77 -34
  106. package/design-system/components/Fold/style.css +48 -0
  107. package/design-system/components/Footer/README.md +62 -39
  108. package/design-system/components/Footer/preview.html +5 -5
  109. package/design-system/components/Footer/style.css +30 -0
  110. package/design-system/components/FunnelBars/README.md +24 -3
  111. package/design-system/components/FunnelBars/preview.html +5 -5
  112. package/design-system/components/FunnelBars/style.css +13 -0
  113. package/design-system/components/GoalBars/README.md +54 -10
  114. package/design-system/components/GoalBars/preview.html +69 -28
  115. package/design-system/components/GoalBars/style.css +51 -0
  116. package/design-system/components/GoalTicks/README.md +74 -58
  117. package/design-system/components/GoalTicks/preview.html +41 -118
  118. package/design-system/components/GoalTicks/style.css +37 -0
  119. package/design-system/components/GoalTubes/README.md +29 -3
  120. package/design-system/components/GoalTubes/preview.html +6 -6
  121. package/design-system/components/GoalTubes/style.css +23 -0
  122. package/design-system/components/Heatmap/README.md +133 -0
  123. package/design-system/components/Heatmap/preview.html +168 -0
  124. package/design-system/components/Heatmap/style.css +119 -0
  125. package/design-system/components/Hero/README.md +58 -40
  126. package/design-system/components/Hero/preview.html +1 -1
  127. package/design-system/components/Hero/style.css +19 -0
  128. package/design-system/components/HoverCard/README.md +114 -0
  129. package/design-system/components/HoverCard/preview.html +107 -0
  130. package/design-system/components/HoverCard/style.css +59 -0
  131. package/design-system/components/IconButton/README.md +69 -38
  132. package/design-system/components/IconButton/preview.html +77 -43
  133. package/design-system/components/IconButton/style.css +45 -0
  134. package/design-system/components/Input/README.md +36 -11
  135. package/design-system/components/Input/preview.html +1 -1
  136. package/design-system/components/Input/style.css +23 -0
  137. package/design-system/components/KanbanBoard/README.md +93 -45
  138. package/design-system/components/KanbanBoard/preview.html +159 -43
  139. package/design-system/components/KanbanBoard/style.css +53 -0
  140. package/design-system/components/Kbd/README.md +25 -9
  141. package/design-system/components/Kbd/preview.html +6 -2
  142. package/design-system/components/Kbd/style.css +9 -0
  143. package/design-system/components/KitButton/README.md +81 -47
  144. package/design-system/components/KitButton/preview.html +35 -13
  145. package/design-system/components/KitButton/style.css +46 -0
  146. package/design-system/components/KitCard/README.md +75 -44
  147. package/design-system/components/KitCard/preview.html +18 -10
  148. package/design-system/components/KitCard/style.css +15 -0
  149. package/design-system/components/KitInput/README.md +54 -39
  150. package/design-system/components/KitInput/preview.html +7 -7
  151. package/design-system/components/KitInput/style.css +23 -0
  152. package/design-system/components/Leaderboard/README.md +76 -49
  153. package/design-system/components/Leaderboard/preview.html +61 -26
  154. package/design-system/components/Leaderboard/style.css +44 -0
  155. package/design-system/components/ListDetail/README.md +222 -0
  156. package/design-system/components/ListDetail/preview.html +213 -0
  157. package/design-system/components/ListDetail/style.css +148 -0
  158. package/design-system/components/LoadBar/README.md +79 -0
  159. package/design-system/components/LoadBar/preview.html +40 -0
  160. package/design-system/components/LoadBar/style.css +17 -0
  161. package/design-system/components/MarketingNav/README.md +76 -42
  162. package/design-system/components/MarketingNav/preview.html +37 -8
  163. package/design-system/components/MarketingNav/style.css +45 -0
  164. package/design-system/components/Matrix/README.md +129 -0
  165. package/design-system/components/Matrix/preview.html +99 -0
  166. package/design-system/components/Matrix/style.css +84 -0
  167. package/design-system/components/Menu/README.md +117 -41
  168. package/design-system/components/Menu/preview.html +85 -23
  169. package/design-system/components/Menu/style.css +61 -0
  170. package/design-system/components/Meter/README.md +39 -4
  171. package/design-system/components/Meter/preview.html +18 -1
  172. package/design-system/components/Meter/style.css +23 -0
  173. package/design-system/components/MetricTile/README.md +237 -0
  174. package/design-system/components/MetricTile/preview.html +241 -0
  175. package/design-system/components/MetricTile/style.css +129 -0
  176. package/design-system/components/Morph/README.md +106 -0
  177. package/design-system/components/Morph/preview.html +103 -0
  178. package/design-system/components/Morph/style.css +31 -0
  179. package/design-system/components/Motion/README.md +164 -0
  180. package/design-system/components/Motion/preview.html +2537 -0
  181. package/design-system/components/NumberInput/README.md +27 -8
  182. package/design-system/components/NumberInput/preview.html +21 -5
  183. package/design-system/components/NumberInput/style.css +34 -0
  184. package/design-system/components/PaceBar/README.md +75 -0
  185. package/design-system/components/PaceBar/preview.html +67 -0
  186. package/design-system/components/PaceBar/style.css +25 -0
  187. package/design-system/components/Page/README.md +57 -18
  188. package/design-system/components/Page/preview.html +15 -17
  189. package/design-system/components/Page/style.css +7 -0
  190. package/design-system/components/PageBar/README.md +90 -34
  191. package/design-system/components/PageBar/preview.html +60 -19
  192. package/design-system/components/PageBar/style.css +35 -0
  193. package/design-system/components/PageHero/README.md +87 -51
  194. package/design-system/components/PageHero/preview.html +35 -6
  195. package/design-system/components/PageHero/style.css +31 -0
  196. package/design-system/components/Pagination/README.md +70 -49
  197. package/design-system/components/Pagination/preview.html +24 -15
  198. package/design-system/components/Pagination/style.css +12 -0
  199. package/design-system/components/Panel/README.md +109 -51
  200. package/design-system/components/Panel/preview.html +118 -20
  201. package/design-system/components/Panel/style.css +51 -0
  202. package/design-system/components/PasswordInput/README.md +50 -18
  203. package/design-system/components/PasswordInput/preview.html +71 -21
  204. package/design-system/components/PasswordInput/style.css +31 -0
  205. package/design-system/components/Plate/README.md +75 -17
  206. package/design-system/components/Plate/preview.html +40 -7
  207. package/design-system/components/Plate/style.css +37 -0
  208. package/design-system/components/Popover/README.md +70 -49
  209. package/design-system/components/Popover/preview.html +44 -22
  210. package/design-system/components/Popover/style.css +41 -0
  211. package/design-system/components/PricingTable/README.md +69 -49
  212. package/design-system/components/PricingTable/preview.html +21 -21
  213. package/design-system/components/PricingTable/style.css +21 -0
  214. package/design-system/components/ProgressPetals/README.md +31 -3
  215. package/design-system/components/ProgressPetals/preview.html +14 -15
  216. package/design-system/components/ProgressPetals/style.css +27 -0
  217. package/design-system/components/README.md +151 -0
  218. package/design-system/components/Radio/README.md +40 -17
  219. package/design-system/components/Radio/preview.html +16 -1
  220. package/design-system/components/Radio/style.css +21 -0
  221. package/design-system/components/RankBar/README.md +29 -2
  222. package/design-system/components/RankBar/preview.html +14 -12
  223. package/design-system/components/RankBar/style.css +18 -0
  224. package/design-system/components/Ring/README.md +61 -11
  225. package/design-system/components/Ring/preview.html +55 -10
  226. package/design-system/components/Ring/style.css +49 -0
  227. package/design-system/components/ScoreHero/README.md +91 -0
  228. package/design-system/components/ScoreHero/preview.html +104 -0
  229. package/design-system/components/ScoreHero/style.css +32 -0
  230. package/design-system/components/Section/README.md +72 -33
  231. package/design-system/components/Section/preview.html +46 -16
  232. package/design-system/components/Section/style.css +18 -0
  233. package/design-system/components/Segmented/README.md +100 -55
  234. package/design-system/components/Segmented/preview.html +107 -26
  235. package/design-system/components/Segmented/style.css +94 -0
  236. package/design-system/components/SegmentedProgress/README.md +64 -0
  237. package/design-system/components/SegmentedProgress/preview.html +69 -0
  238. package/design-system/components/SegmentedProgress/style.css +11 -0
  239. package/design-system/components/Select/README.md +47 -12
  240. package/design-system/components/Select/preview.html +28 -2
  241. package/design-system/components/Select/style.css +22 -0
  242. package/design-system/components/ShareBar/README.md +69 -0
  243. package/design-system/components/ShareBar/preview.html +72 -0
  244. package/design-system/components/ShareBar/style.css +25 -0
  245. package/design-system/components/SideList/README.md +147 -0
  246. package/design-system/components/SideList/preview.html +139 -0
  247. package/design-system/components/SideList/style.css +94 -0
  248. package/design-system/components/Skeleton/README.md +53 -16
  249. package/design-system/components/Skeleton/preview.html +35 -23
  250. package/design-system/components/Skeleton/style.css +17 -0
  251. package/design-system/components/Slider/README.md +95 -0
  252. package/design-system/components/Slider/preview.html +49 -0
  253. package/design-system/components/Slider/style.css +13 -0
  254. package/design-system/components/Split/README.md +112 -0
  255. package/design-system/components/Split/preview.html +105 -0
  256. package/design-system/components/Split/style.css +30 -0
  257. package/design-system/components/Starter/README.md +21 -9
  258. package/design-system/components/Starter/preview.html +5 -5
  259. package/design-system/components/Stat/README.md +64 -43
  260. package/design-system/components/Stat/preview.html +3 -3
  261. package/design-system/components/Stat/style.css +15 -0
  262. package/design-system/components/StatTiles/README.md +88 -44
  263. package/design-system/components/StatTiles/preview.html +142 -71
  264. package/design-system/components/StatTiles/style.css +64 -0
  265. package/design-system/components/StatusLine/README.md +85 -0
  266. package/design-system/components/StatusLine/preview.html +49 -0
  267. package/design-system/components/StatusLine/style.css +14 -0
  268. package/design-system/components/StatusScreen/README.md +49 -35
  269. package/design-system/components/StatusScreen/preview.html +20 -5
  270. package/design-system/components/StatusScreen/style.css +9 -0
  271. package/design-system/components/StepLine/README.md +97 -0
  272. package/design-system/components/StepLine/preview.html +108 -0
  273. package/design-system/components/StepLine/style.css +62 -0
  274. package/design-system/components/Stepper/README.md +35 -13
  275. package/design-system/components/Stepper/style.css +13 -0
  276. package/design-system/components/StreakCalendar/README.md +22 -1
  277. package/design-system/components/StreakCalendar/preview.html +10 -10
  278. package/design-system/components/StreakCalendar/style.css +12 -0
  279. package/design-system/components/SuggestionChip/README.md +87 -0
  280. package/design-system/components/SuggestionChip/preview.html +42 -0
  281. package/design-system/components/SuggestionChip/style.css +20 -0
  282. package/design-system/components/Switch/README.md +44 -24
  283. package/design-system/components/Switch/preview.html +23 -8
  284. package/design-system/components/Switch/style.css +31 -0
  285. package/design-system/components/Table/README.md +129 -58
  286. package/design-system/components/Table/preview.html +106 -72
  287. package/design-system/components/Table/style.css +104 -0
  288. package/design-system/components/Tabs/README.md +71 -46
  289. package/design-system/components/Tabs/preview.html +56 -24
  290. package/design-system/components/Tabs/style.css +44 -0
  291. package/design-system/components/Testimonial/README.md +48 -32
  292. package/design-system/components/Testimonial/preview.html +14 -14
  293. package/design-system/components/Testimonial/style.css +12 -0
  294. package/design-system/components/TextArea/README.md +27 -11
  295. package/design-system/components/TextArea/preview.html +8 -2
  296. package/design-system/components/TextArea/style.css +12 -0
  297. package/design-system/components/ThemeSwitch/README.md +116 -0
  298. package/design-system/components/ThemeSwitch/preview.html +57 -0
  299. package/design-system/components/ThemeSwitch/style.css +6 -0
  300. package/design-system/components/TickRow/README.md +52 -36
  301. package/design-system/components/TickRow/preview.html +64 -41
  302. package/design-system/components/TickRow/style.css +23 -0
  303. package/design-system/components/TickStrip/README.md +95 -0
  304. package/design-system/components/TickStrip/preview.html +88 -0
  305. package/design-system/components/TickStrip/style.css +42 -0
  306. package/design-system/components/Tile/README.md +79 -18
  307. package/design-system/components/Tile/preview.html +91 -40
  308. package/design-system/components/Tile/style.css +36 -0
  309. package/design-system/components/Timeline/README.md +68 -48
  310. package/design-system/components/Timeline/preview.html +27 -15
  311. package/design-system/components/Timeline/style.css +25 -0
  312. package/design-system/components/Toast/README.md +70 -38
  313. package/design-system/components/Toast/preview.html +43 -8
  314. package/design-system/components/Toast/style.css +25 -0
  315. package/design-system/components/Tooltip/README.md +93 -35
  316. package/design-system/components/Tooltip/preview.html +69 -15
  317. package/design-system/components/Tooltip/style.css +78 -0
  318. package/design-system/components/TopBar/README.md +67 -53
  319. package/design-system/components/TopBar/preview.html +14 -27
  320. package/design-system/components/TopBar/style.css +35 -0
  321. package/design-system/components/TopicRail/README.md +108 -0
  322. package/design-system/components/TopicRail/preview.html +58 -0
  323. package/design-system/components/TopicRail/style.css +48 -0
  324. package/design-system/components/TrendBars/README.md +52 -14
  325. package/design-system/components/TrendBars/preview.html +65 -32
  326. package/design-system/components/TrendBars/style.css +25 -0
  327. package/design-system/components/TrendLine/README.md +164 -0
  328. package/design-system/components/TrendLine/preview.html +373 -0
  329. package/design-system/components/TrendLine/style.css +155 -0
  330. package/design-system/components/Well/README.md +64 -22
  331. package/design-system/components/Well/preview.html +14 -14
  332. package/design-system/components/Well/style.css +22 -0
  333. package/design-system/components/_house.css +324 -0
  334. package/design-system/components/bundle.css +4086 -744
  335. package/design-system/tokens.json +1310 -124
  336. package/dist/fello-core.css +2 -2
  337. package/dist/fello.css +6 -1369
  338. package/dist/fello.scoped.css +7 -0
  339. package/dist/index.d.ts +109 -10
  340. package/dist/index.js +781 -96
  341. package/dist/palettes.d.ts +464 -0
  342. package/dist/palettes.js +497 -0
  343. package/dist/scoped.d.ts +3 -0
  344. package/dist/scoped.js +16 -0
  345. package/dist/toast.d.ts +14 -0
  346. package/dist/toast.js +33 -29
  347. package/dist/tokens.d.ts +1686 -62
  348. package/dist/tokens.js +1668 -61
  349. package/package.json +20 -4
  350. package/scripts/check-palette.mjs +295 -0
  351. package/scripts/token-model.mjs +552 -0
  352. package/tokens/_dark.css +173 -0
  353. package/tokens/_palettes.css +46 -0
  354. package/tokens/_product.css +266 -53
  355. package/tokens/_source.hubl.css +39 -19
  356. package/tokens/design-tokens.json +319 -0
  357. package/tokens/palettes.ts +504 -0
  358. package/tokens/shadcn-compat.css +19 -19
  359. package/tokens/shadcn-compat.hsl.css +76 -0
  360. package/tokens/tokens.css +1287 -62
  361. package/tokens/tokens.md +587 -61
  362. package/tokens/tokens.root.css +1293 -62
  363. package/tokens/tokens.ts +1677 -64
@@ -1,69 +1,127 @@
1
- A name on hover or keyboard focus, for a control that shows less than it is: an icon, a circle of initials.
1
+ A name on hover or keyboard focus, for a control that shows less than it is; and Seth's navy note, the full words behind a short one.
2
2
 
3
3
  ## Use it when
4
4
 
5
- - An icon-only button's accessible name should also be visible to a sighted pointer or keyboard user ("Close", "Ask about this").
6
- - A circle of initials or a small mark needs its full name available without taking permanent space.
5
+ - An icon-only button's name should also show to a sighted pointer or keyboard user ("Refresh the audit").
6
+ - A short word stands for longer words that some people want to read (a question cut to "A buyer"): the note.
7
7
 
8
8
  ## Not for
9
9
 
10
- - A control's only label: the tip is for the eyes only; the same words must already be the control's `aria-label`, since a touch screen never shows a tip at all.
11
- - A brief confirmation that something happened: use a toast.
12
- - Extra detail someone opts into reading: use `Fold`.
13
- - A native `title` attribute alongside it: the component replaces that, it does not sit beside it.
10
+ - The only way to know what a control does on touch: a touch screen never shows a plain tip, so the icon must read on
11
+ its own.
12
+ - What a chart mark holds (a title, figures, rows): use `HoverCard`.
13
+ - Content someone acts on (a field, a link): use `Popover`.
14
+ - A receipt for something that happened: use `Toast`.
15
+ - A native `title` beside it: this replaces that.
14
16
 
15
17
  ## Anatomy
16
18
 
17
- - A small card above the control: `--ws-radius-card` (8 px) corners, the cream (`--color-bg-page`) background, the fine hairline edge plus the lifted shadow (`--ws-shadow-hairline`, `--ws-shadow-lift`).
18
- - `--space-xs` (8 px) padding, `--space-xxs` (4 px) gap from the control below it.
19
- - Its words: 12 px semibold, navy (`--color-text-heading`), truncated to one line up to `--ws-thing-width`.
20
- - Positioned above the control (`bottom: 100%`), aligned to the control's centre by default, or flush to its start or end edge when centring would run off the side of its container.
19
+ - The wrapper (`ws-tooltip`): an inline box around the control, which the tip is placed against.
20
+ - The tip (`ws-tooltip__tip`): North's compact tip. `--ws-raised` ground, 6 / 10 inside, 6 px corners
21
+ (`--ws-radius-row`), the 0.5 px hairline and the tip drop (`--ws-shadow-hairline`, `--ws-shadow-tip`), 12 / 16
22
+ semibold navy words on one line; a longer name wraps at 440 (or the window less 32) and is never cut. 8 above its control (`--ws-tooltip-gap`), centred on it.
23
+ - The note (`ws-tooltip--note`, Seth 305:2272): the act's navy (`--ws-inverse`, words `--ws-on-inverse`), the brand navy in dark, 4 px corners, 10 / 12
24
+ inside, 14 / 20 regular white words that wrap at 300 (12.71:1), no shadow (in dark, a faint light rim). Its words start where the word
25
+ it explains starts (its 12 px padding hangs to the left). A 12 x 6 caret points down at that word's middle, 4 above
26
+ it; the note sits 10 above.
27
+ - The term (`ws-tooltip__term`): the word the note explains, a button with a 1 px dotted underline 4 under the words,
28
+ the cue that more shows. On touch its hit area is 24 tall while its words keep the line, so a term inside a sentence
29
+ stays on the sentence's baseline.
21
30
 
22
31
  ## Variants and sizes
23
32
 
24
- | `align` | Behaviour |
33
+ | Class | Where |
25
34
  | --- | --- |
26
- | `center` (default) | Centred over the control |
27
- | `start` | Grows to the right of the control's left edge |
28
- | `end` | Grows to the left of the control's right edge |
35
+ | `ws-tooltip` | Centred, above |
36
+ | `ws-tooltip--below` | Under the control |
37
+ | `ws-tooltip--start` | Flush to the control's left edge, for a control near the left of its container |
38
+ | `ws-tooltip--end` | Flush to its right edge (with the note, its words end where the word ends) |
39
+ | `ws-tooltip--note` | Seth's navy note with its caret, left-anchored |
40
+ | `ws-tooltip--note ws-tooltip--below` | The note under its term, the caret turned up, for a term near the top of its card |
41
+ | `ws-tooltip--open` | Shown, whatever the pointer does (a tap toggle, or a preview) |
42
+ | `ws-tooltip--dismissed` | Hidden after Escape, even while hovered or focused |
29
43
 
30
44
  ## States
31
45
 
32
- - **Hidden** (default): opacity 0, not interactive, present for layout timing only.
33
- - **Shown on hover**: after a short pause under the pointer (the hover delay is `--ws-duration-instant` once the pointer has settled), opacity 1.
34
- - **Shown on focus**: appears at once when the control inside gets keyboard focus (`:focus-visible`), never on a click's own focus.
35
- - **Never on touch**: there is no hover on a touch screen, so the tip never appears there; the control's own accessible name must carry the meaning on its own.
46
+ - **Hidden** (default): not shown, takes no presses; still read: a plain tip names its control (`aria-labelledby`), a
47
+ note describes its term (`aria-describedby`).
48
+ - **Hover**: shown once the pointer has rested 40 ms (`--ws-hover-intent`); only for pointers that hover. A bridge
49
+ over the 8 px gap keeps it while the pointer moves onto it.
50
+ - **Keyboard focus**: shown at once when the control has `:focus-visible`, never on a click's own focus.
51
+ - **Tap**: the note toggles on a tap (`ws-tooltip--open`); a plain tip never shows on touch.
52
+ - **Opening** (North's "Tooltip pop-in", js@484880): it pops out of .9 and 4 px toward its control, growing from the
53
+ edge it hangs from, on the pop spring (`--ws-spring-pop`, .35 / .1, 500 ms to rest); the note's caret pops with it.
54
+ - **Leaving**: it fades off over 180 ms (North's tip fade, css:388; `--ws-tooltip-out`) on `--ws-ease-exit`, faster
55
+ than it came.
36
56
 
37
57
  ## Behaviour
38
58
 
39
- - Keyboard: shows immediately when the control inside receives visible keyboard focus; disappears when focus moves on.
40
- - Pointer: shows after a brief hover delay, disappears when the pointer leaves.
41
- - Touch: never shown; nothing to tap to reveal it.
42
- - Motion: opacity fades in over `--ws-duration-instant` (120 ms) on `--ws-ease-soft`; the hover case adds the same duration as a delay before it starts.
43
- - Reduced motion: the fade itself is a plain opacity change already quick enough to keep as is; no separate reduced-motion branch is needed.
59
+ - Keyboard: shows on visible focus, goes when focus moves on. Escape hides it where it is (WCAG 1.4.13): a few lines
60
+ of script add `ws-tooltip--dismissed` on Escape and take it off when the pointer or focus leaves.
61
+ - Pointer: shows after the hover intent, stays while the pointer is on the control, the gap or the tip.
62
+ - Touch: the plain tip never shows; the note opens on a tap of its term and closes on a second tap or a tap outside.
63
+ - The note hangs from its term's start (or its end, `--end`) and never moves itself onto the screen: a term away from
64
+ both edges of a phone gets its place from the app (Floating UI `shift`), or the note runs past the edge.
65
+ - Where the browser places by anchor (CSS anchor positioning), the tip is fixed to its wrapper as its anchor, so no
66
+ scroller, card or stage that clips can cut it (K9): a tip on AppShell's bar or its body's first row shows whole. A
67
+ plain tip with no room on its side turns to the other one (above to below) or slides along its control to stay on the
68
+ screen; the note keeps its side. Without anchor placement, inside a scroller or a card that clips, render the tip in a
69
+ portal (Radix `Tooltip` with its container on a `class="fello"` root in a scoped app), or it is cut.
70
+ - Reduced motion: it simply appears and goes; so does a browser without `linear()`.
71
+ - The preview's last stack is live: point at or tab to each.
44
72
 
45
73
  ## Content
46
74
 
47
- - The same short name as the control's own `aria-label`, never different words: the tip is a visible echo of the accessible name, not a second source of truth.
48
- - One line, no em dash, sentence case for a phrase ("Ask about this"), title case only for a proper name.
75
+ - A plain tip is the control's name: the control points at it with `aria-labelledby` and has no `aria-label` of its
76
+ own, so the words live in one place and are read once.
77
+ - One line, sentence case ("Ask about this").
78
+ - A note holds the full words the term stands for, as they were written, up to three lines.
49
79
 
50
80
  ## Props
51
81
 
52
82
  | Name | Type | Meaning |
53
83
  | --- | --- | --- |
54
- | `label` | `ReactNode` | The name shown in the tip. |
55
- | `align` | `"center"` \| `"start"` \| `"end"` | Where it sits relative to the control. Default `center`. |
84
+ | `label` | `ReactNode` | The words shown in the tip. |
85
+ | `align` | `"center"` \| `"start"` \| `"end"` | Where it sits along the control. Default `center`. |
86
+ | `side` | `"above"` \| `"below"` | Which side it opens on. Default `above`. |
87
+ | `note` | boolean | Seth's navy note with a caret. |
56
88
  | `className` | string | Layout only. |
57
- | `children` | `ReactNode` | The control the tip names. |
89
+ | `children` | `ReactNode` | The control (or the term) the tip names. |
58
90
 
59
91
  ## Rules
60
92
 
61
- - The tip is for the eyes: the same words always sit in the control's own `aria-label` too, so a screen reader and a touch user get the name regardless.
62
- - No native `title` beside it: `Tip` is the one way to show a name on hover in this system.
63
- - Shows at once on keyboard focus, never only after a delay the way hover does: a keyboard user should not have to wait to learn what a button is.
64
- - **Contradiction found, not resolved**: a brief for this preview once asked for "a small navy tip above a control, 12 px white words, 4 px corners." The actual, and only, tooltip in the reference implementation is the opposite: a small cream card with navy 12 px semibold words and 8 px corners, lifted with the hairline and lift shadows. This README and preview follow the source, since code is truth; the navy-with-white-words version does not exist anywhere in the product.
93
+ - The plain tip names its control (`aria-labelledby`); never an `aria-label` and an `aria-describedby` to the same
94
+ words, which a screen reader reads twice.
95
+ - Shows at once on keyboard focus: a keyboard user never waits to learn what a button is.
96
+ - No hover-only content: the note also opens on focus and on a tap (14-accessibility.md).
97
+ - The note is navy (Seth, 305:2272). It resolves the old open question about a navy tip: the label tip stays the light
98
+ card, the note is navy.
65
99
 
66
100
  ## Source
67
101
 
68
- **Derived from**: the system's cream-card, hairline-plus-lift surface treatment, with navy 12 px semibold words.
69
- **Precedent**: the first product built on the system.
102
+ **Derived from**: North's compact tip and its placement (style1.css:386-398, :484-486: 6 px corners, 8 to 10 from its
103
+ mark, edge-flush near a side, the tip drop; js@484880: pop from .94 and 4 px, 120 ms exit), Seth's note (Figma
104
+ 305:2272: navy, 4 px corners, 10 / 12, 14 / 20, 300 wide, 12 x 6 caret over the word, left-anchored) and the field
105
+ fix (hidden until hover or focus, start and end placement; Ignite Tip.tsx). **Precedent**: the first product built on
106
+ the system.
107
+
108
+ ## Markup
109
+
110
+ ```html
111
+ <span class="ws-tooltip">
112
+ <span class="ws-tooltip__tip" role="tooltip" id="tip-refresh">Refresh the audit</span>
113
+ <button type="button" class="ws-icon-btn" aria-labelledby="tip-refresh">
114
+ <svg viewBox="0 0 16 16" fill="none"><path d="M13 8a5 5 0 1 1-1.5-3.5M13 3v2.5h-2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
115
+ </button>
116
+ </span>
117
+
118
+ <span class="ws-tooltip ws-tooltip--note">
119
+ <span class="ws-tooltip__tip" role="tooltip" id="note-buyer">I'm thinking of buying a home in Chagrin Falls, OH. Which real estate agent or team should I talk to?</span>
120
+ <button type="button" class="ws-tooltip__term" aria-describedby="note-buyer" onclick="this.parentElement.classList.toggle('ws-tooltip--open')">A buyer</button>
121
+ </span>
122
+ ```
123
+
124
+ ## Styles
125
+
126
+ `Tooltip/style.css`, built into `bundle.css` and `fello.css`. Tokens only, so it turns dark with the page (the note
127
+ keeps the brand navy and white words, with a faint light rim, D12).
@@ -1,4 +1,4 @@
1
- <!-- @dsCard group="Overlays" height=128 -->
1
+ <!-- @dsCard group="Overlays" height=340 -->
2
2
  <!doctype html>
3
3
  <html>
4
4
  <head>
@@ -8,24 +8,78 @@
8
8
  <link rel="stylesheet" href="../bundle.css">
9
9
  </head>
10
10
  <body>
11
- <div class="ws-stage ws-stage--grey" style="justify-content: center;">
11
+ <div class="ws-stage ws-stage--grey">
12
12
 
13
- <div class="ws-cluster ws-cluster--start" style="gap: var(--space-xl);">
14
- <div class="ws-stack" style="gap: var(--space-xl); align-items: center;">
15
- <span class="ws-muted">Shown above an icon button</span>
16
- <span class="ws-tooltip">
17
- <span class="ws-tooltip__tip" role="tooltip" id="tip-1">Ask about this</span>
18
- <button type="button" class="ws-icon-btn" aria-label="Ask about this" aria-describedby="tip-1">
19
- <svg viewBox="0 0 16 16" fill="none"><path d="M6 6a2 2 0 1 1 3 1.7c-.6.4-1 .9-1 1.6v.4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="12" r="0.75" fill="currentColor"/></svg>
20
- </button>
21
- </span>
22
- </div>
13
+ <div class="ws-stack" style="width: 160px; gap: var(--space-3xl); align-items: center;">
14
+ <span class="ws-muted">Centred, 8 above its control</span>
15
+ <span class="ws-tooltip ws-tooltip--open">
16
+ <span class="ws-tooltip__tip" role="tooltip" id="tip-centre">Ask about this</span>
17
+ <button type="button" class="ws-icon-btn ws-icon-btn--outline" aria-labelledby="tip-centre">
18
+ <svg viewBox="0 0 16 16" fill="none"><path d="M6 6a2 2 0 1 1 3 1.7c-.6.4-1 .9-1 1.6v.4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="12" r="0.75" fill="currentColor"/></svg>
19
+ </button>
20
+ </span>
21
+ </div>
23
22
 
24
- <div class="ws-stack" style="gap: var(--space-xl); align-items: center;">
25
- <span class="ws-muted">At rest, no tip</span>
26
- <button type="button" class="ws-icon-btn" aria-label="Search">
23
+ <div class="ws-stack" style="width: 160px; gap: var(--space-3xl); align-items: center;">
24
+ <span class="ws-muted">At rest, nothing shows</span>
25
+ <span class="ws-tooltip">
26
+ <span class="ws-tooltip__tip" role="tooltip" id="tip-rest">Search</span>
27
+ <button type="button" class="ws-icon-btn ws-icon-btn--outline" aria-labelledby="tip-rest">
27
28
  <svg viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="4.5" stroke="currentColor" stroke-width="1.5"/><path d="M13 13l-2.5-2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
28
29
  </button>
30
+ </span>
31
+ </div>
32
+
33
+ <div class="ws-stack" style="width: 160px; gap: var(--space-3xl); align-items: flex-start;">
34
+ <span class="ws-muted">Near the left edge: flush to the start</span>
35
+ <span class="ws-tooltip ws-tooltip--start ws-tooltip--open">
36
+ <span class="ws-tooltip__tip" role="tooltip" id="tip-start">Refresh the audit</span>
37
+ <button type="button" class="ws-icon-btn ws-icon-btn--outline" aria-labelledby="tip-start">
38
+ <svg viewBox="0 0 16 16" fill="none"><path d="M13 8a5 5 0 1 1-1.5-3.5M13 3v2.5h-2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
39
+ </button>
40
+ </span>
41
+ </div>
42
+
43
+ <div class="ws-stack" style="width: 160px; padding-bottom: var(--space-2xl); align-items: flex-end;">
44
+ <span class="ws-muted">Near the right edge: below, flush to the end</span>
45
+ <span class="ws-tooltip ws-tooltip--end ws-tooltip--below ws-tooltip--open">
46
+ <span class="ws-tooltip__tip" role="tooltip" id="tip-end">Settings</span>
47
+ <button type="button" class="ws-icon-btn ws-icon-btn--outline" aria-labelledby="tip-end">
48
+ <svg viewBox="0 0 16 16" fill="none"><path d="M8 2v2M8 12v2M3 8H1M15 8h-2M4 4l1.4 1.4M10.6 10.6L12 12M4 12l1.4-1.4M10.6 5.4L12 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="8" r="2.5" stroke="currentColor" stroke-width="1.5"/></svg>
49
+ </button>
50
+ </span>
51
+ </div>
52
+
53
+ <div class="ws-stack" style="width: 360px; gap: var(--space-6xl);">
54
+ <span class="ws-muted">The note: the full words behind a short one, a caret on its middle</span>
55
+ <div class="ws-row__title" style="padding: 0 0 var(--space-md) var(--space-md);">
56
+ <span class="ws-tooltip ws-tooltip--note ws-tooltip--open">
57
+ <span class="ws-tooltip__tip" role="tooltip" id="note-buyer">I'm thinking of buying a home in Chagrin Falls, OH. Which real estate agent or team should I talk to?</span>
58
+ <button type="button" class="ws-tooltip__term" aria-describedby="note-buyer">A buyer</button>
59
+ </span>
60
+ </div>
61
+ </div>
62
+
63
+ <div class="ws-stack" style="width: 360px; padding-bottom: var(--space-5xl);">
64
+ <span class="ws-muted">Near the top of its card the note opens below, its caret turned up; in a sentence the term keeps the line</span>
65
+ <p class="ws-body" style="padding-left: var(--space-md);"><span class="ws-tooltip ws-tooltip--note ws-tooltip--below ws-tooltip--open"><span class="ws-tooltip__tip" role="tooltip" id="note-seller">I'm thinking of selling my home in Chagrin Falls, OH. Who should I list it with?</span><button type="button" class="ws-tooltip__term" aria-describedby="note-seller">A seller</button></span> asked it on Nov 2, and twice more after.</p>
66
+ </div>
67
+
68
+ <div class="ws-stack" style="width: 360px; gap: var(--space-3xl);" data-live>
69
+ <span class="ws-muted">Live: point at or tab to each. The tip pops out of .9 on the pop spring after the 40 ms hover intent and fades off over 180 ms; the note pops with its caret</span>
70
+ <div class="ws-cluster" style="gap: var(--space-xl);">
71
+ <span class="ws-tooltip">
72
+ <span class="ws-tooltip__tip" role="tooltip" id="tip-live-1">Search</span>
73
+ <button type="button" class="ws-icon-btn ws-icon-btn--outline" aria-labelledby="tip-live-1"><svg viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="4.5" stroke="currentColor" stroke-width="1.5"/><path d="M13 13l-2.5-2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button>
74
+ </span>
75
+ <span class="ws-tooltip">
76
+ <span class="ws-tooltip__tip" role="tooltip" id="tip-live-2">Refresh the audit</span>
77
+ <button type="button" class="ws-icon-btn ws-icon-btn--outline" aria-labelledby="tip-live-2"><svg viewBox="0 0 16 16" fill="none"><path d="M13 8a5 5 0 1 1-1.5-3.5M13 3v2.5h-2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
78
+ </span>
79
+ <span class="ws-tooltip ws-tooltip--note">
80
+ <span class="ws-tooltip__tip" role="tooltip" id="note-live">Asked by people who want to buy a home here.</span>
81
+ <button type="button" class="ws-tooltip__term" aria-describedby="note-live">A buyer</button>
82
+ </span>
29
83
  </div>
30
84
  </div>
31
85
 
@@ -0,0 +1,78 @@
1
+ /* ---- Tooltip: a control's name on hover or keyboard focus, North's compact tip; --note is Seth's navy note with a caret
2
+ over the word it explains (305:2272) ---- */
3
+ /* The tip: 6 / 10 inside, 6 px corners, the hairline and the tip drop, 12 px semibold navy on one line (a longer name
4
+ wraps at 440, or the window less 32, and is never cut), 8 above its control and centred on it. Hidden until the control is hovered (after the 40 ms hover intent) or takes keyboard
5
+ focus (at once); never on a tap, so the control's own name must carry the meaning. --open shows it (a tap toggle,
6
+ or a preview). The bridge (::before) keeps it while the pointer crosses the gap, so it can be hovered. */
7
+ .ws-tooltip { --ws-tooltip-gap: var(--space-xs); --ws-tooltip-from: var(--ws-nudge); position: relative; display: inline-flex; vertical-align: top; }
8
+ .ws-tooltip::before { content: ""; position: absolute; right: 0; bottom: 100%; left: 0; height: var(--ws-tooltip-gap); pointer-events: none; }
9
+ .ws-tooltip__tip { position: absolute; bottom: calc(100% + var(--ws-tooltip-gap)); left: 50%; z-index: var(--z-dropdown); width: max-content; max-width: min(var(--ws-thing-width), calc(100vw - var(--space-xl))); padding: var(--space-xxs-plus) var(--space-xs-plus); border-radius: var(--ws-radius-row); background: var(--ws-raised); box-shadow: var(--ws-shadow-hairline), var(--ws-shadow-tip); font-size: var(--ws-text-dense); line-height: var(--space-md); font-weight: 600; color: var(--color-text-heading); white-space: normal; text-wrap: balance; translate: -50% 0; transform-origin: bottom center; opacity: 0; visibility: hidden; pointer-events: none; }
10
+ @media (hover: hover) {
11
+ .ws-tooltip:hover::before { pointer-events: auto; }
12
+ .ws-tooltip:hover > .ws-tooltip__tip { opacity: 1; visibility: visible; pointer-events: auto; }
13
+ }
14
+ .ws-tooltip--open > .ws-tooltip__tip, .ws-tooltip:has(:focus-visible) > .ws-tooltip__tip { opacity: 1; visibility: visible; pointer-events: auto; }
15
+ /* Placement: --below under the control; --start and --end flush to its left or right edge, for a control near the
16
+ side of its container. */
17
+ .ws-tooltip--below { --ws-tooltip-from: calc(var(--ws-nudge) * -1); }
18
+ .ws-tooltip--below::before { top: 100%; bottom: auto; }
19
+ .ws-tooltip--below > .ws-tooltip__tip { top: calc(100% + var(--ws-tooltip-gap)); bottom: auto; transform-origin: top center; }
20
+ .ws-tooltip--start > .ws-tooltip__tip { left: 0; translate: none; transform-origin: bottom left; }
21
+ .ws-tooltip--end > .ws-tooltip__tip { right: 0; left: auto; translate: none; transform-origin: bottom right; }
22
+ /* The note (Seth, 305:2272): the brand navy with white words in both themes (D12; in dark the float rim, a faint light
23
+ line, is its only shadow), 4 px corners, 10 / 12 inside, 14 / 20 words that wrap at 300.
24
+ Its words start where the word it explains starts (the 12 px padding hangs left of it); a 12 x 6 caret points at
25
+ that word's middle, 4 above it. It shows on hover, focus and tap (--open). __term is that word: dotted underline,
26
+ the cue that more shows. */
27
+ .ws-tooltip--note { --ws-tooltip-gap: calc(var(--space-xxs-plus) + var(--ws-nudge)); }
28
+ .ws-tooltip--note > .ws-tooltip__tip { left: calc(var(--space-sm) * -1); max-width: min(calc(var(--ws-float-width) + var(--space-sm) * 2), calc(100vw - var(--space-xl))); padding: var(--space-xs-plus) var(--space-sm); border-radius: var(--ws-radius-control); background: var(--ws-inverse); box-shadow: var(--ws-float-rim); font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); font-weight: 400; color: var(--ws-on-inverse); text-wrap: wrap; translate: none; transform-origin: bottom left; }
29
+ .ws-tooltip--note.ws-tooltip--end > .ws-tooltip__tip { right: calc(var(--space-sm) * -1); left: auto; transform-origin: bottom right; }
30
+ .ws-tooltip--note::after { content: ""; position: absolute; bottom: calc(100% + var(--ws-nudge)); left: 50%; width: var(--space-sm); height: var(--space-xxs-plus); background: var(--ws-inverse); clip-path: polygon(0 0, 100% 0, 50% 100%); translate: -50% 0; opacity: 0; visibility: hidden; pointer-events: none; }
31
+ @media (hover: hover) { .ws-tooltip--note:hover::after { opacity: 1; visibility: visible; } }
32
+ .ws-tooltip--note.ws-tooltip--open::after, .ws-tooltip--note:has(:focus-visible)::after { opacity: 1; visibility: visible; }
33
+ /* --dismissed: Escape hides it while the pointer or focus stays (WCAG 1.4.13); the app removes it when they leave. */
34
+ .ws-tooltip.ws-tooltip--dismissed > .ws-tooltip__tip, .ws-tooltip.ws-tooltip--dismissed::after { opacity: 0; visibility: hidden; pointer-events: none; }
35
+ /* --note --below: the note under its term, the caret turned up to it. */
36
+ .ws-tooltip--note.ws-tooltip--below > .ws-tooltip__tip { transform-origin: top left; }
37
+ .ws-tooltip--note.ws-tooltip--below::after { top: calc(100% + var(--ws-nudge)); bottom: auto; clip-path: polygon(50% 0, 100% 100%, 0 100%); }
38
+ /* Where the browser places by anchor, the tip and the note's caret are fixed to the wrapper as their anchor, so no
39
+ scroller, card or stage that clips can cut them (K9: AppShell's body scrolls and its stage clips, so a tip on the bar
40
+ or on the body's first row was cut). They take the same places from the anchor's edges; a plain tip with no room
41
+ turns to the other side or slides along its control to stay on the screen (the note keeps its side, so its caret
42
+ stays on it). Without anchor placement they hang in the wrapper, and inside a box that clips the app portals them. */
43
+ @supports (anchor-scope: all) {
44
+ .ws-tooltip { --ws-tooltip-block: top; --ws-tooltip-inline: span-all; --ws-tooltip-justify: anchor-center; anchor-name: --ws-tooltip; anchor-scope: --ws-tooltip; }
45
+ .ws-tooltip--below { --ws-tooltip-block: bottom; }
46
+ .ws-tooltip--start, .ws-tooltip--note { --ws-tooltip-inline: span-right; --ws-tooltip-justify: normal; }
47
+ .ws-tooltip--end { --ws-tooltip-inline: span-left; --ws-tooltip-justify: normal; }
48
+ .ws-tooltip__tip, .ws-tooltip--note::after { position: fixed; position-anchor: --ws-tooltip; inset: auto; translate: none; }
49
+ .ws-tooltip__tip { position-area: var(--ws-tooltip-block) var(--ws-tooltip-inline); justify-self: var(--ws-tooltip-justify); margin: var(--ws-tooltip-gap) 0; }
50
+ .ws-tooltip:not(.ws-tooltip--note) > .ws-tooltip__tip { position-try-fallbacks: flip-block, --ws-tooltip-right, --ws-tooltip-left, --ws-tooltip-right flip-block, --ws-tooltip-left flip-block; }
51
+ .ws-tooltip--note > .ws-tooltip__tip { margin-inline: calc(var(--space-sm) * -1) 0; }
52
+ .ws-tooltip--note.ws-tooltip--end > .ws-tooltip__tip { margin-inline: 0 calc(var(--space-sm) * -1); }
53
+ .ws-tooltip--note::after { position-area: var(--ws-tooltip-block) span-all; justify-self: anchor-center; margin: var(--ws-nudge) 0; }
54
+ }
55
+ @position-try --ws-tooltip-right { position-area: top span-right; justify-self: normal; }
56
+ @position-try --ws-tooltip-left { position-area: top span-left; justify-self: normal; }
57
+ .ws-tooltip__term { padding: 0; border: 0; background: none; font-weight: inherit; text-decoration: underline dotted var(--ws-line); text-underline-offset: var(--space-xxs); cursor: help; }
58
+ /* On touch the term is 24 tall to a finger, but only its hit area grows, so a term inside a sentence keeps the
59
+ sentence's baseline (a taller box pushed its words 2 px down). */
60
+ @media (pointer: coarse) {
61
+ .ws-tooltip__term { position: relative; }
62
+ .ws-tooltip__term::after { content: ""; position: absolute; inset: calc((100% - var(--space-lg)) / 2) 0; }
63
+ }
64
+ /* Motion (D7, North's "Tooltip pop-in", the pop spring .35 / .1, js@484880): it pops from scale .9 and 4 px toward its
65
+ control (--ws-tooltip-from), growing out of the edge it hangs from; it fades off over 180 ms (North's tip fade,
66
+ css:388), faster than it came. A pointer waits the 40 ms hover intent; keyboard focus shows it at once. With reduced
67
+ motion it simply appears and goes. */
68
+ .ws-tooltip { --ws-tooltip-scale: 0.9; --ws-tooltip-out: 180ms; /* raw: North's .18 s tip fade; a global --ws-duration-fade wanted */ }
69
+ @media (prefers-reduced-motion: no-preference) {
70
+ .ws-tooltip__tip { transform: translateY(var(--ws-tooltip-from)) scale(var(--ws-tooltip-scale)); transition: opacity var(--ws-tooltip-out) var(--ws-ease-exit), transform var(--ws-tooltip-out) var(--ws-ease-exit), visibility 0s linear var(--ws-tooltip-out); }
71
+ .ws-tooltip--note::after { transition: opacity var(--ws-tooltip-out) var(--ws-ease-exit), visibility 0s linear var(--ws-tooltip-out); }
72
+ @media (hover: hover) {
73
+ .ws-tooltip:hover > .ws-tooltip__tip { transform: none; transition: opacity var(--ws-spring-pop-duration) var(--ws-spring-pop) var(--ws-hover-intent), transform var(--ws-spring-pop-duration) var(--ws-spring-pop) var(--ws-hover-intent), visibility 0s linear var(--ws-hover-intent); }
74
+ .ws-tooltip--note:hover::after { transition: opacity var(--ws-spring-pop-duration) var(--ws-spring-pop) var(--ws-hover-intent), visibility 0s linear var(--ws-hover-intent); }
75
+ }
76
+ .ws-tooltip--open > .ws-tooltip__tip, .ws-tooltip:has(:focus-visible) > .ws-tooltip__tip { transform: none; transition: opacity var(--ws-spring-pop-duration) var(--ws-spring-pop), transform var(--ws-spring-pop-duration) var(--ws-spring-pop), visibility 0s; }
77
+ .ws-tooltip--note.ws-tooltip--open::after, .ws-tooltip--note:has(:focus-visible)::after { transition: opacity var(--ws-spring-pop-duration) var(--ws-spring-pop), visibility 0s; }
78
+ }
@@ -1,65 +1,57 @@
1
- The 56 px bar alone: a wordmark, up to five page links, a search box, an icon act and the person, for a product that
2
- puts its navigation across the top rather than down the side.
1
+ The 56 px bar alone: a wordmark, up to five page links, a search box, one quiet act and the person, for a product
2
+ that puts its navigation across the top rather than down the side.
3
3
 
4
4
  ## Use it when
5
5
 
6
- - A product reads better as a row of top-level sections than as a side nav: a handful of destinations, no groups, no
7
- nesting worth an eyebrow.
8
- - A page needs its navigation and its identity in one fixed strip, with the content free to use the full width below it.
9
- - A phone needs the same bar collapsed to the wordmark and a menu.
6
+ - A product reads better as a row of top-level sections than as a side nav: a handful of destinations, no groups.
7
+ - A page needs its navigation and identity in one strip, with the content free to use the width below it.
10
8
 
11
9
  ## Not for
12
10
 
13
- - Five or more sections that fall into groups, or a product with any depth to its hierarchy: use `AppShell`'s side nav
14
- instead; a top bar with eight crowded links is the wrong shape for that.
15
- - Moving between views of the same thing (list and board, this month and last): that is `Segmented` or `Tabs`, not a
16
- second row under this bar (no inner tabs on a page, per the desk's own rule).
17
- - The chat-first desk: the system's own `Sidebar` is the nav there; this bar is for a conventional product shape instead.
11
+ - More than five sections, or sections that fall into groups: `AppShell`.
12
+ - Moving between views of the same thing (list and board, this month and last): `Segmented` or `Tabs`.
13
+ - The chat-first workspace: the chat's own frame.
18
14
 
19
15
  ## Anatomy
20
16
 
21
- - Bar: `--ws-header-h` (56 px) tall, the width of the page, flat (shown here on a hairline card so its edge reads in the
22
- preview; in product it usually sits directly on the page with a hairline beneath it).
23
- - Wordmark: 16 px semibold (`--ws-text-value`), navy, at the left. The reference implementation has no logo file to
24
- draw here; a real product supplies its mark.
25
- - Links: up to five, 14 px medium, `--color-text-body` at rest; the current one `--color-text-heading` with a 2 px navy
26
- underline (`--ws-ring`) sitting on the bar's own baseline.
27
- - Search: a 36 px box? No: in the top bar it sits at the default control height (`--ws-control-h`, 32 px) like any inline
28
- field, 4 px corners, the field's hairline, placed to the right of the links.
29
- - Icon act: one 32 px `IconButton` (notifications, help, or whatever the one quiet global act is).
30
- - The person: a `--ws-avatar` neutral disc with navy initials, at the far right.
17
+ - Bar (`.ws-top-bar`): 56 tall (`--ws-header-h`), 24 at the sides, its parts 24 apart. Flat; in the preview it sits on
18
+ a card so its edge reads.
19
+ - Wordmark (`__wordmark`): 16 / 24 semibold navy.
20
+ - Links (`__links`, `__link`): up to five, 14 px medium in the body grey, 24 apart, each the bar's full height, its
21
+ box bleeding 8 past its words (so the focus ring clears them); the current one (`aria-current="page"`, or `--current`) navy with a 2 px navy underline (`--ws-ring`) on the bar's foot.
22
+ - Search (`__search`): a `<label>` round an `<input type="search">`, 200 wide (it gives way first, down to 96, so
23
+ five links of two words still fit at 60rem), 32 tall (40 on touch), 4 px corners, the field's inset hairline, a 12 px
24
+ icon, the focus ring on the box. The links never shrink under their words.
25
+ - End (`__end`): one 32 px `IconButton` and the person's 32 px avatar, 8 apart.
26
+ - Menu button (`__menu`): an `IconButton`, shown only in the phone form.
31
27
 
32
28
  ## Variants and sizes
33
29
 
34
30
  | Variant | Shows |
35
31
  | --- | --- |
36
- | Default (desktop) | Wordmark, links, search, an icon button, the avatar |
37
- | Phone (375 px and under) | Wordmark and one menu `IconButton` only; links, search and the icon act move into the menu it opens |
32
+ | Default | Wordmark, links, search, an icon button, the avatar |
33
+ | Phone form (under 60rem of window, or `--phone` at any width) | The wordmark at the left, the menu button at the right, 16 px sides; the links, search and quiet act move into the menu it opens |
38
34
 
39
35
  ## States
40
36
 
41
- - **Default**: as above.
42
- - **Current link**: navy words, the 2 px underline; set by `aria-current="page"`, never colour alone.
43
- - **Hover**: a link darkens toward `--color-text-heading`; underline only on the current link, never on hover.
44
- - **Focus**: the kit's `--shadow-focus-ring` on the link, the search box and the icon button.
45
- - **Long link label**: the bar does not wrap or stack; a label this long belongs in `AppShell`'s side nav instead, where
46
- rows can grow.
47
- - **Empty search**: shows the placeholder "Search" only; it is never the only way to reach a page.
48
- - **Narrow desktop**: below the five links' combined width, drop the least-used link into an overflow `Menu` rather than
49
- shrinking words or wrapping the bar.
37
+ - **Current link**: navy words and the underline, set by `aria-current="page"`, never colour alone.
38
+ - **Hover** (pointers that hover): a link's words darken to navy over 200 ms; the underline stays the current link's.
39
+ - **Focus**: the house's 2 px navy ring on each link (drawn inside it on 4 px corners, since a link is the bar's full
40
+ height), on the search's box and on the icon button.
41
+ - **Long link label**: the bar never wraps; a label this long belongs in `AppShell`'s nav.
42
+ - **Empty search**: "Search" only; it is never the only way to reach a page.
50
43
 
51
44
  ## Behaviour
52
45
 
53
- - Keyboard: links are real `<a>`/`Link`s in document order; Tab reaches search and the icon button after them.
54
- - Pointer: hover darkens a link's words; the search box and icon button take the standard control hover.
55
- - Touch: the bar's controls grow to the 40 px floor; on a phone the bar collapses to wordmark and menu, per the house
56
- responsive rule that a phone stays at 100% and boxed controls grow to 40 px.
57
- - Motion: none of its own; the underline can be a sliding highlight (`layoutId`) between links as the sidebar's wash does,
58
- or simply present under the current link.
46
+ - Keyboard: links are real `<a>`s in document order, then the search and the icon button.
47
+ - Touch: the bar's controls grow to the 40 px floor.
48
+ - The phone form is set by the window (60rem, where five links and the search stop fitting), since the bar is the
49
+ window's own strip.
50
+ - Motion: only the link colour. The underline can slide between links in React; in plain HTML it is simply present.
59
51
 
60
52
  ## Content
61
53
 
62
- - The wordmark is the product's name only, never a slogan.
54
+ - The wordmark is the product's name only.
63
55
  - Link words are the page's own name, sentence case, one or two words.
64
56
  - No em dashes.
65
57
 
@@ -71,23 +63,45 @@ puts its navigation across the top rather than down the side.
71
63
  | `search` | `{ placeholder, onSearch }` | The inline search box |
72
64
  | `actions` | `IconButtonProps[]` | Quiet global acts, icon only |
73
65
  | `person` | `{ name, initials }` | The avatar at the right |
74
- | `phone` | `boolean` | Collapses to wordmark and menu |
66
+ | `phone` | `boolean` | The phone form at any width |
75
67
 
76
68
  ## Rules
77
69
 
78
- - **Five links, not more**: past five, the hierarchy has outgrown a top bar; move to `AppShell`.
79
- - **One underline, the current page's**: never two links styled as current, never a filled pill (navy fills are for the
80
- one act and a selected toggle only, not for navigation links).
81
- - **No inner tabs on a page** carries over here too: a page below this bar still never draws its own second tab row for
82
- the same navigation job.
83
- - **Source**: Intentional addition, decided from the system (26 Oct 2026): a flat-hierarchy product reads better with its
84
- navigation across the top than down a 232 px side nav it would barely fill. Derived from `--ws-header-h` (the desk's
85
- and a page's own header line), the link colour and underline rule already used for the current item elsewhere (the
86
- navy thumb pattern in `Segmented`'s tabs), and the avatar and icon-button sizes used everywhere else in the system.
70
+ - **Five links, not more**: past five, move to `AppShell`.
71
+ - **One underline, the current page's**: never two, never a filled pill.
72
+ - **No inner tabs on a page** for the same navigation job.
87
73
 
88
74
  ## Source
89
75
 
90
- **Derived from**: `--ws-header-h` (the same header line used elsewhere), the current-link treatment adapted from
91
- `Segmented`'s navy-thumb rule into an underline, and the avatar and icon-button sizes used throughout the system.
92
- **Precedent**: an intentional addition decided from the system, 26 Oct 2026; no card in the first product builds a
93
- top bar, since its chat-first desk uses a side `Sidebar` instead.
76
+ **Derived from**: `--ws-header-h`, the current-link underline, the avatar and icon-button sizes used across the
77
+ system, and the geometry check (4 Nov 2026: at 390 the end ran to 428 and the fifth link to 445, the page scrolled
78
+ 55 px sideways, because only `--phone` collapsed it). **Precedent**: an intentional addition decided from the system,
79
+ 26 Oct 2026.
80
+
81
+ ## Markup
82
+
83
+ ```html
84
+ <nav class="ws-top-bar" aria-label="Main">
85
+ <span class="ws-top-bar__wordmark">Fello</span>
86
+ <div class="ws-top-bar__links">
87
+ <a href="/pipeline" class="ws-top-bar__link" aria-current="page">Pipeline</a>
88
+ <a href="/leads" class="ws-top-bar__link">Leads</a>
89
+ </div>
90
+ <label class="ws-top-bar__search">
91
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="6.8" cy="6.8" r="3.8"/><path d="M9.7 9.7l3.6 3.6"/></svg>
92
+ <input type="search" placeholder="Search" aria-label="Search">
93
+ </label>
94
+ <div class="ws-top-bar__end">
95
+ <button type="button" class="ws-icon-btn" aria-label="Notifications"><!-- 16 px icon --></button>
96
+ <span class="ws-avatar">PN</span>
97
+ </div>
98
+ <button type="button" class="ws-icon-btn ws-top-bar__menu" aria-label="Open the menu"><!-- 16 px icon --></button>
99
+ </nav>
100
+ ```
101
+
102
+ ## Styles
103
+
104
+ `TopBar/style.css`, with `IconButton` and `Avatar` for the menu button, the quiet act and the person. Tokens: `--ws-header-h`, `--ws-text-value`,
105
+ `--ws-text-body`, `--ws-text-meta`, `--ws-control-h`, `--ws-ring`, `--ws-radius-control`, `--ws-radius-bar`,
106
+ `--ws-field`, `--ws-field-ink`, `--ws-edge-card`, `--ws-hairline`, `--ws-touch`, `--ws-focus-width`, `--ws-focus-color`,
107
+ `--ws-focus-offset`, `--space-6xl`, `--color-text-heading`, `--color-text-body`, `--color-text-muted`.
@@ -1,4 +1,4 @@
1
- <!-- @dsCard group="Navigation" height=320 -->
1
+ <!-- @dsCard group="Navigation" height=232 -->
2
2
  <!doctype html>
3
3
  <html>
4
4
  <head>
@@ -8,53 +8,40 @@
8
8
  <link rel="stylesheet" href="../bundle.css">
9
9
  </head>
10
10
  <body>
11
- <div class="ws-stage ws-stage--grey ws-stage--column" style="gap: var(--ws-gap-component);">
11
+ <div class="ws-stage ws-stage--grey ws-stage--column">
12
12
 
13
13
  <div class="ws-stack">
14
- <span class="ws-muted">Default: wordmark, up to five links, the current one navy and underlined, search, an icon button, the avatar</span>
14
+ <span class="ws-muted">Default: wordmark, up to five links, the current one navy and underlined, search, an icon button, the avatar; under 60rem of window it is the phone form</span>
15
15
  <nav class="ws-card ws-top-bar" aria-label="Main">
16
16
  <span class="ws-top-bar__wordmark">Fello</span>
17
17
  <div class="ws-top-bar__links">
18
- <a href="#" class="ws-top-bar__link ws-top-bar__link--current" aria-current="page" onclick="return false;">Pipeline</a>
18
+ <a href="#" class="ws-top-bar__link" aria-current="page" onclick="return false;">Pipeline</a>
19
19
  <a href="#" class="ws-top-bar__link" onclick="return false;">Leads</a>
20
20
  <a href="#" class="ws-top-bar__link" onclick="return false;">Listings</a>
21
21
  <a href="#" class="ws-top-bar__link" onclick="return false;">Reports</a>
22
+ <a href="#" class="ws-top-bar__link" onclick="return false;">Settings</a>
22
23
  </div>
23
- <span class="ws-top-bar__search" role="searchbox" aria-label="Search">
24
- <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="6.8" cy="6.8" r="3.8"/><path d="M9.7 9.7l3.6 3.6"/></svg>
25
- Search
26
- </span>
24
+ <label class="ws-top-bar__search">
25
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="6.8" cy="6.8" r="3.8"/><path d="M9.7 9.7l3.6 3.6"/></svg>
26
+ <input type="search" placeholder="Search" aria-label="Search">
27
+ </label>
27
28
  <div class="ws-top-bar__end">
28
29
  <button type="button" class="ws-icon-btn" aria-label="Notifications">
29
30
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11.5V7a4 4 0 0 1 8 0v4.5l1 1.5H3l1-1.5Z"/><path d="M6.5 14.5h3"/></svg>
30
31
  </button>
31
32
  <span class="ws-avatar">PN</span>
32
33
  </div>
33
- </nav>
34
- </div>
35
-
36
- <div class="ws-stack" style="max-width: 760px;">
37
- <span class="ws-muted">Five links, a long one truncating never: it wraps the bar before it clips</span>
38
- <nav class="ws-card ws-top-bar" aria-label="Main">
39
- <span class="ws-top-bar__wordmark">Fello</span>
40
- <div class="ws-top-bar__links">
41
- <a href="#" class="ws-top-bar__link" onclick="return false;">Pipeline</a>
42
- <a href="#" class="ws-top-bar__link ws-top-bar__link--current" aria-current="page" onclick="return false;">Leads</a>
43
- <a href="#" class="ws-top-bar__link" onclick="return false;">Listings</a>
44
- <a href="#" class="ws-top-bar__link" onclick="return false;">Reports</a>
45
- <a href="#" class="ws-top-bar__link" onclick="return false;">Settings</a>
46
- </div>
47
- <div class="ws-top-bar__end">
48
- <span class="ws-avatar">PN</span>
49
- </div>
34
+ <button type="button" class="ws-icon-btn ws-top-bar__menu" aria-label="Open the menu">
35
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 4.5h11M2.5 8h11M2.5 11.5h11"/></svg>
36
+ </button>
50
37
  </nav>
51
38
  </div>
52
39
 
53
40
  <div class="ws-stack" style="width: 375px; max-width: 100%;">
54
- <span class="ws-muted">Phone, 375 px: the wordmark and a menu icon button only</span>
41
+ <span class="ws-muted">--phone at any width: the wordmark and the menu button only; the links, search and quiet act are in the menu it opens</span>
55
42
  <nav class="ws-card ws-top-bar ws-top-bar--phone" aria-label="Main">
56
43
  <span class="ws-top-bar__wordmark">Fello</span>
57
- <button type="button" class="ws-icon-btn" aria-label="Open the menu">
44
+ <button type="button" class="ws-icon-btn ws-top-bar__menu" aria-label="Open the menu">
58
45
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 4.5h11M2.5 8h11M2.5 11.5h11"/></svg>
59
46
  </button>
60
47
  </nav>