@repobit/dex-system-design 1.0.1 → 2.0.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 (136) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +43 -12
  3. package/package.json +6 -4
  4. package/schemas/component-manifest.json +207 -49
  5. package/schemas/manifest-omissions.json +2 -0
  6. package/src/components/accordion/accordion.css.js +12 -1
  7. package/src/components/agent-endpoints/agent-endpoints.js +1 -1
  8. package/src/components/alert/alert.css.js +22 -1
  9. package/src/components/anchor/anchor-nav.css.js +6 -1
  10. package/src/components/annotation/annotation.css.js +6 -1
  11. package/src/components/architecture.stories.js +6 -4
  12. package/src/components/avatar/avatar.js +37 -11
  13. package/src/components/avatar/avatar.stories.js +10 -6
  14. package/src/components/awards/awards-flex.css.js +31 -129
  15. package/src/components/awards/awards-flex.js +66 -116
  16. package/src/components/awards/awards-flex.stories.js +8 -6
  17. package/src/components/back/back.css.js +6 -1
  18. package/src/components/back/back.js +24 -12
  19. package/src/components/back/back.stories.js +3 -3
  20. package/src/components/badge/badge.js +6 -3
  21. package/src/components/badge/badge.stories.js +13 -9
  22. package/src/components/bento-grid/bento-grid.js +1 -1
  23. package/src/components/blockquote/blockquote.css.js +12 -3
  24. package/src/components/blockquote/blockquote.js +12 -5
  25. package/src/components/blockquote/blockquote.stories.js +6 -4
  26. package/src/components/button/button.css.js +46 -25
  27. package/src/components/button/button.js +44 -43
  28. package/src/components/button/button.stories.js +2 -2
  29. package/src/components/caption/caption.css.js +10 -0
  30. package/src/components/caption/caption.stories.js +4 -4
  31. package/src/components/carousel/carousel.css.js +19 -116
  32. package/src/components/carousel/carousel.js +70 -74
  33. package/src/components/carousel/carousel.stories.js +9 -8
  34. package/src/components/checkbox/checkbox.css.js +6 -1
  35. package/src/components/code/code.css.js +6 -1
  36. package/src/components/code/code.js +48 -7
  37. package/src/components/code/code.stories.js +7 -4
  38. package/src/components/divider/divider-horizontal.js +11 -3
  39. package/src/components/divider/divider-vertical.js +12 -5
  40. package/src/components/divider/divider.css.js +4 -1
  41. package/src/components/divider/divider.stories.js +6 -6
  42. package/src/components/dropdown/dropdown.js +67 -13
  43. package/src/components/dropdown/dropdown.stories.js +15 -14
  44. package/src/components/fieldset/fieldset.stories.js +41 -16
  45. package/src/components/footer/footer-nav-menu.css.js +6 -1
  46. package/src/components/footer/footer.css.js +6 -1
  47. package/src/components/header/header-code.js +1 -1
  48. package/src/components/header/header.css.js +9 -4
  49. package/src/components/heading/heading.stories.js +64 -31
  50. package/src/components/highlight/highlight.js +74 -34
  51. package/src/components/highlight/highlight.stories.js +5 -4
  52. package/src/components/icons/icon-element.js +49 -6
  53. package/src/components/icons/icons.stories.js +7 -0
  54. package/src/components/image/image.css.js +9 -98
  55. package/src/components/image/image.js +7 -2
  56. package/src/components/input/custom-form.stories.js +14 -19
  57. package/src/components/input/input-clipboard.css.js +6 -1
  58. package/src/components/input/input-clipboard.js +56 -8
  59. package/src/components/input/input.js +19 -2
  60. package/src/components/label/label.js +87 -1
  61. package/src/components/label/label.stories.js +21 -12
  62. package/src/components/link/link.css.js +52 -3
  63. package/src/components/link/link.js +52 -17
  64. package/src/components/link/link.stories.js +114 -3
  65. package/src/components/list/list-item/list-item.css.js +4 -6
  66. package/src/components/list/list-item/list-item.js +53 -5
  67. package/src/components/list/list-item/list-item.stories.js +22 -0
  68. package/src/components/list/list.js +27 -0
  69. package/src/components/modal/modal.css.js +6 -1
  70. package/src/components/pagination-dots/pagination-dots.css.js +161 -0
  71. package/src/components/pagination-dots/pagination-dots.js +202 -0
  72. package/src/components/pagination-dots/pagination-dots.stories.js +171 -0
  73. package/src/components/paragraph/paragraph.css.js +37 -1
  74. package/src/components/paragraph/paragraph.js +19 -2
  75. package/src/components/paragraph/paragraph.stories.js +40 -6
  76. package/src/components/picture/picture.css.js +8 -67
  77. package/src/components/picture/picture.js +15 -8
  78. package/src/components/pricing-cards/pricing-card.css.js +12 -2
  79. package/src/components/pricing-cards/pricing-card.js +4 -4
  80. package/src/components/progress/progress.js +24 -4
  81. package/src/components/progress/progress.stories.js +8 -6
  82. package/src/components/radio/radio.css.js +6 -1
  83. package/src/components/radio/radio.js +48 -3
  84. package/src/components/radio/radio.stories.js +1 -1
  85. package/src/components/shared/form-field.css.js +30 -4
  86. package/src/components/shared/token-scales.js +16 -0
  87. package/src/components/spinner/spinner.css.js +31 -4
  88. package/src/components/spinner/spinner.js +26 -4
  89. package/src/components/spinner/spinner.stories.js +10 -7
  90. package/src/components/switch/switch.css.js +21 -3
  91. package/src/components/switch/switch.js +8 -1
  92. package/src/components/switch/switch.stories.js +2 -1
  93. package/src/components/tab-list/tab-list.css.js +6 -1
  94. package/src/components/terminal/terminal-cursor.js +11 -0
  95. package/src/components/terminal/terminal-message.js +1 -1
  96. package/src/components/termsOfUse/terms.css.js +6 -1
  97. package/src/components/textarea/textarea.stories.js +23 -0
  98. package/src/components/tooltip/tooltip.css.js +24 -1
  99. package/src/components/tooltip/tooltip.js +135 -5
  100. package/src/components/tooltip/tooltip.stories.js +20 -14
  101. package/src/components/trust-panel/trust-panel.js +2 -2
  102. package/src/components/video/video.js +38 -3
  103. package/src/components/video/video.stories.js +10 -5
  104. package/src/components/video-player/video-player.css.js +6 -1
  105. package/src/tokens/elevation.stories.js +2 -3
  106. package/src/tokens/focus.stories.js +81 -15
  107. package/src/tokens/motion.stories.js +0 -6
  108. package/src/tokens/tokens.css +174 -97
  109. package/src/tokens/tokens.js +174 -97
  110. package/src/tokens/vertical-rhythm.stories.js +1 -1
  111. package/src/stories/Button.js +0 -20
  112. package/src/stories/Button.stories.js +0 -46
  113. package/src/stories/Configure.mdx +0 -364
  114. package/src/stories/Header.js +0 -45
  115. package/src/stories/Header.stories.js +0 -24
  116. package/src/stories/Page.js +0 -62
  117. package/src/stories/Page.stories.js +0 -20
  118. package/src/stories/assets/accessibility.png +0 -0
  119. package/src/stories/assets/accessibility.svg +0 -1
  120. package/src/stories/assets/addon-library.png +0 -0
  121. package/src/stories/assets/assets.png +0 -0
  122. package/src/stories/assets/avif-test-image.avif +0 -0
  123. package/src/stories/assets/context.png +0 -0
  124. package/src/stories/assets/discord.svg +0 -1
  125. package/src/stories/assets/docs.png +0 -0
  126. package/src/stories/assets/figma-plugin.png +0 -0
  127. package/src/stories/assets/github.svg +0 -1
  128. package/src/stories/assets/share.png +0 -0
  129. package/src/stories/assets/styling.png +0 -0
  130. package/src/stories/assets/testing.png +0 -0
  131. package/src/stories/assets/theming.png +0 -0
  132. package/src/stories/assets/tutorials.svg +0 -1
  133. package/src/stories/assets/youtube.svg +0 -1
  134. package/src/stories/button.css +0 -30
  135. package/src/stories/header.css +0 -32
  136. package/src/stories/page.css +0 -68
