@karmo-co/drift-kit-reference 0.0.0-stage → 23.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 (93) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +24 -2
  3. package/bin/drift-kit-ref.js +197 -0
  4. package/manifest/components/accordion.md +47 -0
  5. package/manifest/components/action-bar.md +51 -0
  6. package/manifest/components/alert.md +53 -0
  7. package/manifest/components/app-shell.md +43 -0
  8. package/manifest/components/avatar.md +42 -0
  9. package/manifest/components/body.md +48 -0
  10. package/manifest/components/box.md +58 -0
  11. package/manifest/components/breadcrumb.md +59 -0
  12. package/manifest/components/button.md +46 -0
  13. package/manifest/components/calendar.md +69 -0
  14. package/manifest/components/card.md +71 -0
  15. package/manifest/components/checkbox.md +43 -0
  16. package/manifest/components/choice-card.md +61 -0
  17. package/manifest/components/code-input.md +56 -0
  18. package/manifest/components/date-picker.md +66 -0
  19. package/manifest/components/day-of-month.md +54 -0
  20. package/manifest/components/detail-list.md +37 -0
  21. package/manifest/components/divider.md +33 -0
  22. package/manifest/components/dropdown.md +52 -0
  23. package/manifest/components/dropzone.md +56 -0
  24. package/manifest/components/eyebrow.md +45 -0
  25. package/manifest/components/feedback-badge.md +35 -0
  26. package/manifest/components/glider.md +34 -0
  27. package/manifest/components/heading.md +47 -0
  28. package/manifest/components/hero.md +46 -0
  29. package/manifest/components/icon-button.md +41 -0
  30. package/manifest/components/icon-tile.md +44 -0
  31. package/manifest/components/icon.md +30 -0
  32. package/manifest/components/input.md +63 -0
  33. package/manifest/components/link.md +42 -0
  34. package/manifest/components/loading-spinner.md +42 -0
  35. package/manifest/components/logo.md +38 -0
  36. package/manifest/components/modal.md +53 -0
  37. package/manifest/components/pagination.md +54 -0
  38. package/manifest/components/payment-form.md +29 -0
  39. package/manifest/components/price-summary.md +30 -0
  40. package/manifest/components/radio.md +42 -0
  41. package/manifest/components/section-heading.md +41 -0
  42. package/manifest/components/select.md +99 -0
  43. package/manifest/components/shield.md +34 -0
  44. package/manifest/components/sidebar.md +73 -0
  45. package/manifest/components/stepper.md +53 -0
  46. package/manifest/components/switch.md +45 -0
  47. package/manifest/components/table.md +77 -0
  48. package/manifest/components/tabs.md +43 -0
  49. package/manifest/components/tag.md +43 -0
  50. package/manifest/components/textarea.md +52 -0
  51. package/manifest/components/toast.md +52 -0
  52. package/manifest/components/tooltip.md +42 -0
  53. package/manifest/components/top-bar.md +60 -0
  54. package/manifest/components/vehicle.md +43 -0
  55. package/manifest/index.json +1171 -0
  56. package/manifest/migrations/1.0.0.md +50 -0
  57. package/manifest/migrations/10.0.0.md +154 -0
  58. package/manifest/migrations/11.0.0.md +24 -0
  59. package/manifest/migrations/12.0.0.md +38 -0
  60. package/manifest/migrations/13.0.0.md +50 -0
  61. package/manifest/migrations/14.0.0.md +148 -0
  62. package/manifest/migrations/15.0.0.md +140 -0
  63. package/manifest/migrations/16.0.0.md +196 -0
  64. package/manifest/migrations/17.0.0.md +19 -0
  65. package/manifest/migrations/18.0.0.md +23 -0
  66. package/manifest/migrations/19.0.0.md +15 -0
  67. package/manifest/migrations/2.0.0.md +18 -0
  68. package/manifest/migrations/20.0.0.md +43 -0
  69. package/manifest/migrations/21.0.0.md +97 -0
  70. package/manifest/migrations/22.0.0.md +17 -0
  71. package/manifest/migrations/23.0.0.md +86 -0
  72. package/manifest/migrations/3.0.0.md +34 -0
  73. package/manifest/migrations/4.0.0.md +147 -0
  74. package/manifest/migrations/5.0.0.md +73 -0
  75. package/manifest/migrations/6.0.0.md +67 -0
  76. package/manifest/migrations/7.0.0.md +71 -0
  77. package/manifest/migrations/8.0.0.md +16 -0
  78. package/manifest/migrations/9.0.0.md +107 -0
  79. package/manifest/rules.md +37 -0
  80. package/manifest/setup.md +70 -0
  81. package/manifest/tokens/border-width.md +12 -0
  82. package/manifest/tokens/border.md +147 -0
  83. package/manifest/tokens/font.md +90 -0
  84. package/manifest/tokens/icon.md +84 -0
  85. package/manifest/tokens/interactive.md +53 -0
  86. package/manifest/tokens/layout.md +8 -0
  87. package/manifest/tokens/motion.md +97 -0
  88. package/manifest/tokens/radius.md +20 -0
  89. package/manifest/tokens/shadow.md +28 -0
  90. package/manifest/tokens/spacing.md +35 -0
  91. package/manifest/tokens/surface.md +138 -0
  92. package/manifest/tokens/text.md +92 -0
  93. package/package.json +20 -4
