@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,204 @@
1
+ ---
2
+ name: add-brand
3
+ description: Create a custom brand in this repo that overrides Muka UI's brand layer, so every Muka component picks up your colours and typography.
4
+ disable-model-invocation: true
5
+ argument-hint: "[brand-name]"
6
+ ---
7
+
8
+ # Add your own brand
9
+
10
+ Give this repo its own brand without forking Muka UI. You author **only the T2
11
+ brand layer**; primitives, semantics, and all component tokens keep coming from
12
+ the installed package, so every Muka component restyles itself with no component
13
+ code changes.
14
+
15
+ `$ARGUMENTS` is the brand name — lowercase, starting with a letter, letters,
16
+ numbers and hyphens only (e.g. `acme`, `northwind`, `blue-harbour`).
17
+
18
+ **Prerequisite:** `@revikornmann/muka-ui` is installed. If not, run
19
+ `/setup-muka` first.
20
+
21
+ See [`reference.md`](reference.md) for the full token map, the available
22
+ primitive palettes, and the colour-scale conventions.
23
+
24
+ ## How the override works
25
+
26
+ ```
27
+ T1 Primitives (package) raw ramps: color.indigo.9, spacing.4, size.md
28
+ T2 Alias base (package) brand-agnostic defaults
29
+ T2 Alias brand ← YOU brand/light.json, brand/dark.json, brand/fonts.json
30
+ T3 Semantics (package) color.surface.level1, color.action.default
31
+ T4 Components (package) button.color.primary.background.default
32
+ ```
33
+
34
+ Your three files replace the brand slice of T2. Everything above resolves
35
+ through them, which is why one file of colour references repaints the whole
36
+ library.
37
+
38
+ ## Step 1: Scaffold the brand
39
+
40
+ ```bash
41
+ npx muka-ui brand init $ARGUMENTS
42
+ ```
43
+
44
+ This creates, prompting before overwriting anything that exists:
45
+
46
+ | Path | Contents |
47
+ |---|---|
48
+ | `brand/fonts.json` | Font families and weights for the `brand`, `plain`, `mono`, `script` roles |
49
+ | `brand/light.json` | Light-mode colour aliases (78 tokens) |
50
+ | `brand/dark.json` | Dark-mode colour aliases |
51
+ | `brand/muka.brand.json` | Build manifest — your brand files plus package token globs |
52
+ | `brand/build.js` | Build entry using `TokenBuilder` from the package |
53
+
54
+ It also adds a `build:tokens` script to `package.json` if one is not there.
55
+
56
+ The scaffolded files are copies of Muka's `wireframe` brand, so they are a
57
+ complete, valid, buildable starting point rather than empty stubs. Confirm the
58
+ build works before editing anything:
59
+
60
+ ```bash
61
+ npm run build:tokens
62
+ ```
63
+
64
+ That must print `All theme combinations built successfully!` and produce
65
+ `styles/tokens-$ARGUMENTS-light.css` and `styles/tokens-$ARGUMENTS-dark.css`.
66
+ Fix any failure here before moving on — it is much easier to debug against
67
+ known-good token values.
68
+
69
+ ## Step 2: Decide the brand identity
70
+
71
+ Before editing, settle these and write them down in the conversation:
72
+
73
+ | Decision | Feeds |
74
+ |---|---|
75
+ | Neutral ramp | `alias.color.neutral.*` — every surface, border, and text colour |
76
+ | Accent colour | `alias.color.accent.*` — buttons, links, focus rings, selection |
77
+ | Primary / secondary brand colours | `alias.color.brand.*` — logos, marketing accents |
78
+ | State colours | `alias.color.state.{success,warning,error,info}` |
79
+ | Brand and body typefaces | `alias.font.brand.family`, `alias.font.plain.family` |
80
+
81
+ Pick a **hue-matched pair** of primitive ramps for light and dark: light mode
82
+ uses `{color.<name>.N}`, dark mode uses `{color.<name>dark.N}`. Ask the user for
83
+ the brand's colours if they have not supplied them; do not invent a palette.
84
+
85
+ ## Step 3: Edit the colour aliases
86
+
87
+ Edit `brand/light.json`. Every value is a **reference to a T1 primitive**, never
88
+ a raw hex — that is what keeps the ramp coherent and lets dark mode mirror it:
89
+
90
+ ```json
91
+ "neutral": {
92
+ "1": { "$type": "color", "$value": "{color.slate.1}" },
93
+ "12": { "$type": "color", "$value": "{color.slate.12}" }
94
+ },
95
+ "accent": {
96
+ "default": { "$type": "color", "$value": "{color.violet.9}" },
97
+ "hover": { "$type": "color", "$value": "{color.violet.10}" },
98
+ "pressed": { "$type": "color", "$value": "{color.violet.11}" },
99
+ "contrast": { "$type": "color", "$value": "{color.violet.12}" },
100
+ "muted": { "$type": "color", "$value": "{color.violet.8}" }
101
+ }
102
+ ```
103
+
104
+ Then mirror it in `brand/dark.json` using the `Dark` ramps —
105
+ `{color.slateDark.1}`, `{color.violetDark.9}`. The suffix is **camelCase
106
+ `Dark`**, and references are case-sensitive: `{color.slatedark.1}` resolves to
107
+ nothing. Keep the **same step numbers** in both files; the ramps are designed so
108
+ equal steps read as equal emphasis in either mode.
109
+
110
+ Rules that matter:
111
+
112
+ - **Reference primitives, never hex.** A raw hex silently opts that token out of
113
+ the system and will not have a dark-mode counterpart.
114
+ - **Keep every key.** Deleting a token leaves the semantic layer resolving to
115
+ the base alias value, which is rarely what you want. Repoint it instead.
116
+ - **Do not edit anything above T2.** Semantics and component tokens come from
117
+ the package and are overwritten on every upgrade.
118
+ - Only `alias.*` belongs in these files.
119
+
120
+ Available palettes and the meaning of each step are in
121
+ [`reference.md`](reference.md).
122
+
123
+ ## Step 4: Set the typography
124
+
125
+ Edit `brand/fonts.json`. The `family` values must be the **exact `@font-face`
126
+ family name** that will be loaded, because the generated CSS passes the literal
127
+ through to `font-family`.
128
+
129
+ - **Reusing a font Muka already bundles** (Funnel Display, Funnel Sans, Red Hat
130
+ Display, Red Hat Text, Quicksand, IBM Plex Sans, Lora, Yesteryear) — import
131
+ that brand's fonts file and you are done:
132
+ ```ts
133
+ import '@revikornmann/muka-ui/styles/fonts-muka.css';
134
+ ```
135
+ - **Using a system font** (Helvetica, Arial, Menlo, Georgia, …) — nothing to
136
+ load.
137
+ - **Bringing your own font** — self-host it with your own `@font-face` under the
138
+ exact family name you put in `fonts.json`. Do not use a Google Fonts `<link>`:
139
+ it breaks offline and air-gapped installs and adds a render-blocking request.
140
+ Muka's own font pipeline is not available to consumer brands, so this
141
+ `@font-face` block is yours to own.
142
+
143
+ ## Step 5: Build and wire up the CSS
144
+
145
+ ```bash
146
+ npm run build:tokens
147
+ ```
148
+
149
+ Import your brand CSS **after** Muka's base and component styles so your `:root`
150
+ block wins the cascade:
151
+
152
+ ```ts
153
+ import '@revikornmann/muka-ui/styles/base.css';
154
+ import './styles/tokens-acme-light.css';
155
+ import '@revikornmann/muka-ui/styles/fonts-muka.css'; // or your own @font-face
156
+ ```
157
+
158
+ Do **not** import `@revikornmann/muka-ui/styles` as well — that bundle contains
159
+ the full muka-light token set and, depending on import order, will fight your
160
+ brand. Use `base.css` instead, which is the component and reset layer only.
161
+
162
+ There are no `[data-brand]` / `[data-theme]` selectors; to toggle light/dark at
163
+ runtime, swap the stylesheet rather than importing both:
164
+
165
+ ```ts
166
+ link.href = `/styles/tokens-acme-${mode}.css`;
167
+ ```
168
+
169
+ ## Step 6: Rebuild on every Muka upgrade
170
+
171
+ Your brand CSS is generated against the package's primitives, semantics, and
172
+ component tokens, so it goes stale when Muka publishes new ones. Add the build
173
+ to the existing pipeline so it cannot be forgotten:
174
+
175
+ ```json
176
+ "scripts": {
177
+ "build": "npm run build:tokens && <your existing build>",
178
+ "postinstall": "npm run build:tokens"
179
+ }
180
+ ```
181
+
182
+ Commit `brand/**`. Generated CSS under `styles/tokens-*.css` can be committed or
183
+ gitignored — commit it if your host does not run a build step.
184
+
185
+ ## Verify
186
+
187
+ - `styles/tokens-$ARGUMENTS-light.css` and `-dark.css` exist and were rebuilt
188
+ after your last edit.
189
+ - Neither file contains `undefined` — that means an unresolved `{reference}`,
190
+ usually a typo'd primitive name.
191
+ - Your accent colour appears as a real value:
192
+ ```bash
193
+ grep -- '--alias-color-accent-default' styles/tokens-$ARGUMENTS-light.css
194
+ ```
195
+ - A primary `<Button>` renders in your accent colour and body text in your
196
+ typeface, in both light and dark.
197
+ - Toggling to dark mode changes surfaces without leaving light-mode text
198
+ unreadable.
199
+
200
+ ## Next
201
+
202
+ - `/push-to-figma` — publish this brand to Figma as variables, so designers can
203
+ design in your brand.
204
+ - `/figma-to-code` — turn a Figma link into a screen built from Muka components.
@@ -0,0 +1,160 @@
1
+ # Brand token reference
2
+
3
+ Everything you need while editing `brand/light.json`, `brand/dark.json`, and
4
+ `brand/fonts.json`. The authoritative values live in
5
+ `node_modules/@revikornmann/muka-ui/tokens/t1-primitives/color.json`.
6
+
7
+ ## Available primitive palettes
8
+
9
+ Each palette is a 12-step ramp. Light-mode references use the base name; dark
10
+ mode appends **camelCase `Dark`** (`{color.violet.9}` → `{color.violetDark.9}`).
11
+ References are case-sensitive.
12
+
13
+ **Neutrals** — `gray`, `mauve`, `slate`, `sage`, `olive`, `sand`
14
+
15
+ **Colours** — `tomato`, `red`, `ruby`, `crimson`, `pink`, `plum`, `purple`,
16
+ `violet`, `iris`, `indigo`, `blue`, `cyan`, `teal`, `jade`, `green`, `grass`,
17
+ `bronze`, `gold`, `brown`, `orange`, `amber`, `yellow`, `lime`, `mint`, `sky`
18
+
19
+ **Absolutes** — `white`, `black`, and the transparency ramps `black-alpha`,
20
+ `white-alpha`
21
+
22
+ **Hued neutral alphas** — `mauveA`, `grayA`, `sandA`, `sageA` (and
23
+ `mauveDarkA`, `grayDarkA`, `sandDarkA`, `sageDarkA`) back
24
+ `alias.color.neutral.alphaHued.*`, which is used for overlays and scrims that
25
+ must tint rather than cover. Match these to whichever neutral ramp you chose.
26
+
27
+ Every palette has a dark counterpart except `white`, `black`, and the alpha
28
+ ramps, which are mode-independent.
29
+
30
+ ## What each step in a 12-step ramp is for
31
+
32
+ | Steps | Use |
33
+ |---|---|
34
+ | 1–2 | App and component backgrounds |
35
+ | 3–5 | Subtle backgrounds: hover, selected, muted fills |
36
+ | 6–8 | Borders and separators (8 is the strongest border / focus ring) |
37
+ | 9–10 | Solid fills — 9 is the palette's most saturated step, 10 is its hover |
38
+ | 11 | Low-contrast text, and text on light backgrounds |
39
+ | 12 | High-contrast text and headings |
40
+
41
+ Pick `accent.default` at step **9**, `hover` at **10**, `pressed` at **11**, and
42
+ `contrast` at **12** unless you have a reason not to. This pattern is what every
43
+ shipped brand uses.
44
+
45
+ ## Token groups in `light.json` / `dark.json`
46
+
47
+ 78 tokens across five groups. All are `$type: "color"`.
48
+
49
+ ### `alias.color.neutral`
50
+
51
+ - `1`–`12` — the neutral ramp. Drives every surface level, border, and text
52
+ colour in the semantic layer, so this choice has the widest visual effect of
53
+ anything in the file.
54
+ - `alphaHued.1`–`12` — translucent neutrals for overlays and scrims.
55
+
56
+ ### `alias.color.brand`
57
+
58
+ - `primary.{regular,contrast,muted}`
59
+ - `secondary.{regular,contrast,muted}`
60
+
61
+ Brand identity colours. Used for marketing surfaces and accents rather than
62
+ ordinary controls — controls read from `accent`.
63
+
64
+ ### `alias.color.accent`
65
+
66
+ - `{default,hover,pressed,contrast,muted}`
67
+ - `inverse.{default,hover,pressed,contrast,muted}`
68
+
69
+ The interactive colour: primary buttons, links, focus rings, selection,
70
+ active navigation. `inverse` is the same role on top of an accent-filled
71
+ surface, so it usually runs from step 1 up to `white`.
72
+
73
+ If you only change one thing, change this.
74
+
75
+ ### `alias.color.state`
76
+
77
+ For each of `success`, `warning`, `error`, `info`:
78
+
79
+ - `{contrast,default,muted}`
80
+ - `surface.{level0,level1,level2,level3}`
81
+
82
+ Keep the hues conventional — green for success, red for error, amber for
83
+ warning. `default` is the solid step 9, `contrast` the readable step 11, `muted`
84
+ a step 3 fill, and the `surface` levels are the elevation ramp for banners and
85
+ alerts.
86
+
87
+ ### `alias.color.conversation`
88
+
89
+ For each of `own`, `peer`, `agent`:
90
+
91
+ - `surface.{default,raised}`
92
+ - `border`
93
+
94
+ Plus `meta` for timestamps and read receipts. These back the chat components. If
95
+ your app has no chat surface, leave them as scaffolded — they still need to
96
+ resolve.
97
+
98
+ ## `fonts.json` roles
99
+
100
+ | Role | Used for |
101
+ |---|---|
102
+ | `brand` | Headings, buttons, labels — the expressive typeface |
103
+ | `plain` | Body copy and UI text — the readable typeface |
104
+ | `mono` | Code, numeric tables, anything needing fixed advance |
105
+ | `script` | Decorative accents only |
106
+
107
+ Each role has `family` plus a `weight` map. `brand` and `plain` carry
108
+ `regular` / `medium` / `semibold` / `bold`; `mono` carries `regular` / `bold`;
109
+ `script` is `family` only.
110
+
111
+ Weights are CSS numerics as strings (`"400"`, `"600"`). Only list weights the
112
+ font actually provides — naming a weight the file lacks makes the browser
113
+ synthesise it, which looks wrong at large sizes.
114
+
115
+ Pointing `brand` and `plain` at the same family is normal and is what the
116
+ `wireframe` and `fscl` brands do.
117
+
118
+ ## Fonts Muka already bundles
119
+
120
+ Reference any of these and you get a real self-hosted face by importing the
121
+ matching `fonts-<brand>.css`:
122
+
123
+ | Family | Import this brand's fonts file |
124
+ |---|---|
125
+ | `Funnel Display`, `Funnel Sans` | `fonts-muka.css` |
126
+ | `Quicksand` | `fonts-grip.css` |
127
+ | `IBM Plex Sans`, `Lora` | `fonts-fscl.css` |
128
+ | `Red Hat Display`, `Red Hat Text` | `fonts-bouwplan.css` |
129
+ | `Yesteryear` (script role) | `fonts-muka.css`, `fonts-grip.css`, or `fonts-bouwplan.css` |
130
+
131
+ `fonts-wireframe.css` is empty — that brand is system fonts only.
132
+
133
+ System families need no loading: `Helvetica`, `Arial`, `Menlo`,
134
+ `Lucida Console`, `Times New Roman`, `Courier New`, `Georgia`, `Verdana`,
135
+ `Tahoma`, `Trebuchet MS`, `system-ui`, `ui-sans-serif`, `ui-serif`,
136
+ `ui-monospace`, and the generic `sans-serif` / `serif` / `monospace`.
137
+
138
+ ## Generated CSS names
139
+
140
+ Token paths become custom properties by flattening on `-`:
141
+
142
+ | Token | Custom property |
143
+ |---|---|
144
+ | `alias.color.accent.default` | `--alias-color-accent-default` |
145
+ | `alias.color.neutral.3` | `--alias-color-neutral-3` |
146
+ | `alias.font.brand.family` | `--alias-font-brand-family` |
147
+
148
+ You will rarely reference these directly — components read T3/T4 properties like
149
+ `--color-action-default` and `--button-color-primary-background-default`, which
150
+ resolve through your aliases. Grep for `--alias-` only when debugging.
151
+
152
+ ## Debugging
153
+
154
+ | Symptom | Cause |
155
+ |---|---|
156
+ | `undefined` in the generated CSS | An unresolved `{reference}` — usually a typo'd palette name or a missing `Dark` suffix |
157
+ | Dark mode looks identical to light | `dark.json` still references light ramps |
158
+ | Text unreadable on accent fills | `accent.contrast` is too close to `accent.default`; move it to step 12 or `white` |
159
+ | Fonts fall back to a system face | No `@font-face` is loaded for that family name, or the name does not match byte-for-byte |
160
+ | Colours unchanged after a rebuild | The brand CSS is imported before Muka's token bundle, so the bundle wins. Import `base.css` instead of `styles`, and your brand CSS last |
@@ -0,0 +1,127 @@
1
+ ---
2
+ name: figma-to-code
3
+ description: Build a screen in this app from a Figma link, composing Muka UI components and design tokens instead of writing new styled markup.
4
+ disable-model-invocation: true
5
+ argument-hint: "[figma-url]"
6
+ ---
7
+
8
+ # Figma link → Muka UI screen
9
+
10
+ Turn a Figma design into working code in **this app** by assembling it from Muka
11
+ UI components. `$ARGUMENTS` is the Figma URL.
12
+
13
+ The goal is assembly, not reproduction. A Figma frame drawn with the Muka UI
14
+ Figma Library is already made of Muka components, so the right output is those
15
+ same components with props — not a pixel-matched rebuild in bespoke CSS. If the
16
+ result is a pile of `<div>`s with hardcoded colours, the design was
17
+ re-implemented instead of mapped, and it will not follow brand or theme changes.
18
+
19
+ **Prerequisite:** `@revikornmann/muka-ui` is installed (`/setup-muka`) and a
20
+ Figma MCP server is connected.
21
+
22
+ ## Step 1: Read the design
23
+
24
+ Extract `fileKey` and `nodeId` from the URL:
25
+
26
+ - `figma.com/design/:fileKey/:fileName?node-id=:nodeId` — convert `-` to `:` in
27
+ the nodeId (`472-4248` → `472:4248`)
28
+ - `figma.com/design/:fileKey/branch/:branchKey/…` — use `branchKey` as the
29
+ fileKey
30
+
31
+ Then pull the design context:
32
+
33
+ ```
34
+ get_code_connect_map(fileKey, nodeId) # FIRST — Figma node → real component names
35
+ get_design_context(fileKey, nodeId) # structure, tokens, variants
36
+ get_screenshot(fileKey, nodeId) # visual reference to check yourself against
37
+ ```
38
+
39
+ Read `get_code_connect_map` **first**. Muka publishes Code Connect mappings for
40
+ its components, so this is what tells you a node is a Muka `Button` rather than a
41
+ rounded rectangle with a label. Treat its answer as authoritative over your own
42
+ reading of the layers.
43
+
44
+ Treat `get_design_context` output as a **reference, not final code**. It
45
+ describes the design in generic terms; your job is the translation.
46
+
47
+ ## Step 2: Inventory what Muka already provides
48
+
49
+ Before writing any JSX, find out what exists. In order of reliability:
50
+
51
+ 1. **Storybook MCP**, if Muka's Storybook is running — the component list and
52
+ live prop tables are the most accurate source there is.
53
+ 2. **The published Storybook** at <https://muka.kornmann.com> — component APIs,
54
+ Playgrounds, and usage patterns.
55
+ 3. **The installed types**:
56
+ ```bash
57
+ grep 'export' node_modules/@revikornmann/muka-ui/dist/types/components/index.d.ts
58
+ ```
59
+ 4. **`npx muka-ui components -v`** — names and prop summaries from the package
60
+ manifest.
61
+ 5. **`docs/muka-ui-guidelines.md`** in this repo, if `npx muka-ui init` has run.
62
+
63
+ Write out the mapping before you build, so the gaps are visible up front:
64
+
65
+ | Figma layer | Muka component | Props |
66
+ |---|---|---|
67
+ | "Primary CTA" | `Button` | `variant="primary" size="md"` |
68
+ | "Search field" | `Input` | `placeholder`, leading icon slot |
69
+ | "Nav bar" | `TopBar` | title, actions |
70
+
71
+ ## Step 3: Compose the screen
72
+
73
+ Build from the outside in: page shell, then regions, then controls.
74
+
75
+ - **Use Muka components for anything Muka has.** Reach for a raw element only
76
+ when nothing fits.
77
+ - **Compose, don't fork.** Prefer slots and composition over copying a
78
+ component's source to tweak it. A forked component stops receiving upgrades
79
+ and brand changes.
80
+ - **Only Muka's CSS custom properties for styling.** For app-specific layout,
81
+ read T3 semantic properties:
82
+ ```css
83
+ .checkout-summary {
84
+ background-color: var(--color-surface-level1);
85
+ color: var(--color-text-default-default);
86
+ padding: var(--spacing-4);
87
+ border-radius: var(--radius-md);
88
+ }
89
+ ```
90
+ Never paste a hex, px type size, or radius out of Figma. Those values belong to
91
+ one brand in one theme; the token survives both.
92
+ - **Match the design's intent, not its pixels.** If Figma shows 15px of padding
93
+ and the nearest token is 16, use the token. Report differences larger than a
94
+ step rather than hardcoding around them.
95
+ - **Mobile first.** Muka is designed mobile-up with the desktop breakpoint at
96
+ `1024px`. Build the narrow layout first, then enhance.
97
+
98
+ ## Step 4: Handle what Muka does not have
99
+
100
+ When a design needs something Muka does not ship, say so explicitly rather than
101
+ quietly inventing a lookalike — a bespoke component that mimics a Muka one is
102
+ the most expensive outcome, because it looks correct until the brand changes.
103
+
104
+ 1. Re-check whether composing existing primitives covers it.
105
+ 2. If not, build it in **this repo** (not in Muka) styled entirely with Muka's
106
+ token custom properties, so it tracks brand and theme.
107
+ 3. Tell the user it is app-local and worth requesting upstream at
108
+ <https://github.com/revikornmann/muka/issues> if it is generic.
109
+
110
+ ## Step 5: Verify
111
+
112
+ - The screen renders and type-checks.
113
+ - **No hardcoded colours, type sizes, or radii.** Grep your diff for `#`,
114
+ `rgb(`, and `px` and justify every remaining hit.
115
+ - Compare against `get_screenshot` for structure, hierarchy, and spacing rhythm.
116
+ - **Switch theme and confirm nothing breaks.** Load the dark stylesheet and
117
+ re-check: anything that does not react was hardcoded. This is the single most
118
+ effective check that the mapping was done properly.
119
+ - Keyboard reachability: every interactive element is tabbable with a visible
120
+ focus ring.
121
+ - If this repo has a custom brand, confirm the screen picks it up.
122
+
123
+ ## Next
124
+
125
+ - `/add-brand` — if the design is in a brand this repo does not have yet.
126
+ - `/pull-from-figma` — if a designer changed brand colours in Figma and the code
127
+ needs to catch up.
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: pull-from-figma
3
+ description: Pull brand colour and font changes a designer made in Figma back into this repo's brand token files.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Pull brand changes from Figma
8
+
9
+ A designer retuned your brand's colours in Figma; bring those edits back into
10
+ `brand/light.json`, `brand/dark.json`, and `brand/fonts.json`.
11
+
12
+ **Direction:** Figma → Code. **Scope:** only your brand's `alias/*` tokens.
13
+ Nothing is created or deleted — existing `$value` fields are updated in place.
14
+
15
+ ## Prerequisites
16
+
17
+ 1. This repo has a custom brand with modes pushed to Figma — run `/add-brand`
18
+ then `/push-to-figma` 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
+
25
+ ## Why only the alias layer
26
+
27
+ Your repo owns exactly one layer of the token system. Semantics and component
28
+ tokens come from the installed package and are overwritten on the next upgrade,
29
+ so pulling them in would produce changes that silently disappear. If a designer
30
+ changed `color/surface/level1` or a `button/*` variable, that is a **library**
31
+ change: it belongs in a pull request against
32
+ <https://github.com/revikornmann/muka>, and you should say so rather than writing
33
+ it into `brand/`.
34
+
35
+ ## Step 1: Confirm the bridge is live
36
+
37
+ ```
38
+ figma_get_status { probe: true }
39
+ ```
40
+
41
+ ## Step 2: Find your brand's modes
42
+
43
+ Look collections and modes up by name — never hardcode IDs:
44
+
45
+ ```javascript
46
+ const collections = await figma.variables.getLocalVariableCollectionsAsync();
47
+ const theme = collections.find((c) => c.name === 'Theme');
48
+ const light = theme.modes.find((m) => m.name === 'Acme Light').modeId;
49
+ const dark = theme.modes.find((m) => m.name === 'Acme Dark').modeId;
50
+ ```
51
+
52
+ ## Step 3: Read the alias variables
53
+
54
+ Read every variable whose name starts with `alias/`, taking the value for your
55
+ two modes only. Ignore all other modes — they are other brands.
56
+
57
+ ## Step 4: Resolve each value back to a token reference
58
+
59
+ - **Variable alias** (the normal case, pointing into Primitives): resolve the
60
+ target variable's name and convert slashes to dots.
61
+ `color/violet/9` → `{color.violet.9}`
62
+ - **Concrete string** (font family or weight): keep the string, converting
63
+ Figma's style names back to CSS numerics — `SemiBold` → `600`, `Bold` → `700`,
64
+ `Medium` → `500`, `Regular` → `400`.
65
+ - **Raw colour value**: this is a problem, not a value to pull. It means the
66
+ variable was detached from the ramp. Report it and re-push that token as an
67
+ alias instead of writing a hex into the token files, which would opt it out of
68
+ the system permanently.
69
+
70
+ ## Step 5: Map to files
71
+
72
+ | Variable prefix | Target file |
73
+ |---|---|
74
+ | `alias/color/*` read at your **light** mode | `brand/light.json` |
75
+ | `alias/color/*` read at your **dark** mode | `brand/dark.json` |
76
+ | `alias/font/*` | `brand/fonts.json` |
77
+
78
+ Figma slash-paths map to token dot-paths directly:
79
+ `alias/color/accent/default` → `alias.color.accent.default`.
80
+
81
+ ## Step 6: Apply the diff
82
+
83
+ For each token that already exists in the target file:
84
+
85
+ 1. Compare the current `$value` with the resolved Figma value.
86
+ 2. **Show the user the full diff before writing anything.**
87
+ 3. Update only `$value`. Never touch `$type`, and preserve the `$schema` entry
88
+ and key order so the diff stays reviewable.
89
+
90
+ Safety rules:
91
+
92
+ - Only tokens already present in the files are updated.
93
+ - No tokens are created or deleted. A variable in Figma with no counterpart in
94
+ the files is reported, not added — it usually means the designer added
95
+ something that needs a code-side decision first.
96
+ - A token missing from Figma is left alone, not blanked.
97
+
98
+ ## Step 7: Rebuild and check
99
+
100
+ ```bash
101
+ npm run build:tokens
102
+ ```
103
+
104
+ Then verify:
105
+
106
+ - No `undefined` in `styles/tokens-<brand>-*.css` — a failed reference resolution.
107
+ - Both light and dark still read correctly; a designer tuning light mode in
108
+ isolation is the usual way dark-mode contrast regresses.
109
+ - Muka components pick up the change in the app.
110
+ - The dark values reference `Dark` ramps (`{color.violetDark.9}`). If a dark
111
+ token came back pointing at a light ramp, the designer edited the wrong mode.
112
+
113
+ Commit `brand/**` with a message naming what the designer changed.
114
+
115
+ ## Troubleshooting
116
+
117
+ | Symptom | Cause |
118
+ |---|---|
119
+ | Values come back as raw colours | The variable was detached from the ramp — re-push it as an alias |
120
+ | Everything appears unchanged | Reading the wrong modes; confirm the mode names match your brand |
121
+ | Dark values reference light ramps | The wrong Figma mode was edited |
122
+ | Semibold and bold pull the same weight | They were pushed as numerics and collapsed in Figma |
123
+ | `figma-console` tools absent | Session predates `.mcp.json`; approve via `/mcp` and restart |