@@ -81,8 +81,11 @@ const declarations = `
81
81
  t-comma, -0.275em in Bold, a third deeper than g -- and the tallest
82
82
  are grave and acute capitals at +0.977em. Measured per weight, ink
83
83
  spans 1.174 Light, 1.194 Regular, 1.219 Medium, 1.235 SemiBold,
84
- 1.252 Bold. Nothing may be set below the floor for its weight, which
85
- is why the display roles stay at tight and are not tightened further.
84
+ 1.252 Bold. Tight, 1.25, sits 0.002 under the Bold floor -- the
85
+ tallest accented capital and the deepest comma-below letter would
86
+ have to fall in consecutive lines, one above the other, to touch --
87
+ which is why the display roles stay at tight and are never tightened
88
+ further.
86
89
  Foundations/Typography/Vertical Rhythm reaches 1.231 for 48px Bold
87
90
  from canvas metrics; this is the same number from outline bounds. */
88
91
  --typography-lineHeight-130: 1.3;
@@ -243,13 +246,12 @@ const declarations = `
243
246
  /* The CSS default curve. It was missing, and its absence was the single
244
247
  reason eleven component transitions could not be expressed in tokens:
245
248
  ease is not ease-in-out — it front-loads, and swapping one for the
246
- other is a visible change. Named after the keyword, as linear,
247
- ease-in and ease-out beside it are; smooth is the outlier that
248
- aliases a keyword rather than naming it. */
249
+ other is a visible change. Named after the keyword, as linear and
250
+ ease-out beside it are; smooth is the outlier that aliases a keyword
251
+ rather than naming it. ease-in and a bounce curve stood here too and
252
+ went on 2026-10-15, because nothing read either. */
249
253
  --transition-easing-ease: ease;
