@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,4 +1,4 @@
1
- <!-- @dsCard group="Overlays" height=420 -->
1
+ <!-- @dsCard group="Overlays" height=1270 -->
2
2
  <!doctype html>
3
3
  <html>
4
4
  <head>
@@ -8,34 +8,132 @@
8
8
  <link rel="stylesheet" href="../bundle.css">
9
9
  </head>
10
10
  <body>
11
- <div class="ws-stage ws-stage--bleed">
11
+ <div class="ws-stage ws-stage--column">
12
+
12
13
  <div class="ws-stack">
13
- <span class="ws-muted">A panel beside a dimmed chat column: the one thing opened from it</span>
14
- <div style="position: relative; display: flex; width: 900px; max-width: 100%; height: 380px; overflow: hidden; border-radius: var(--ws-radius-panel); background: var(--color-bg-page); box-shadow: var(--ws-shadow-hairline);">
15
- <ol aria-hidden="true" style="flex: 1; min-width: 0; padding: var(--space-lg); opacity: 0.5;">
16
- <li class="ws-chat-turn ws-chat-turn--agent">
17
- <p class="ws-chat-turn__words">Dana hasn't made a cold call in nine days, and Priya is worth a call.</p>
18
- </li>
19
- <li class="ws-chat-turn ws-chat-turn--person">
20
- <p class="ws-chat-turn__bubble">Open Priya's card</p>
21
- </li>
22
- </ol>
23
- <div class="ws-panel" role="dialog" aria-labelledby="panel-title">
14
+ <span class="ws-muted">Beside the page, 24 from it: head, a scrolling body of sections, the next fix pinned at the foot (the whole screen on a phone)</span>
15
+ <div style="position: relative; contain: layout; display: flex; gap: var(--space-lg); height: 640px; padding: var(--space-lg); border-radius: var(--ws-radius-panel); background: var(--color-bg-page); box-shadow: inset 0 0 0 var(--ws-line) var(--ws-edge-stage);">
16
+ <div class="ws-well" style="flex: 1; min-width: 0; align-self: flex-start;" aria-hidden="true">
17
+ <div class="ws-well-row" style="background: var(--ws-surface-sunk);"><span class="ws-row-name" style="flex: 1;">Local search</span><span class="ws-figure ws-figure--row">39</span></div>
18
+ <div class="ws-well-row"><span class="ws-row-name" style="flex: 1;">Website</span><span class="ws-figure ws-figure--row">75</span></div>
19
+ <div class="ws-well-row"><span class="ws-row-name" style="flex: 1;">Social</span><span class="ws-figure ws-figure--row">92</span></div>
20
+ </div>
21
+ <aside class="ws-panel" aria-labelledby="panel-title">
24
22
  <header class="ws-panel__head">
25
- <span class="ws-eyebrow">Lead</span>
26
- <button type="button" class="ws-btn ws-btn--ghost">Close</button>
23
+ <div class="ws-panel__heading">
24
+ <div class="ws-panel__title-row">
25
+ <h2 id="panel-title" class="ws-title">Local search</h2>
26
+ <span class="ws-chip ws-chip--needs-work">Needs work</span>
27
+ </div>
28
+ <p class="ws-body">How often you show up on Google Maps when buyers search near you.</p>
29
+ </div>
30
+ <button type="button" class="ws-icon-btn ws-panel__close" aria-label="Close Local search">
31
+ <svg viewBox="0 0 16 16" fill="none"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
32
+ </button>
27
33
  </header>
28
34
  <div class="ws-panel__body">
