@primitiv-ui/tokens 0.1.28 → 0.1.30
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 +30 -2
- package/package.json +1 -1
- package/src/breakpoint.json +10 -0
- package/src/code-typography.test.ts +64 -0
- package/src/context.json +10501 -4489
- package/src/dark-mode-content.test.ts +168 -9
- package/src/dtcg.ts +1 -1
- package/src/heading-tracking.test.ts +88 -0
- package/src/intent.json +438 -22
- package/src/palette.json +534 -30
- package/src/primitives.json +8 -0
- package/src/swatch-sizing.test.ts +76 -0
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ and `dist/index.html` (UI). Stays running and rebuilds on change.
|
|
|
58
58
|
### 4. Load the plugin into Figma desktop (one-time per machine)
|
|
59
59
|
|
|
60
60
|
In Figma desktop: **Plugins → Development → Import plugin from
|
|
61
|
-
manifest
|
|
61
|
+
manifest...** and select
|
|
62
62
|
`apps/primitiv-sync-figma-plugin/manifest.json`. After this Figma
|
|
63
63
|
remembers it under **Plugins → Development → Primitiv Sync**.
|
|
64
64
|
|
|
@@ -110,7 +110,7 @@ backup "real".
|
|
|
110
110
|
## Conventions
|
|
111
111
|
|
|
112
112
|
- Slash-separated Figma variable names (`font-family/heading`) become
|
|
113
|
-
nested DTCG groups (`{ "font-family": { "heading": {
|
|
113
|
+
nested DTCG groups (`{ "font-family": { "heading": {...} } }`).
|
|
114
114
|
- Each token has `$type` and `$value`. Aliases (next cycle) will use
|
|
115
115
|
DTCG's `{group.sub.name}` reference string.
|
|
116
116
|
- Colours emit as hex: `#rrggbb` opaque, `#rrggbbaa` translucent.
|
|
@@ -119,3 +119,31 @@ backup "real".
|
|
|
119
119
|
key in that file (e.g. `palette.json` has `light` and `dark` keys).
|
|
120
120
|
- Single-mode collections (`Primitives`, `Interaction`) are read from
|
|
121
121
|
`defaultModeId` and emitted flat.
|
|
122
|
+
|
|
123
|
+
## Dark mode: this repo and Figma encode it *oppositely*
|
|
124
|
+
|
|
125
|
+
Both are correct. They just put the inversion in different places, and knowing
|
|
126
|
+
which is which is the difference between reading a real bug and inventing one.
|
|
127
|
+
|
|
128
|
+
| | Where the flip lives | `content/primary` |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| **This repo** (`intent.json` + `palette.json`) | the **palette** flips per theme (`color.neutral.900` is `#121418` under `light`, `#e5ecf6` under `dark`) | `{color.neutral.900}` in **both** blocks |
|
|
131
|
+
| **Figma** | the **semantic alias** flips; `Primitives / Palette` stays pinned to **Light** on dark frames | `neutral/900` in Light, `neutral/50` in Dark |
|
|
132
|
+
|
|
133
|
+
So in `intent.json` a semantic token usually names the **same ramp step in both
|
|
134
|
+
blocks** and lets the inverted dark palette do the work; in Figma the palette is
|
|
135
|
+
held still and the alias moves. Emitted CSS follows the repo model — the
|
|
136
|
+
`[data-theme="dark"]` block re-points `--primitiv-color-neutral-*` itself, so
|
|
137
|
+
every semantic token above it can keep one step.
|
|
138
|
+
|
|
139
|
+
> **Don't diff the two by ramp index.** Doing that reports ~24 bogus mismatches
|
|
140
|
+
> across nearly all of Dark mode (hit 2026-07-28 during the RFC 0022/0023 Figma
|
|
141
|
+
> cross-check). Resolve each side through its own model first: **13 of 23** dark
|
|
142
|
+
> tokens are then byte-identical, and the remaining 10 differ by only 3–9/255
|
|
143
|
+
> because harmoni *generates* the dark ramp instead of literally reversing it, so
|
|
144
|
+
> only the endpoints (`50`↔`900`) mirror exactly. Reviewed and accepted as-is —
|
|
145
|
+
> those interior dark values aren't expressible as a light-palette alias, so
|
|
146
|
+
> there is nothing better for Figma to point at, and retargeting its dark aliases
|
|
147
|
+
> to this repo's steps would resolve them through Palette/Light and turn dark
|
|
148
|
+
> greys pale. The Figma side of this is in the `figma-variable-architecture`
|
|
149
|
+
> skill.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primitiv-ui/tokens",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "The DTCG-conformant design-token layer for the Primitiv ecosystem, plus helpers that transform Figma variable exports into DTCG JSON.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"breakpoint": {
|
|
3
|
+
"xs": { "$type": "number", "$value": 360 },
|
|
4
|
+
"sm": { "$type": "number", "$value": 640 },
|
|
5
|
+
"md": { "$type": "number", "$value": 768 },
|
|
6
|
+
"lg": { "$type": "number", "$value": 1024 },
|
|
7
|
+
"xl": { "$type": "number", "$value": 1280 },
|
|
8
|
+
"2xl": { "$type": "number", "$value": 1536 }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import context from './context.json'
|
|
2
|
+
import primitives from './primitives.json'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* `code.*` is the mono typographic family — the one every colour value, hex,
|
|
6
|
+
* ratio and percentage in Harmoni is set in.
|
|
7
|
+
*
|
|
8
|
+
* Why this needs a test: the family was authored size-first, so it shipped with
|
|
9
|
+
* `font-size` and `line-height` and nothing else. That is invisible in the JSON
|
|
10
|
+
* (a group with two members looks intentional) but it is not a usable family —
|
|
11
|
+
* a Figma text style binds four members, so `code.*` could not be expressed as
|
|
12
|
+
* a text style at all, and `font-family.mono` sat in the primitives with
|
|
13
|
+
* nothing consuming it. This asserts the family is complete *and* that it is
|
|
14
|
+
* the mono one, which is the whole reason it exists apart from `body`.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
type Mode = 'dense' | 'compact' | 'comfortable' | 'spacious'
|
|
18
|
+
type DtcgNode = { $type?: string; $value?: unknown; [key: string]: unknown }
|
|
19
|
+
|
|
20
|
+
const MODES: Mode[] = ['dense', 'compact', 'comfortable', 'spacious']
|
|
21
|
+
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl'] as const
|
|
22
|
+
|
|
23
|
+
/** The members a Figma text style binds, plus the weight the CSS layer needs. */
|
|
24
|
+
const MEMBERS = {
|
|
25
|
+
'font-family': 'string',
|
|
26
|
+
'font-weight': 'number',
|
|
27
|
+
'font-size': 'number',
|
|
28
|
+
'line-height': 'number',
|
|
29
|
+
'font-style': 'string',
|
|
30
|
+
} as const
|
|
31
|
+
|
|
32
|
+
function codeSize(mode: Mode, size: string): DtcgNode {
|
|
33
|
+
const code = (context as Record<Mode, DtcgNode>)[mode].code as DtcgNode
|
|
34
|
+
return code[size] as DtcgNode
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Resolve a DTCG alias (`{font-family.mono}`) against the primitive tree. */
|
|
38
|
+
function resolvePrimitive(ref: string): unknown {
|
|
39
|
+
const path = ref.slice(1, -1).split('.')
|
|
40
|
+
let node: DtcgNode = primitives as DtcgNode
|
|
41
|
+
for (const key of path) node = node[key] as DtcgNode
|
|
42
|
+
return node.$value
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe('code typography', () => {
|
|
46
|
+
for (const mode of MODES) {
|
|
47
|
+
for (const size of SIZES) {
|
|
48
|
+
it(`${mode} / code / ${size} is a complete typographic family`, () => {
|
|
49
|
+
const entry = codeSize(mode, size)
|
|
50
|
+
for (const [member, type] of Object.entries(MEMBERS)) {
|
|
51
|
+
const node = entry[member] as DtcgNode | undefined
|
|
52
|
+
expect(node, `${mode}.code.${size}.${member} is missing`).toBeDefined()
|
|
53
|
+
expect(node?.$type).toBe(type)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it(`${mode} / code / ${size} is set in the mono face`, () => {
|
|
58
|
+
const family = codeSize(mode, size)['font-family'] as DtcgNode | undefined
|
|
59
|
+
expect(family?.$value).toBe('{font-family.mono}')
|
|
60
|
+
expect(resolvePrimitive(family?.$value as string)).toBe('JetBrains Mono')
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})
|