250
- --transition-easing-ease-in: ease-in;
251
254
  --transition-easing-ease-out: ease-out;
252
- --transition-easing-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
253
255
 
254
256
  /* ================================
255
257
  CORE SHADOW TOKENS
@@ -261,37 +263,62 @@ const declarations = `
261
263
  0 4px 6px -4px rgb(0 0 0 / 0.1);
262
264
  --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1),
263
265
  0 8px 10px -6px rgb(0 0 0 / 0.1);
264
- --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
265
- --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
266
+ /* The ladder stops at xl. --shadow-2xl and the inset --shadow-inner
267
+ stood beside it until 2026-10-15, and nothing read either. */
266
268
 
267
- /* Focus Shadows - Box Shadow Version */
268
- --shadow-focus-outline-blue: 0 0 0 3px rgba(6, 109, 255, 0.15);
269
- --shadow-focus-outline-red: 0 0 0 3px rgba(239, 68, 68, 0.15);
270
- --shadow-focus-outline-green: 0 0 0 3px rgba(34, 197, 94, 0.15);
269
+ /* The focus glow: --color-blue-500 at 15%, mixed from the palette. It
270
+ was rgba(6, 109, 255, 0.15) -- a blue one unit off blue-500 -- with a
271
+ red and a green beside it that were Tailwind's red-500 and green-500,
272
+ in no palette here. Nothing read the red or the green, so they went;
273
+ --form-input-focus-boxShadow reads this one. */
274
+ --shadow-focus-outline-blue: 0 0 0 3px color-mix(in srgb, var(--color-blue-500) 15%, transparent);
271
275
 
272
276
  /* Focus Outlines - Two-Color System for Universal Visibility */
273
277
  --focus-outline-primary: 0 0 0 2px var(--color-neutral-0),
274
278
  0 0 0 4px var(--color-blue-500);
275
- --focus-outline-secondary: 0 0 0 2px var(--color-neutral-0),
276
- 0 0 0 4px var(--color-neutral-600);
277
- --focus-outline-error: 0 0 0 2px var(--color-neutral-0),
278
- 0 0 0 4px var(--color-red-500);
279
- --focus-outline-success: 0 0 0 2px var(--color-neutral-0),
280
- 0 0 0 4px var(--color-green-500);
281
-
282
- /* The four above put the WHITE band against the control, which is what
283
- keeps them visible on a coloured hero. That fails on a control which
279
+
280
+ /* Secondary, error and success rings stood here until 2026-10-15.
281
+ Nothing read them -- every component rings with primary -- and the
282
+ success ring was green-500, 2.39:1 against white, under the 3:1 a
283
+ focus indicator needs. */
284
+
285
+ /* The one above puts the WHITE band against the control, which is what
286
+ keeps it visible on a coloured hero. That fails on a control which
284
287
  is itself white: bd-button-link variant="white" measured 1.00:1 for
285
288
  the inner band against its own fill and 2.38:1 for the blue band
286
289
  against a blue hero, so neither band reached the 3:1 WCAG 2.4.11
287
290
  asks of a focus indicator. This one inverts the pair -- dark band
288
291
  against the control, white band against the ground -- so the inner
289
- band carries the contrast whatever the control is sitting on. */
292
+ band carries the contrast whatever the control is sitting on.
293
+
294
+ This is also what the Figma library draws around a filled or
295
+ bordered button on a dark or brand ground (focus/ring-inverse).
296
+ Figma builds a ring from spread shadows, so every band in it is a
297
+ painted colour, and the band between control and ring has to be
298
+ chosen per surface. On a brand hero that painted neutral-900 band
299
+ reads as a black outline. bd-button does not use this one; it draws
300
+ the outline below, whose gap is not painted at all. */
290
301
  --focus-outline-inverse: 0 0 0 2px var(--color-neutral-900),
291
302
  0 0 0 4px var(--color-neutral-0);
292
303
 
293
- /* Traditional Outline Values for Fallback */
304
+ /* The CTA ring in code: one band, held off the control by
305
+ --focus-outline-offset. The offset leaves the gap TRANSPARENT, so it
306
+ is always the surface the control sits on -- white on a light page,
307
+ neutral-900 on a dark hero, blue on a brand one -- and never merges
308
+ into a white fill or a white border, which is the job the painted
309
+ inner band of every two-band ring above does by hand. Figma has no
310
+ offset outline, which is why its rings paint that gap and need one
311
+ style per surface; code does not.
312
+
313
+ Only the band's colour follows the ground. Blue on light, 4.53:1
314
+ against white. White on dark and brand, the one colour that clears
315
+ 3:1 on both: 4.53:1 on blue-500, 6.18 on blue-600, 8.56 on blue-700,
316
+ 17.97 on neutral-900. A blue band on a brand hero is 1.00:1.
317
+
318
+ An outline also survives forced-colors mode, where a box-shadow ring
319
+ is not drawn at all. bd-button reads these; see its FOCUS block. */
294
320
  --focus-outline-traditional: 2px solid var(--color-blue-500);