29
- <h2 id="panel-title" class="ws-section">Priya Nair</h2>
30
- <p class="ws-body">A complicated sale, cold call. Twelve years in the neighbourhood, referred by two agents on the team.</p>
31
- <div class="ws-plate">
32
- <p class="ws-muted">Stage</p>
33
- <p class="ws-body" style="font-weight: 600; color: var(--color-text-heading);">Qualified</p>
35
+ <section class="ws-panel__section" aria-label="Score">
36
+ <div class="ws-panel__figure-row">
37
+ <span class="ws-figure ws-figure--pane">39</span>
38
+ <span aria-hidden="true" style="display: flex; gap: var(--space-3xs); height: var(--space-xl);"><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-rating-needs-work);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span><span style="flex: 1; border-radius: var(--ws-radius-bar); background: var(--ws-track);"></span></span>
39
+ </div>
40
+ </section>
41
+ <section class="ws-panel__section" aria-label="Where we checked">
42
+ <p class="ws-section">Buyers near you see you in 5 of 9 spots, never in the top three.</p>
43
+ <div class="ws-plate ws-stack">
44
+ <p class="ws-row__title" style="font-weight: 600;">Chagrin Falls, OH: “real estate agent in chagrin falls, oh”</p>
45
+ <div class="ws-cluster ws-cluster--group">
46
+ <div class="ws-stack" style="gap: var(--space-3xs);"><span class="ws-control-words" style="color: var(--color-text-muted);">Found</span><span class="ws-section">5 of 9</span></div>
47
+ <div class="ws-stack" style="gap: var(--space-3xs);"><span class="ws-control-words" style="color: var(--color-text-muted);">Top three</span><span class="ws-section">0 of 9</span></div>
48
+ </div>
49
+ </div>
50
+ </section>
51
+ </div>
52
+ <footer class="ws-panel__foot">
53
+ <section class="ws-panel__section" aria-label="Next fix">
54
+ <div class="ws-panel__next">
55
+ <span class="ws-control-words">Next fix · 1 of 2</span>
56
+ <p class="ws-section">Name the towns you serve in your Google Business Profile.</p>
57
+ </div>
58
+ <div class="ws-panel__acts">
59
+ <button type="button" class="ws-btn ws-btn--well ws-btn--lg">Send to my web person</button>
60
+ <button type="button" class="ws-btn ws-btn--primary ws-btn--lg">Review Grow's rewrite</button>
61
+ </div>
62
+ </section>
63
+ </footer>
64
+ </aside>
65
+ </div>
66
+ </div>
67
+
68
+ <div class="ws-stack">
69
+ <span class="ws-muted">Busy: the chrome stays, the body shows ghosts of what is coming; a long title wraps beside the close</span>
70
+ <div style="position: relative; contain: layout; display: flex; justify-content: flex-end; height: 520px; padding: var(--space-lg); border-radius: var(--ws-radius-panel); background: var(--color-bg-page); box-shadow: inset 0 0 0 var(--ws-line) var(--ws-edge-stage);">
71
+ <aside class="ws-panel" aria-labelledby="panel-busy-title" aria-busy="true">
72
+ <header class="ws-panel__head">
73
+ <div class="ws-panel__heading">
74
+ <div class="ws-panel__title-row">
75
+ <h2 id="panel-busy-title" class="ws-title">Google Business Profile and maps listing</h2>
76
+ </div>
77
+ <p class="ws-body">Checking 9 searches near you.</p>
34
78
  </div>
79
+ <button type="button" class="ws-icon-btn ws-panel__close" aria-label="Close Google Business Profile">
80
+ <svg viewBox="0 0 16 16" fill="none"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
81
+ </button>
82
+ </header>
83
+ <div class="ws-panel__body" aria-hidden="true">
84
+ <section class="ws-panel__section">
85
+ <div class="ws-panel__figure-row">
86
+ <span class="ws-shimmer ws-figure ws-figure--pane">00</span>
87
+ <span class="ws-shimmer" style="height: var(--space-xl);"></span>
88
+ </div>
89
+ </section>
90
+ <section class="ws-panel__section">
91
+ <span class="ws-shimmer" style="height: var(--ws-leading-section); width: 80%;"></span>
92
+ <span class="ws-shimmer" style="height: var(--space-6xl);"></span>
93
+ </section>
35
94
  </div>
95
+ </aside>
96
+ </div>
97
+ </div>
98
+
99
+ <div class="ws-stack" data-live>
100
+ <span class="ws-muted">Live: press Website. The panel grows out of .97 from the edge it docks to on the panel spring, fading in over .2 s and out of an 8 px blur over .24 s; its close sends it back over .16 s, faster</span>
101
+ <div id="panel-live" style="position: relative; contain: layout; display: flex; gap: var(--space-lg); height: 360px; padding: var(--space-lg); border-radius: var(--ws-radius-panel); background: var(--ws-page);">
102
+ <div class="ws-stack" style="flex: 1; min-width: 0;">
103
+ <button type="button" class="ws-btn ws-btn--secondary" style="align-self: flex-start;" id="panel-live-open">Website</button>
36
104
  </div>
37
105
  </div>
106
+ <template id="panel-live-tpl">
107
+ <aside class="ws-panel" aria-labelledby="panel-live-title">
108
+ <header class="ws-panel__head">
109
+ <div class="ws-panel__heading">
110
+ <div class="ws-panel__title-row"><h2 id="panel-live-title" class="ws-title">Website</h2></div>
111
+ <p class="ws-body">How quickly your site answers a buyer on a phone.</p>
112
+ </div>
113
+ <button type="button" class="ws-icon-btn ws-panel__close" aria-label="Close Website" data-close>
114
+ <svg viewBox="0 0 16 16" fill="none"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
115
+ </button>
116
+ </header>
117
+ <div class="ws-panel__body">
118
+ <section class="ws-panel__section" aria-label="Score">
119
+ <p class="ws-section">It loads in 2.1 s on a phone, quicker than most sites near you.</p>
120
+ </section>
121
+ </div>
122
+ </aside>
123
+ </template>
38
124
  </div>
