@revikornmann/muka-ui 0.17.0 → 0.19.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 (182) hide show
  1. package/README.md +81 -60
  2. package/cli/bin/muka-ui.js +12 -5
  3. package/cli/commands/brand.js +33 -14
  4. package/cli/commands/init.js +21 -17
  5. package/cli/commands/install-skill.js +84 -26
  6. package/cli/templates/AGENTS.md +128 -0
  7. package/cli/templates/CLAUDE.md +8 -88
  8. package/cli/templates/muka-ui-guidelines.md +50 -14
  9. package/dist/cjs/components/ActionSheet/ActionSheet.js +1 -1
  10. package/dist/cjs/components/Breadcrumb/Breadcrumb.css +11 -3
  11. package/dist/cjs/components/Breadcrumb/Breadcrumb.js +19 -1
  12. package/dist/cjs/components/Breadcrumb/Breadcrumb.js.map +1 -1
  13. package/dist/cjs/components/Combobox/Combobox.css +116 -132
  14. package/dist/cjs/components/Combobox/Combobox.js +175 -52
  15. package/dist/cjs/components/Combobox/Combobox.js.map +1 -1
  16. package/dist/cjs/components/Combobox/index.js.map +1 -1
  17. package/dist/cjs/components/ContextSelect/ContextSelect.css +117 -0
  18. package/dist/cjs/components/ContextSelect/ContextSelect.js +49 -0
  19. package/dist/cjs/components/ContextSelect/ContextSelect.js.map +1 -0
  20. package/dist/cjs/components/ContextSelect/index.js +11 -0
  21. package/dist/cjs/components/ContextSelect/index.js.map +1 -0
  22. package/dist/cjs/components/DropdownSelect/DropdownSelect.css +225 -0
  23. package/dist/cjs/components/DropdownSelect/DropdownSelect.js +106 -0
  24. package/dist/cjs/components/DropdownSelect/DropdownSelect.js.map +1 -0
  25. package/dist/cjs/components/DropdownSelect/index.js +6 -0
  26. package/dist/cjs/components/DropdownSelect/index.js.map +1 -0
  27. package/dist/cjs/components/Icon/custom/ArrowDropUpDownIcon.js +11 -0
  28. package/dist/cjs/components/Icon/custom/ArrowDropUpDownIcon.js.map +1 -0
  29. package/dist/cjs/components/Icon/custom/ArrowReturnIcon.js +11 -0
  30. package/dist/cjs/components/Icon/custom/ArrowReturnIcon.js.map +1 -0
  31. package/dist/cjs/components/Icon/iconRegistry.js +11 -0
  32. package/dist/cjs/components/Icon/iconRegistry.js.map +1 -1
  33. package/dist/cjs/components/Input/Input.css +45 -8
  34. package/dist/cjs/components/Input/Input.js +14 -3
  35. package/dist/cjs/components/Input/Input.js.map +1 -1
  36. package/dist/cjs/components/Menu/Menu.css +213 -17
  37. package/dist/cjs/components/Menu/Menu.js +429 -39
  38. package/dist/cjs/components/Menu/Menu.js.map +1 -1
  39. package/dist/cjs/components/Menu/index.js +5 -1
  40. package/dist/cjs/components/Menu/index.js.map +1 -1
  41. package/dist/cjs/components/ProgressTracker/ProgressTracker.css +273 -22
  42. package/dist/cjs/components/ProgressTracker/ProgressTracker.js +109 -9
  43. package/dist/cjs/components/ProgressTracker/ProgressTracker.js.map +1 -1
  44. package/dist/cjs/components/Scrollbar/Scrollbar.css +124 -0
  45. package/dist/cjs/components/Scrollbar/Scrollbar.js +118 -0
  46. package/dist/cjs/components/Scrollbar/Scrollbar.js.map +1 -0
  47. package/dist/cjs/components/Scrollbar/index.js +11 -0
  48. package/dist/cjs/components/Scrollbar/index.js.map +1 -0
  49. package/dist/cjs/components/index.js +13 -16
  50. package/dist/cjs/components/index.js.map +1 -1
  51. package/dist/esm/components/ActionSheet/ActionSheet.js +1 -1
  52. package/dist/esm/components/Breadcrumb/Breadcrumb.css +11 -3
  53. package/dist/esm/components/Breadcrumb/Breadcrumb.js +19 -1
  54. package/dist/esm/components/Breadcrumb/Breadcrumb.js.map +1 -1
  55. package/dist/esm/components/Combobox/Combobox.css +116 -132
  56. package/dist/esm/components/Combobox/Combobox.js +177 -54
  57. package/dist/esm/components/Combobox/Combobox.js.map +1 -1
  58. package/dist/esm/components/Combobox/index.js +1 -1
  59. package/dist/esm/components/Combobox/index.js.map +1 -1
  60. package/dist/esm/components/ContextSelect/ContextSelect.css +117 -0
  61. package/dist/esm/components/ContextSelect/ContextSelect.js +45 -0
  62. package/dist/esm/components/ContextSelect/ContextSelect.js.map +1 -0
  63. package/dist/esm/components/ContextSelect/index.js +3 -0
  64. package/dist/esm/components/ContextSelect/index.js.map +1 -0
  65. package/dist/esm/components/DropdownSelect/DropdownSelect.css +225 -0
  66. package/dist/esm/components/DropdownSelect/DropdownSelect.js +102 -0
  67. package/dist/esm/components/DropdownSelect/DropdownSelect.js.map +1 -0
  68. package/dist/esm/components/DropdownSelect/index.js +2 -0
  69. package/dist/esm/components/DropdownSelect/index.js.map +1 -0
  70. package/dist/esm/components/Icon/custom/ArrowDropUpDownIcon.js +7 -0
  71. package/dist/esm/components/Icon/custom/ArrowDropUpDownIcon.js.map +1 -0
  72. package/dist/esm/components/Icon/custom/ArrowReturnIcon.js +7 -0
  73. package/dist/esm/components/Icon/custom/ArrowReturnIcon.js.map +1 -0
  74. package/dist/esm/components/Icon/iconRegistry.js +11 -0
  75. package/dist/esm/components/Icon/iconRegistry.js.map +1 -1
  76. package/dist/esm/components/Input/Input.css +45 -8
  77. package/dist/esm/components/Input/Input.js +14 -3
  78. package/dist/esm/components/Input/Input.js.map +1 -1
  79. package/dist/esm/components/Menu/Menu.css +213 -17
  80. package/dist/esm/components/Menu/Menu.js +428 -40
  81. package/dist/esm/components/Menu/Menu.js.map +1 -1
  82. package/dist/esm/components/Menu/index.js +1 -1
  83. package/dist/esm/components/Menu/index.js.map +1 -1
  84. package/dist/esm/components/ProgressTracker/ProgressTracker.css +273 -22
  85. package/dist/esm/components/ProgressTracker/ProgressTracker.js +110 -10
  86. package/dist/esm/components/ProgressTracker/ProgressTracker.js.map +1 -1
  87. package/dist/esm/components/Scrollbar/Scrollbar.css +124 -0
  88. package/dist/esm/components/Scrollbar/Scrollbar.js +114 -0
  89. package/dist/esm/components/Scrollbar/Scrollbar.js.map +1 -0
  90. package/dist/esm/components/Scrollbar/index.js +3 -0
  91. package/dist/esm/components/Scrollbar/index.js.map +1 -0
  92. package/dist/esm/components/index.js +4 -2
  93. package/dist/esm/components/index.js.map +1 -1
  94. package/dist/styles/components/Breadcrumb.css +11 -3
  95. package/dist/styles/components/Combobox.css +116 -132
  96. package/dist/styles/components/ContextSelect.css +117 -0
  97. package/dist/styles/components/DropdownSelect.css +225 -0
  98. package/dist/styles/components/Input.css +45 -8
  99. package/dist/styles/components/Menu.css +213 -17
  100. package/dist/styles/components/ProgressTracker.css +273 -22
  101. package/dist/styles/components/Scrollbar.css +124 -0
  102. package/dist/styles/components/Typography.css +89 -0
  103. package/dist/styles/index.css +1526 -480
  104. package/dist/styles/muka-dark.css +1526 -480
  105. package/dist/styles/muka-light.css +1526 -480
  106. package/dist/styles/tokens-bouwplan-dark.css +9 -6
  107. package/dist/styles/tokens-bouwplan-light.css +9 -6
  108. package/dist/styles/tokens-fscl-dark.css +29 -26
  109. package/dist/styles/tokens-fscl-light.css +29 -26
  110. package/dist/styles/tokens-grip-dark.css +8 -5
  111. package/dist/styles/tokens-grip-light.css +8 -5
  112. package/dist/styles/tokens-muka-dark.css +8 -5
  113. package/dist/styles/tokens-muka-light.css +8 -5
  114. package/dist/styles/tokens-wireframe-dark.css +53 -50
  115. package/dist/styles/tokens-wireframe-light.css +53 -50
  116. package/dist/styles/wireframe-dark.css +1571 -525
  117. package/dist/styles/wireframe-light.css +1571 -525
  118. package/dist/types/components/ActionSheet/ActionSheet.d.ts +1 -1
  119. package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +27 -3
  120. package/dist/types/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
  121. package/dist/types/components/Combobox/Combobox.d.ts +38 -38
  122. package/dist/types/components/Combobox/Combobox.d.ts.map +1 -1
  123. package/dist/types/components/Combobox/index.d.ts +1 -1
  124. package/dist/types/components/Combobox/index.d.ts.map +1 -1
  125. package/dist/types/components/ContextSelect/ContextSelect.d.ts +62 -0
  126. package/dist/types/components/ContextSelect/ContextSelect.d.ts.map +1 -0
  127. package/dist/types/components/ContextSelect/index.d.ts +3 -0
  128. package/dist/types/components/ContextSelect/index.d.ts.map +1 -0
  129. package/dist/types/components/DataTable/DataTable.d.ts +1 -1
  130. package/dist/types/components/DropdownSelect/DropdownSelect.d.ts +71 -0
  131. package/dist/types/components/DropdownSelect/DropdownSelect.d.ts.map +1 -0
  132. package/dist/types/components/DropdownSelect/index.d.ts +2 -0
  133. package/dist/types/components/DropdownSelect/index.d.ts.map +1 -0
  134. package/dist/types/components/Icon/custom/ArrowDropUpDownIcon.d.ts +9 -0
  135. package/dist/types/components/Icon/custom/ArrowDropUpDownIcon.d.ts.map +1 -0
  136. package/dist/types/components/Icon/custom/ArrowReturnIcon.d.ts +9 -0
  137. package/dist/types/components/Icon/custom/ArrowReturnIcon.d.ts.map +1 -0
  138. package/dist/types/components/Icon/iconRegistry.d.ts.map +1 -1
  139. package/dist/types/components/Input/Input.d.ts +5 -0
  140. package/dist/types/components/Input/Input.d.ts.map +1 -1
  141. package/dist/types/components/Menu/Menu.d.ts +99 -29
  142. package/dist/types/components/Menu/Menu.d.ts.map +1 -1
  143. package/dist/types/components/Menu/index.d.ts +2 -2
  144. package/dist/types/components/Menu/index.d.ts.map +1 -1
  145. package/dist/types/components/ProgressTracker/ProgressTracker.d.ts +29 -8
  146. package/dist/types/components/ProgressTracker/ProgressTracker.d.ts.map +1 -1
  147. package/dist/types/components/Scrollbar/Scrollbar.d.ts +19 -0
  148. package/dist/types/components/Scrollbar/Scrollbar.d.ts.map +1 -0
  149. package/dist/types/components/Scrollbar/index.d.ts +3 -0
  150. package/dist/types/components/Scrollbar/index.d.ts.map +1 -0
  151. package/dist/types/components/index.d.ts +5 -4
  152. package/dist/types/components/index.d.ts.map +1 -1
  153. package/docs/consumers/README.md +116 -37
  154. package/docs/consumers/brand.md +261 -0
  155. package/docs/consumers/figma-console-mcp.md +116 -0
  156. package/docs/consumers/setup-instructions.md +98 -12
  157. package/docs/consumers/skills.md +79 -0
  158. package/package.json +4 -6
  159. package/scripts/postinstall-nudge.js +8 -4
  160. package/skills/add-brand/SKILL.md +204 -0
  161. package/skills/add-brand/reference.md +160 -0
  162. package/skills/figma-to-code/SKILL.md +127 -0
  163. package/skills/pull-from-figma/SKILL.md +123 -0
  164. package/skills/push-to-figma/SKILL.md +172 -0
  165. package/skills/setup-muka/SKILL.md +73 -13
  166. package/tokens/README.md +40 -18
  167. package/tokens/t2-alias/brand/bouwplan/fonts.json +1 -1
  168. package/tokens/t2-alias/brand/fscl/fonts.json +3 -3
  169. package/tokens/t2-alias/brand/wireframe/fonts.json +2 -2
  170. package/tokens/t4-components/menu.json +20 -5
  171. package/dist/cjs/components/ContextMenu/ContextMenu.js +0 -186
  172. package/dist/cjs/components/ContextMenu/ContextMenu.js.map +0 -1
  173. package/dist/cjs/components/ContextMenu/index.js +0 -17
  174. package/dist/cjs/components/ContextMenu/index.js.map +0 -1
  175. package/dist/esm/components/ContextMenu/ContextMenu.js +0 -145
  176. package/dist/esm/components/ContextMenu/ContextMenu.js.map +0 -1
  177. package/dist/esm/components/ContextMenu/index.js +0 -2
  178. package/dist/esm/components/ContextMenu/index.js.map +0 -1
  179. package/dist/types/components/ContextMenu/ContextMenu.d.ts +0 -188
  180. package/dist/types/components/ContextMenu/ContextMenu.d.ts.map +0 -1
  181. package/dist/types/components/ContextMenu/index.d.ts +0 -3
  182. package/dist/types/components/ContextMenu/index.d.ts.map +0 -1
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: push-to-figma
3
+ description: Push this repo's custom brand tokens into Figma as variables, so designers can design in your brand using the Muka UI Figma Library.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Push your brand to Figma
8
+
9
+ Publish this repo's brand layer (`brand/light.json`, `brand/dark.json`,
10
+ `brand/fonts.json`) into Figma as variables, so designers see your brand in the
11
+ Muka UI Figma Library instead of one of Muka's.
12
+
13
+ **Direction:** Code → Figma. **The codebase is the source of truth.** Use
14
+ `/pull-from-figma` for the other direction.
15
+
16
+ ## Prerequisites
17
+
18
+ 1. This repo has a custom brand — run `/add-brand` first.
19
+ 2. **Figma Console MCP** is configured and the Desktop Bridge plugin is running.
20
+ One-time setup:
21
+ `node_modules/@revikornmann/muka-ui/docs/consumers/figma-console-mcp.md`
22
+ (also online at
23
+ <https://github.com/revikornmann/muka/blob/main/docs/consumers/figma-console-mcp.md>).
24
+ 3. You can edit the target Figma file (your own copy or branch of the Muka UI
25
+ Figma Library).
26
+
27
+ ## What gets pushed, and what does not
28
+
29
+ Only your **brand alias layer** — the ~78 `alias/color/*` tokens plus
30
+ `alias/font/*`. That is the whole point: primitives, semantics, and component
31
+ tokens belong to the Muka library and are inherited, exactly as in code.
32
+
33
+ ```
34
+ component (button/color/primary/background/default) → library, do not touch
35
+ semantic (color/surface/level0, color/action/default) → library, do not touch
36
+ alias (alias/color/accent/default) → YOURS, pushed here
37
+ Primitives (color/violet/9) → library, raw values
38
+ ```
39
+
40
+ If you find yourself pushing a `button/*` or `color/surface/*` variable, stop —
41
+ that is a library-level change and belongs in a Muka pull request, not your
42
+ brand.
43
+
44
+ ## Step 1: Confirm the bridge is live
45
+
46
+ ```
47
+ figma_get_status { probe: true }
48
+ ```
49
+
50
+ Expect `setup.valid: true`, `probeResult.success: true`, and the connected file
51
+ name. If the tools are missing entirely, the session started before the MCP
52
+ server was registered — restart it.
53
+
54
+ ## Step 2: Locate the collections
55
+
56
+ Do **not** hardcode collection or mode IDs; look them up by name in the file you
57
+ are connected to. Muka's own IDs differ in every copy and branch of the library.
58
+
59
+ ```javascript
60
+ const collections = await figma.variables.getLocalVariableCollectionsAsync();
61
+ for (const c of collections) {
62
+ console.log(c.name, c.id, c.modes.map((m) => `${m.name}=${m.modeId}`));
63
+ }
64
+ ```
65
+
66
+ You need two:
67
+
68
+ - **Primitives** — the raw ramps (`color/violet/9`). One mode. Read-only for you.
69
+ - **Theme** — one mode per brand/theme combination (`Muka Light`, `Muka Dark`, …).
70
+ This is where your brand's modes live.
71
+
72
+ Build a name → id map across **both** collections; you will alias into Primitives
73
+ by name.
74
+
75
+ ## Step 3: Add modes for your brand
76
+
77
+ Your brand needs two modes in the Theme collection, named to match the existing
78
+ convention:
79
+
80
+ ```javascript
81
+ const theme = collections.find((c) => c.name === 'Theme');
82
+ const lightModeId = theme.addMode('Acme Light');
83
+ const darkModeId = theme.addMode('Acme Dark');
84
+ ```
85
+
86
+ Skip this if the modes already exist — find them by name and reuse their
87
+ `modeId`. Figma has a per-collection mode limit that depends on the plan; if
88
+ `addMode` throws, the file has hit it and modes must be freed before continuing.
89
+
90
+ ## Step 4: Bind values — as aliases, never raw colours
91
+
92
+ **This is the step that goes wrong.** Every alias token must be a variable
93
+ **alias** pointing at a Primitive, not a resolved hex. A raw value is a
94
+ disconnected duplicate: it will not cascade when the ramp is edited, and it
95
+ drifts silently from your `{color.violet.9}` references in code.
96
+
97
+ Your `brand/light.json` already stores exactly the reference you need
98
+ (`{color.violet.9}` → the Primitives variable `color/violet/9`), so convert the
99
+ path and bind:
100
+
101
+ ```javascript
102
+ // '{color.violet.9}' → 'color/violet/9'
103
+ const target = value.replace(/[{}]/g, '').split('.').join('/');
104
+
105
+ const variable = await figma.variables.getVariableByIdAsync(aliasVarId);
106
+ variable.setValueForMode(lightModeId, {
107
+ type: 'VARIABLE_ALIAS',
108
+ id: nameToId[target],
109
+ });
110
+ ```
111
+
112
+ Each mode gets a **different** Primitives target — light mode points at
113
+ `color/violet/9`, dark mode at `color/violetDark/9`. Per-mode cross-collection
114
+ aliases do cascade, and that is what makes mode switching work.
115
+
116
+ > `figma_batch_create_variables` and `figma_batch_update_variables` accept only
117
+ > scalar/hex `valuesByMode` — they **cannot** set alias bindings. Use them to
118
+ > create bare variables in bulk, then bind in a separate `figma_execute` pass.
119
+
120
+ Raw `{r, g, b, a}` values are correct only in the Primitives collection. If you
121
+ ever must write one, pass an object — **never a hex string**, which drops the
122
+ alpha channel and flattens the `black-alpha` / `white-alpha` ramps to opaque.
123
+
124
+ ## Step 5: Push the fonts
125
+
126
+ `alias/font/*` variables are strings, not aliases, so set them directly — with
127
+ two conversions:
128
+
129
+ - **Family**: reduce a CSS stack to the single installed face name. Pushing
130
+ `'IBM Plex Sans', ui-sans-serif, system-ui` makes every bound text node render
131
+ as a **missing font**; push `IBM Plex Sans`.
132
+ - **Weight**: Figma `fontStyle` bindings need style **names**, not the CSS
133
+ numerics your tokens hold — `600` → `SemiBold`, `700` → `Bold`, `500` →
134
+ `Medium`, `400` → `Regular`. Pushing numerics collapses semibold and bold onto
135
+ the same face.
136
+
137
+ The font must be installed locally or available to the file, or every bound text
138
+ node shows as missing.
139
+
140
+ > Repairing a font-family variable that *already* holds a missing font is
141
+ > blocked, because Figma cannot re-render bound text in a font that will not
142
+ > load. Temporarily flip the consuming containers' mode
143
+ > (`node.explicitVariableModes`), set the variable, then flip back. Do **not**
144
+ > override `node.fontName` on bound instance text — that detaches the binding and
145
+ > breaks the other modes.
146
+
147
+ ## Step 6: Verify
148
+
149
+ 1. **Every pushed token reads as an _Alias_** in the inspector — an alias pill,
150
+ not a colour swatch. Programmatically, assert every `valuesByMode` entry has
151
+ `type === 'VARIABLE_ALIAS'`. A swatch means Step 4 was done wrong.
152
+ 2. **Resolve and diff against the source.** Follow each alias to a concrete
153
+ colour and confirm it matches `styles/tokens-<brand>-light.css`. A
154
+ concatenated-hex checksum per variable across both modes is a cheap way to
155
+ compare in bulk.
156
+ 3. **Switch modes in Figma** and confirm the design visibly changes: your light
157
+ mode shows your accent colour, dark mode shows dark surfaces with readable
158
+ text.
159
+ 4. **Spot-check a component instance.** A primary Button in your mode should be
160
+ your accent colour, which proves the whole component → semantic → alias →
161
+ primitive chain resolved.
162
+
163
+ ## Troubleshooting
164
+
165
+ | Symptom | Cause |
166
+ |---|---|
167
+ | Tokens show a colour swatch, not an alias pill | Pushed as raw hex — rebind per Step 4 |
168
+ | Mode switching changes nothing | Every mode was bound to the same target; each needs its own |
169
+ | Text renders as missing font | A CSS stack or an uninstalled family was pushed |
170
+ | Semibold and bold look identical | Numeric weights were pushed instead of style names |
171
+ | Transparency ramps look opaque | A hex string was pushed instead of `{r, g, b, a}` |
172
+ | `figma-console` tools absent | Session predates `.mcp.json`; approve via `/mcp` and restart |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup-muka
3
- description: Set up a consumer repo to use Muka UI from npm — install the package, wire up the auto-update workflow, and register as a consumer.
3
+ description: Install Muka UI in this repo from npm, wire up styles and fonts, add the auto-update workflow, and register as a consumer.
4
4
  disable-model-invocation: true
5
5
  argument-hint: "[brand]"
6
6
  ---
@@ -8,16 +8,17 @@ argument-hint: "[brand]"
8
8
  # Set up Muka UI in this repo
9
9
 
10
10
  Configure the current repo to consume the **public npm package**
11
- `@revikornmann/muka-ui` and stay current with releases automatically. The
12
- optional `$ARGUMENTS` is the brand to theme with (e.g. `fscl`, `grip`,
13
- `bouwplan`); default brand is `muka`.
11
+ `@revikornmann/muka-ui`. The optional `$ARGUMENTS` is the brand to theme with
12
+ (`muka`, `wireframe`, `grip`, `fscl`, `bouwplan`); default is `muka`.
14
13
 
15
14
  Muka UI is a public npm package — **no `.npmrc`, registry config, or auth token
16
- is required**. When the package is installed, the version-locked copy of these
17
- steps lives at
15
+ is required**. Once installed, the version-locked copy of these steps lives at
18
16
  `node_modules/@revikornmann/muka-ui/docs/consumers/setup-instructions.md`; read
19
17
  it and prefer it when present, since it always matches the installed version.
20
18
 
19
+ To build a **custom brand** for this repo instead of using one of Muka's five,
20
+ finish this skill first and then run `/add-brand`.
21
+
21
22
  ## Step 1: Install the package
22
23
 
23
24
  If a previous git dependency exists (e.g.
@@ -39,26 +40,59 @@ npm ls @revikornmann/muka-ui # or: pnpm why … / yarn why …
39
40
 
40
41
  ## Step 2: Import styles and components
41
42
 
42
- In the app's root/entry, import the stylesheet once, then components anywhere:
43
+ In the app's root/entry, import one stylesheet, then components anywhere:
43
44
 
44
45
  ```ts
45
46
  import '@revikornmann/muka-ui/styles';
46
47
  import { Button, Card, Input } from '@revikornmann/muka-ui';
47
48
  ```
48
49
 
49
- For a brand theme, also import the brand token CSS and set the data attributes.
50
- Replace `<brand>` with `$ARGUMENTS` (or `muka`) and pick `light`/`dark`:
50
+ `/styles` is the **`muka-light`** bundle: base styles, the muka-light tokens,
51
+ every component's CSS, and muka's self-hosted fonts. Four pre-bundled
52
+ alternatives ship alongside it and are drop-in replacements:
53
+
54
+ ```ts
55
+ import '@revikornmann/muka-ui/styles/muka-dark.css';
56
+ import '@revikornmann/muka-ui/styles/wireframe-light.css';
57
+ import '@revikornmann/muka-ui/styles/wireframe-dark.css';
58
+ ```
59
+
60
+ For `grip`, `fscl`, or `bouwplan` there is no pre-bundled file, so compose it
61
+ from three imports — base styles, the brand's raw token CSS, and the brand's
62
+ fonts (the raw token CSS carries no `@font-face`):
51
63
 
52
64
  ```ts
65
+ import '@revikornmann/muka-ui/styles/base.css';
53
66
  import '@revikornmann/muka-ui/styles/tokens-<brand>-light.css';
67
+ import '@revikornmann/muka-ui/styles/fonts-<brand>.css';
54
68
  ```
55
69
 
56
- ```html
57
- <html data-brand="<brand>" data-theme="light">
70
+ ### How brand and theme selection actually works
71
+
72
+ Every token stylesheet declares its variables on `:root`. There are **no
73
+ `[data-brand]` or `[data-theme]` selectors**, so you select a brand and theme by
74
+ **choosing which stylesheet is loaded**, and importing two of them means the
75
+ last one wins. Do not add `data-brand` / `data-theme` attributes expecting them
76
+ to switch anything.
77
+
78
+ For a runtime light/dark toggle, swap a `<link>` instead of importing both:
79
+
80
+ ```ts
81
+ const link = document.getElementById('muka-theme') as HTMLLinkElement;
82
+ link.href = `/styles/tokens-${brand}-${mode}.css`;
58
83
  ```
59
84
 
60
- Apps that already centralize CSS (e.g. a `src/styles/index.css`) may instead
61
- `@import '@revikornmann/muka-ui/styles/base.css'` and the brand token CSS there.
85
+ Copy the brand's token CSS into the app's static/public directory (or let the
86
+ bundler emit it with a stable URL) so those hrefs resolve. This is exactly what
87
+ Muka's own Storybook does.
88
+
89
+ ### Fonts come from Muka — do not load them yourself
90
+
91
+ Muka self-hosts every brand's fonts as `@font-face` backed by bundled WOFF2.
92
+ Do **not** load them again via `next/font`, a Google Fonts `<link>`, or another
93
+ loader, and do **not** override the `--alias-font-*-family` tokens — the token
94
+ values are the exact `@font-face` family names, so overriding them only breaks
95
+ resolution.
62
96
 
63
97
  ## Step 3: Add the auto-update workflow
64
98
 
@@ -82,6 +116,10 @@ lockfile, `lockfileVersion: 9.0` → pnpm 9). Without it, CI's corepack pulls th
82
116
  `minimumReleaseAge` default rejecting a just-published version). Add it if
83
117
  missing.
84
118
 
119
+ Muka is under active development and a new version can change component
120
+ behaviour or appearance. Keep CI checks in place so the update workflow's bump
121
+ is reviewed rather than trusted.
122
+
85
123
  ## Step 4: Register this repo as a consumer
86
124
 
87
125
  So releases dispatch updates here, add `owner/repo` to Muka's
@@ -90,9 +128,31 @@ Open a PR against `revikornmann/muka` adding the line (skip if it is already
90
128
  listed). The Muka `CONSUMER_DISPATCH_TOKEN` PAT must have **write** access to
91
129
  this repo for the `repository_dispatch` to succeed.
92
130
 
131
+ ## Step 5: Give future agents the house rules
132
+
133
+ ```bash
134
+ npx muka-ui init
135
+ ```
136
+
137
+ This writes `AGENTS.md`, `CLAUDE.md`, and `docs/muka-ui-guidelines.md` into the
138
+ repo (it prompts before overwriting any of them) so any agent working here knows
139
+ to compose Muka components and use token custom properties instead of hardcoded
140
+ values. It also warns about styling libraries that conflict with Muka.
141
+
142
+ `AGENTS.md` carries the rules — it is the filename most agents read — and
143
+ `CLAUDE.md` is a short pointer to it, so there is only one copy to keep current.
144
+
93
145
  ## Verify
94
146
 
95
147
  - `npm ls @revikornmann/muka-ui` shows a semver version resolved from npmjs.
96
148
  - The app builds/type-checks and Muka components render with brand tokens.
149
+ - A component's computed `background-color` resolves to a real value, not the
150
+ `var(...)` fallback — that is the check that the token CSS actually loaded.
97
151
  - `.github/workflows/update-muka.yml` exists and is valid YAML.
98
152
  - This repo appears in Muka's `.github/consumers.txt` (or a PR is open for it).
153
+
154
+ ## Next
155
+
156
+ - `/add-brand` — build a custom brand that overrides Muka's brand layer.
157
+ - `/figma-to-code` — turn a Figma link into a screen built from Muka components.
158
+ - `npx muka-ui install-skill --list` — every skill shipped with the package.
package/tokens/README.md CHANGED
@@ -1,26 +1,37 @@
1
- `# Design Tokens
1
+ # Design Tokens
2
2
 
3
3
  ## Directory Structure
4
4
 
5
5
  ```
6
6
  tokens/
7
- ├── t1-primitives/ # Raw values: color.json, scale.json, typography.json
7
+ ├── t1-primitives/ # Raw values: color.json, scale.json, typography.json, motion.json
8
8
  ├── t2-alias/
9
9
  │ ├── base.json # Brand-agnostic aliases
10
- │ ├── brand/
11
- │ │ ├── muka/ # fonts.json, light.json, dark.json
12
- │ │ ├── wireframe/ # fonts.json, light.json, dark.json
13
- │ │ └── grip/ # fonts.json, light.json, dark.json
10
+ │ ├── brand/ # One directory per brand, each with fonts/light/dark.json
11
+ │ │ ├── muka/ # + radius.json
12
+ │ │ ├── wireframe/
13
+ │ │ ├── grip/
14
+ │ │ ├── fscl/
15
+ │ │ └── bouwplan/
14
16
  │ └── layout/
15
17
  │ ├── mobile.json # Mobile-first base (default)
16
18
  │ ├── tablet.json # md breakpoint (768px) overrides
17
- │ └── desktop.json # lg breakpoint (1024px) overrides
19
+ │ ├── desktop.json # lg breakpoint (1024px) overrides
20
+ │ └── wide.json # xl breakpoint (1280px) overrides
18
21
  ├── t3-semantics/ # ui.json — usage-intent tokens
19
- ├── t4-components/ # 28 component token files
22
+ ├── t4-components/ # 35 component token files
20
23
  ├── $themes.json # Tokens Studio theme config
21
24
  └── $metadata.json # Token metadata
22
25
  ```
23
26
 
27
+ Five brands × light/dark = **10 theme builds**, each emitting one
28
+ `styles/tokens-<brand>-<theme>.css`. The authoritative list is
29
+ `build/manifest.json`.
30
+
31
+ The whole `tokens/` tree ships in the npm package, so consumers can build a
32
+ custom brand against these primitives without forking. See
33
+ `docs/consumers/brand.md`.
34
+
24
35
  ## Naming Convention
25
36
 
26
37
  ```
@@ -38,14 +49,18 @@ tokens/
38
49
  { "$value": "{color.gray.11}" }
39
50
  ```
40
51
 
52
+ Dark-mode ramps append a **camelCase `Dark`** — `{color.mauveDark.1}`,
53
+ `{color.indigoDark.9}`. References are case-sensitive, so `{color.mauvedark.1}`
54
+ resolves to nothing.
55
+
41
56
  ## Inheritance Order
42
57
 
43
58
  1. T1 Primitives (base values)
44
59
  2. T2 Alias (brand-agnostic references)
45
60
  3. T3 Semantic (meaningful abstractions)
46
61
  4. T4 Component (component-specific)
47
- 5. Brand overrides (muka/, wireframe/, grip/)
48
- 6. Theme overrides (light.json, dark.json)
62
+ 5. Brand overrides (`brand/<name>/`)
63
+ 6. Theme overrides (`light.json`, `dark.json`)
49
64
 
50
65
  ## CSS Output
51
66
 
@@ -55,17 +70,23 @@ tokens/
55
70
 
56
71
  ## Brand/Theme Switching
57
72
 
58
- ```html
59
- <html data-theme="light" data-brand="muka">
60
- ```
73
+ Every generated stylesheet declares its variables on `:root`, so the active
74
+ brand and theme are determined by **which stylesheet is loaded** — there are no
75
+ `[data-brand]` / `[data-theme]` selectors, and loading two means the last one
76
+ wins.
77
+
78
+ Storybook switches by swapping a `<link href>` (`.storybook/ThemeDecorator.ts`);
79
+ consumers do the same, or import a single theme statically.
61
80
 
62
81
  ## Build Commands
63
82
 
64
83
  ```bash
65
- node scripts/build-tokens.js # Build all tokens → CSS
66
- node scripts/export-for-figma.js # Export for Figma Variables
67
- node scripts/export-for-penpot.js # Export for Penpot
84
+ npm run build:tokens # Build all themes → styles/tokens-*.css
85
+ npm run build:fonts # Self-host brand fonts → styles/fonts-*.css
68
86
  npm run validate:tokens # Validate JSON structure
87
+ node scripts/export-figma-variables.js # Export for Figma Variables
88
+ node scripts/export-for-penpot.js # Export for Penpot
89
+ node scripts/export-for-webflow.js # Export for Webflow
69
90
  ```
70
91
 
71
92
  ## Rules
@@ -91,6 +112,7 @@ to flow upward by reference; a brand file cannot override a `{component}.*` toke
91
112
 
92
113
  ## Human Docs
93
114
  <!-- SYNC: Update paths when Storybook docs change -->
94
- - Token architecture guide: components/Documentation/TokenArchitecture.mdx
95
- - Design Tokens in Storybook: stories/tokens/*.mdx (6 pages)
115
+ - Token architecture guide: stories/tokens/01-Introduction.mdx
116
+ - Design Tokens in Storybook: stories/tokens/*.mdx (7 pages)
117
+ - Consumer brand guide: docs/consumers/brand.md
96
118
  - All Storybook docs: `npm run dev` → localhost:6006
@@ -18,7 +18,7 @@
18
18
  "family": { "$type": "fontFamilies", "$value": "Red Hat Text" },
19
19
  "weight": {
20
20
  "regular": { "$type": "fontWeights", "$value": "400" },
21
- "medium": { "$type": "fontWeights", "$value": "400" },
21
+ "medium": { "$type": "fontWeights", "$value": "500" },
22
22
  "semibold": { "$type": "fontWeights", "$value": "500" },
23
23
  "bold": { "$type": "fontWeights", "$value": "600" }
24
24
  }
@@ -18,9 +18,9 @@
18
18
  "family": { "$type": "fontFamilies", "$value": "'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif" },
19
19
  "weight": {
20
20
  "regular": { "$type": "fontWeights", "$value": "400" },
21
- "medium": { "$type": "fontWeights", "$value": "400" },
22
- "semibold": { "$type": "fontWeights", "$value": "400" },
23
- "bold": { "$type": "fontWeights", "$value": "400" }
21
+ "medium": { "$type": "fontWeights", "$value": "500" },
22
+ "semibold": { "$type": "fontWeights", "$value": "500" },
23
+ "bold": { "$type": "fontWeights", "$value": "600" }
24
24
  }
25
25
  },
26
26
  "mono": {
@@ -6,7 +6,7 @@
6
6
  "alias": {
7
7
  "font": {
8
8
  "brand": {
9
- "family": { "$type": "fontFamilies", "$value": "Helvetica" },
9
+ "family": { "$type": "fontFamilies", "$value": "Roboto" },
10
10
  "weight": {
11
11
  "regular": { "$type": "fontWeights", "$value": "400" },
12
12
  "medium": { "$type": "fontWeights", "$value": "500" },
@@ -15,7 +15,7 @@
15
15
  }
16
16
  },
17
17
  "plain": {
18
- "family": { "$type": "fontFamilies", "$value": "Helvetica" },
18
+ "family": { "$type": "fontFamilies", "$value": "Roboto" },
19
19
  "weight": {
20
20
  "regular": { "$type": "fontWeights", "$value": "400" },
21
21
  "medium": { "$type": "fontWeights", "$value": "500" },
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "border": {
14
14
  "$type": "color",
15
- "$value": "{color.border.muted}"
15
+ "$value": "{color.border.contrast}"
16
16
  }
17
17
  },
18
18
  "border": {
@@ -35,11 +35,15 @@
35
35
  },
36
36
  "min-width": {
37
37
  "$type": "dimension",
38
- "$value": "12rem"
38
+ "$value": "280px"
39
39
  },
40
40
  "max-height": {
41
41
  "$type": "dimension",
42
42
  "$value": "20rem"
43
+ },
44
+ "scroll-height": {
45
+ "$type": "dimension",
46
+ "$value": "20.625rem"
43
47
  }
44
48
  },
45
49
  "item": {
@@ -61,7 +65,7 @@
61
65
  "hover": {
62
66
  "background": {
63
67
  "$type": "color",
64
- "$value": "{color.surface.hover}"
68
+ "$value": "{color.surface.level2}"
65
69
  },
66
70
  "foreground": {
67
71
  "$type": "color",
@@ -75,7 +79,7 @@
75
79
  "focus": {
76
80
  "background": {
77
81
  "$type": "color",
78
- "$value": "{color.action.muted}"
82
+ "$value": "{color.surface.level2}"
79
83
  },
80
84
  "foreground": {
81
85
  "$type": "color",
@@ -86,6 +90,12 @@
86
90
  "$value": "{color.text.default.default}"
87
91
  }
88
92
  },
93
+ "selected": {
94
+ "background": {
95
+ "$type": "color",
96
+ "$value": "{color.surface.level1}"
97
+ }
98
+ },
89
99
  "disabled": {
90
100
  "background": {
91
101
  "$type": "color",
@@ -138,9 +148,14 @@
138
148
  "$type": "dimension",
139
149
  "$value": "{spacing.3}"
140
150
  },
151
+ "right": {
152
+ "$type": "dimension",
153
+ "$value": "{spacing.9}",
154
+ "$description": "Reserve trailing-icon gutter (Figma Dropdown Menu Item). Icons float in this space so hover/selected trailers do not change wrapping."
155
+ },
141
156
  "y": {
142
157
  "$type": "dimension",
143
- "$value": "{spacing.2}"
158
+ "$value": "{spacing.3}"
144
159
  }
145
160
  },
146
161
  "gap": {