321
+ --focus-outline-traditional-inverse: 2px solid var(--color-neutral-0);
295
322
  --focus-outline-offset: 2px;
296
323
 
297
324
  /* The ring is a box-shadow, so it already follows the radius of the
@@ -441,7 +468,7 @@ const declarations = `
441
468
  /* Subordinate text on an inverse surface. --color-text-inverse is the
442
469
  high-emphasis step there; this is the one below it, and until now the
443
470
  system had no such step at all -- the light-side -secondary, -muted and
444
- -subtle roles invert to 1.91, 2.73 and 3.85 against
471
+ -subtle roles invert to 1.91, 2.73 and 3.74 against
445
472
  --color-surface-inverse and cannot be used.
446
473
 
447
474
  Two components had already invented it independently and spelled it
@@ -482,6 +509,14 @@ const declarations = `
482
509
  the link it borrowed falls to 2.27:1. */
483
510
  --color-text-brand-hover: var(--color-blue-700);
484
511
  --color-text-success: var(--color-green-800);
512
+ /* Warning text sits on the page, a card, surface-subtle (5.16) or its
513
+ own tint (--color-surface-warning, 5.59) -- not on surface-muted,
514
+ where it measures 4.49:1, a hair under 4.5. The same rule as
515
+ -subtle above: the role is right and the ground is wrong, so the
516
+ fix is the ground. Nothing in the package draws the pair today:
517
+ bd-tag and bd-badge draw this yellow through --status-warning-text,
518
+ on --status-warning-surface. The way a page reaches it is the
519
+ color="warning" value the text atoms take (token-scales.js). */
485
520
  --color-text-warning: var(--color-yellow-800);
486
521
  --color-text-error: var(--color-red-700);
487
522
 
@@ -541,7 +576,18 @@ const declarations = `
541
576
  -subtle is a decorative hairline between things that already belong
542
577
  together, not the edge of anything actionable, and disabled controls
543
578
  are exempt under 1.4.3. Do not reach for -subtle to outline a
544
- control. */
579
+ control.
580
+
581
+ -default's 3.23 holds on white and nowhere darker: on a band it is
582
+ 2.88 (surface-subtle) and 2.51 (surface-muted), and on the brand
583
+ tint 2.99. So a control drawn in it carries a fill of its own --
584
+ surface-default, the ground it was measured on -- and its edge is
585
+ read against that fill wherever it is placed. A field already does
586
+ (--form-input-default-backgroundColor). A control with no fill, or
587
+ whose fill IS this colour (the switch's off track), belongs on the
588
+ page or a card, not on a band. The rule is where it goes; a darker
589
+ -default would move every field edge in the system to fix a case
590
+ nothing in the package draws. */
545
591
  --color-border-subtle: var(--color-neutral-200);
546
592
  --color-border-default: var(--color-neutral-400);
547
593
  --color-border-strong: var(--color-neutral-900);
@@ -605,7 +651,9 @@ const declarations = `
605
651
  this theme, which is why the borrowing never showed -- and both must
606
652
  flip to near-black in a dark one, which would turn every label and
607
653
  mark dark on its blue or red fill. White clears every action fill:
608
- 4.53 to 10.51:1. Measured in the Figma Dark mode study. */
654
+ 4.53 to 10.51:1 on the blue and red ones, 13.28 to 21:1 on the
655
+ near-black secondary ones. Measured in the Figma Dark mode study,
656
+ and re-measured on the live tokens 2026-10-15. */
609
657
  --color-action-on-fill: var(--color-neutral-0);
610
658
 