125
+ <script>
126
+ (() => {
127
+ const frame = document.getElementById('panel-live');
128
+ document.getElementById('panel-live-open').addEventListener('click', () => {
129
+ if (frame.querySelector('.ws-panel:not([data-leaving])')) return;
130
+ frame.append(document.getElementById('panel-live-tpl').content.cloneNode(true));
131
+ const panel = frame.lastElementChild;
132
+ panel.querySelector('[data-close]').addEventListener('click', () => { panel.setAttribute('data-leaving', ''); setTimeout(() => panel.remove(), 160); });
133
+ });
134
+ })();
135
+ </script>
136
+
39
137
  </div>
40
138
  </body>
41
139
  </html>
@@ -0,0 +1,51 @@
1
+ /* ---- Panel: the one thing opened beside the page or the chat, Seth's check drawer (301:2011): a cream pane in a 1 px tray
2
+ rim, 12 px corners, sections 28 in from its sides split by full-width hairlines ---- */
3
+ /* No shadow and no edge of its own: it sits in the page, not over it. It is the panel width (440) and never wider than
4
+ its row; under 40rem it is the whole screen (K5). A plate inside sits half a step above the pane, as in Dialog, so
5
+ Seth's plate still shows in dark. */
6
+ .ws-panel { display: flex; flex-shrink: 0; flex-direction: column; width: var(--ws-thing-width); max-width: 100%; min-height: 0; border: var(--ws-rim) solid var(--ws-tray); border-radius: var(--ws-radius-drawer); background: var(--ws-raised); color: var(--color-text-body); }
7
+ /* The head (K6): the title line, with its tag, and one sentence under it; the close on the title's first line. It
8
+ never scrolls; everything else does. */
9
+ .ws-panel__head { display: flex; flex-shrink: 0; align-items: flex-start; justify-content: space-between; gap: var(--space-sm); padding: var(--ws-card-inset) var(--ws-band-inset) 0; }
10
+ .ws-panel__heading { display: flex; flex: 1; flex-direction: column; gap: var(--space-xxs); min-width: 0; }
11
+ .ws-panel__title-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-xxs) var(--space-xs-plus); }
12
+ .ws-panel__close { --ws-panel-close: var(--ws-control-h); margin-block: calc((var(--ws-leading-title) - var(--ws-panel-close)) / 2 - var(--ws-hairline)) calc((var(--ws-leading-title) - var(--ws-panel-close)) / 2 + var(--ws-hairline)); border-radius: var(--ws-radius-row); background: var(--ws-tray); color: var(--color-text-heading); }
13
+ @media (hover: hover) { .ws-panel__close:hover { background: var(--ws-well-hover); } }
14
+ @media (pointer: coarse) { .ws-panel__close { --ws-panel-close: var(--ws-touch); } }
15
+ /* The body scrolls on its own (K1, K2). Sections are 20 / 28 inside with 16 between their parts; each after the first
16
+ opens with the full-width hairline. The first follows the head at 16, as Seth's figure row does. */
17
+ .ws-panel__body { display: flex; flex: 1 1 auto; flex-direction: column; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
18
+ /* A keyboard scroller that runs the pane's full width: its focus ring is drawn inside it, never over the rim (D8). */
19
+ .ws-panel__body:focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
20
+ .ws-panel__section { display: flex; flex-direction: column; gap: var(--space-md); padding: var(--ws-card-inset) var(--ws-band-inset); border-top: var(--ws-divider); }
21
+ .ws-panel__body > .ws-panel__section:first-child { padding-top: var(--space-md); border-top: 0; }
22
+ .ws-panel__section--tight { padding-block: var(--space-sm); }
23
+ /* The foot: what to do next, pinned under the body. Its acts share the width in equal halves, the one primary last,
24
+ and stack, the primary at the bottom, where a half would be under 160 or a label would break (301:2099; Button's
25
+ ws-btn-split rule). */
26
+ .ws-panel__foot { display: flex; flex-shrink: 0; flex-direction: column; }
27
+ /* Seth's next fix (301:2085): its 13 / 18 label 6 above the 15 / 22 fix, as one part of the section. */
28
+ .ws-panel__next { display: flex; flex-direction: column; gap: var(--space-xxs-plus); }
29
+ .ws-panel__next > .ws-control-words { color: var(--color-text-muted); }
30
+ .ws-panel__acts { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
31
+ .ws-panel__acts > * { flex: 1 1 var(--space-8xl); min-width: min(100%, max-content); }
32
+ /* A figure and the strip it reads from, on one line (Seth's 44 / 48 figure, 20 apart). The strip takes the rest of the
33
+ line, so it must be able to shrink (ticks that flex, as Seth's TickStrip); fixed ticks run past the pane on a phone. */
34
+ .ws-panel__figure-row { display: flex; align-items: center; gap: var(--ws-card-inset); }
35
+ .ws-panel__figure-row > :last-child { flex: 1; min-width: 0; }
36
+ @media (max-width: 40rem) {
37
+ .ws-panel { position: fixed; inset: 0; z-index: var(--z-modal); width: auto; max-width: none; border: 0; border-radius: 0; }
38
+ }
39
+ /* Motion (D7, North's docked panel, js@647700, "Opens / moves / docks (spring)"): it grows out of .97 from the edge it
40
+ docks to on the panel spring (.42 / .12), fading in over .2 s and out of an 8 px blur over .24 s, each on its own
41
+ clock as North runs them. --leaving (or data-leaving) goes back over .16 s, faster
42
+ (opacity 0, .97, a 6 px blur, js@674822), before the page without it renders. */
43
+ .ws-panel { --ws-panel-out: 160ms; /* raw: North's chat exit .16 s */ --ws-panel-unblur: 240ms; /* raw: North's .24 s blur in */ transform-origin: 100% 50%; }
44
+ @keyframes ws-panel-fade { from { opacity: 0; } }
45
+ @keyframes ws-panel-grow { from { scale: var(--ws-open-scale); } }
46
+ @keyframes ws-panel-unblur { from { filter: blur(var(--ws-blur-panel)); } }
47
+ @keyframes ws-panel-out { to { opacity: 0; scale: var(--ws-open-scale); filter: blur(calc(var(--ws-blur-arrive) * 2)); } }
48
+ @media (prefers-reduced-motion: no-preference) {
49
+ .ws-panel { animation: ws-panel-fade var(--ws-duration-hover) var(--ws-ease-soft) backwards, ws-panel-grow var(--ws-spring-panel-duration) var(--ws-spring-panel) backwards, ws-panel-unblur var(--ws-panel-unblur) var(--ws-ease-soft) backwards; }
50
+ .ws-panel--leaving, .ws-panel[data-leaving] { animation: ws-panel-out var(--ws-panel-out) var(--ws-ease-exit) forwards; pointer-events: none; }
51
+ }
@@ -17,9 +17,11 @@ A password field with a visibility toggle and a strength line, so a person can s
17
17
  - Field: `Input`'s exact shell, `type="password"`, with a 16 px eye line icon button at the right, toggling to plain
18
18
  text.
19
19
  - Strength line, under the field: four 4 px-tall segments (`--ws-radius-bar`, the token literally named "a thin bar in
20
- a strip," which fits even at this height), 4 px apart, filling in the system's own goal gradient, salmon through
21
- purple to teal, by strength, with a 12 px word ("Weak", "Good", "Strong") beside them.
22
- - "Forgot it?" sits beside the label, as a quiet link (`--color-text-interactive`, underline on hover).
20
+ a strip," which fits even at this height), 4 px apart, one tone a level (Seth's strips: one solid tone, never a
21
+ gradient): weak one part salmon, fair two and good three purple, strong all four teal, with a 12 px word ("Weak",
22
+ "Fair", "Good", "Strong") beside them in that tone's AA step.
23
+ - "Forgot it?" sits beside the label, as a quiet link (`--color-text-interactive`, underline on hover), at the label's
24
+ 12 / 16 (the label row sets it), so the secondary act never outweighs the field's name.
23
25
 
24
26
  ## Variants and sizes
25
27
 
@@ -33,26 +35,34 @@ There is one size; it grows to 40 px on a touch screen.
33
35
  ## States
34
36
 
35
37
  - **Empty:** masked, no strength line until something is typed.
36
- - **Visible:** the eye button is pressed (`aria-pressed="true"`), the value shows as plain text.
38
+ - **Visible:** the value shows as plain text; the eye's icon and label change to "Hide password" (no `aria-pressed`,
39
+ which would give it a pressed icon button's navy fill).
37
40
  - **Weak:** one segment filled salmon, the word "Weak" in `--ws-word-needs`.
38
- - **Good:** two to three segments filled through purple, the word "Good" in `--ws-chart-past-half`, which is already
39
- AA-safe on the cream (5.7:1) without a separate word token.
41
+ - **Fair:** two segments purple, the word "Fair" in `--ws-word-past-half` (the purple's AA step).
42
+ - **Good:** three segments purple, the word "Good" in `--ws-word-past-half`.
40
43
  - **Strong:** all four segments filled through teal, the word "Strong" in `--ws-word-positive`.
41
- - **Focus:** the edge turns mid blue with the focus halo, exactly as `Input`.
42
- - **Error:** as `Input`'s error state, for a password that fails a server-side check.
44
+ - **Focus:** the kit's focus ring, 2 px navy, 2 px out, as `Input`'s (a field's edge darkens under the pointer).
45
+ - **Error:** as `Input`'s error state, for a password that fails a server-side check: `aria-invalid="true"` and a
46
+ `ws-field-msg--error` below that says what to do, named by `aria-describedby`.
47
+ - **Disabled:** `Input`'s sunk ground and muted words; the eye is disabled with it.
48
+ - **Dark and the navy ground:** the eye is a field mark (`--ws-field-mark`), so it stays navy on the navy ground's
49
+ white field.
43
50
 
44
51
  ## Behaviour
45
52
 
46
53
  - Keyboard: a native `<input>` whose `type` toggles between `password` and `text`; Tab reaches the eye button like any
47
54
  icon button.
48
55
  - Pointer / touch: pressing the eye toggles visibility; it stays toggled until pressed again.
49
- - Motion: the strength segments fill with a colour change only, no motion beyond the field's own instant transitions.
56
+ - Motion: the strength segments change colour only.
57
+ - The eye changes its icon and its label ("Show password", "Hide password"); it does not wear the navy fill a pressed
58
+ icon button wears.
50
59
  - Reduced motion: no change, since nothing moves beyond a colour swap.
51
60
 
52
61
  ## Content
53
62
 
54
63
  - "Forgot it?" exactly, not "Forgot password?": short, plain, second person.
55
- - The strength word is one of three: "Weak", "Good", "Strong", never a percentage or a score.
64
+ - The strength word is one of four: "Weak", "Fair", "Good", "Strong", never a percentage or a score. The field names it
65
+ through `aria-describedby`, so it is heard with the field.
56
66
  - No em dashes. Never send a typed password to a model, extending `Input`'s own rule against sending emails or phone
57
67
  numbers to AI.
58
68
 
@@ -63,22 +73,44 @@ There is one size; it grows to 40 px on a touch screen.
63
73
  | `label` | `string` | the field's name |
64
74
  | `value` | `string` | the typed password |
65
75
  | `onChange` | `(value: string) => void` | fires on type |
66
- | `strength` | `'weak' \| 'good' \| 'strong' \| null` | drives the segment fill and word |
76
+ | `strength` | `'weak' \| 'fair' \| 'good' \| 'strong' \| null` | drives the segment fill and word |
67
77
  | `onForgot` | `() => void` | present when "Forgot it?" is shown |
68
78
  | `error` | `string` | the message shown below in the error state |
69
79
 
70
80
  ## Rules
71
81
 
72
- - The eye button is a toggle like any other: `aria-pressed` carries its state, per 08-choosing-components.md's own rule
73
- ("a toggle's state is `aria-pressed`").
74
- - The three strength colours are the system's own goal gradient (03-colour.md: "a strip of ticks runs salmon (start)
75
- through purple (past halfway) to teal (finished)"), reused here as a strength scale rather than a goal's progress.
76
- - "Good" borrows the raw `--ws-chart-past-half` purple as a word colour, not a `--ws-word-*` token, because the Colour
77
- doc's own contrast table already clears purple for words ("purple #6D4FC2 on cream | 5.7 | AA, a word in purple is
78
- allowed"); only salmon and teal need their AA-renamed steps.
82
+ - The eye says its state in its label and its icon, not with `aria-pressed` (a pressed icon button turns navy, which
83
+ inside a field reads as a second act).
84
+ - The strength tones are the goal's tones, one a level (`ws-password-input__strength--weak`, `--fair`, `--good`,
85
+ `--strong`), never a gradient across the strip; each word is its tone's AA step (`--ws-word-needs`,
86
+ `--ws-word-past-half`, `--ws-word-positive`).
79
87
 
80
88
  ## Source
81
89
 
82
90
  Intentional addition, decided from the system (26 Oct 2026): the system had `Input` with `type="password"` but no
83
91
  visibility toggle or strength feedback. Built on `Input`'s field chrome, `IconButton`'s toggle pattern, and the goal
84
92
  gradient already used elsewhere for progress.
93
+
94
+ ## Markup
95
+
96
+ ```html
97
+ <div class="ws-field">
98
+ <div class="ws-password-input__label-row">
99
+ <label class="ws-label" for="pw">Create your password</label>
100
+ <button type="button" class="ws-btn ws-btn--link">Forgot it?</button>
101
+ </div>
102
+ <span class="ws-password-input">
103
+ <input class="ws-input" id="pw" type="password" aria-describedby="pw-level">
104
+ <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-label="Show password" title="Show password">...</button>
105
+ </span>
106
+ <div class="ws-password-input__strength ws-password-input__strength--good">
107
+ <span class="ws-password-input__segments" aria-hidden="true"><span class="ws-password-input__segment"></span><span class="ws-password-input__segment"></span><span class="ws-password-input__segment"></span><span class="ws-password-input__segment"></span></span>
108
+ <span class="ws-password-input__word" id="pw-level">Good</span>
109
+ </div>
110
+ </div>
111
+ ```
112
+
113
+ ## Styles
114
+
115
+ `design-system/components/PasswordInput/style.css`: `ws-password-input`, `__label-row`, `__eye`, `__strength` and its
116
+ four levels, `__segments`, `__segment`, `__word`.
@@ -9,7 +9,7 @@
9
9
  </head>
10
10
  <body>
11
11
  <div class="ws-stage">
12
- <div style="display: grid; grid-template-columns: repeat(3, minmax(var(--space-9xl), 1fr)); gap: var(--ws-gap-component); width: 100%;">
12
+ <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr)); gap: var(--ws-gap-component); width: 100%;">
13
13
 