@@ -0,0 +1,147 @@
1
+ ## 4.0.0 — 2026-09-10
2
+
3
+ ### Breaking
4
+
5
+ - **The type scale is rebuilt, and sizes leave the h1…h6 vocabulary.** Hero,
6
+ Heading and Body now read one Karmo-owned scale
7
+ (`alias/karmo/font/type-scale.css`) and name their rungs from one shared
8
+ t-shirt vocabulary, `xxlarge` … `xxsmall` — the words Body and Eyebrow already
9
+ used. The audit that prompted this found three faults: Hero read the HEADING
10
+ ramp, so the display tier topped out at the same 48px as an H1 and ran down to
11
+ 24px; the heading ramp had a 36% gap at the top and three rungs inside 4px at
12
+ the bottom (28 · 26 · 24); and Body jumped 16 → 20 with no reading rung
13
+ between. Every value is now a `--dk-primitive-scale-*` step (one noted
14
+ exception, 26px) and every ramp is monotonic in size and in leading ratio.
15
+ - Hero — its own DISPLAY ramp, 72 · 64 · 56 · 48 · 40 desktop, scaling over
16
+ three breakpoints, never below 40px.
17
+ - Heading — seven rungs, 48 · 40 · 36 · 32 · 28 · 24 · 20 desktop, every step
18
+ at least 11%.
19
+ - Body — five rungs, 20 · 18 · 16 · 14 · 12, fixed at every breakpoint.
20
+ **`large` changed VALUE, not just name**: it was 20px, it is now the new
21
+ 18px rung, and the old 20px is `xlarge`. This is the only silent change in
22
+ the release — a stale `data-size="large"` still renders, 2px smaller.
23
+ - Eyebrow — unchanged.
24
+ Why not h1…h6: the size axis exists to decouple the visual size from the
25
+ element, and naming its values after the HTML outline invites exactly the
26
+ coupling it was built to break — an author who needs an `<h2>` reaches for
27
+ `data-size="h2"` and lets the look pick the semantics. The element is still
28
+ yours; the size is now named in a vocabulary that cannot be mistaken for it.
29
+ - **`disabled` is a state, not a colour.** `data-color="disabled"` is gone from
30
+ body, heading, hero, eyebrow and icon; the state is `[data-disabled]` or
31
+ `[aria-disabled="true"]`, the pair Button already accepts. It composes with
32
+ `data-color` instead of replacing it, so a disabled error label is finally
33
+ expressible. Not `data-status` — that axis is the five semantic statuses, and
34
+ disabled is an interaction state, which is how every other component models it.
35
+ - **`data-dc-tooltip` → `data-dk-tooltip`.** The last public name still carrying
36
+ the pre-2.0.0 prefix.
37
+
38
+ #### Added
39
+
40
+ - `data-style="italic"` on Heading. Inter ships a drawn italic at every weight,
41
+ so this selects a real face. Hero deliberately does NOT get one: Syne ships
42
+ `normal` only, and a browser-synthesised oblique is not a typeface decision.
43
+
44
+ ##### Fixed
45
+
46
+ - Stale `--dc-*` collection names and retired `window.dc*` globals in source
47
+ comments, which had named tokens and functions that no longer exist since
48
+ 2.0.0. Prose only.
49
+
50
+ Migration map: `migrations/4.0.0-type-scale.json`. Design note: forge
51
+ `docs/superpowers/specs/2026-09-10-drift-type-scale-design.md`.
52
+
53
+ ### Rename map: `migrations/4.0.0-type-scale.json`
54
+
55
+ ```json
56
+ {
57
+ "version": "4.0.0",
58
+ "date": "2026-09-10",
59
+ "note": "The type scale rebuild. Four breaking changes: (1) Hero and Heading size values move from the h1…h6 outline vocabulary to the shared t-shirt rungs, and Hero moves onto its own display ramp; (2) Body gains an 18px rung, which means `large` CHANGES VALUE — see sizes.body.warning; (3) disabled stops being a data-color value and becomes a state; (4) the tooltip markup attribute takes the dk prefix. Every entry below is a mechanical find-and-replace except the one flagged warning.",
60
+
61
+ "sizes": {
62
+ "note": "data-size values, per component. Old px are desktop values.",
63
+
64
+ "hero": {
65
+ "note": "Hero now reads the DISPLAY ramp (72 · 64 · 56 · 48 · 40 desktop) instead of the heading ramp, so it is bigger at every rung and stops below 40px on purpose. h1 keeps its exact size under a new name; h2 rounds up 2px. h3/h4/h5 have NO display equivalent — 28/26/24px in Syne was a heading wearing the display face, and the right migration is to change the element to a Heading, not to pick the nearest Hero rung.",
66
+ "h1": "medium",
67
+ "h2": "small",
68
+ "h3": "→ use .dk-component-heading[data-size=\"small\"]",
69
+ "h4": "→ use .dk-component-heading[data-size=\"xsmall\"]",
70
+ "h5": "→ use .dk-component-heading[data-size=\"xsmall\"]"
71
+ },
72
+
73
+ "heading": {
74
+ "note": "Seven rungs, 48 → 20 desktop. h1, h3, h5 and h6 keep their exact px; h2 rounds up 2px (38 → 40); h4 rounds down 2px (26 → 24) and lands on the same rung as h5, which is the point — 28/26/24 were three rungs inside 4px.",
75
+ "h1": "xxlarge",
76
+ "h2": "xlarge",
77
+ "h3": "small",
78
+ "h4": "xsmall",
79
+ "h5": "xsmall",
80
+ "h6": "xxsmall"
81
+ },
82
+
83
+ "body": {
84
+ "warning": "`large` is the one name in 4.0.0 whose VALUE changed rather than its name: it was 20px and is now 18px, the new reading rung. Anything that wants the old 20px must move to `xlarge`. A stale data-size=\"large\" keeps rendering — 2px smaller — so this one cannot be caught by a missing-token check. Search for it explicitly.",
85
+ "large": "xlarge",
86
+ "medium": "medium",
87
+ "small": "small",
88
+ "extra-small": "xsmall"
89
+ },
90
+
91
+ "eyebrow": {
92
+ "note": "Unchanged — large/medium/small at 12/11/10 were already on the shared vocabulary."
93
+ }
94
+ },
95
+
96
+ "states": {
97
+ "note": "disabled is a state now, not a palette value, so it composes with data-color instead of replacing it. Applies to body, heading, hero, eyebrow and icon. Use aria-disabled=\"true\" when the thing really is announced as disabled; data-disabled when it is purely presentational.",
98
+ "data-color=\"disabled\"": "data-disabled",
99
+ "data-color=\"disabled\" data-emphasis=\"subtle\"": "data-disabled data-emphasis=\"subtle\""
100
+ },
101
+
102
+ "attributes": {
103
+ "note": "The last public name still on the pre-2.0.0 prefix.",
104
+ "data-dc-tooltip": "data-dk-tooltip"
105
+ },
106
+
107
+ "tokens": {
108
+ "--dk-component-hero-h1-font-size": "--dk-component-hero-medium-font-size",
109
+ "--dk-component-hero-h1-line-height": "--dk-component-hero-medium-line-height",
110
+ "--dk-component-hero-h2-font-size": "--dk-component-hero-small-font-size",
111
+ "--dk-component-hero-h2-line-height": "--dk-component-hero-small-line-height",
112
+ "--dk-component-hero-h3-font-size": "--dk-component-heading-small-font-size",
113
+ "--dk-component-hero-h3-line-height": "--dk-component-heading-small-line-height",
114
+ "--dk-component-hero-h4-font-size": "--dk-component-heading-xsmall-font-size",
115
+ "--dk-component-hero-h4-line-height": "--dk-component-heading-xsmall-line-height",
116
+ "--dk-component-hero-h5-font-size": "--dk-component-heading-xsmall-font-size",
117
+ "--dk-component-hero-h5-line-height": "--dk-component-heading-xsmall-line-height",
118
+
119
+ "--dk-component-heading-h1-font-size": "--dk-component-heading-xxlarge-font-size",
120
+ "--dk-component-heading-h1-line-height": "--dk-component-heading-xxlarge-line-height",
121
+ "--dk-component-heading-h2-font-size": "--dk-component-heading-xlarge-font-size",
122
+ "--dk-component-heading-h2-line-height": "--dk-component-heading-xlarge-line-height",
123
+ "--dk-component-heading-h3-font-size": "--dk-component-heading-small-font-size",
124
+ "--dk-component-heading-h3-line-height": "--dk-component-heading-small-line-height",
125
+ "--dk-component-heading-h4-font-size": "--dk-component-heading-xsmall-font-size",
126
+ "--dk-component-heading-h4-line-height": "--dk-component-heading-xsmall-line-height",
127
+ "--dk-component-heading-h5-font-size": "--dk-component-heading-xsmall-font-size",
128
+ "--dk-component-heading-h5-line-height": "--dk-component-heading-xsmall-line-height",
129
+ "--dk-component-heading-h6-font-size": "--dk-component-heading-xxsmall-font-size",
130
+ "--dk-component-heading-h6-line-height": "--dk-component-heading-xxsmall-line-height",
131
+
132
+ "--dk-component-body-large-font-size": "--dk-component-body-xlarge-font-size",
133
+ "--dk-component-body-large-line-height": "--dk-component-body-xlarge-line-height",
134
+ "--dk-component-body-extra-small-font-size": "--dk-component-body-xsmall-font-size",
135
+ "--dk-component-body-extra-small-line-height": "--dk-component-body-xsmall-line-height"
136
+ },
137
+
138
+ "new-names": {
139
+ "note": "Added, with nothing to migrate from.",
140
+ "--dk-font-scale-display-{xxlarge|xlarge|large|medium|small}-{text-size|line-height}": "the display ramp, responsive",
141
+ "--dk-font-scale-heading-{xxlarge…xxsmall}-{text-size|line-height}": "the heading ramp, responsive",
142
+ "--dk-font-scale-body-{xlarge…xsmall}-{text-size|line-height}": "the body ramp, fixed",
143
+ "--dk-component-heading-italic-font-style": "data-style=\"italic\" on Heading — Inter has a drawn italic; Hero has no equivalent because Syne ships none"
144
+ }
145
+ }
146
+ ```
147
+
@@ -0,0 +1,73 @@
1
+ ## 5.0.0 — 2026-09-10
2
+
3
+ ### Breaking
4
+
5
+ - **Disabled loses its emphasis axis on the type primitives.** `data-emphasis="subtle"`
6
+ no longer does anything alongside the disabled state on body, heading, hero,
7
+ eyebrow or icon, and the five `--dk-component-{x}-disabled-subtle-color`
8
+ tokens are removed. The surviving token drops the segment it no longer varies
9
+ on: `--dk-component-{x}-disabled-default-color` → `--dk-component-{x}-disabled-color`.
10
+
11
+ The two rungs were never two strengths of one idea. Measured against the page
12
+ surface, `-default` is **2.88:1** in light and **5.57:1** in dark; `-subtle` is
13
+ **1.80:1** and **2.33:1**. Below 2:1 is not a quieter label, it is text nobody
14
+ can read — so on prose the axis offered a choice between "dim" and "invisible"
15
+ with no rule for picking one. The kit itself never had to pick: every disabled
16
+ CONTROL label (Button's six variants, Checkbox's description) takes `-subtle`,
17
+ and `-default` was referenced exactly once outside the type primitives, by the
18
+ accent Button's text-*shadow*. Prose keeps the readable rung; controls keep
19
+ theirs.
20
+
21
+ #### Unchanged, deliberately
22
+
23
+ - `--dk-text-disabled-subtle` / `--dk-icon-disabled-subtle` stay — Button and
24
+ Checkbox still read them, and a disabled control is conventionally near-
25
+ invisible in a way a paragraph is not.
26
+ - `data-emphasis="subtle"` still works with `data-color="neutral"`, whose subtle
27
+ rung is a real, legible de-emphasis.
28
+
29
+ Migration map: `migrations/5.0.0-disabled-one-rung.json`.
30
+
31
+ ### Rename map: `migrations/5.0.0-disabled-one-rung.json`
32
+
33
+ ```json
34
+ {
35
+ "version": "5.0.0",
36
+ "date": "2026-09-10",
37
+ "note": "Disabled loses its emphasis axis on the type primitives. One breaking change, and the mechanical part of it is a token rename; the behavioural part is that `data-emphasis=\"subtle\"` no longer does anything alongside the disabled state. Nothing else moves — data-emphasis still works for data-color=\"neutral\", and the Mapped --dk-text-disabled-subtle / --dk-icon-disabled-subtle tokens are untouched because Button and Checkbox still read them.",
38
+
39
+ "why": "The two rungs were never two strengths of one idea. Measured against the page surface: -default is 2.88:1 in light and 5.57:1 in dark; -subtle is 1.80:1 and 2.33:1. Below 2:1 is not a quieter label, it is text nobody can read, so on prose the axis offered a choice between 'dim' and 'invisible' with no rule for picking. The kit never had to pick either: every disabled CONTROL label (Button's six variants, Checkbox's description) takes -subtle, and -default was referenced exactly once outside the type primitives (the accent Button's text-shadow). Prose keeps the readable rung; controls keep theirs.",
40
+
41
+ "attributes": {
42
+ "note": "Applies to body, heading, hero, eyebrow and icon. Drop the emphasis attribute; the disabled state alone is now the whole answer. If it was the ONLY reason data-emphasis was on the element, remove the attribute entirely.",
43
+ "data-disabled data-emphasis=\"subtle\"": "data-disabled",
44
+ "aria-disabled=\"true\" data-emphasis=\"subtle\"": "aria-disabled=\"true\""
45
+ },
46
+
47
+ "tokens": {
48
+ "note": "The `-default` segment goes with the axis: the collection grammar omits any segment a token no longer varies on. The -subtle component tokens are REMOVED, not renamed — a var() to one now resolves to nothing, which is the intended loud failure.",
49
+ "--dk-component-body-disabled-default-color": "--dk-component-body-disabled-color",
50
+ "--dk-component-heading-disabled-default-color": "--dk-component-heading-disabled-color",
51
+ "--dk-component-hero-disabled-default-color": "--dk-component-hero-disabled-color",
52
+ "--dk-component-eyebrow-disabled-default-color": "--dk-component-eyebrow-disabled-color",
53
+ "--dk-component-icon-disabled-default-color": "--dk-component-icon-disabled-color"
54
+ },
55
+
56
+ "removed": {
57
+ "note": "No replacement. If a design genuinely needs 1.8:1 text, it is not a disabled label and should say what it actually is.",
58
+ "--dk-component-body-disabled-subtle-color": null,
59
+ "--dk-component-heading-disabled-subtle-color": null,
60
+ "--dk-component-hero-disabled-subtle-color": null,
61
+ "--dk-component-eyebrow-disabled-subtle-color": null,
62
+ "--dk-component-icon-disabled-subtle-color": null
63
+ },
64
+
65
+ "unchanged": {
66
+ "note": "Called out because they look like they should have moved and did not.",
67
+ "--dk-text-disabled-subtle": "still the Mapped token every disabled control label reads",
68
+ "--dk-icon-disabled-subtle": "same, for icons inside disabled controls",
69
+ "data-emphasis=\"subtle\" with data-color=\"neutral\"": "unaffected — neutral's subtle rung is a real, legible de-emphasis"
70
+ }
71
+ }
72
+ ```
73
+
@@ -0,0 +1,67 @@
1
+ ## 6.0.0 — 2026-09-11
2
+
3
+ - **BREAKING: icon sizes are the pixel number, not a t-shirt.** Five rungs become
4
+ nine: 12, 14, 16, 20, 24, 28, 32, 40, 48. Every old name maps to the pixel value
5
+ it already resolved to, so nothing renders differently:
6
+ `extra-small`→`12`, `small`→`16`, `medium`→`20`, `large`→`24`,
7
+ `extra-large`→`32`. Map in `migrations/6.0.0-icon-numeric-sizes.json`.
8
+ - **Why numbers.** Figma has always called these 12 and 24, so every handoff
9
+ carried a translation step that existed only because the kit disagreed with the
10
+ design tool. Numbers also let a rung slot in without renaming its neighbours:
11
+ adding 14 between 12 and 16 meant inventing a name between extra-small and
12
+ small, and 40 and 48 meant extra-extra-large and worse.
13
+ - **Four genuinely new rungs**: 14 for dense chrome, 28 between 24 and 32, and 40
14
+ and 48 for display sizes the old scale could not reach. Every value is an
15
+ existing scale primitive; nothing new was invented.
16
+ - **The old names are removed, not aliased.** A `var()` to one now resolves to
17
+ nothing, which is the loud failure this kit prefers over a silent fallback to
18
+ some default size.
19
+ - **data-weight is unchanged at three rungs**, and that is a decision rather than
20
+ an omission. The stroke scales with the icon, so `bold` at 12px already renders
21
+ around 1.25px; splitting that into five or seven steps would be differences
22
+ nobody can see at the sizes icons are actually used, and every extra rung is
23
+ another cell in the token matrix.
24
+
25
+ ### Rename map: `migrations/6.0.0-icon-numeric-sizes.json`
26
+
27
+ ```json
28
+ {
29
+ "version": "6.0.0",
30
+ "date": "2026-09-11",
31
+ "note": "Icon sizes stop being t-shirts and become the pixel number. Five rungs become nine: 12, 14, 16, 20, 24, 28, 32, 40, 48. The four existing values keep their pixel sizes exactly, so this is a rename and not a resize, except that `medium` was 20px and anyone who assumed it meant 24 will now have to say which they meant.",
32
+
33
+ "why": "Figma has always called these 12 and 24, so every handoff carried a translation step that existed only because the kit disagreed with the design tool. Numbers also let a rung slot in without renaming its neighbours: adding 14 between 12 and 16 under the old scheme meant inventing a name between extra-small and small, and adding 40 and 48 meant extra-extra-large and worse. More sizes were asked for because five did not cover the range; nine do, and the scheme no longer fights the next addition.",
34
+
35
+ "attributes": {
36
+ "note": "Applies to any element with class dk-component-icon. The pixel value each old name resolved to is the new name, so rendering is unchanged.",
37
+ "data-size=\"extra-small\"": "data-size=\"12\"",
38
+ "data-size=\"small\"": "data-size=\"16\"",
39
+ "data-size=\"medium\"": "data-size=\"20\"",
40
+ "data-size=\"large\"": "data-size=\"24\"",
41
+ "data-size=\"extra-large\"": "data-size=\"32\""
42
+ },
43
+
44
+ "tokens": {
45
+ "note": "Same mapping at the token level. The five old names are REMOVED, not aliased: a var() to one resolves to nothing, which is the loud failure this kit prefers over a silent fallback to some default size.",
46
+ "--dk-component-icon-extra-small-size": "--dk-component-icon-12-size",
47
+ "--dk-component-icon-small-size": "--dk-component-icon-16-size",
48
+ "--dk-component-icon-medium-size": "--dk-component-icon-20-size",
49
+ "--dk-component-icon-large-size": "--dk-component-icon-24-size",
50
+ "--dk-component-icon-extra-large-size": "--dk-component-icon-32-size"
51
+ },
52
+
53
+ "added": {
54
+ "note": "Four new rungs with no predecessor. 14 for dense table chrome, 28 between 24 and 32, and 40 and 48 for the display sizes the old scale simply could not reach.",
55
+ "--dk-component-icon-14-size": "var(--dk-primitive-scale-3-5)",
56
+ "--dk-component-icon-28-size": "var(--dk-primitive-scale-7)",
57
+ "--dk-component-icon-40-size": "var(--dk-primitive-scale-10)",
58
+ "--dk-component-icon-48-size": "var(--dk-primitive-scale-12)"
59
+ },
60
+
61
+ "unchanged": {
62
+ "note": "data-weight stays at three rungs. The stroke scales with the icon, so bold at 12px already renders around 1.25px; splitting that further would be differences nobody can see at the sizes icons are actually used.",
63
+ "data-weight": ["light", "regular", "bold"]
64
+ }
65
+ }
66
+ ```
67
+
@@ -0,0 +1,71 @@
1
+ ## 7.0.0 — 2026-09-12
2
+
3
+ - **Shadows are one ramp, named for the job.** `raised` (a thing resting on the
4
+ page), `lifted` (the same thing picked up), `floating` (a layer above the
5
+ page), `overlay` (a layer above everything), `on-color` (any of those on a
6
+ coloured fill). A token called `card` was already doing the work for toasts,
7
+ menus and a search field, which is the tell that it was named for the wrong
8
+ thing. Light values are unchanged, so the rename alone repaints nothing.
9
+ - **Thirteen of the nineteen shadow tokens had never been used anywhere.** The
10
+ upstream `drop-*` and `inner-*` families are COLOURS, not shadows:
11
+ `color-mix` with no offset or blur, unusable without writing the geometry
12
+ yourself, and only one thing in the kit ever did. They still resolve, since
13
+ upstream owns the block above the fence and a refresh would restore them, but
14
+ they are no longer part of the system and the Shadows page no longer presents
15
+ them as shadows. Markup composing them by hand keeps working.
16
+ - **The modal had no shadow token at all.** It hardcoded
17
+ `0 4px 8px -2px rgba(0,0,0,.1), 0 2px 4px -2px rgba(0,0,0,.06)`, which never
18
+ themed and was lighter than a card on hover, leaving the most elevated thing
19
+ in the system with the least considered elevation. It uses `overlay` now.
20
+ - **Dark is not light with more alpha.** Black falling on a dark surface has
21
+ almost no range, which is why the old dark values read as nothing. Alpha
22
+ roughly doubles again (raised 0.16 to 0.36, lifted 0.20 to 0.44), and the two
23
+ genuinely floating rungs gain a hairline of light along the top edge, which is
24
+ how a raised surface catches light in a dark room. The lower rungs skip it: a
25
+ tab is not floating, and a bright line across its top reads as a seam.
26
+ - **`--dk-shadow-focus-ring`** for the `0 0 0 4px` primary ring that card,
27
+ checkbox, switch and select each wrote out raw. Button keeps its own, whose
28
+ width and colour are already component tokens that vary by variant.
29
+ - Migration map in `migrations/7.0.0-shadow-roles.json`.
30
+
31
+ ### Rename map: `migrations/7.0.0-shadow-roles.json`
32
+
33
+ ```json
34
+ {
35
+ "version": "7.0.0",
36
+ "summary": "Shadows become one ramp named for the job. Light values are unchanged; dark is deeper, and the two floating rungs gain a hairline top edge.",
37
+ "rename": {
38
+ "--dk-shadow-card-faint": "--dk-shadow-raised",
39
+ "--dk-shadow-card-subtle": "--dk-shadow-lifted",
40
+ "--dk-shadow-card-default": "--dk-shadow-floating",
41
+ "--dk-shadow-card-on-color": "--dk-shadow-on-color",
42
+ "--dk-shadow-popover": "--dk-shadow-overlay"
43
+ },
44
+ "added": {
45
+ "--dk-shadow-focus-ring": "The 0 0 0 4px primary-300 ring that card, checkbox, switch and select each wrote out raw. Button keeps its own, whose width and colour are already component tokens."
46
+ },
47
+ "removedFromTheSystem": {
48
+ "note": "The upstream drop-* and inner-* families are colours, not shadows: color-mix with no offset or blur, unusable without writing the geometry yourself. Thirteen of the fourteen had never been used anywhere, and the one that was (drop-lg, in the select menu) now uses --dk-shadow-floating. They still resolve, because upstream owns the block above the fence in tokens/mapped/shadow.css and a refresh would restore them, but they are no longer part of the shadow system and the Shadows page no longer lists them. Markup that composes them by hand keeps working.",
49
+ "tokens": [
50
+ "--dk-shadow-drop-xs",
51
+ "--dk-shadow-drop-sm-1",
52
+ "--dk-shadow-drop-sm-2",
53
+ "--dk-shadow-drop-md-1",
54
+ "--dk-shadow-drop-md-2",
55
+ "--dk-shadow-drop-lg-1",
56
+ "--dk-shadow-drop-lg-2",
57
+ "--dk-shadow-drop-xl-1",
58
+ "--dk-shadow-drop-xl-2",
59
+ "--dk-shadow-drop-2xl",
60
+ "--dk-shadow-drop-3xl",
61
+ "--dk-shadow-inner-sm",
62
+ "--dk-shadow-inner-md",
63
+ "--dk-shadow-inner-lg"
64
+ ]
65
+ },
66
+ "alsoFixed": {
67
+ "modal": "dk-component-modal hardcoded 0 4px 8px -2px rgba(0,0,0,.1), 0 2px 4px -2px rgba(0,0,0,.06): it never themed, and it was lighter than a card on hover. It now uses --dk-shadow-overlay."
68
+ }
69
+ }
70
+ ```
71
+
@@ -0,0 +1,16 @@
1
+ ## 8.0.0 - 2026-09-12
2
+
3
+ - **drift-kit is importable from React.** New `@karmo-co/drift-kit/react` covering
4
+ Body, Heading, Hero, Eyebrow, Divider and Button, and `@karmo-co/drift-kit/icons/<set>`
5
+ covering all eight icon sets. Props are the data attributes with `data-` dropped,
6
+ so a prototype and an app use the same words in the same order.
7
+ - Components are thin: class names and attributes, no styling runtime, no state.
8
+ All of them render on the server and none carries a `'use client'` directive.
9
+ - Icons are per-icon named exports annotated `/*#__PURE__*/`, so an app ships only
10
+ what it imports rather than all 2,090 lucide glyphs.
11
+ - One prop had to be renamed: `data-style` is `textStyle`, because React owns `style`.
12
+ - Button's variant and status pairs are a union, so a combination the CSS does not
13
+ style fails to compile.
14
+ - The repo gains its first dependencies, all of them dev only. The published package
15
+ still declares no runtime dependencies; react is a peer.
16
+
@@ -0,0 +1,107 @@
1
+ ## 9.0.0 - 2026-09-14
2
+
3
+ Three changes to Alert, all of them breaking, all of them asked for together.
4
+
5
+ - **The status tint on the words is now opt-in, via `data-text="tinted"`.**
6
+ 8.3.0 made every status paint its own title and body on `subtle`, `outline`
7
+ and `plain`. By default they are back to the flat pair the base rule always
8
+ carried: default-coloured title, grey body. The **icon stays status-coloured
9
+ either way**, so turning the tint off never costs the reader the signal, it
10
+ just stops the message carrying it twice.
11
+ - Mechanically the five status rules no longer write `--_alert-title` and
12
+ `--_alert-body` at all. They write one new private slot,
13
+ `--_alert-tint-text`, and only the `[data-text="tinted"]` rule spends it.
14
+ That rule and `[data-variant="solid"]` are the only two writers of the text
15
+ slots and they have identical specificity, so **source order alone** decides
16
+ which wins and solid has to stay last: tinted words on a solid status fill
17
+ would be that colour on itself. `test/order.test.js` now asserts both the
18
+ writer list and the order, and fails if a third rule appears or the two swap.
19
+ - **`neutral` is a named status.** `data-status="neutral"` renders exactly what
20
+ omitting the attribute always did, so nothing changes visually; what changes
21
+ is that you can now say it. A sixth component token,
22
+ `--dk-component-alert-neutral-color`, completes the per-status text family,
23
+ so a tinted neutral alert has a colour of its own rather than falling back to
24
+ nothing. React's `AlertStatus` gains `'neutral'`.
25
+ - **Width follows Button: content width by default, `data-full-width="true"`
26
+ to fill.** This reverses 8.1.0, which shipped `width: 100%`. Two width
27
+ vocabularies pointing opposite ways across one kit is worse than either
28
+ default on its own. `fit-content` is less narrow than it sounds: it resolves
29
+ to `min(max-content, max(min-content, available))`, so a long body still
30
+ fills the column and only a short one hugs. React gains `fullWidth`.
31
+ - **Content width gained a trailing-padding rule**, found by looking at the new
32
+ default on the docs page. A leading icon with no trailing dismiss button is
33
+ the one configuration whose two insets do not match: measured at the default
34
+ size, the words start 48px in (padding, icon column, gap) and stop 18px from
35
+ the other edge. Its `padding-inline-end` becomes `padding + gap`, the same
36
+ step that separates the icon from the words, mirrored, so it tracks both the
37
+ size axis and the responsive spacing scale with no second number to keep in
38
+ sync. Content width only, since full width has slack there and widening it
39
+ would buy nothing but a narrower column. The other two configurations are
40
+ already even (48/48 with a dismiss button in the mirror column, 18/18 with no
41
+ icon) and are excluded, as is `plain`, whose whole job is to have no padding.
42
+ - **The icon and the dismiss button now centre correctly when there is no
43
+ title.** Both align to `--_alert-title-line`, so with title omitted they were
44
+ centring on a line that is not there: the body is a size step smaller
45
+ (body-small 20px against body-medium 24px), which left both 2px low. With no
46
+ title the slot now resolves to the body's line height instead. Only visible at
47
+ the default size, since `data-size="small"` steps the title down to
48
+ body-small too and the two already coincided. Measured across all 26
49
+ icon-bearing alerts on the docs page: every one centres within 0.6px.
50
+ - **The dismiss button now has hover and press states**, borrowed from Icon
51
+ button and recoloured by the status: a background that appears on hover, one
52
+ rung further on press, the glyph stepping `-hover` then `-focus` alongside it,
53
+ and a `scale(0.9)`. Two deliberate differences from `.dk-component-icon-btn`:
54
+ it is transparent at rest, since it already sits on a tinted panel and a
55
+ resting fill would read as a second panel inside the first, and its colours
56
+ come from the status rather than from neutral.
57
+ - Its box is now the glyph plus a padding ring (`28px` around a `20px` glyph at
58
+ the default size, `24px` around `16px` at small), which is what gives the
59
+ hover background a body instead of a smudge hugging the X, and it sits
60
+ `--dk-component-alert-dismiss-offset` further from the words: the gap from the
61
+ text to the button goes from 12px to 20px, and to the glyph itself 24px.
62
+ - **`solid` needed a scrim rather than a rung.** Measured before building it:
63
+ `--dk-surface-<status>-focus` is byte-identical to `--dk-surface-<status>-solid`
64
+ in light for all five statuses, and `--dk-surface-error-default` collides with
65
+ it the same way in dark, so no existing ladder gives two distinct steps above
66
+ a solid fill in both themes. Two new primitives,
67
+ `--dk-primitive-opacity-scrim-{hover,active}` (0.14 and 0.24), are laid over
68
+ the fill in the on-solid colour, which self-adapts: white over a status fill,
69
+ black over the neutral one. The glyph stays on-solid, since dimming the one
70
+ white thing on a saturated fill would cost contrast to signal a hover.
71
+ - `kit/reduced-motion.css` gains the dismiss button in both its lists, so the
72
+ new press does not move the element for a reader who asked for less motion.
73
+ That file exists precisely to stop new motion shipping unguarded.
74
+ - **Padding is even on all sides**, `--dk-component-alert-padding` (16px, 12px
75
+ at small), replacing the 12px-vertical against 16px-horizontal pair. That is
76
+ the shape a control has, not a panel, and Toast, the component Alert is the
77
+ persistent half of, already sets a flat `padding: var(--dk-spacing-4)`. ONE
78
+ token per size rather than two holding the same value, so the two edges
79
+ cannot drift apart again: `--dk-component-alert-padding-block` and
80
+ `-padding-inline` are gone, at both sizes.
81
+ - **An alert with no dismiss button gets a bit more room on its trailing
82
+ edge**, 24px against 16px (16px against 12px at small). With the button
83
+ there the right side already holds something and the words stop well short
84
+ of the edge; take it away and they run up against the padding alone. The
85
+ extra is its own token, `--dk-component-alert-trailing-extra`. It was the
86
+ dismiss offset until that offset was raised: the two had been equal only
87
+ because they arrived at the same size, and reusing one for the other meant
88
+ widening the gap before the button silently widened the padding of every
89
+ alert that has no button. This replaces the narrower rule that only fired at
90
+ content width and only with a leading icon: with even padding it is the
91
+ missing control that makes the difference, not the icon, and it is missing
92
+ at any width. `plain` stays excluded.
93
+ - **More room between the words and the dismiss button**, 28px rather than
94
+ 20px at the default size and 20px rather than 12px at small.
95
+ `--dk-component-alert-dismiss-offset` is now the panel's own padding, so the
96
+ control stands as far from the words as the words stand from the edge, and
97
+ it tracks the size axis with them rather than being a number of its own.
98
+ - **Migrating.** An alert that wants its 8.3.0 look adds `data-text="tinted"`;
99
+ one that wants its 8.1.0 width adds `data-full-width="true"`. Neither has a
100
+ silent failure mode: leave them off and you get the new defaults, which
101
+ render correctly, just differently.
102
+ - Contrast moves the right way. Neutral words on the status tints clear AA in
103
+ 29 of 30 theme and surface-level combinations, against 12 below-AA readings
104
+ of 288 for the tinted path. The single miss is error body text in dark at
105
+ ambient `data-surface="2"`, 3.95:1, and it only applies to an alert that has
106
+ opted out of the tint on a level-2 surface.
107
+
@@ -0,0 +1,37 @@
1
+ # The kit's own guard tests
2
+
3
+ Every rule below is a test in drift-kit. A component the kit ships passes all of them.
4
+
5
+ ## motion-guard
6
+
7
+ - components read motion patterns only
8
+ - behaviours read motion through dk.motion, never their own parser
9
+
10
+ ## invariant
11
+
12
+ - bundle canonical form matches the pre-restructure fixture
13
+ - no component transition writes a literal duration
14
+ - comment delimiters in the built bundle are balanced
15
+ - no declaration in the built bundle has unbalanced parentheses
16
+ - every component with a size axis sets those properties without one too
17
+
18
+ ## tone-boundary
19
+
20
+ - the audit's self-surfaced components are boundaries
21
+ - the element that paints is reset, not the component around it
22
+ - a variant that paints nothing at rest is excluded
23
+ - parts that paint only under the pointer, and disabled or control fills, are not boundaries
24
+ - Box is never a boundary: it is a toned surface by design
25
+ - Card is never a boundary either: rebuilt on the kit surface, 16 Oct
26
+ - a fill that stays put (a selected row) is a boundary; one that comes and goes (hover) is not
27
+ - status tints count as a component's own surface, followed through a private variable
28
+ - a fill no tone remaps is the component's own surface (Choice card, by name)
29
+ - a state set on an ancestor that repaints a part with a tone-following fill excludes it (an inset Input)
30
+ - an ancestor tone treatment that repaints a part excludes it (Icon tile on a solid)
31
+ - review: variants are also judged in pairs (a status solid Tag is a mark rule C inverts, not a surface)
32
+ - the derivation follows --dk-interactive-* to the surface it reads
33
+
34
+ ## anatomy
35
+
36
+ - every component stylesheet opens with an Anatomy block rooted at its own class
37
+ - every component stylesheet says what the component is for, in a Purpose line
@@ -0,0 +1,70 @@
1
+ # The package
2
+
3
+ Installed as `@karmo-co/drift-kit` from GitHub Packages.
4
+
5
+ ## Entry points
6
+
7
+ | Import | File |
8
+ |---|---|
9
+ | `@karmo-co/drift-kit/drift-kit.css` | `./dist/drift-kit.css` |
10
+ | `@karmo-co/drift-kit/drift-kit.min.css` | `./dist/drift-kit.min.css` |
11
+ | `@karmo-co/drift-kit/components.js` | `./dist/components.js` |
12
+ | `@karmo-co/drift-kit/fonts/*` | `./dist/fonts/*` |
13
+ | `@karmo-co/drift-kit/vehicles/*` | `./dist/vehicles/*` |
14
+ | `@karmo-co/drift-kit/manifest/*` | `./dist/manifest/*` |
15
+ | `@karmo-co/drift-kit/package.json` | `./package.json` |
16
+ | `@karmo-co/drift-kit/react` | `./dist/react/index.js` |
17
+ | `@karmo-co/drift-kit/behaviours/*` | `./dist/react/behaviours/*.js` |
18
+ | `@karmo-co/drift-kit/icons/*` | `./dist/icons/*.js` |
19
+
20
+ ## Root attributes the stylesheet reads
21
+
22
+ | Attribute | Values |
23
+ |---|---|
24
+ | `data-brand` | `karmo`, `volvo` |
25
+ | `data-theme` | `dark`, `light` |
26
+ | `data-border-radius` | `bubbly`, `normal`, `sharp` |
27
+ | `data-border-width` | `medium`, `thick`, `thin` |
28
+ | `data-density` | `normal`, `spacious`, `tight` |
29
+
30
+ ## Container attributes
31
+
32
+ Set on a panel, not the root: everything inside follows. A container that sets a level or a tone paints `background: var(--dk-surface-current)`, the colour of the place it is in.
33
+
34
+ | Attribute | Values |
35
+ |---|---|
36
+ | `data-surface` | `0`, `1`, `2`, `raised` |
37
+ | `data-tone` | `credit`, `error`, `information`, `inverse`, `muted`, `neutral`, `none`, `primary`, `success`, `warning` |
38
+ | `data-tone-fill` | `solid` |
39
+
40
+ ## Verify
41
+
42
+ 1. **The tokens resolve:** `getComputedStyle(document.body).getPropertyValue('--dk-surface-current')` is non-empty. Probe on `document.body`, never `document.documentElement`: the tokens live on the attribute scopes. Empty means the root attributes are missing or miscased.
43
+ 2. **The components are styled:** add a temporary main-action Button, `<button class="dk-component-button" data-variant="default" data-status="primary">`, to the body, read its computed `padding-inline-start`, then remove it. Zero means the stylesheet is missing, truncated or from another major.
44
+
45
+ A component token resolves only on its own component's element, so never probe one on `body`: it is empty on a healthy install.
46
+
47
+ ## The public kit
48
+
49
+ A page with no npm install (a plain HTML prototype, a slide deck) links this version from the public kit. Every address carries the version, and a version's files never change, so never write a link without one. Link the stylesheet in the head (`drift-kit.min.css` when shipping), `components.js` only when a component on the page has behaviour, and one icon script, only for the set the page's icons come from. Fonts load from the same folder, relative to the stylesheet.
50
+
51
+ Both scripts are deferred, and an icon script goes after `components.js`, which replaces the icon registry when it runs: an icon script that runs first loses its set. Every tag carries its file's `integrity` from the table below and `crossorigin="anonymous"`, so a file whose bytes changed never loads. In this order:
52
+
53
+ ```html
54
+ <link rel="stylesheet" href="https://storage.googleapis.com/drift-kit-public/23.5.0/drift-kit.css" integrity="sha384-QFAunRFb04Unk6O2tsutrhpLKyCKeEZldwHPxXbbx26XezTwo15nej6qBMcQQE2O" crossorigin="anonymous" />
55
+ <script src="https://storage.googleapis.com/drift-kit-public/23.5.0/components.js" integrity="sha384-2YLDzdQXhMM10gupcoaZoir9HCk4EILtF7OdDjFoMUhPNkLbre117mISxLLOTojA" crossorigin="anonymous" defer></script>
56
+ <script src="https://storage.googleapis.com/drift-kit-public/23.5.0/drift-icons-<set>.js" integrity="<its integrity>" crossorigin="anonymous" defer></script>
57
+ ```
58
+
59
+ | File | Address | Integrity |
60
+ |---|---|---|
61
+ | `drift-kit.css` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-kit.css` | `sha384-QFAunRFb04Unk6O2tsutrhpLKyCKeEZldwHPxXbbx26XezTwo15nej6qBMcQQE2O` |
62
+ | `drift-kit.min.css` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-kit.min.css` | `sha384-1XaZo6zP9LN6tnGxlT7VlqgL3SV7MkmSiUZxNho5joChtDA27cxjK4bEOI71HYya` |
63
+ | `components.js` | `https://storage.googleapis.com/drift-kit-public/23.5.0/components.js` | `sha384-2YLDzdQXhMM10gupcoaZoir9HCk4EILtF7OdDjFoMUhPNkLbre117mISxLLOTojA` |
64
+ | `drift-icons-card.js` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-icons-card.js` | `sha384-Dl18Pw11QQ+/KtbAzbjt1DdP6fJwFNn4kCykxtFsF+uEZtUfMIseQJ7s14OmqEmg` |
65
+ | `drift-icons-coolicon.js` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-icons-coolicon.js` | `sha384-8WaBw9LZuhj/u7G2Jf1aiVo8HBhMj1fBsmpSWJnYi5Xdi2ANXB3BLBZxz2NLQGYl` |
66
+ | `drift-icons-feather.js` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-icons-feather.js` | `sha384-MFEltFi3gRSwgNdAMXGCYo+6Ri9iXLLWp4Gq3AeayzeliWAo3czd+pTTLAFHHU6s` |
67
+ | `drift-icons-flag-circle.js` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-icons-flag-circle.js` | `sha384-gtU89vOz98Fbn0KZWzkHVQA4+kFhOFYoT/Dr43ljTskqkyGDMw1ihH1I7Zn25dzZ` |
68
+ | `drift-icons-flag.js` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-icons-flag.js` | `sha384-VtM24IDkwASIVzD5hI+ijQYRXufYQbveKM0+wqaFSGZbrYZlodFlnvZOexhCpiH9` |
69
+ | `drift-icons-lucide.js` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-icons-lucide.js` | `sha384-yEy9LdLbPCXUy577fmNq4loJ2G9AQ1InAW54iozwnVfe8KIK6gVe2227eaNmxFi1` |
70
+ | `drift-icons-social.js` | `https://storage.googleapis.com/drift-kit-public/23.5.0/drift-icons-social.js` | `sha384-+hPy5fFjIqfIAmDN+WOwekTLu7NIFZvyzOwXUTAiBEQqnZ0/kXgd4frbSGOr/c8c` |
@@ -0,0 +1,12 @@
1
+ # Border width tokens
2
+
3
+ Declared values, light and dark. Reference a token by name; never copy its value.
4
+
5
+ | Token | Light | Dark |
6
+ |---|---|---|
7
+ | `--dk-border-width-0` | `var(--dk-dynamic-border-width-0)` | `var(--dk-dynamic-border-width-0)` |
8
+ | `--dk-border-width-25` | `var(--dk-dynamic-border-width-25)` | `var(--dk-dynamic-border-width-25)` |
9
+ | `--dk-border-width-50` | `var(--dk-dynamic-border-width-50)` | `var(--dk-dynamic-border-width-50)` |
10
+ | `--dk-border-width-100` | `var(--dk-dynamic-border-width-100)` | `var(--dk-dynamic-border-width-100)` |
11
+ | `--dk-border-width-150` | `var(--dk-dynamic-border-width-150)` | `var(--dk-dynamic-border-width-150)` |
12
+ | `--dk-border-width-200` | `var(--dk-dynamic-border-width-200)` | `var(--dk-dynamic-border-width-200)` |