611
659
  /* ================================
@@ -734,10 +782,14 @@ const declarations = `
734
782
  takes, it is whether the longest UNBREAKABLE WORD fits the column.
735
783
  The column is the viewport minus twice --grid-padding-*, so a 320px
736
784
  phone gives type 288px -- 10% of the screen is gutter. Against that
737
- column the ceiling for the worst realistic compound (Finnish
738
- "Henkilollisyyssuoja", 9.07px of width per px of size) is 31px.
739
- heading-h1 is 48. It overflowed at 375 as well as at 320, in German
740
- and Finnish both.
785
+ column, the widest real words in the nine shipping languages --
786
+ harvested from live localized copy, see type-ceiling-words.js and
787
+ plans/LATER.md entry 8 -- set two ceilings: 28px for any copy
788
+ (Swedish "kalenderinbjudningar", 10.16px of width per px of size)
789
+ and 31px for headline copy (German "Gründungsmitglied", 9.05).
790
+ heading-h1 is 48. It overflowed at 375 as well as at 320. (This
791
+ rested on a Finnish word once; Finnish is not a market, and three
792
+ real words beat it.)
741
793
 
742
794
  WHY NOT A BREAKPOINT STEP. Tried first and abandoned, measured:
743
795
  stepping h1 to 36 below sm still overflows at 320 (needs 327 of
@@ -779,20 +831,24 @@ const declarations = `
779
831
  numbers: 30 27 24 21 18 16 for h1 through h6. Its ratios are 1.11,
780
832
  1.13, 1.14, 1.17, 1.13 -- near-even, against the desktop ladder's
781
833
  irregular 1.33, 1.13, 1.33, 1.20, 1.11. A phone has less range to
782
- spend (16 to the 31px ceiling, a factor of 1.94, where desktop has
783
- 18 to 48, a factor of 2.67), so the ladder has to compress, and
784
- compressing it evenly is what keeps six levels distinguishable.
785
- Measured: h1 at 30 needs 272px of a 288px column, so there is 16px
786
- of margin for a word longer than any tested.
834
+ spend (16 to the 31px headline ceiling, a factor of 1.94, where
835
+ desktop has 18 to 48, a factor of 2.67), so the ladder has to
836
+ compress, and compressing it evenly is what keeps six levels
837
+ distinguishable. Measured on the harvest: h1 at 30 sets the widest
838
+ headline word in 272px of a 288px column. The widest word of any
839
+ tier would need 305, so a headline word wider than 960px per 100px
840
+ of size is a stated copy constraint (LATER.md entry 8) rather than
841
+ a margin the ladder holds -- none of the five such words is a
842
+ headline today.
787
843
 
788
844
  WHAT IS DELIBERATELY NOT FLUID.
789
845
  - Every role at 24px and below except body-lead. Nothing that small
790
846
  overflows, and the small end's own failure is measure, not
791
847
  overflow -- see below.
792
848
  - display-lg, display-xl, display-2xl, display-3xl (60 to 128px).
793
- Above display-md the family has NO mobile identity
794
- to give it: five roles would all have to squeeze between 30 and
795
- the 31px ceiling. They are desktop-only, and a component that
849
+ Above display-md the family has NO mobile identity to give it:
850
+ four roles would all have to squeeze between 30 and the 31px
851
+ headline ceiling. They are desktop-only, and a component that
796
852
  needs one of them on a phone should reach for a heading role
797
853
  instead. Recorded rather than papered over.
798
854
  - body-regular at 16px. It runs 40 characters per line on a 288px
@@ -1024,6 +1080,11 @@ const declarations = `
1024
1080
  --typography-caption-default-fontFamily: var(--typography-fontFamily-sans);
1025
1081
  --typography-caption-default-color: var(--color-text-muted);
1026
1082
 
1083
+ /* "small" is not smaller. It is caption-default with the tracking
1084
+ opened to wide -- 0.025em, which is the design's caption/small at
1085
+ 2.5% -- and every other axis equal. The repeated axes are the role
1086
+ being complete, as every role is, not a second copy to remove;
1087
+ bd-caption size="sm" reads all five. */
1027
1088
  --typography-caption-small-fontSize: var(--typography-fontSize-xs);
1028
1089
  --typography-caption-small-fontWeight: var(--typography-fontWeight-normal);
1029
1090
  --typography-caption-small-lineHeight: var(--typography-lineHeight-normal);