14
14
  <div class="ws-field">
15
15
  <div class="ws-password-input__label-row">
@@ -17,17 +17,17 @@
17
17
  <button type="button" class="ws-btn ws-btn--link">Forgot it?</button>
18
18
  </div>
19
19
  <span class="ws-password-input">
20
- <input class="ws-input" id="pw-weak" type="password" value="summer">
21
- <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-pressed="false" aria-label="Show password">
20
+ <input class="ws-input" id="pw-weak" type="password" value="summer" aria-describedby="pw-weak-level">
21
+ <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-label="Show password" title="Show password">
22
22
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1.5 8S4 3.5 8 3.5 14.5 8 14.5 8 12 12.5 8 12.5 1.5 8 1.5 8Z"/><circle cx="8" cy="8" r="2"/></svg>
23
23
  </button>
24
24
  </span>
25
- <div class="ws-password-input__strength">
25
+ <div class="ws-password-input__strength ws-password-input__strength--weak">
26
26
  <span class="ws-password-input__segments" aria-hidden="true">
27
- <span class="ws-password-input__segment" style="background: var(--color-brand-salmon);"></span>
27
+ <span class="ws-password-input__segment"></span>
28
28
  <span class="ws-password-input__segment"></span><span class="ws-password-input__segment"></span><span class="ws-password-input__segment"></span>
