@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.
- package/README.md +81 -60
- package/cli/bin/muka-ui.js +12 -5
- package/cli/commands/brand.js +33 -14
- package/cli/commands/init.js +21 -17
- package/cli/commands/install-skill.js +84 -26
- package/cli/templates/AGENTS.md +128 -0
- package/cli/templates/CLAUDE.md +8 -88
- package/cli/templates/muka-ui-guidelines.md +50 -14
- package/dist/cjs/components/ActionSheet/ActionSheet.js +1 -1
- package/dist/cjs/components/Breadcrumb/Breadcrumb.css +11 -3
- package/dist/cjs/components/Breadcrumb/Breadcrumb.js +19 -1
- package/dist/cjs/components/Breadcrumb/Breadcrumb.js.map +1 -1
- package/dist/cjs/components/Combobox/Combobox.css +116 -132
- package/dist/cjs/components/Combobox/Combobox.js +175 -52
- package/dist/cjs/components/Combobox/Combobox.js.map +1 -1
- package/dist/cjs/components/Combobox/index.js.map +1 -1
- package/dist/cjs/components/ContextSelect/ContextSelect.css +117 -0
- package/dist/cjs/components/ContextSelect/ContextSelect.js +49 -0
- package/dist/cjs/components/ContextSelect/ContextSelect.js.map +1 -0
- package/dist/cjs/components/ContextSelect/index.js +11 -0
- package/dist/cjs/components/ContextSelect/index.js.map +1 -0
- package/dist/cjs/components/DropdownSelect/DropdownSelect.css +225 -0
- package/dist/cjs/components/DropdownSelect/DropdownSelect.js +106 -0
- package/dist/cjs/components/DropdownSelect/DropdownSelect.js.map +1 -0
- package/dist/cjs/components/DropdownSelect/index.js +6 -0
- package/dist/cjs/components/DropdownSelect/index.js.map +1 -0
- package/dist/cjs/components/Icon/custom/ArrowDropUpDownIcon.js +11 -0
- package/dist/cjs/components/Icon/custom/ArrowDropUpDownIcon.js.map +1 -0
- package/dist/cjs/components/Icon/custom/ArrowReturnIcon.js +11 -0
- package/dist/cjs/components/Icon/custom/ArrowReturnIcon.js.map +1 -0
- package/dist/cjs/components/Icon/iconRegistry.js +11 -0
- package/dist/cjs/components/Icon/iconRegistry.js.map +1 -1
- package/dist/cjs/components/Input/Input.css +45 -8
- package/dist/cjs/components/Input/Input.js +14 -3
- package/dist/cjs/components/Input/Input.js.map +1 -1
- package/dist/cjs/components/Menu/Menu.css +213 -17
- package/dist/cjs/components/Menu/Menu.js +429 -39
- package/dist/cjs/components/Menu/Menu.js.map +1 -1
- package/dist/cjs/components/Menu/index.js +5 -1
- package/dist/cjs/components/Menu/index.js.map +1 -1
- package/dist/cjs/components/ProgressTracker/ProgressTracker.css +273 -22
- package/dist/cjs/components/ProgressTracker/ProgressTracker.js +109 -9
- package/dist/cjs/components/ProgressTracker/ProgressTracker.js.map +1 -1
- package/dist/cjs/components/Scrollbar/Scrollbar.css +124 -0
- package/dist/cjs/components/Scrollbar/Scrollbar.js +118 -0
- package/dist/cjs/components/Scrollbar/Scrollbar.js.map +1 -0
- package/dist/cjs/components/Scrollbar/index.js +11 -0
- package/dist/cjs/components/Scrollbar/index.js.map +1 -0
- package/dist/cjs/components/index.js +13 -16
- package/dist/cjs/components/index.js.map +1 -1
- package/dist/esm/components/ActionSheet/ActionSheet.js +1 -1
- package/dist/esm/components/Breadcrumb/Breadcrumb.css +11 -3
- package/dist/esm/components/Breadcrumb/Breadcrumb.js +19 -1
- package/dist/esm/components/Breadcrumb/Breadcrumb.js.map +1 -1
- package/dist/esm/components/Combobox/Combobox.css +116 -132
- package/dist/esm/components/Combobox/Combobox.js +177 -54
- package/dist/esm/components/Combobox/Combobox.js.map +1 -1
- package/dist/esm/components/Combobox/index.js +1 -1
- package/dist/esm/components/Combobox/index.js.map +1 -1
- package/dist/esm/components/ContextSelect/ContextSelect.css +117 -0
- package/dist/esm/components/ContextSelect/ContextSelect.js +45 -0
- package/dist/esm/components/ContextSelect/ContextSelect.js.map +1 -0
- package/dist/esm/components/ContextSelect/index.js +3 -0
- package/dist/esm/components/ContextSelect/index.js.map +1 -0
- package/dist/esm/components/DropdownSelect/DropdownSelect.css +225 -0
- package/dist/esm/components/DropdownSelect/DropdownSelect.js +102 -0
- package/dist/esm/components/DropdownSelect/DropdownSelect.js.map +1 -0
- package/dist/esm/components/DropdownSelect/index.js +2 -0
- package/dist/esm/components/DropdownSelect/index.js.map +1 -0
- package/dist/esm/components/Icon/custom/ArrowDropUpDownIcon.js +7 -0
- package/dist/esm/components/Icon/custom/ArrowDropUpDownIcon.js.map +1 -0
- package/dist/esm/components/Icon/custom/ArrowReturnIcon.js +7 -0
- package/dist/esm/components/Icon/custom/ArrowReturnIcon.js.map +1 -0
- package/dist/esm/components/Icon/iconRegistry.js +11 -0
- package/dist/esm/components/Icon/iconRegistry.js.map +1 -1
- package/dist/esm/components/Input/Input.css +45 -8
- package/dist/esm/components/Input/Input.js +14 -3
- package/dist/esm/components/Input/Input.js.map +1 -1
- package/dist/esm/components/Menu/Menu.css +213 -17
- package/dist/esm/components/Menu/Menu.js +428 -40
- package/dist/esm/components/Menu/Menu.js.map +1 -1
- package/dist/esm/components/Menu/index.js +1 -1
- package/dist/esm/components/Menu/index.js.map +1 -1
- package/dist/esm/components/ProgressTracker/ProgressTracker.css +273 -22
- package/dist/esm/components/ProgressTracker/ProgressTracker.js +110 -10
- package/dist/esm/components/ProgressTracker/ProgressTracker.js.map +1 -1
- package/dist/esm/components/Scrollbar/Scrollbar.css +124 -0
- package/dist/esm/components/Scrollbar/Scrollbar.js +114 -0
- package/dist/esm/components/Scrollbar/Scrollbar.js.map +1 -0
- package/dist/esm/components/Scrollbar/index.js +3 -0
- package/dist/esm/components/Scrollbar/index.js.map +1 -0
- package/dist/esm/components/index.js +4 -2
- package/dist/esm/components/index.js.map +1 -1
- package/dist/styles/components/Breadcrumb.css +11 -3
- package/dist/styles/components/Combobox.css +116 -132
- package/dist/styles/components/ContextSelect.css +117 -0
- package/dist/styles/components/DropdownSelect.css +225 -0
- package/dist/styles/components/Input.css +45 -8
- package/dist/styles/components/Menu.css +213 -17
- package/dist/styles/components/ProgressTracker.css +273 -22
- package/dist/styles/components/Scrollbar.css +124 -0
- package/dist/styles/components/Typography.css +89 -0
- package/dist/styles/index.css +1526 -480
- package/dist/styles/muka-dark.css +1526 -480
- package/dist/styles/muka-light.css +1526 -480
- package/dist/styles/tokens-bouwplan-dark.css +9 -6
- package/dist/styles/tokens-bouwplan-light.css +9 -6
- package/dist/styles/tokens-fscl-dark.css +29 -26
- package/dist/styles/tokens-fscl-light.css +29 -26
- package/dist/styles/tokens-grip-dark.css +8 -5
- package/dist/styles/tokens-grip-light.css +8 -5
- package/dist/styles/tokens-muka-dark.css +8 -5
- package/dist/styles/tokens-muka-light.css +8 -5
- package/dist/styles/tokens-wireframe-dark.css +53 -50
- package/dist/styles/tokens-wireframe-light.css +53 -50
- package/dist/styles/wireframe-dark.css +1571 -525
- package/dist/styles/wireframe-light.css +1571 -525
- package/dist/types/components/ActionSheet/ActionSheet.d.ts +1 -1
- package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +27 -3
- package/dist/types/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
- package/dist/types/components/Combobox/Combobox.d.ts +38 -38
- package/dist/types/components/Combobox/Combobox.d.ts.map +1 -1
- package/dist/types/components/Combobox/index.d.ts +1 -1
- package/dist/types/components/Combobox/index.d.ts.map +1 -1
- package/dist/types/components/ContextSelect/ContextSelect.d.ts +62 -0
- package/dist/types/components/ContextSelect/ContextSelect.d.ts.map +1 -0
- package/dist/types/components/ContextSelect/index.d.ts +3 -0
- package/dist/types/components/ContextSelect/index.d.ts.map +1 -0
- package/dist/types/components/DataTable/DataTable.d.ts +1 -1
- package/dist/types/components/DropdownSelect/DropdownSelect.d.ts +71 -0
- package/dist/types/components/DropdownSelect/DropdownSelect.d.ts.map +1 -0
- package/dist/types/components/DropdownSelect/index.d.ts +2 -0
- package/dist/types/components/DropdownSelect/index.d.ts.map +1 -0
- package/dist/types/components/Icon/custom/ArrowDropUpDownIcon.d.ts +9 -0
- package/dist/types/components/Icon/custom/ArrowDropUpDownIcon.d.ts.map +1 -0
- package/dist/types/components/Icon/custom/ArrowReturnIcon.d.ts +9 -0
- package/dist/types/components/Icon/custom/ArrowReturnIcon.d.ts.map +1 -0
- package/dist/types/components/Icon/iconRegistry.d.ts.map +1 -1
- package/dist/types/components/Input/Input.d.ts +5 -0
- package/dist/types/components/Input/Input.d.ts.map +1 -1
- package/dist/types/components/Menu/Menu.d.ts +99 -29
- package/dist/types/components/Menu/Menu.d.ts.map +1 -1
- package/dist/types/components/Menu/index.d.ts +2 -2
- package/dist/types/components/Menu/index.d.ts.map +1 -1
- package/dist/types/components/ProgressTracker/ProgressTracker.d.ts +29 -8
- package/dist/types/components/ProgressTracker/ProgressTracker.d.ts.map +1 -1
- package/dist/types/components/Scrollbar/Scrollbar.d.ts +19 -0
- package/dist/types/components/Scrollbar/Scrollbar.d.ts.map +1 -0
- package/dist/types/components/Scrollbar/index.d.ts +3 -0
- package/dist/types/components/Scrollbar/index.d.ts.map +1 -0
- package/dist/types/components/index.d.ts +5 -4
- package/dist/types/components/index.d.ts.map +1 -1
- package/docs/consumers/README.md +116 -37
- package/docs/consumers/brand.md +261 -0
- package/docs/consumers/figma-console-mcp.md +116 -0
- package/docs/consumers/setup-instructions.md +98 -12
- package/docs/consumers/skills.md +79 -0
- package/package.json +4 -6
- package/scripts/postinstall-nudge.js +8 -4
- package/skills/add-brand/SKILL.md +204 -0
- package/skills/add-brand/reference.md +160 -0
- package/skills/figma-to-code/SKILL.md +127 -0
- package/skills/pull-from-figma/SKILL.md +123 -0
- package/skills/push-to-figma/SKILL.md +172 -0
- package/skills/setup-muka/SKILL.md +73 -13
- package/tokens/README.md +40 -18
- package/tokens/t2-alias/brand/bouwplan/fonts.json +1 -1
- package/tokens/t2-alias/brand/fscl/fonts.json +3 -3
- package/tokens/t2-alias/brand/wireframe/fonts.json +2 -2
- package/tokens/t4-components/menu.json +20 -5
- package/dist/cjs/components/ContextMenu/ContextMenu.js +0 -186
- package/dist/cjs/components/ContextMenu/ContextMenu.js.map +0 -1
- package/dist/cjs/components/ContextMenu/index.js +0 -17
- package/dist/cjs/components/ContextMenu/index.js.map +0 -1
- package/dist/esm/components/ContextMenu/ContextMenu.js +0 -145
- package/dist/esm/components/ContextMenu/ContextMenu.js.map +0 -1
- package/dist/esm/components/ContextMenu/index.js +0 -2
- package/dist/esm/components/ContextMenu/index.js.map +0 -1
- package/dist/types/components/ContextMenu/ContextMenu.d.ts +0 -188
- package/dist/types/components/ContextMenu/ContextMenu.d.ts.map +0 -1
- package/dist/types/components/ContextMenu/index.d.ts +0 -3
- 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:
|
|
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`
|
|
12
|
-
|
|
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**.
|
|
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
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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/ #
|
|
12
|
-
│ │ ├── wireframe/
|
|
13
|
-
│ │
|
|
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
|
-
│
|
|
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/ #
|
|
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 (
|
|
48
|
-
6. Theme overrides (light.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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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:
|
|
95
|
-
- Design Tokens in Storybook: stories/tokens/*.mdx (
|
|
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": "
|
|
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": "
|
|
22
|
-
"semibold": { "$type": "fontWeights", "$value": "
|
|
23
|
-
"bold": { "$type": "fontWeights", "$value": "
|
|
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": "
|
|
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": "
|
|
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.
|
|
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": "
|
|
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.
|
|
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.
|
|
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.
|
|
158
|
+
"$value": "{spacing.3}"
|
|
144
159
|
}
|
|
145
160
|
},
|
|
146
161
|
"gap": {
|