@@ -1125,28 +1186,43 @@ const declarations = `
1125
1186
  COMPONENT TOKENS - FORM
1126
1187
  ================================ */
1127
1188
 
1128
- /* Form Label Tokens */
1129
- --form-label-default-fontSize: var(--typography-fontSize-sm);
1130
- --form-label-default-fontWeight: var(--typography-fontWeight-medium);
1131
- --form-label-default-lineHeight: var(--typography-lineHeight-normal);
1132
- --form-label-default-letterSpacing: var(--typography-letterSpacing-normal);
1133
- --form-label-default-fontFamily: var(--typography-fontFamily-sans);
1189
+ /* Form Label Tokens
1190
+
1191
+ The type of a field's label is the label role's, read through: the
1192
+ default and small rungs were the same five values as
1193
+ --typography-label-default-* and -small-*, written out a second time,
1194
+ so a change to the role would have reached bd-text and missed every
1195
+ form. Colour and margin stay here; they are the form's own and no
1196
+ role carries them.
1197
+
1198
+ Large differs on one axis. Its size is 18px where label-large is
1199
+ 16px, set to the large field's own text (--form-input-large-fontSize)
1200
+ so the label and the value read at the same step; a field's own
1201
+ label at size lg, bd-label size=lg and the bd-fieldset legend all
1202
+ render it. That size stays a
1203
+ primitive -- no label role sits at 18px -- and the other four axes
1204
+ are label-large's. */
1205
+ --form-label-default-fontSize: var(--typography-label-default-fontSize);
1206
+ --form-label-default-fontWeight: var(--typography-label-default-fontWeight);
1207
+ --form-label-default-lineHeight: var(--typography-label-default-lineHeight);
1208
+ --form-label-default-letterSpacing: var(--typography-label-default-letterSpacing);
1209
+ --form-label-default-fontFamily: var(--typography-label-default-fontFamily);
1134
1210
  --form-label-default-color: var(--color-text-primary);
1135
1211
  --form-label-default-marginBottom: var(--spacing-4);
1136
1212
 
1137
1213
  --form-label-large-fontSize: var(--typography-fontSize-lg);
1138
- --form-label-large-fontWeight: var(--typography-fontWeight-medium);
1139
- --form-label-large-lineHeight: var(--typography-lineHeight-normal);
1140
- --form-label-large-letterSpacing: var(--typography-letterSpacing-normal);
1141
- --form-label-large-fontFamily: var(--typography-fontFamily-sans);
1214
+ --form-label-large-fontWeight: var(--typography-label-large-fontWeight);
1215
+ --form-label-large-lineHeight: var(--typography-label-large-lineHeight);
1216
+ --form-label-large-letterSpacing: var(--typography-label-large-letterSpacing);
1217
+ --form-label-large-fontFamily: var(--typography-label-large-fontFamily);
1142
1218
  --form-label-large-color: var(--color-text-primary);
1143
1219
  --form-label-large-marginBottom: var(--spacing-8);
1144
1220
 
1145
- --form-label-small-fontSize: var(--typography-fontSize-xs);
1146
- --form-label-small-fontWeight: var(--typography-fontWeight-medium);
1147
- --form-label-small-lineHeight: var(--typography-lineHeight-normal);
1148
- --form-label-small-letterSpacing: var(--typography-letterSpacing-wide);
1149
- --form-label-small-fontFamily: var(--typography-fontFamily-sans);
1221
+ --form-label-small-fontSize: var(--typography-label-small-fontSize);
1222
+ --form-label-small-fontWeight: var(--typography-label-small-fontWeight);
1223
+ --form-label-small-lineHeight: var(--typography-label-small-lineHeight);
1224
+ --form-label-small-letterSpacing: var(--typography-label-small-letterSpacing);
1225
+ --form-label-small-fontFamily: var(--typography-label-small-fontFamily);
1150
1226
  --form-label-small-color: var(--color-text-primary);
1151
1227
  --form-label-small-marginBottom: var(--spacing-4);
1152
1228
 
@@ -1169,11 +1245,14 @@ const declarations = `
1169
1245
  three times. Changing them changes no pixel; it changes what the
1170
1246
  package tells a consumer the height is.
1171
1247
 
1172
- The line box these leave is 2px taller than the content box, because
1173
- the derivation counts padding but not the 1px border on each edge.
1174
- Measured at every size: no glyph clips, because an input centres its
1175
- line and paints into the padding. Correcting it would move a field
1176
- off its button, so it stays a recorded fact rather than a fix. */
1248
+ The derivation counts padding but not the 1px border on each edge,
1249
+ and the box is border-box, so these heights leave the line box 2px
1250
+ taller than the content box. An input hides that -- it centres its
1251
+ line and paints into the padding -- but a select clips at the
1252
+ content box, and at md its descenders lost their bottoms.
1253
+ Correcting the height would move a field off its button, so the
1254
+ border comes out of the block padding instead: see the note after
1255
+ the size rules in shared/form-field.css.js. */
1177
1256
  --form-input-small-height: calc(
1178
1257
  var(--spacing-8) * 2 + var(--typography-fontSize-sm) * var(--typography-lineHeight-tight)
1179
1258
  );
@@ -1220,16 +1299,13 @@ const declarations = `
1220
1299
 
1221
1300
  --form-input-error-backgroundColor: var(--status-error-surface);
1222
1301
  --form-input-error-borderColor: var(--status-error-accent);
1223
- --form-input-error-placeholderColor: var(--color-red-600);
1224
1302
 
1225
1303
  --form-input-success-backgroundColor: var(--status-success-surface);
1226
1304
  --form-input-success-borderColor: var(--color-green-600);
1227
- --form-input-success-placeholderColor: var(--color-green-700);
1228
1305
 
1229
1306
  --form-input-disabled-backgroundColor: var(--color-surface-disabled);
1230
1307
  --form-input-disabled-borderColor: var(--color-border-disabled);
1231
1308
  --form-input-disabled-color: var(--color-text-subtle);
1232
- --form-input-disabled-placeholderColor: var(--color-neutral-400);
1233
1309
 
1234
1310
  /* Form Help Text Tokens */
1235
1311
  --form-help-text-default-fontSize: var(--typography-fontSize-xs);
@@ -1267,9 +1343,6 @@ const declarations = `
1267
1343
  32, which is what a form actually wants and what the value is set to
1268
1344
  here. */
1269
1345
  --form-fieldSpacing: var(--spacing-24);
1270
- --form-sectionSpacing: var(--spacing-32);
1271
- --form-labelSpacing: var(--spacing-4);
1272
- --form-helpTextSpacing: var(--spacing-4);
1273
1346
 