29
29
  </span>
30
- <span class="ws-password-input__word" style="color: var(--ws-word-needs);">Weak</span>
30
+ <span class="ws-password-input__word" id="pw-weak-level">Weak</span>
31
31
  </div>
32
32
  </div>
33
33
 
@@ -37,19 +37,19 @@
37
37
  <button type="button" class="ws-btn ws-btn--link">Forgot it?</button>
38
38
  </div>
39
39
  <span class="ws-password-input">
40
- <input class="ws-input" id="pw-good" type="text" value="priya-and-team26">
41
- <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-pressed="true" aria-label="Hide password">
40
+ <input class="ws-input" id="pw-good" type="text" value="priya-and-team26" aria-describedby="pw-good-level">
41
+ <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-label="Hide password" title="Hide password">
42
42
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 2l12 12M6.3 6.4a2 2 0 0 0 2.9 2.8M4.2 4.5C2.6 5.6 1.5 8 1.5 8s2.5 4.5 6.5 4.5c1.1 0 2.1-.3 2.9-.8M9.8 3.7C9.2 3.6 8.6 3.5 8 3.5c-.4 0-.8 0-1.2.1"/></svg>
43
43
  </button>
44
44
  </span>
45
- <div class="ws-password-input__strength">
45
+ <div class="ws-password-input__strength ws-password-input__strength--good">
46
46
  <span class="ws-password-input__segments" aria-hidden="true">
