@oneli8/tokens 1.0.0-beta.2 → 1.0.0-beta.4
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/AGENTS.md +12 -0
- package/FONTS-LICENSE.txt +113 -0
- package/PRINCIPLES.md +48 -0
- package/README.md +75 -69
- package/ai-context.json +53 -0
- package/build/css/tokens.css +294 -288
- package/build/figma/variables.json +63 -0
- package/build/manifest.json +4 -4
- package/build/tailwind/preset.mjs +42 -41
- package/build/tailwind/theme.css +41 -40
- package/build/typescript/index.d.ts +1 -1
- package/build/typescript/index.js +57 -0
- package/fonts/ar-one-sans-latin-ext.woff2 +0 -0
- package/fonts/ar-one-sans-latin.woff2 +0 -0
- package/fonts/ar-one-sans-vietnamese.woff2 +0 -0
- package/fonts/syne-greek.woff2 +0 -0
- package/fonts/syne-latin-ext.woff2 +0 -0
- package/fonts/syne-latin.woff2 +0 -0
- package/fonts.css +63 -0
- package/overrides.example.css +53 -0
- package/package.json +56 -16
- package/scripts/build.mjs +60 -5
- package/scripts/test.mjs +5 -5
- package/src/component.json +904 -155
- package/src/figma-code-syntax.txt +551 -0
- package/src/primitive.json +1057 -163
- package/src/semantic.json +825 -126
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/* OneLi8 override surface.
|
|
2
|
+
Copy what you need into your own stylesheet, after importing the tokens.
|
|
3
|
+
Nothing here is required. Every value below is already set by the system;
|
|
4
|
+
these are the supported places to change it. */
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
/* ---- Type ------------------------------------------------------------
|
|
8
|
+
Two brand faces and one shared fallback. Replace a face and the fallback
|
|
9
|
+
still applies, because the fallback is referenced rather than inlined.
|
|
10
|
+
Drop the @oneli8/tokens/fonts.css import if you are supplying your own. */
|
|
11
|
+
|
|
12
|
+
/* --ol8-font-family-display: "Your Display Face", var(--ol8-font-family-fallback); */
|
|
13
|
+
/* --ol8-font-family-functional: "Your Text Face", var(--ol8-font-family-fallback); */
|
|
14
|
+
/* --ol8-font-family-fallback: ui-sans-serif, system-ui, sans-serif; */
|
|
15
|
+
|
|
16
|
+
/* ---- Colour ----------------------------------------------------------
|
|
17
|
+
data-ol8-primary switches between the three built families. For any other
|
|
18
|
+
hue, repoint the eleven primary steps here instead. 47 semantic roles read
|
|
19
|
+
this ramp, so buttons, links, focus, selection and the Gem material all
|
|
20
|
+
follow without touching a component.
|
|
21
|
+
|
|
22
|
+
Keep the tonal relationships: 030 lightest through 930 darkest, with 600
|
|
23
|
+
carrying the default action surface and 120 doing the same job in dark. */
|
|
24
|
+
|
|
25
|
+
/* --ol8-color-primary-030: #f6f2ff; */
|
|
26
|
+
/* --ol8-color-primary-060: #ece4ff; */
|
|
27
|
+
/* --ol8-color-primary-090: #d9c9ff; */
|
|
28
|
+
/* --ol8-color-primary-120: #c0a6ff; */
|
|
29
|
+
/* --ol8-color-primary-240: #a077f2; */
|
|
30
|
+
/* --ol8-color-primary-360: #8250d6; */
|
|
31
|
+
/* --ol8-color-primary-480: #6838b2; */
|
|
32
|
+
/* --ol8-color-primary-600: #542e8e; */
|
|
33
|
+
/* --ol8-color-primary-720: #452771; */
|
|
34
|
+
/* --ol8-color-primary-840: #38225b; */
|
|
35
|
+
/* --ol8-color-primary-930: #221238; */
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Override at :root, not on a subtree.
|
|
39
|
+
A role such as --ol8-color-action-primary-default is declared as
|
|
40
|
+
var(--ol8-color-primary-600) on :root, so CSS substitutes it there and
|
|
41
|
+
descendants inherit an already resolved colour. Re-pointing the ramp deeper
|
|
42
|
+
in the tree changes the ramp but not the roles that read it.
|
|
43
|
+
|
|
44
|
+
Verified: overriding --ol8-color-primary-600 on :root repaints the primary
|
|
45
|
+
action surface. The same override on a wrapper element does nothing.
|
|
46
|
+
|
|
47
|
+
To theme one region, re-declare the roles you want on that region:
|
|
48
|
+
|
|
49
|
+
.marketing-section {
|
|
50
|
+
--ol8-color-action-primary-default: #0f766e;
|
|
51
|
+
--ol8-color-action-primary-hover: #115e59;
|
|
52
|
+
}
|
|
53
|
+
*/
|
package/package.json
CHANGED
|
@@ -1,30 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oneli8/tokens",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
|
+
"description": "OneLi8 Design System \u2014 design tokens compiled from Figma. CSS custom properties and a flat JS object.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"design-tokens",
|
|
7
|
+
"css-variables",
|
|
8
|
+
"design-system",
|
|
9
|
+
"figma",
|
|
10
|
+
"oneli8"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/Akshay007ad/OneLi8-Design-System#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Akshay007ad/OneLi8-Design-System/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/Akshay007ad/OneLi8-Design-System.git",
|
|
19
|
+
"directory": "packages/tokens"
|
|
20
|
+
},
|
|
6
21
|
"license": "Apache-2.0",
|
|
22
|
+
"author": "Akshay Dhore",
|
|
7
23
|
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./build/typescript/index.d.ts",
|
|
27
|
+
"import": "./build/typescript/index.js",
|
|
28
|
+
"default": "./build/typescript/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./css": "./build/css/tokens.css",
|
|
31
|
+
"./fonts.css": "./fonts.css",
|
|
32
|
+
"./fonts/*": "./fonts/*",
|
|
33
|
+
"./overrides.example.css": "./overrides.example.css",
|
|
34
|
+
"./tailwind": "./build/tailwind/preset.mjs",
|
|
35
|
+
"./tailwind.css": "./build/tailwind/theme.css",
|
|
36
|
+
"./figma": "./build/figma/variables.json",
|
|
37
|
+
"./status": "./build/status/components.json",
|
|
38
|
+
"./source": "./src/semantic.json",
|
|
39
|
+
"./package.json": "./package.json",
|
|
40
|
+
"./ai-context.json": "./ai-context.json"
|
|
41
|
+
},
|
|
42
|
+
"types": "./build/typescript/index.d.ts",
|
|
43
|
+
"sideEffects": [
|
|
44
|
+
"./build/css/tokens.css",
|
|
45
|
+
"./fonts.css"
|
|
46
|
+
],
|
|
8
47
|
"files": [
|
|
9
48
|
"build",
|
|
10
|
-
"schema",
|
|
11
|
-
"scripts",
|
|
12
49
|
"src",
|
|
13
50
|
"status",
|
|
51
|
+
"schema",
|
|
52
|
+
"scripts",
|
|
53
|
+
"fonts",
|
|
54
|
+
"fonts.css",
|
|
55
|
+
"overrides.example.css",
|
|
56
|
+
"ai-context.json",
|
|
57
|
+
"PRINCIPLES.md",
|
|
58
|
+
"AGENTS.md",
|
|
14
59
|
"README.md",
|
|
15
60
|
"LICENSE",
|
|
16
|
-
"NOTICE"
|
|
61
|
+
"NOTICE",
|
|
62
|
+
"FONTS-LICENSE.txt"
|
|
17
63
|
],
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"test": "node scripts/test.mjs --standalone"
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=20"
|
|
21
66
|
},
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"./css": "./build/css/tokens.css",
|
|
25
|
-
"./tailwind": "./build/tailwind/preset.mjs",
|
|
26
|
-
"./tailwind.css": "./build/tailwind/theme.css",
|
|
27
|
-
"./figma": "./build/figma/variables.json",
|
|
28
|
-
"./status": "./build/status/components.json"
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public"
|
|
29
69
|
}
|
|
30
70
|
}
|
package/scripts/build.mjs
CHANGED
|
@@ -86,8 +86,33 @@ for(const axis of ["colorScheme","primaryFamily","typographyViewport"]){
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// Figma is the naming authority. Its variables run compound words together
|
|
90
|
+
// (cutedge, innerwidth, actionprimary), so a camelCase source key must NOT be
|
|
91
|
+
// split on the capital. Only the path separator becomes a hyphen. The authored
|
|
92
|
+
// JSON keeps camelCase for readability; the emitted name matches Figma exactly.
|
|
93
|
+
const kebab=value=>value.replace(/\./g,"-").toLowerCase();
|
|
94
|
+
// Figma is the naming authority, and it says so explicitly: every variable
|
|
95
|
+
// carries a codeSyntax.WEB field. src/figma-code-syntax.txt records that field
|
|
96
|
+
// verbatim. Nothing here infers a name, because Figma is not internally regular
|
|
97
|
+
// enough to infer from — it writes color-icon-button-quiet-hover-surface
|
|
98
|
+
// hyphenated and component-iconbutton-box-compact joined.
|
|
99
|
+
const CONTRACT=new Map(
|
|
100
|
+
fs.readFileSync(path.join(root,"src/figma-code-syntax.txt"),"utf8")
|
|
101
|
+
.split("\n").filter(l=>l && !l.startsWith("#"))
|
|
102
|
+
.map(l=>{const i=l.indexOf("|");return [l.slice(0,i), l.slice(i+1)];})
|
|
103
|
+
);
|
|
104
|
+
const uncontracted=new Set();
|
|
105
|
+
const figmaVarName=tokenPath=>{
|
|
106
|
+
const contracted=CONTRACT.get(tokenPath);
|
|
107
|
+
if(contracted)return contracted.replace(/^--ol8-/,"");
|
|
108
|
+
uncontracted.add(tokenPath);
|
|
109
|
+
return kebab(tokenPath);
|
|
110
|
+
};
|
|
111
|
+
const cssName=tokenPath=>`--ol8-${figmaVarName(tokenPath)}`;
|
|
112
|
+
// A composite role's sub key is a CSS property, not a Figma variable, so it
|
|
113
|
+
// keeps the hyphen: font-weight, line-height, letter-spacing.
|
|
114
|
+
const cssProperty=key=>key.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();
|
|
115
|
+
const cssCompositeName=(tokenPath,key)=>`--ol8-${figmaVarName(tokenPath)}-${cssProperty(key)}`;
|
|
91
116
|
const unitValue=value=>value&&typeof value==="object"&&typeof value.value==="number"&&value.unit?`${value.value}${value.unit}`:null;
|
|
92
117
|
const hexRgb=value=>{
|
|
93
118
|
const match=typeof value==="string"&&value.match(/^#([0-9a-f]{6})$/i);
|
|
@@ -110,6 +135,15 @@ const shadowValue=value=>{
|
|
|
110
135
|
};
|
|
111
136
|
const cssScalar=(value,token)=>{
|
|
112
137
|
const unit=unitValue(value);if(unit)return unit;
|
|
138
|
+
// A bare family name is not usable CSS on its own. Quote anything with a
|
|
139
|
+
// space, and resolve brand faces through the shared fallback so a face that
|
|
140
|
+
// is missing, still loading, or deliberately replaced lands somewhere sane.
|
|
141
|
+
if(token?.$type==="fontFamily"){
|
|
142
|
+
const name=String(value);
|
|
143
|
+
const quoted=/[^A-Za-z0-9_-]/.test(name)?`"${name}"`:name;
|
|
144
|
+
return token?.$extensions?.["oneli8.role"]==="brand-face"
|
|
145
|
+
?`${quoted}, var(--ol8-font-family-fallback)`:name;
|
|
146
|
+
}
|
|
113
147
|
if(token?.$extensions?.["oneli8.unit"]==="em")return `${value}em`;
|
|
114
148
|
if(token?.$type==="color"){const color=alphaColor(value);if(color)return color}
|
|
115
149
|
if(token?.$type==="shadow"){const shadow=shadowValue(value);if(shadow!==null)return shadow}
|
|
@@ -126,8 +160,14 @@ const cssLinesFor=(tokenPath,token,{preserveColorReference=false}={})=>{
|
|
|
126
160
|
const scalar=cssScalar(value,token);
|
|
127
161
|
if(scalar!==null)return [` ${cssName(tokenPath)}: ${scalar};`];
|
|
128
162
|
if(value&&typeof value==="object")return Object.entries(value).flatMap(([key,item])=>{
|
|
129
|
-
const
|
|
130
|
-
|
|
163
|
+
const rawReference=String(token.$value?.[key]||"").match(referencePattern);
|
|
164
|
+
// Keep the family as a reference rather than baking the resolved name.
|
|
165
|
+
// A composite role that inlines "AR One Sans" cannot follow a replaced
|
|
166
|
+
// brand face; one that points at the family token can.
|
|
167
|
+
if(preserveColorReference&&rawReference&&rawReference[1].startsWith("font.family."))
|
|
168
|
+
return [` ${cssCompositeName(tokenPath,key)}: var(${cssName(rawReference[1])});`];
|
|
169
|
+
const itemScalar=cssScalar(item,all[(rawReference||[])[1]]||token);
|
|
170
|
+
return itemScalar===null?[]:[` ${cssCompositeName(tokenPath,key)}: ${itemScalar};`];
|
|
131
171
|
});
|
|
132
172
|
return [];
|
|
133
173
|
};
|
|
@@ -161,7 +201,11 @@ const duration=Object.fromEntries(Object.entries(byTier.primitive).filter(([p])=
|
|
|
161
201
|
const easing=Object.fromEntries(Object.entries(byTier.primitive).filter(([p])=>p.startsWith("motion.easing.")).map(([p,t])=>[p.split(".").at(-1),cssScalar(resolveValue(t.$value),t)]));
|
|
162
202
|
const boxShadow=Object.fromEntries(Object.keys(byTier.semantic).filter(p=>p.startsWith("elevation.surface.")).map(p=>[p.split(".").at(-1),`var(${cssName(p)})`]));
|
|
163
203
|
const zIndex=Object.fromEntries(Object.entries(byTier.semantic).filter(([p])=>p.startsWith("layer.stack.")).map(([p,t])=>[p.split(".").at(-1),String(resolveValue(t.$value))]));
|
|
164
|
-
|
|
204
|
+
// A Tailwind utility is something a person types, so it stays readable and
|
|
205
|
+
// hyphenated: bg-ol8-action-primary-default. The variable it points at follows
|
|
206
|
+
// Figma. Readable surface, exact underlying name.
|
|
207
|
+
const utilityName=path=>path.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/\./g,"-").toLowerCase();
|
|
208
|
+
const semanticColors=Object.fromEntries(Object.keys(byTier.semantic).filter(p=>p.startsWith("color.")&&!p.startsWith("color.primary.")).map(p=>[utilityName(p.slice(6)),`var(${cssName(p)})`]));
|
|
165
209
|
const tailwind={theme:{extend:{colors:{ol8:semanticColors},spacing:dimensionScale,borderRadius:radii,borderWidth,boxShadow,zIndex,fontFamily:{display:["Syne","system-ui","sans-serif"],functional:["AR One Sans","system-ui","sans-serif"]},fontWeight:{regular:"450",semibold:"600",bold:"690"},fontSize,transitionDuration:duration,transitionTimingFunction:easing}}};
|
|
166
210
|
|
|
167
211
|
const title=value=>value.replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/^./,char=>char.toUpperCase());
|
|
@@ -188,4 +232,15 @@ write("build/figma/variables.json",`${JSON.stringify(figma,null,2)}\n`);
|
|
|
188
232
|
write("build/status/components.json",`${JSON.stringify({...status,$schema:"https://oneli8.org/schema/component-status.schema.json",generated:true},null,2)}\n`);
|
|
189
233
|
write("build/manifest.json",`${JSON.stringify({generated:true,version:"1.0.0-beta.1",counts:{primitive:Object.keys(byTier.primitive).length,semantic:Object.keys(byTier.semantic).length,component:Object.keys(byTier.component).length,componentStatuses:status.components.length,figmaVariables:figma.collections.primitive.length+figma.collections.semantic.length+figma.collections.component.length,figmaTypographyStyles:figma.typographyStyles.length,figmaElevationStyles:figma.elevationStyles.length},outputs:["css/tokens.css","typescript/index.js","typescript/index.d.ts","tailwind/preset.mjs","tailwind/theme.css","figma/variables.json","status/components.json"]},null,2)}\n`);
|
|
190
234
|
|
|
235
|
+
// Drift report, both directions. A token with no contract entry means the name
|
|
236
|
+
// was invented here rather than agreed in Figma. A contract entry with no token
|
|
237
|
+
// means Figma is ahead, which is a reference for what to build next.
|
|
238
|
+
const contractPaths=new Set(CONTRACT.keys());
|
|
239
|
+
const authoredPaths=new Set(Object.keys(all));
|
|
240
|
+
const figmaAhead=[...contractPaths].filter(p=>!authoredPaths.has(p));
|
|
241
|
+
if(uncontracted.size>0){
|
|
242
|
+
console.log(`\u26a0 ${uncontracted.size} token(s) have no Figma codeSyntax; their names are inferred, not agreed:`);
|
|
243
|
+
for(const p of [...uncontracted].sort().slice(0,10))console.log(` \u00b7 ${p}`);
|
|
244
|
+
}
|
|
245
|
+
if(figmaAhead.length>0)console.log(`\u2139 Figma defines ${figmaAhead.length} variables this build does not emit yet (see src/figma-code-syntax.txt)`);
|
|
191
246
|
console.log(`Built ${tokenPaths.length} Oneli8 tokens for CSS, TypeScript, Tailwind, and Figma.`);
|
package/scripts/test.mjs
CHANGED
|
@@ -53,9 +53,9 @@ ok(semantic.color.focus.inner.$value==="{color.primary.030}"&&semantic.color.foc
|
|
|
53
53
|
ok(themes.colorScheme.dark.overrides["color.focus.inner"].$value==="{color.primary.930}"&&themes.colorScheme.dark.overrides["color.focus.outer"].$value==="{color.primary.030}","Dark Focus must reverse the active family's 930 shadow and 030 highlight");
|
|
54
54
|
ok(semantic.color.icon.onGem.$value==="{color.neutral.030}"&&!themes.colorScheme.dark.overrides["color.icon.onGem"],"On Gem icon artwork must remain near-white across Light and Dark appearances");
|
|
55
55
|
ok(css.includes('[data-ol8-color-scheme="dark"]')&&css.includes('[data-ol8-primary="orange"]')&&css.includes('[data-ol8-primary="green"]'),"CSS must emit independent scheme and Primary-family axes");
|
|
56
|
-
ok(css.includes("--ol8-color-
|
|
57
|
-
ok(tw.includes('"background-canvas": "var(--ol8-color-background-canvas)"')&&tw.includes('"action-primary-default": "var(--ol8-color-
|
|
58
|
-
ok(tw4.includes("--color-ol8-background-canvas: var(--ol8-color-background-canvas)")&&tw4.includes("--color-ol8-action-primary-default: var(--ol8-color-
|
|
56
|
+
ok(css.includes("--ol8-color-actionprimary-default: var(--ol8-color-primary-600)")&&css.includes("--ol8-color-primary-600: var(--ol8-color-orange-600)"),"CSS color aliases must remain reference-preserving and composable");
|
|
57
|
+
ok(tw.includes('"background-canvas": "var(--ol8-color-background-canvas)"')&&tw.includes('"action-primary-default": "var(--ol8-color-actionprimary-default)"'),"Tailwind v3 must expose Semantic colors rather than raw palette meaning");
|
|
58
|
+
ok(tw4.includes("--color-ol8-background-canvas: var(--ol8-color-background-canvas)")&&tw4.includes("--color-ol8-action-primary-default: var(--ol8-color-actionprimary-default)"),"Tailwind v4 must expose Semantic color utilities");
|
|
59
59
|
ok(figma.modes.colorScheme.dark.length>60&&figma.modes.primaryFamily.orange.length===11&&figma.modes.primaryFamily.green.length===11,"Figma data must preserve independent color modes");
|
|
60
60
|
ok(semantic.elevation.surface.floating.$value.length===2&&semantic.elevation.surface.floating.$value[0].alpha===0.18,"Light Floating elevation must retain its approved two-shadow recipe");
|
|
61
61
|
ok(themes.colorScheme.dark.overrides["elevation.surface.blocking"].$value[0].alpha===0.72,"Dark Blocking elevation must retain its 72% Cyan 840 cue");
|
|
@@ -67,7 +67,7 @@ ok(semantic.material.control.gem.primary.start.$value==="{color.primary.480}"&&t
|
|
|
67
67
|
ok(semantic.material.control.gem.secondary.high.$value.alpha===0.22&&semantic.material.control.gem.rim.default.$value.alpha===0.24,"Gem Secondary transparency and inset rim must retain the approved quiet material recipe");
|
|
68
68
|
ok(semantic.material.control.gem.primary.loadingStart.$value==="{color.primary.720}"&&semantic.material.control.gem.primary.loadingEnd.$value==="{color.primary.930}","Gem Loading must use a distinct shared deep tonal state");
|
|
69
69
|
ok(semantic.material.control.gem.secondary.loadingHigh.$value.alpha===0.39&&semantic.material.control.gem.secondary.loadingLow.$value.alpha===0.24,"Gem Secondary Loading must remain transparent but visibly distinct");
|
|
70
|
-
ok(css.includes("--ol8-material-gem-color-family-amethyst-
|
|
70
|
+
ok(css.includes("--ol8-material-gem-color-family-amethyst-environmentalface: color-mix(in srgb, var(--ol8-color-violet-930) 48%, transparent)"),"Gem CSS colors must preserve Primitive references and approved alpha");
|
|
71
71
|
ok(tw.includes('"overlay": "var(--ol8-elevation-surface-overlay)"')&&tw.includes('"notification": "1200"'),"Tailwind v3 must expose Elevation and stacking roles");
|
|
72
72
|
ok(tw4.includes("--shadow-ol8-blocking: var(--ol8-elevation-surface-blocking)")&&tw4.includes("--z-ol8-modal: 900"),"Tailwind v4 must expose Elevation and stacking roles");
|
|
73
73
|
ok(figma.elevationStyles.length===5,"Figma data must emit five Elevation styles");
|
|
@@ -91,6 +91,6 @@ ok(component.component.choiceChip.terminal.$value==="{spacing.inline.standard}"&
|
|
|
91
91
|
ok(component.component.choiceChip.inset.compact.$value.value===21&&component.component.choiceChip.inset.standard.$value==="{spacing.inset.comfortable}"&&component.component.choiceChip.inset.comfortable.$value.value===27,"Soft Hexagon insets must retain the approved 21px, 24px, and 27px progression");
|
|
92
92
|
ok(component.component.suggestionRail.gap.$value==="{component.choiceChip.gap}"&&component.component.suggestionRail.fieldSeparation.$value==="{spacing.stack.tight}","Suggested Values rail must reuse Choice Chip and structural spacing roles");
|
|
93
93
|
ok(component.component.tabBar.gap.$value==="{spacing.none}"&&component.component.tabBar.inlineInset.$value==="{spacing.inline.minimal}","Tab Bar spacing must map to canonical zero gap and 3px Inline protection");
|
|
94
|
-
ok(manifest.counts.primitive===
|
|
94
|
+
ok(manifest.counts.primitive===170&&manifest.counts.semantic===270&&manifest.counts.component===152&&manifest.counts.componentStatuses===10&&manifest.counts.figmaVariables===578&&manifest.counts.figmaElevationStyles===5,"Navigation spacing and contextual token output counts must remain deterministic");
|
|
95
95
|
|
|
96
96
|
console.log(`PASS ${checks}/${checks} canonical token package checks.`);
|