1274
1347
  /* ================================
1275
1348
  COMPONENT TOKENS - BUTTON
@@ -1299,15 +1372,14 @@ const declarations = `
1299
1372
  --button-large-gap: var(--spacing-8);
1300
1373
  --button-large-borderRadius: var(--radius-lg);
1301
1374
 
1302
- /* Button Default State */
1303
- --button-default-backgroundColor: var(--color-neutral-0);
1304
- --button-default-borderColor: var(--color-neutral-400);
1305
- --button-default-borderWidth: var(--border-width-1);
1306
- --button-default-color: var(--color-neutral-900);
1307
- --button-default-fontFamily: var(--typography-fontFamily-sans);
1308
- --button-default-fontWeight: var(--typography-fontWeight-medium);
1309
- --button-default-lineHeight: var(--typography-lineHeight-normal);
1310
-
1375
+ /* Removed 2026-10-15, because nothing read them: the seven
1376
+ --button-default-* (a state no variant draws), and
1377
+ --button-shared-focus-boxShadow, -disabled-opacity and
1378
+ -disabled-cursor (the button draws focus with the outline below and
1379
+ disabled through its own rules). Likewise the three
1380
+ --form-input-*-placeholderColor tokens, the --form-section-,
1381
+ -label- and -helpTextSpacing trio, and two --layout-awards- widths.
1382
+ None was mirrored in Figma, which carries no component tokens. */
1311
1383
  /* Button Shared Tokens */
1312
1384
  --button-shared-fontFamily: var(--typography-fontFamily-sans);
1313
1385
  --button-shared-fontWeight: var(--typography-fontWeight-medium);
@@ -1320,9 +1392,6 @@ const declarations = `
1320
1392
  --button-shared-transition-easing: var(--transition-easing-smooth);
1321
1393
  --button-shared-focus-outline: var(--focus-outline-traditional);
1322
1394
  --button-shared-focus-outlineOffset: var(--focus-outline-offset);
1323
- --button-shared-focus-boxShadow: var(--focus-outline-primary);
1324
- --button-shared-disabled-opacity: 0.6;
1325
- --button-shared-disabled-cursor: not-allowed;
1326
1395
 
1327
1396
  /* ---- variant colours -------------------------------------------
1328
1397
  Named for the kind attribute they serve, because that is the public
@@ -1343,10 +1412,12 @@ const declarations = `
1343
1412
  wiring them up unchanged would have moved pixels.
1344
1413
 
1345
1414
  The per-variant -focus and -disabled sets are gone. The component
1346
- draws one focus ring for every variant, from --focus-outline-primary,
1347
- and one shared disabled treatment; thirty tokens described per-variant
1348
- versions of both that nothing rendered. --button-empty-* went with
1349
- them: there is no empty kind. */
1415
+ draws its focus ring as an offset outline -- the blue one through
1416
+ --button-shared-focus-outline by default, the white one from
1417
+ --focus-outline-traditional-inverse under surface="dark" and on
1418
+ white and outline-white -- and one shared disabled treatment;
1419
+ thirty tokens described per-variant versions of both that nothing
1420
+ rendered. --button-empty-* went with them: there is no empty kind. */
1350
1421
 
1351
1422
  /* primary: blue fill */
1352
1423
  --button-primary-background-default: var(--color-action-primary);
@@ -1561,8 +1632,11 @@ const declarations = `
1561
1632
  /* Accessibility Spacing Tokens
1562
1633
  -----------------------------------------------*/
1563
1634
  /* Touch Target Sizes - WCAG 2.1 Success Criterion 2.5.5 (AAA) and 2.5.8 (AA) */
1564
- --touch-target-size: 44px; /* Minimum touch target size (44px × 44px) */
1565
- --touch-target-spacing: 8px; /* Minimum space between touch targets */
1635
+ /* The dimension steps that carry these values, rather than px
1636
+ literals beside them: a target is matched to a finger, not to the
1637
+ reader's text size, so it is px, which --dimension-* is. */
1638
+ --touch-target-size: var(--dimension-44); /* Minimum touch target size (44px × 44px) */
1639
+ --touch-target-spacing: var(--dimension-8); /* Minimum space between touch targets */
1566
1640
 
1567
1641
  /* Readable Line Lengths - For optimal readability */
1568
1642
  --content-width-readable: 70ch; /* Approximately 70 characters per line */
@@ -1570,18 +1644,21 @@ const declarations = `
1570
1644
 