47
- <span class="ws-password-input__segment" style="background: var(--color-brand-salmon);"></span>
48
- <span class="ws-password-input__segment" style="background: var(--ws-chart-past-half);"></span>
49
- <span class="ws-password-input__segment" style="background: var(--ws-chart-past-half);"></span>
47
+ <span class="ws-password-input__segment"></span>
48
+ <span class="ws-password-input__segment"></span>
49
+ <span class="ws-password-input__segment"></span>
50
50
  <span class="ws-password-input__segment"></span>
51
51
  </span>
52
- <span class="ws-password-input__word" style="color: var(--ws-chart-past-half);">Good</span>
52
+ <span class="ws-password-input__word" id="pw-good-level">Good</span>
53
53
  </div>
54
54
  </div>
55
55
 
@@ -59,20 +59,70 @@
59
59
  <button type="button" class="ws-btn ws-btn--link">Forgot it?</button>
60
60
  </div>
61
61
  <span class="ws-password-input">
62
- <input class="ws-input" id="pw-strong" type="password" value="nine-cold-calls-friday">
63
- <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-pressed="false" aria-label="Show password">
62
+ <input class="ws-input" id="pw-strong" type="password" value="nine-cold-calls-friday" aria-describedby="pw-strong-level">
63
+ <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-label="Show password" title="Show password">
64
64
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1.5 8S4 3.5 8 3.5 14.5 8 14.5 8 12 12.5 8 12.5 1.5 8 1.5 8Z"/><circle cx="8" cy="8" r="2"/></svg>
65
65
  </button>
66
66
  </span>
67
- <div class="ws-password-input__strength">
67
+ <div class="ws-password-input__strength ws-password-input__strength--strong">
68
68
  <span class="ws-password-input__segments" aria-hidden="true">
69
- <span class="ws-password-input__segment" style="background: var(--color-brand-salmon);"></span>
70
- <span class="ws-password-input__segment" style="background: var(--ws-chart-past-half);"></span>
71
- <span class="ws-password-input__segment" style="background: var(--ws-chart-past-half);"></span>
72
- <span class="ws-password-input__segment" style="background: var(--color-stat-positive);"></span>
69
+ <span class="ws-password-input__segment"></span>
70
+ <span class="ws-password-input__segment"></span>
71
+ <span class="ws-password-input__segment"></span>
72
+ <span class="ws-password-input__segment"></span>
73
73
  </span>
74
- <span class="ws-password-input__word" style="color: var(--ws-word-positive);">Strong</span>
74
+ <span class="ws-password-input__word" id="pw-strong-level">Strong</span>
75
+ </div>
76
+ </div>
77
+
78
+ <div class="ws-field">
79
+ <div class="ws-password-input__label-row">
80
+ <label class="ws-label" for="pw-fair">Create your password</label>
75
81
  </div>
82
+ <span class="ws-password-input">
83
+ <input class="ws-input" id="pw-fair" type="password" value="openhouse7" aria-describedby="pw-fair-level">
84
+ <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-label="Show password" title="Show password">
85
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1.5 8S4 3.5 8 3.5 14.5 8 14.5 8 12 12.5 8 12.5 1.5 8 1.5 8Z"/><circle cx="8" cy="8" r="2"/></svg>
86
+ </button>
87
+ </span>
88
+ <div class="ws-password-input__strength ws-password-input__strength--fair">
89
+ <span class="ws-password-input__segments" aria-hidden="true">
90
+ <span class="ws-password-input__segment"></span>
91
+ <span class="ws-password-input__segment"></span>
92
+ <span class="ws-password-input__segment"></span>
93
+ <span class="ws-password-input__segment"></span>
94
+ </span>
95
+ <span class="ws-password-input__word" id="pw-fair-level">Fair</span>
96
+ </div>
97
+ </div>
98
+
99
+ <div class="ws-field">
100
+ <div class="ws-password-input__label-row">
101
+ <label class="ws-label" for="pw-err">Your password</label>
102
+ <button type="button" class="ws-btn ws-btn--link">Forgot it?</button>
103
+ </div>
104
+ <span class="ws-password-input">
105
+ <input class="ws-input" id="pw-err" type="password" value="summer2026" aria-invalid="true" aria-describedby="pw-err-msg">
106
+ <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-label="Show password" title="Show password">
107
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1.5 8S4 3.5 8 3.5 14.5 8 14.5 8 12 12.5 8 12.5 1.5 8 1.5 8Z"/><circle cx="8" cy="8" r="2"/></svg>
108
+ </button>
109
+ </span>
110
+ <p class="ws-field-msg ws-field-msg--error" id="pw-err-msg">
111
+ <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="M8 2.3 14.6 13.5H1.4L8 2.3Z"/><path d="M8 6.4v3"/><path d="M8 11.5v.05"/></svg>
112
+ That password does not match this email; try again or reset it
113
+ </p>
114
+ </div>
115
+
116
+ <div class="ws-field">
117
+ <div class="ws-password-input__label-row">
118
+ <label class="ws-label" for="pw-off">Your password</label>
119
+ </div>
120
+ <span class="ws-password-input">
121
+ <input class="ws-input" id="pw-off" type="password" value="nine-cold-calls" disabled>
122
+ <button type="button" class="ws-icon-btn ws-icon-btn--xs ws-password-input__eye" aria-label="Show password" title="Show password" disabled>
123
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1.5 8S4 3.5 8 3.5 14.5 8 14.5 8 12 12.5 8 12.5 1.5 8 1.5 8Z"/><circle cx="8" cy="8" r="2"/></svg>
124
+ </button>
125
+ </span>
76
126
  </div>