1571
1645
  /** GRID AND BREAKPOINT TOKENS
1572
1646
 
1573
- These have no consumer in this package and are kept deliberately.
1574
- A media condition cannot read a custom property — @media
1647
+ The breakpoint tokens have no consumer in this package and are kept
1648
+ deliberately. A media condition cannot read a custom property — @media
1575
1649
  (max-width: var(--breakpoint-sm)) is not valid CSS — so a breakpoint
1576
1650
  token can never be referenced the way a colour is. It exists so the
1577
1651
  six values have one place to be read from, by the lint that checks
1578
1652
  media queries against them and by anything generating CSS. The grid
1579
- tokens are the same case: a layout that wants them reads them, and
1580
- nothing in the atom layer does.
1581
-
1582
- They are the exception. Everything else here that nothing references
1583
- is dead weight, and the OLD VALUES and FONT TOKENS blocks that used
1584
- to sit below were removed for exactly that reason.
1653
+ tokens below are read: --grid-container-max-* and --grid-padding-*
1654
+ are the page column, through shared/ensemble.css.js and layout.css,
1655
+ and the header and compare read them too. --grid-gutter-* has no
1656
+ reader outside the blog's own token copy, and carries the same
1657
+ values as --grid-padding-*.
1658
+
1659
+ The breakpoints are the exception. Everything else here that nothing
1660
+ references is dead weight, and the OLD VALUES and FONT TOKENS blocks
1661
+ that used to sit below were removed for exactly that reason.
1585
1662
  */
1586
1663
  /* Grid container max widths */
1587
1664
  --grid-container-max-xs: 100%; /* mobile full width */
@@ -1630,18 +1707,18 @@ const declarations = `
1630
1707
 
1631
1708
  /* Awards section — Orbit Frame 14 (node 1882:9342) */
1632
1709
  --layout-awards-section-background: var(--color-blue-50);
1633
- --layout-awards-banner-max-width: 37.5rem; /* legacy narrow cap; prefer cluster */
1634
1710
  --layout-awards-cluster-max-width: 49.5625rem; /* 793px — trust banner column */
1635
1711
  --layout-awards-intro-max-width: 39.4375rem; /* 631px — headline stack */
1636
- /* Legacy: narrow lower block from Orbit; prefer --layout-ensemble-content-max for compare-aligned grids */
1637
- --layout-awards-content-max-width: 53.0625rem; /* 849px */
1638
- /* There is no page-width token. The page column is an implementation --
1712
+ /* The page column has no width token of its own. --content-width-max,
1713
+ above, is a 1200px cap that no component reads; only the blog's own
1714
+ token copy aliases it. The page column is an implementation --
1639
1715
  src/components/shared/ensemble.css.js -- on the grid container scale
1640
1716
  above (--grid-container-max-* with --grid-padding-*), which is what
1641
1717
  Foundations/Grid System documented and nothing had built. A width token
1642
1718
  here is how six ensembles came to hold one literal each and seven other
1643
- modules a different one. This padding step stays only because the blog
1644
- still reads it. */
1719
+ modules a different one. This padding step stays because bd-features
1720
+ reads it for its inline padding; the blog declares its own copy and
1721
+ does not read this one. */
1645
1722
  --layout-ensemble-inline-padding: var(--spacing-24);
1646
1723
 
1647
1724
  `;
@@ -32,7 +32,7 @@ const ROLES = [
32
32
  ['label-small', 'label', 'A small label: a badge, a tag, a field hint.'],
33
33
  ['label-extra-small', 'label', 'The smallest label step, 11px.'],
34
34
  ['caption-default', 'caption', 'Text under an image or a figure.'],
35
- ['caption-small', 'caption', 'The same, tighter.'],
35
+ ['caption-small', 'caption', 'The same size, tracked wider: 0.025em against none.'],
36
36
  ['link-standalone', 'link', 'A link that is not inside a sentence.'],
37
37
  ['code-block', 'code', 'A block of monospaced code.']
38
38
  ];
@@ -1,20 +0,0 @@
1
- import { html } from 'lit';
2
- import { styleMap } from 'lit/directives/style-map.js';
3
-
4
- import './button.css';
5
-
6
- /** Primary UI component for user interaction */
7
- export const Button = ({ primary, backgroundColor = null, size, label, onClick }) => {
8
- const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
9
-
10
- return html`
11
- <button
12
- type="button"
13
- class=${['storybook-button', `storybook-button--${size || 'medium'}`, mode].join(' ')}
14
- style=${styleMap({ backgroundColor })}
15
- @click=${onClick}
16
- >
17
- ${label}
18
- </button>
19
- `;
20
- };
@@ -1,46 +0,0 @@
1
- import { fn } from '@storybook/test';
2
-
3
- import { Button } from './Button';
4
-
5
- // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
6
- export default {
7
- title : 'Example/Button',
8
- tags : ['autodocs'],
9
- render : (args) => Button(args),
10
- argTypes: {
11
- backgroundColor: { control: 'color' },
12
- size : {
13
- control: { type: 'select' },
14
- options: ['small', 'medium', 'large']
15
- }
16
- },
17
- args: { onClick: fn() }
18
- };
19
-
20
- // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
21
- export const Primary = {
22
- args: {
23
- primary: true,
24
- label : 'Button'
25
- }
26
- };
27
-
28
- export const Secondary = {
29
- args: {
30
- label: 'Button'
31
- }
32
- };
33
-
34
- export const Large = {
35
- args: {
36
- size : 'large',
37
- label: 'Button'
38
- }
39
- };
40
-
41
- export const Small = {
42
- args: {
43
- size : 'small',
44
- label: 'Button'
45
- }
46
- };