77
127
 
78
128
  </div>
@@ -0,0 +1,31 @@
1
+ /* ---- PasswordInput: the house field with a show/hide eye inside it, and a four-part strength meter. The meter is one
2
+ tone a level (Seth's strips: one solid tone, never a gradient): weak one part salmon, fair two and good three purple,
3
+ strong all four teal; its word says the level in that tone's AA step. ---- */
4
+ /* The label row speaks at the label's size (12 / 16), so "Forgot it?" (a word act, which takes its row's size) never
5
+ outweighs the field's name beside it. */
6
+ .ws-password-input__label-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-sm); font-size: var(--ws-text-dense); line-height: var(--space-md); }
7
+ .ws-password-input { position: relative; display: flex; min-width: 0; }
8
+ .ws-password-input .ws-input { padding-right: var(--space-xl); }
9
+ /* The eye is a field mark (--ws-field-mark, Input/style.css). It is centred with translate, so the icon button's press
10
+ (a transform) gives in place instead of dropping the eye out of the field. */
11
+ .ws-password-input__eye { position: absolute; right: var(--space-xxs); top: 50%; translate: 0 -50%; }
12
+ .ws-password-input .ws-icon-btn { color: var(--ws-field-mark, var(--color-text-heading)); }
13
+ /* It sits on the field, not the ground, so its ring and its wash take the mark's colour too (on the navy ground the
14
+ white ring and wash vanished on the white field). */
15
+ .ws-password-input .ws-icon-btn:focus-visible { outline-color: var(--ws-field-mark, var(--ws-focus-color)); }
16
+ @media (hover: hover) { .ws-password-input .ws-icon-btn:not(:disabled):hover { background-color: color-mix(in srgb, var(--ws-field-mark, var(--color-text-heading)) 6%, transparent); } }
17
+ .ws-password-input:has(> .ws-input:disabled) .ws-icon-btn { color: var(--color-text-muted); }
18
+ .ws-password-input__strength { --ws-password-tone: var(--ws-track); display: flex; align-items: center; gap: var(--space-xs); margin-top: var(--space-xxs); }
19
+ .ws-password-input__segments { display: flex; gap: var(--space-xxs); }
20
+ .ws-password-input__segment { width: calc(var(--space-md) + var(--space-xxs)); height: var(--space-xxs); border-radius: var(--ws-radius-bar); background: var(--ws-track); }
21
+ .ws-password-input__word { font-size: var(--ws-text-dense); line-height: var(--space-md); font-weight: 500; }
22
+ .ws-password-input__strength--weak { --ws-password-tone: var(--ws-chart-under-half); }
23
+ .ws-password-input__strength--weak .ws-password-input__word { color: var(--ws-word-needs); }
24
+ .ws-password-input__strength--fair, .ws-password-input__strength--good { --ws-password-tone: var(--ws-chart-past-half); }
25
+ .ws-password-input__strength--fair .ws-password-input__word, .ws-password-input__strength--good .ws-password-input__word { color: var(--ws-word-past-half); }
26
+ .ws-password-input__strength--strong { --ws-password-tone: var(--ws-chart-finished); }
27
+ .ws-password-input__strength--strong .ws-password-input__word { color: var(--ws-word-positive); }
28
+ .ws-password-input__strength--weak .ws-password-input__segment:nth-child(-n + 1),
29
+ .ws-password-input__strength--fair .ws-password-input__segment:nth-child(-n + 2),
30
+ .ws-password-input__strength--good .ws-password-input__segment:nth-child(-n + 3),
31
+ .ws-password-input__strength--strong .ws-password-input__segment { background: var(--ws-password-tone); }