@apollion-dsi/tokens 4.0.0 → 4.1.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 +18 -9
- package/lib/build.cjs +5 -6
- package/lib/build.esm.js +5 -6
- package/lib/cli.mjs +7 -8
- package/package.json +1 -1
- package/src/build.ts +7 -5
- package/src/ir.ts +405 -0
- package/src/renderers/css.ts +36 -44
- package/src/renderers/json.ts +93 -51
- package/src/renderers/ts.ts +31 -32
- package/src/theme-factory.ts +25 -10
package/README.md
CHANGED
|
@@ -19,6 +19,15 @@ import { TOKENS_RUNTIME_API_VERSION } from '@apollion-dsi/tokens/runtime/v1';
|
|
|
19
19
|
- **In:** OKLch palette derivation (S2/S4), Dimension axis (S3), Foundation/Surface builders (S4), build-time CSS/JSON/TS variant emission (S5), Config-First loader (S6).
|
|
20
20
|
- **Out:** Runtime React components (lives in `@apollion-dsi/core`), framework adapters (Vue/Flutter consumers consume `dist/json/*.json` directly).
|
|
21
21
|
|
|
22
|
+
## DTCG output (`dist/json/*.json`)
|
|
23
|
+
|
|
24
|
+
Emitted per [Design Tokens Format Module 2025.10](https://www.designtokens.org/TR/drafts/format/) — values are **structured objects**, not shorthand strings:
|
|
25
|
+
|
|
26
|
+
- `color` → `{ "colorSpace": "oklch", "components": [L, C, H], "hex": "#rrggbb" }` — OKLch working space (ADR-006 E1) + sRGB `hex` fallback. `alpha` emitted only when below 1.
|
|
27
|
+
- `dimension` → `{ "value": <number>, "unit": "rem" }`.
|
|
28
|
+
|
|
29
|
+
`dist/css/*.css` and `dist/ts/*.d.ts` stay string-based (hex / `rem`) — JSON is the spec-interop surface; CSS/TS are the resolved consumer surfaces.
|
|
30
|
+
|
|
22
31
|
## Coupling to `@apollion-dsi/core`
|
|
23
32
|
|
|
24
33
|
Hard rule per ADR-006 §3.1:
|
|
@@ -29,15 +38,15 @@ Hard rule per ADR-006 §3.1:
|
|
|
29
38
|
|
|
30
39
|
## Slicing (ADR-006 §6)
|
|
31
40
|
|
|
32
|
-
| Slice
|
|
33
|
-
|
|
34
|
-
| S1 — scaffold + versioned API
|
|
35
|
-
| S2 — culori vendor + OKLch lerp (in core)
|
|
36
|
-
| S3 — Dimension axis
|
|
37
|
-
| S4 — builders duplicated here via Strangler Fig | pending
|
|
38
|
-
| S5 — atomic+idempotent build CLI
|
|
39
|
-
| S6 — Config-First + sandboxed loader
|
|
40
|
-
| S7 — release `4.0.0`
|
|
41
|
+
| Slice | Status | Deliverable |
|
|
42
|
+
| ----------------------------------------------- | -------------- | -------------------------------------------------------------- |
|
|
43
|
+
| S1 — scaffold + versioned API | ✅ this commit | `runtime/v1` namespace, esbuild ESM/CJS dual emit, smoke test |
|
|
44
|
+
| S2 — culori vendor + OKLch lerp (in core) | pending | spike `spike/culori-oklch-lerp-parity` validated 0 regressions |
|
|
45
|
+
| S3 — Dimension axis | pending | `compact/normal/spacious` multipliers |
|
|
46
|
+
| S4 — builders duplicated here via Strangler Fig | pending | parity tests vs core |
|
|
47
|
+
| S5 — atomic+idempotent build CLI | pending | `apollion-tokens build` + `dist/manifest.json` |
|
|
48
|
+
| S6 — Config-First + sandboxed loader | pending | `node:vm` + zod schema |
|
|
49
|
+
| S7 — release `4.0.0` | pending | first major + lockstep changesets |
|
|
41
50
|
|
|
42
51
|
## Refs
|
|
43
52
|
|
package/lib/build.cjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`)}s(
|
|
3
|
-
`}s(
|
|
4
|
-
`)}s(
|
|
5
|
-
`
|
|
6
|
-
`}s(V,"serializeManifest");var O=require("@apollion-dsi/core/themes/foundation"),z=require("@apollion-dsi/core/themes/semantic"),P=require("@apollion-dsi/core/themes/spacing"),_=require("@apollion-dsi/core/themes/surface");var g=require("culori");var Do=(0,g.converter)("oklch");function a(o,e,n){let r=(0,g.parse)(o),t=(0,g.parse)(e);if(!r||!t)return o;let i=(0,g.interpolate)([r,t],"oklch");return(0,g.formatHex)(i(n))??o}s(a,"mixOklch");function so(o){return o?typeof o=="string"?o:o.base:"#000"}s(so,"coerceToHex");function u(o,e){let n=so(o),r=e.deepDark,t=e.deepLight;return{base:n,dark:a(n,r,.6),action:a(n,r,.2),active:a(n,t,.6),light:a(n,t,.9)}}s(u,"mountSingleColor");function G(o){let e=o.deepLight,n=o.deepDark;return{0:e,5:a(e,n,.05),10:a(e,n,.1),20:a(e,n,.2),30:a(e,n,.3),40:a(e,n,.4),50:a(e,n,.5),60:a(e,n,.6),70:a(e,n,.7),80:a(e,n,.8),90:a(e,n,.9),100:a(e,n,1)}}s(G,"mountGreyscaleColors");function B(o){let e=o.deepLight,n=o.deepDark,r=o.primary,t=s(p=>1/17*(p-1),"interpolation"),i=a(r,n,.5);return{0:a(i,e,t(18)),5:a(i,e,t(17.5)),10:a(i,e,t(17)),20:a(i,e,t(16)),30:a(i,e,t(15)),40:a(i,e,t(14)),50:a(i,e,t(13)),60:a(i,e,t(12)),70:a(i,e,t(11)),80:a(i,e,t(10)),90:a(i,e,t(9)),100:a(i,e,t(8)),110:a(i,e,t(7)),120:a(i,e,t(6)),130:a(i,e,t(5)),140:a(i,e,t(4)),150:a(i,e,t(3)),160:a(i,e,t(2)),170:i,180:a(i,n,.25)}}s(B,"mountNeutralColors");var H=require("@apollion-dsi/core/themes/dimension");function J({multiplier:o=.25,alias:e},n){let r=n!==void 0?H.DIMENSION_MULTIPLIERS[n]:o;return(...t)=>t.map(i=>{let p=typeof i=="string"?e[i]:i;return`${r*p}rem`}).join(" ")}s(J,"createSpacing");function ao(o){return{baseDark:o.baseDark,baseLight:o.baseLight,deepDark:o.deepDark,deepLight:o.deepLight,transparent:o.transparent??"transparent",neutral:B(o),grayscale:G(o),main:u(o.main,o),opposite:u(o.opposite,o),complementary:u(o.complementary,o),danger:u(o.danger,o),information:u(o.information,o),success:u(o.success,o),warning:u(o.warning,o),primary:u(o.primary,o),secondary:u(o.secondary,o),tertiary:u(o.tertiary,o)}}s(ao,"composeColors");function U(o,e=P.defaultInputSpacing){let n=ao(o.colors),r=J(e,o.dimension),t=(0,z.createSemantic)({colors:n,spacing:r}),i=(0,O.createFoundation)(t);if(o.surface==="negative"){let l=(0,_.invertForSurface)({semantic:t,colors:n},"negative");return(0,O.createFoundation)(l.semantic)}return i}s(U,"buildFoundationForVariant");async function co(o){let{config:e,outDir:n,cwd:r=process.cwd(),verbose:t=!1}=o,i=v(e),p=e.output??{};if(i.length===0)throw new Error("apollion-tokens build: no variants (config.brands is empty)");let l=await(0,c.mkdtemp)((0,y.join)((0,Z.tmpdir)(),"apollion-tokens-"));try{let m=[];for(let h of i){let C=U(h),D=L(h);if(p.css){let d=I(C,h),f=`css/${D}.css`;await E(l,f,d),m.push({path:f,sha256:b(d)}),t&&console.log(` \u2713 ${f}`)}if(p.json){let d=F(C,h),f=`json/${D}.json`;await E(l,f,d),m.push({path:f,sha256:b(d)}),t&&console.log(` \u2713 ${f}`)}if(p.ts){let d=j(C,h),f=`ts/${D}.d.ts`;await E(l,f,d),m.push({path:f,sha256:b(d)}),t&&console.log(` \u2713 ${f}`)}}let $={configHash:T(e),files:m,gitCommit:M(r),buildEnv:N()},x=V($);return await(0,c.writeFile)((0,y.join)(l,"manifest.json"),x),await(0,c.rm)(n,{recursive:!0,force:!0}),await(0,c.mkdir)((0,y.dirname)(n),{recursive:!0}),await(0,c.rename)(l,n),t&&console.log(`\u2713 ${m.length} files \u2192 ${n}`),{manifest:$,outDir:n}}catch(m){throw await(0,c.rm)(l,{recursive:!0,force:!0}).catch(()=>{}),m}}s(co,"build");async function po(o){let{config:e,outDir:n}=o,r;try{r=JSON.parse(await(0,c.readFile)((0,y.join)(n,"manifest.json"),"utf8"))}catch{return!1}return r.configHash===T(e)}s(po,"check");async function E(o,e,n){let r=(0,y.join)(o,e);await(0,c.mkdir)((0,y.dirname)(r),{recursive:!0}),await(0,c.writeFile)(r,n)}s(E,"writeAt");0&&(module.exports={build,check});
|
|
1
|
+
"use strict";var w=Object.defineProperty;var ye=Object.getOwnPropertyDescriptor;var he=Object.getOwnPropertyNames;var be=Object.prototype.hasOwnProperty;var s=(e,o)=>w(e,"name",{value:o,configurable:!0});var Re=(e,o)=>{for(var n in o)w(e,n,{get:o[n],enumerable:!0})},ke=(e,o,n,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let i of he(o))!be.call(e,i)&&i!==n&&w(e,i,{get:()=>o[i],enumerable:!(r=ye(o,i))||r.enumerable});return e};var Ie=e=>ke(w({},"__esModule",{value:!0}),e);var ze={};Re(ze,{build:()=>_e,check:()=>Je});module.exports=Ie(ze);var u=require("node:fs/promises"),de=require("node:os"),b=require("node:path");var C=require("@apollion-dsi/core/themes/border"),v=require("@apollion-dsi/core/themes/depth"),h=require("@apollion-dsi/core/themes/font"),k=require("culori");var xe=(0,k.converter)("oklch");function R(e){return typeof e.type=="string"&&"value"in e}s(R,"isToken");function I(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}s(I,"kebab");function W(e){return e.split(".").map(I).join(".")}s(W,"kebabPath");function x(e){return e.kind==="ref"?e.resolved.raw:e.raw}s(x,"rawValue");function S(e,o){return Number(e.toFixed(o))||0}s(S,"round");function G(e){let o=xe((0,k.parse)(e));if(!o)return{kind:"color",raw:e,components:[0,0,0],hex:"#000000"};let n=[S(o.l,4),S(o.c,4),S(o.h??0,2)],r=(0,k.formatHex)(o)??"#000000";return o.alpha!==void 0&&o.alpha!==1?{kind:"color",raw:e,components:n,alpha:S(o.alpha,4),hex:r}:{kind:"color",raw:e,components:n,hex:r}}s(G,"toColorValue");var _=/^(-?(?:\d+\.?\d*|\.\d+))(px|rem)$/;function $(e){let o=_.exec(e.trim());if(o)return{kind:"dimension",raw:e,value:Number(o[1]),unit:o[2]};let n=Number.parseFloat(e);return{kind:"dimension",raw:e,value:Number.isFinite(n)?n:0,unit:"rem"}}s($,"toDimensionValue");function d(e){return{type:"color",value:G(e)}}s(d,"colorToken");function H(e){let o={};for(let[n,r]of Object.entries(e))o[n]=d(r);return o}s(H,"recordColorGroup");var De=["main","opposite","complementary","primary","secondary","tertiary","success","danger","warning","information"];function Ve(e){let o={};for(let n of De){let r=e[n];o[n]={base:d(r.base),dark:d(r.dark),action:d(r.action),active:d(r.active),light:d(r.light)}}return o.grayscale=H(e.grayscale),o.neutral=H(e.neutral),o.baseDark=d(e.baseDark??"#000000"),o.baseLight=d(e.baseLight??"#ffffff"),o.deepDark=d(e.deepDark??"#000000"),o.deepLight=d(e.deepLight??"#ffffff"),o}s(Ve,"buildPrimitives");var Te={primary:"color.primary.base",secondary:"color.secondary.base",tertiary:"color.tertiary.base",success:"color.success.base",danger:"color.danger.base",warning:"color.warning.base",info:"color.information.base"},we={onPrimary:"color.baseLight",onSecondary:"color.baseLight",onTertiary:"color.baseDark",onSuccess:"color.baseLight",onDanger:"color.baseLight",onWarning:"color.baseDark",onInfo:"color.baseLight"};function Se(e,o){let n=e;for(let r of o.split(".").slice(1))if(n&&typeof n=="object"&&r in n)n=n[r];else return;return typeof n=="string"?n:void 0}s(Se,"lookupPrimitive");function P(e,o,n){let r={};for(let[i,t]of Object.entries(e)){let c=n[i],l=c?Se(o,c):void 0;r[i]=c&&l===t?{type:"color",value:{kind:"ref",path:c,resolved:G(t)}}:d(t)}return r}s(P,"colorRefGroup");function Ce(e){let o={};for(let[n,r]of Object.entries(e))o[n]={type:"dimension",value:$(r)};return o}s(Ce,"dimensionGroup");function $e(){let e={},o={};for(let[i,t]of Object.entries(C.defaultInputBorder.borderRadius))_.test(t)?e[i]={type:"dimension",value:$(t)}:o[i]=t;let n={};for(let[i,t]of Object.entries(C.defaultInputBorder.borderWidth))n[i]={type:"dimension",value:$(t)};let r={};for(let[i,t]of Object.entries(C.defaultInputBorder.borderStyle))r[i]={type:"strokeStyle",value:{kind:"strokeStyle",raw:t,style:t}};return{group:{radius:e,width:n,style:r},extensions:o}}s($e,"buildBorder");function ve(){let e={};for(let[t,c]of Object.entries(h.defaultInputFont.fontFamily)){let l=c.replace(/;\s*$/,"").trim();e[t]={type:"fontFamily",value:{kind:"fontFamily",raw:l,family:l}}}let o={};for(let[t,c]of Object.entries(h.defaultInputFont.fontSize))o[t]={type:"dimension",value:{kind:"dimension",raw:`${c}px`,value:c,unit:"px"}};let n={};for(let[t,c]of Object.entries(h.defaultInputFont.fontWeight))n[t]={type:"fontWeight",value:{kind:"fontWeight",raw:`${c}`,weight:c}};let r={};for(let[t,c]of Object.entries(h.defaultInputFont.lineHeight))r[t]={type:"number",value:{kind:"number",raw:`${c}`,number:c}};let i={letterSpacing:h.defaultInputFont.letterSpacing,textTransform:h.defaultInputFont.textTransform,fontStyle:h.defaultInputFont.fontStyle};return{group:{family:e,size:o,weight:n,lineHeight:r},extensions:i}}s(ve,"buildFont");var Oe=/rgba?\([^)]*\)|#[0-9a-fA-F]+|[a-zA-Z]+$/;function Fe(e){let o=e.trim(),n=o.startsWith("inset");n&&(o=o.slice(5).trim());let r=o.match(Oe),i=r?r[0]:"#000000",c=(r?o.slice(0,r.index).trim():o).split(/\s+/).filter(Boolean),l=s(p=>$(c[p]??"0px"),"dim");return{kind:"shadow",raw:e,shadow:{color:G(i),offsetX:l(0),offsetY:l(1),blur:l(2),spread:l(3),inset:n}}}s(Fe,"parseShadow");function je(e){let o=(0,v.createDeth)(e),n={};for(let r of Object.keys(v.levelValues))n[r]={type:"shadow",value:Fe(o(r))};return n}s(je,"buildShadow");function J(e,o,n){let r={brand:n.brand,mode:n.mode,surface:n.surface,dimension:n.dimension},i=$e(),t=ve(),c={"com.apollion.variant":r,"com.apollion.font":t.extensions};return Object.keys(i.extensions).length>0&&(c["com.apollion.border"]={radius:i.extensions}),{description:`Apollion DS tokens \u2014 DTCG 2025.10. Variant: ${r.brand}/${r.mode}/${r.surface}/${r.dimension}.`,meta:r,primitives:Ve(o),groups:{bg:P(e.bg,o,Te),text:P(e.text,o,we),spacing:Ce(e.spacing),border:i.group,font:t.group,shadow:je(o)},extensions:c}}s(J,"buildIR");var Ee="/* Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs. */";function z(e,o,n){for(let[r,i]of Object.entries(e)){let t=[...o,I(r)];R(i)?n.push(` --apollion-${t.join("-")}: ${x(i.value)};`):z(i,t,n)}}s(z,"emitVars");function X(e,o,n){for(let[r,i]of Object.entries(e)){let t=[...o,I(r)];R(i)?i.type==="color"&&n.push(` @property --apollion-${t.join("-")} { syntax: '<color>'; inherits: true; initial-value: ${x(i.value)}; }`):X(i,t,n)}}s(X,"emitProperties");function U(e){let{brand:o,mode:n,surface:r,dimension:i}=e.meta,t=[Ee,""];return t.push(`/* Variant: brand=${o} mode=${n} surface=${r} dimension=${i} */`,"",":root {"),z(e.groups,[],t),t.push("}",""),t.push("@supports (background: paint(squircle)) or (color: oklch(0% 0 0)) {"),X(e.groups,[],t),t.push("}",""),t.join(`
|
|
2
|
+
`)}s(U,"renderCss");function Y(e){return e.alpha!==void 0?{colorSpace:"oklch",components:[...e.components],alpha:e.alpha,hex:e.hex}:{colorSpace:"oklch",components:[...e.components],hex:e.hex}}s(Y,"colorValue");function D(e){return{value:e.value,unit:e.unit}}s(D,"dimensionValue");function Ge(e){let o=e.shadow,n={color:Y(o.color),offsetX:D(o.offsetX),offsetY:D(o.offsetY),blur:D(o.blur),spread:D(o.spread)};return o.inset&&(n.inset=!0),n}s(Ge,"shadowValue");function Le(e){switch(e.kind){case"ref":return`{${W(e.path)}}`;case"color":return Y(e);case"dimension":return D(e);case"fontFamily":return e.family;case"fontWeight":return e.weight;case"number":return e.number;case"strokeStyle":return e.style;case"shadow":return Ge(e)}}s(Le,"projectValue");function Ae(e){let o={$value:Le(e.value),$type:e.type};return e.description!==void 0&&(o.$description=e.description),e.deprecated!==void 0&&(o.$deprecated=e.deprecated),o}s(Ae,"projectToken");function L(e){let o={};for(let[n,r]of Object.entries(e))o[I(n)]=R(r)?Ae(r):L(r);return o}s(L,"projectGroup");function q(e){let o={$description:e.description,color:L(e.primitives),...L(e.groups),$extensions:e.extensions};return`${JSON.stringify(o,null,2)}
|
|
3
|
+
`}s(q,"renderJson");var Ne="// Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs.";function Z(e,o,n){let r=" ".repeat(o);for(let[i,t]of Object.entries(e))R(t)?n.push(`${r}${JSON.stringify(i)}: ${JSON.stringify(x(t.value))},`):(n.push(`${r}${i}: {`),Z(t,o+1,n),n.push(`${r}},`))}s(Z,"emitGroup");function K(e){let{brand:o,mode:n,surface:r,dimension:i}=e.meta,t=[Ne,`// Variant: brand=${o} mode=${n} surface=${r} dimension=${i}`,"","export const tokens = {"];return Z(e.groups,1,t),t.push("} as const;","","export type Tokens = typeof tokens;",""),t.join(`
|
|
4
|
+
`)}s(K,"renderTs");var Me=["light","dark"],Be=["positive","negative"],He=["compact","normal","spacious"];function Q(e){let o=e.modes??Me,n=e.surfaces??Be,r=e.dimensions??He,i=Object.entries(e.brands).sort(([c],[l])=>c.localeCompare(l)),t=[];for(let[c,l]of i)for(let p of o)for(let T of n)for(let O of r)t.push({brand:c,colors:l,mode:p,surface:T,dimension:O});return t}s(Q,"expandVariants");function ee(e){return`${e.brand}.${e.mode}.${e.surface}.${e.dimension}`}s(ee,"variantName");var oe=require("node:crypto");function V(e){return(0,oe.createHash)("sha256").update(e).digest("hex")}s(V,"sha256");function N(e){return V(A(e))}s(N,"hashConfig");function A(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(A).join(",")}]`:`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${A(e[r])}`).join(",")}}`}s(A,"stableStringify");function ne(e){try{let{execSync:o}=require("node:child_process");return o("git rev-parse --short HEAD",{cwd:e,stdio:["ignore","pipe","ignore"],encoding:"utf8"}).trim()}catch{return""}}s(ne,"detectGitCommit");function re(){return{node:process.version,platform:`${process.platform}-${process.arch}`}}s(re,"buildEnv");function te(e){let o=[...e.files].sort((r,i)=>r.path.localeCompare(i.path)),n={configHash:e.configHash,files:o,gitCommit:e.gitCommit,buildEnv:e.buildEnv};return`${JSON.stringify(n,null,2)}
|
|
5
|
+
`}s(te,"serializeManifest");var M=require("@apollion-dsi/core/themes/foundation"),le=require("@apollion-dsi/core/themes/semantic"),ue=require("@apollion-dsi/core/themes/spacing"),pe=require("@apollion-dsi/core/themes/surface");var g=require("culori");var lo=(0,g.converter)("oklch");function a(e,o,n){let r=(0,g.parse)(e),i=(0,g.parse)(o);if(!r||!i)return e;let t=(0,g.interpolate)([r,i],"oklch");return(0,g.formatHex)(t(n))??e}s(a,"mixOklch");function Pe(e){return e?typeof e=="string"?e:e.base:"#000"}s(Pe,"coerceToHex");function m(e,o){let n=Pe(e),r=o.deepDark,i=o.deepLight;return{base:n,dark:a(n,r,.6),action:a(n,r,.2),active:a(n,i,.6),light:a(n,i,.9)}}s(m,"mountSingleColor");function ie(e){let o=e.deepLight,n=e.deepDark;return{0:o,5:a(o,n,.05),10:a(o,n,.1),20:a(o,n,.2),30:a(o,n,.3),40:a(o,n,.4),50:a(o,n,.5),60:a(o,n,.6),70:a(o,n,.7),80:a(o,n,.8),90:a(o,n,.9),100:a(o,n,1)}}s(ie,"mountGreyscaleColors");function se(e){let o=e.deepLight,n=e.deepDark,r=e.primary,i=s(c=>1/17*(c-1),"interpolation"),t=a(r,n,.5);return{0:a(t,o,i(18)),5:a(t,o,i(17.5)),10:a(t,o,i(17)),20:a(t,o,i(16)),30:a(t,o,i(15)),40:a(t,o,i(14)),50:a(t,o,i(13)),60:a(t,o,i(12)),70:a(t,o,i(11)),80:a(t,o,i(10)),90:a(t,o,i(9)),100:a(t,o,i(8)),110:a(t,o,i(7)),120:a(t,o,i(6)),130:a(t,o,i(5)),140:a(t,o,i(4)),150:a(t,o,i(3)),160:a(t,o,i(2)),170:t,180:a(t,n,.25)}}s(se,"mountNeutralColors");var ae=require("@apollion-dsi/core/themes/dimension");function ce({multiplier:e=.25,alias:o},n){let r=n!==void 0?ae.DIMENSION_MULTIPLIERS[n]:e;return(...i)=>i.map(t=>{let c=typeof t=="string"?o[t]:t;return`${r*c}rem`}).join(" ")}s(ce,"createSpacing");function We(e){return{baseDark:e.baseDark,baseLight:e.baseLight,deepDark:e.deepDark,deepLight:e.deepLight,transparent:e.transparent??"transparent",neutral:se(e),grayscale:ie(e),main:m(e.main,e),opposite:m(e.opposite,e),complementary:m(e.complementary,e),danger:m(e.danger,e),information:m(e.information,e),success:m(e.success,e),warning:m(e.warning,e),primary:m(e.primary,e),secondary:m(e.secondary,e),tertiary:m(e.tertiary,e)}}s(We,"composeColors");function fe(e,o=ue.defaultInputSpacing){let n=We(e.colors),r=ce(o,e.dimension),i=(0,le.createSemantic)({colors:n,spacing:r}),t=(0,M.createFoundation)(i);return e.surface==="negative"&&(t=(0,M.createFoundation)((0,pe.invertForSurface)({semantic:i,colors:n},"negative").semantic)),{foundation:t,colors:n}}s(fe,"buildVariantThemes");async function _e(e){let{config:o,outDir:n,cwd:r=process.cwd(),verbose:i=!1}=e,t=Q(o),c=o.output??{};if(t.length===0)throw new Error("apollion-tokens build: no variants (config.brands is empty)");let l=await(0,u.mkdtemp)((0,b.join)((0,de.tmpdir)(),"apollion-tokens-"));try{let p=[];for(let F of t){let{foundation:me,colors:ge}=fe(F),j=J(me,ge,F),E=ee(F);if(c.css){let y=U(j),f=`css/${E}.css`;await B(l,f,y),p.push({path:f,sha256:V(y)}),i&&console.log(` \u2713 ${f}`)}if(c.json){let y=q(j),f=`json/${E}.json`;await B(l,f,y),p.push({path:f,sha256:V(y)}),i&&console.log(` \u2713 ${f}`)}if(c.ts){let y=K(j),f=`ts/${E}.d.ts`;await B(l,f,y),p.push({path:f,sha256:V(y)}),i&&console.log(` \u2713 ${f}`)}}let T={configHash:N(o),files:p,gitCommit:ne(r),buildEnv:re()},O=te(T);return await(0,u.writeFile)((0,b.join)(l,"manifest.json"),O),await(0,u.rm)(n,{recursive:!0,force:!0}),await(0,u.mkdir)((0,b.dirname)(n),{recursive:!0}),await(0,u.rename)(l,n),i&&console.log(`\u2713 ${p.length} files \u2192 ${n}`),{manifest:T,outDir:n}}catch(p){throw await(0,u.rm)(l,{recursive:!0,force:!0}).catch(()=>{}),p}}s(_e,"build");async function Je(e){let{config:o,outDir:n}=e,r;try{r=JSON.parse(await(0,u.readFile)((0,b.join)(n,"manifest.json"),"utf8"))}catch{return!1}return r.configHash===N(o)}s(Je,"check");async function B(e,o,n){let r=(0,b.join)(e,o);await(0,u.mkdir)((0,b.dirname)(r),{recursive:!0}),await(0,u.writeFile)(r,n)}s(B,"writeAt");0&&(module.exports={build,check});
|
package/lib/build.esm.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}s(
|
|
3
|
-
`}s(
|
|
4
|
-
`)}s(
|
|
5
|
-
`
|
|
6
|
-
`}s(v,"serializeManifest");import{createFoundation as V}from"@apollion-dsi/core/themes/foundation";import{createSemantic as ro}from"@apollion-dsi/core/themes/semantic";import{defaultInputSpacing as io}from"@apollion-dsi/core/themes/spacing";import{invertForSurface as so}from"@apollion-dsi/core/themes/surface";import{converter as Y,formatHex as oo,interpolate as eo,parse as L}from"culori";var To=Y("oklch");function a(o,e,n){let r=L(o),t=L(e);if(!r||!t)return o;let i=eo([r,t],"oklch");return oo(i(n))??o}s(a,"mixOklch");function no(o){return o?typeof o=="string"?o:o.base:"#000"}s(no,"coerceToHex");function f(o,e){let n=no(o),r=e.deepDark,t=e.deepLight;return{base:n,dark:a(n,r,.6),action:a(n,r,.2),active:a(n,t,.6),light:a(n,t,.9)}}s(f,"mountSingleColor");function R(o){let e=o.deepLight,n=o.deepDark;return{0:e,5:a(e,n,.05),10:a(e,n,.1),20:a(e,n,.2),30:a(e,n,.3),40:a(e,n,.4),50:a(e,n,.5),60:a(e,n,.6),70:a(e,n,.7),80:a(e,n,.8),90:a(e,n,.9),100:a(e,n,1)}}s(R,"mountGreyscaleColors");function M(o){let e=o.deepLight,n=o.deepDark,r=o.primary,t=s(c=>1/17*(c-1),"interpolation"),i=a(r,n,.5);return{0:a(i,e,t(18)),5:a(i,e,t(17.5)),10:a(i,e,t(17)),20:a(i,e,t(16)),30:a(i,e,t(15)),40:a(i,e,t(14)),50:a(i,e,t(13)),60:a(i,e,t(12)),70:a(i,e,t(11)),80:a(i,e,t(10)),90:a(i,e,t(9)),100:a(i,e,t(8)),110:a(i,e,t(7)),120:a(i,e,t(6)),130:a(i,e,t(5)),140:a(i,e,t(4)),150:a(i,e,t(3)),160:a(i,e,t(2)),170:i,180:a(i,n,.25)}}s(M,"mountNeutralColors");import{DIMENSION_MULTIPLIERS as to}from"@apollion-dsi/core/themes/dimension";function N({multiplier:o=.25,alias:e},n){let r=n!==void 0?to[n]:o;return(...t)=>t.map(i=>{let c=typeof i=="string"?e[i]:i;return`${r*c}rem`}).join(" ")}s(N,"createSpacing");function ao(o){return{baseDark:o.baseDark,baseLight:o.baseLight,deepDark:o.deepDark,deepLight:o.deepLight,transparent:o.transparent??"transparent",neutral:M(o),grayscale:R(o),main:f(o.main,o),opposite:f(o.opposite,o),complementary:f(o.complementary,o),danger:f(o.danger,o),information:f(o.information,o),success:f(o.success,o),warning:f(o.warning,o),primary:f(o.primary,o),secondary:f(o.secondary,o),tertiary:f(o.tertiary,o)}}s(ao,"composeColors");function G(o,e=io){let n=ao(o.colors),r=N(e,o.dimension),t=ro({colors:n,spacing:r}),i=V(t);if(o.surface==="negative"){let p=so({semantic:t,colors:n},"negative");return V(p.semantic)}return i}s(G,"buildFoundationForVariant");async function Ko(o){let{config:e,outDir:n,cwd:r=process.cwd(),verbose:t=!1}=o,i=I(e),c=e.output??{};if(i.length===0)throw new Error("apollion-tokens build: no variants (config.brands is empty)");let p=await co(h(fo(),"apollion-tokens-"));try{let u=[];for(let g of i){let $=G(g),k=A(g);if(c.css){let m=w($,g),l=`css/${k}.css`;await S(p,l,m),u.push({path:l,sha256:d(m)}),t&&console.log(` \u2713 ${l}`)}if(c.json){let m=O($,g),l=`json/${k}.json`;await S(p,l,m),u.push({path:l,sha256:d(m)}),t&&console.log(` \u2713 ${l}`)}if(c.ts){let m=E($,g),l=`ts/${k}.d.ts`;await S(p,l,m),u.push({path:l,sha256:d(m)}),t&&console.log(` \u2713 ${l}`)}}let y={configHash:D(e),files:u,gitCommit:F(r),buildEnv:j()},b=v(y);return await J(h(p,"manifest.json"),b),await B(n,{recursive:!0,force:!0}),await H(z(n),{recursive:!0}),await lo(p,n),t&&console.log(`\u2713 ${u.length} files \u2192 ${n}`),{manifest:y,outDir:n}}catch(u){throw await B(p,{recursive:!0,force:!0}).catch(()=>{}),u}}s(Ko,"build");async function Qo(o){let{config:e,outDir:n}=o,r;try{r=JSON.parse(await po(h(n,"manifest.json"),"utf8"))}catch{return!1}return r.configHash===D(e)}s(Qo,"check");async function S(o,e,n){let r=h(o,e);await H(z(r),{recursive:!0}),await J(r,n)}s(S,"writeAt");export{Ko as build,Qo as check};
|
|
1
|
+
var ue=Object.defineProperty;var s=(e,o)=>ue(e,"name",{value:o,configurable:!0}),pe=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(o,n)=>(typeof require<"u"?require:o)[n]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});import{mkdir as ie,mkdtemp as Xe,readFile as Ue,rename as Ye,rm as te,writeFile as se}from"node:fs/promises";import{tmpdir as qe}from"node:os";import{dirname as ae,join as V}from"node:path";import{defaultInputBorder as $}from"@apollion-dsi/core/themes/border";import{createDeth as fe,levelValues as de}from"@apollion-dsi/core/themes/depth";import{defaultInputFont as g}from"@apollion-dsi/core/themes/font";import{converter as me,formatHex as ge,parse as ye}from"culori";var he=me("oklch");function y(e){return typeof e.type=="string"&&"value"in e}s(y,"isToken");function h(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}s(h,"kebab");function A(e){return e.split(".").map(h).join(".")}s(A,"kebabPath");function b(e){return e.kind==="ref"?e.resolved.raw:e.raw}s(b,"rawValue");function x(e,o){return Number(e.toFixed(o))||0}s(x,"round");function v(e){let o=he(ye(e));if(!o)return{kind:"color",raw:e,components:[0,0,0],hex:"#000000"};let n=[x(o.l,4),x(o.c,4),x(o.h??0,2)],r=ge(o)??"#000000";return o.alpha!==void 0&&o.alpha!==1?{kind:"color",raw:e,components:n,alpha:x(o.alpha,4),hex:r}:{kind:"color",raw:e,components:n,hex:r}}s(v,"toColorValue");var N=/^(-?(?:\d+\.?\d*|\.\d+))(px|rem)$/;function D(e){let o=N.exec(e.trim());if(o)return{kind:"dimension",raw:e,value:Number(o[1]),unit:o[2]};let n=Number.parseFloat(e);return{kind:"dimension",raw:e,value:Number.isFinite(n)?n:0,unit:"rem"}}s(D,"toDimensionValue");function f(e){return{type:"color",value:v(e)}}s(f,"colorToken");function G(e){let o={};for(let[n,r]of Object.entries(e))o[n]=f(r);return o}s(G,"recordColorGroup");var be=["main","opposite","complementary","primary","secondary","tertiary","success","danger","warning","information"];function Re(e){let o={};for(let n of be){let r=e[n];o[n]={base:f(r.base),dark:f(r.dark),action:f(r.action),active:f(r.active),light:f(r.light)}}return o.grayscale=G(e.grayscale),o.neutral=G(e.neutral),o.baseDark=f(e.baseDark??"#000000"),o.baseLight=f(e.baseLight??"#ffffff"),o.deepDark=f(e.deepDark??"#000000"),o.deepLight=f(e.deepLight??"#ffffff"),o}s(Re,"buildPrimitives");var ke={primary:"color.primary.base",secondary:"color.secondary.base",tertiary:"color.tertiary.base",success:"color.success.base",danger:"color.danger.base",warning:"color.warning.base",info:"color.information.base"},Ie={onPrimary:"color.baseLight",onSecondary:"color.baseLight",onTertiary:"color.baseDark",onSuccess:"color.baseLight",onDanger:"color.baseLight",onWarning:"color.baseDark",onInfo:"color.baseLight"};function xe(e,o){let n=e;for(let r of o.split(".").slice(1))if(n&&typeof n=="object"&&r in n)n=n[r];else return;return typeof n=="string"?n:void 0}s(xe,"lookupPrimitive");function L(e,o,n){let r={};for(let[i,t]of Object.entries(e)){let c=n[i],l=c?xe(o,c):void 0;r[i]=c&&l===t?{type:"color",value:{kind:"ref",path:c,resolved:v(t)}}:f(t)}return r}s(L,"colorRefGroup");function De(e){let o={};for(let[n,r]of Object.entries(e))o[n]={type:"dimension",value:D(r)};return o}s(De,"dimensionGroup");function Ve(){let e={},o={};for(let[i,t]of Object.entries($.borderRadius))N.test(t)?e[i]={type:"dimension",value:D(t)}:o[i]=t;let n={};for(let[i,t]of Object.entries($.borderWidth))n[i]={type:"dimension",value:D(t)};let r={};for(let[i,t]of Object.entries($.borderStyle))r[i]={type:"strokeStyle",value:{kind:"strokeStyle",raw:t,style:t}};return{group:{radius:e,width:n,style:r},extensions:o}}s(Ve,"buildBorder");function Te(){let e={};for(let[t,c]of Object.entries(g.fontFamily)){let l=c.replace(/;\s*$/,"").trim();e[t]={type:"fontFamily",value:{kind:"fontFamily",raw:l,family:l}}}let o={};for(let[t,c]of Object.entries(g.fontSize))o[t]={type:"dimension",value:{kind:"dimension",raw:`${c}px`,value:c,unit:"px"}};let n={};for(let[t,c]of Object.entries(g.fontWeight))n[t]={type:"fontWeight",value:{kind:"fontWeight",raw:`${c}`,weight:c}};let r={};for(let[t,c]of Object.entries(g.lineHeight))r[t]={type:"number",value:{kind:"number",raw:`${c}`,number:c}};let i={letterSpacing:g.letterSpacing,textTransform:g.textTransform,fontStyle:g.fontStyle};return{group:{family:e,size:o,weight:n,lineHeight:r},extensions:i}}s(Te,"buildFont");var we=/rgba?\([^)]*\)|#[0-9a-fA-F]+|[a-zA-Z]+$/;function Se(e){let o=e.trim(),n=o.startsWith("inset");n&&(o=o.slice(5).trim());let r=o.match(we),i=r?r[0]:"#000000",c=(r?o.slice(0,r.index).trim():o).split(/\s+/).filter(Boolean),l=s(u=>D(c[u]??"0px"),"dim");return{kind:"shadow",raw:e,shadow:{color:v(i),offsetX:l(0),offsetY:l(1),blur:l(2),spread:l(3),inset:n}}}s(Se,"parseShadow");function Ce(e){let o=fe(e),n={};for(let r of Object.keys(de))n[r]={type:"shadow",value:Se(o(r))};return n}s(Ce,"buildShadow");function M(e,o,n){let r={brand:n.brand,mode:n.mode,surface:n.surface,dimension:n.dimension},i=Ve(),t=Te(),c={"com.apollion.variant":r,"com.apollion.font":t.extensions};return Object.keys(i.extensions).length>0&&(c["com.apollion.border"]={radius:i.extensions}),{description:`Apollion DS tokens \u2014 DTCG 2025.10. Variant: ${r.brand}/${r.mode}/${r.surface}/${r.dimension}.`,meta:r,primitives:Re(o),groups:{bg:L(e.bg,o,ke),text:L(e.text,o,Ie),spacing:De(e.spacing),border:i.group,font:t.group,shadow:Ce(o)},extensions:c}}s(M,"buildIR");var $e="/* Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs. */";function B(e,o,n){for(let[r,i]of Object.entries(e)){let t=[...o,h(r)];y(i)?n.push(` --apollion-${t.join("-")}: ${b(i.value)};`):B(i,t,n)}}s(B,"emitVars");function H(e,o,n){for(let[r,i]of Object.entries(e)){let t=[...o,h(r)];y(i)?i.type==="color"&&n.push(` @property --apollion-${t.join("-")} { syntax: '<color>'; inherits: true; initial-value: ${b(i.value)}; }`):H(i,t,n)}}s(H,"emitProperties");function P(e){let{brand:o,mode:n,surface:r,dimension:i}=e.meta,t=[$e,""];return t.push(`/* Variant: brand=${o} mode=${n} surface=${r} dimension=${i} */`,"",":root {"),B(e.groups,[],t),t.push("}",""),t.push("@supports (background: paint(squircle)) or (color: oklch(0% 0 0)) {"),H(e.groups,[],t),t.push("}",""),t.join(`
|
|
2
|
+
`)}s(P,"renderCss");function W(e){return e.alpha!==void 0?{colorSpace:"oklch",components:[...e.components],alpha:e.alpha,hex:e.hex}:{colorSpace:"oklch",components:[...e.components],hex:e.hex}}s(W,"colorValue");function R(e){return{value:e.value,unit:e.unit}}s(R,"dimensionValue");function ve(e){let o=e.shadow,n={color:W(o.color),offsetX:R(o.offsetX),offsetY:R(o.offsetY),blur:R(o.blur),spread:R(o.spread)};return o.inset&&(n.inset=!0),n}s(ve,"shadowValue");function Oe(e){switch(e.kind){case"ref":return`{${A(e.path)}}`;case"color":return W(e);case"dimension":return R(e);case"fontFamily":return e.family;case"fontWeight":return e.weight;case"number":return e.number;case"strokeStyle":return e.style;case"shadow":return ve(e)}}s(Oe,"projectValue");function Fe(e){let o={$value:Oe(e.value),$type:e.type};return e.description!==void 0&&(o.$description=e.description),e.deprecated!==void 0&&(o.$deprecated=e.deprecated),o}s(Fe,"projectToken");function O(e){let o={};for(let[n,r]of Object.entries(e))o[h(n)]=y(r)?Fe(r):O(r);return o}s(O,"projectGroup");function _(e){let o={$description:e.description,color:O(e.primitives),...O(e.groups),$extensions:e.extensions};return`${JSON.stringify(o,null,2)}
|
|
3
|
+
`}s(_,"renderJson");var je="// Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs.";function J(e,o,n){let r=" ".repeat(o);for(let[i,t]of Object.entries(e))y(t)?n.push(`${r}${JSON.stringify(i)}: ${JSON.stringify(b(t.value))},`):(n.push(`${r}${i}: {`),J(t,o+1,n),n.push(`${r}},`))}s(J,"emitGroup");function z(e){let{brand:o,mode:n,surface:r,dimension:i}=e.meta,t=[je,`// Variant: brand=${o} mode=${n} surface=${r} dimension=${i}`,"","export const tokens = {"];return J(e.groups,1,t),t.push("} as const;","","export type Tokens = typeof tokens;",""),t.join(`
|
|
4
|
+
`)}s(z,"renderTs");var Ee=["light","dark"],Ge=["positive","negative"],Le=["compact","normal","spacious"];function X(e){let o=e.modes??Ee,n=e.surfaces??Ge,r=e.dimensions??Le,i=Object.entries(e.brands).sort(([c],[l])=>c.localeCompare(l)),t=[];for(let[c,l]of i)for(let u of o)for(let I of n)for(let T of r)t.push({brand:c,colors:l,mode:u,surface:I,dimension:T});return t}s(X,"expandVariants");function U(e){return`${e.brand}.${e.mode}.${e.surface}.${e.dimension}`}s(U,"variantName");import{createHash as Ae}from"node:crypto";function k(e){return Ae("sha256").update(e).digest("hex")}s(k,"sha256");function j(e){return k(F(e))}s(j,"hashConfig");function F(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(F).join(",")}]`:`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${F(e[r])}`).join(",")}}`}s(F,"stableStringify");function Y(e){try{let{execSync:o}=pe("node:child_process");return o("git rev-parse --short HEAD",{cwd:e,stdio:["ignore","pipe","ignore"],encoding:"utf8"}).trim()}catch{return""}}s(Y,"detectGitCommit");function q(){return{node:process.version,platform:`${process.platform}-${process.arch}`}}s(q,"buildEnv");function Z(e){let o=[...e.files].sort((r,i)=>r.path.localeCompare(i.path)),n={configHash:e.configHash,files:o,gitCommit:e.gitCommit,buildEnv:e.buildEnv};return`${JSON.stringify(n,null,2)}
|
|
5
|
+
`}s(Z,"serializeManifest");import{createFoundation as ne}from"@apollion-dsi/core/themes/foundation";import{createSemantic as We}from"@apollion-dsi/core/themes/semantic";import{defaultInputSpacing as _e}from"@apollion-dsi/core/themes/spacing";import{invertForSurface as Je}from"@apollion-dsi/core/themes/surface";import{converter as Ne,formatHex as Me,interpolate as Be,parse as K}from"culori";var ko=Ne("oklch");function a(e,o,n){let r=K(e),i=K(o);if(!r||!i)return e;let t=Be([r,i],"oklch");return Me(t(n))??e}s(a,"mixOklch");function He(e){return e?typeof e=="string"?e:e.base:"#000"}s(He,"coerceToHex");function d(e,o){let n=He(e),r=o.deepDark,i=o.deepLight;return{base:n,dark:a(n,r,.6),action:a(n,r,.2),active:a(n,i,.6),light:a(n,i,.9)}}s(d,"mountSingleColor");function Q(e){let o=e.deepLight,n=e.deepDark;return{0:o,5:a(o,n,.05),10:a(o,n,.1),20:a(o,n,.2),30:a(o,n,.3),40:a(o,n,.4),50:a(o,n,.5),60:a(o,n,.6),70:a(o,n,.7),80:a(o,n,.8),90:a(o,n,.9),100:a(o,n,1)}}s(Q,"mountGreyscaleColors");function ee(e){let o=e.deepLight,n=e.deepDark,r=e.primary,i=s(c=>1/17*(c-1),"interpolation"),t=a(r,n,.5);return{0:a(t,o,i(18)),5:a(t,o,i(17.5)),10:a(t,o,i(17)),20:a(t,o,i(16)),30:a(t,o,i(15)),40:a(t,o,i(14)),50:a(t,o,i(13)),60:a(t,o,i(12)),70:a(t,o,i(11)),80:a(t,o,i(10)),90:a(t,o,i(9)),100:a(t,o,i(8)),110:a(t,o,i(7)),120:a(t,o,i(6)),130:a(t,o,i(5)),140:a(t,o,i(4)),150:a(t,o,i(3)),160:a(t,o,i(2)),170:t,180:a(t,n,.25)}}s(ee,"mountNeutralColors");import{DIMENSION_MULTIPLIERS as Pe}from"@apollion-dsi/core/themes/dimension";function oe({multiplier:e=.25,alias:o},n){let r=n!==void 0?Pe[n]:e;return(...i)=>i.map(t=>{let c=typeof t=="string"?o[t]:t;return`${r*c}rem`}).join(" ")}s(oe,"createSpacing");function ze(e){return{baseDark:e.baseDark,baseLight:e.baseLight,deepDark:e.deepDark,deepLight:e.deepLight,transparent:e.transparent??"transparent",neutral:ee(e),grayscale:Q(e),main:d(e.main,e),opposite:d(e.opposite,e),complementary:d(e.complementary,e),danger:d(e.danger,e),information:d(e.information,e),success:d(e.success,e),warning:d(e.warning,e),primary:d(e.primary,e),secondary:d(e.secondary,e),tertiary:d(e.tertiary,e)}}s(ze,"composeColors");function re(e,o=_e){let n=ze(e.colors),r=oe(o,e.dimension),i=We({colors:n,spacing:r}),t=ne(i);return e.surface==="negative"&&(t=ne(Je({semantic:i,colors:n},"negative").semantic)),{foundation:t,colors:n}}s(re,"buildVariantThemes");async function _o(e){let{config:o,outDir:n,cwd:r=process.cwd(),verbose:i=!1}=e,t=X(o),c=o.output??{};if(t.length===0)throw new Error("apollion-tokens build: no variants (config.brands is empty)");let l=await Xe(V(qe(),"apollion-tokens-"));try{let u=[];for(let w of t){let{foundation:ce,colors:le}=re(w),S=M(ce,le,w),C=U(w);if(c.css){let m=P(S),p=`css/${C}.css`;await E(l,p,m),u.push({path:p,sha256:k(m)}),i&&console.log(` \u2713 ${p}`)}if(c.json){let m=_(S),p=`json/${C}.json`;await E(l,p,m),u.push({path:p,sha256:k(m)}),i&&console.log(` \u2713 ${p}`)}if(c.ts){let m=z(S),p=`ts/${C}.d.ts`;await E(l,p,m),u.push({path:p,sha256:k(m)}),i&&console.log(` \u2713 ${p}`)}}let I={configHash:j(o),files:u,gitCommit:Y(r),buildEnv:q()},T=Z(I);return await se(V(l,"manifest.json"),T),await te(n,{recursive:!0,force:!0}),await ie(ae(n),{recursive:!0}),await Ye(l,n),i&&console.log(`\u2713 ${u.length} files \u2192 ${n}`),{manifest:I,outDir:n}}catch(u){throw await te(l,{recursive:!0,force:!0}).catch(()=>{}),u}}s(_o,"build");async function Jo(e){let{config:o,outDir:n}=e,r;try{r=JSON.parse(await Ue(V(n,"manifest.json"),"utf8"))}catch{return!1}return r.configHash===j(o)}s(Jo,"check");async function E(e,o,n){let r=V(e,o);await ie(ae(r),{recursive:!0}),await se(r,n)}s(E,"writeAt");export{_o as build,Jo as check};
|
package/lib/cli.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`)}
|
|
4
|
-
`}
|
|
5
|
-
`)}
|
|
6
|
-
`
|
|
7
|
-
`
|
|
8
|
-
`);
|
|
9
|
-
${c}`,n)}return l.data}i(Y,"loadConfig");function jo(){return{log:i((...o)=>console.log("[apollion.config]",...o),"log"),warn:i((...o)=>console.warn("[apollion.config]",...o),"warn"),error:i((...o)=>console.error("[apollion.config]",...o),"error")}}i(jo,"makeReadOnlyConsole");function Io(o){let n=o.slice(2),e=n[0];e!=="build"&&$(`Unknown command "${e??""}". Usage: apollion-tokens build [--check] [--verbose] --config <path> [--out <dir>]`);let r="apollion.config.mjs",t="dist",s=!1,f=!1;for(let l=1;l<n.length;l++){let c=n[l];switch(c){case"--config":r=n[++l]??$("--config needs a path");break;case"--out":t=n[++l]??$("--out needs a path");break;case"--check":s=!0;break;case"--verbose":f=!0;break;default:$(`Unknown flag "${c}"`)}}return{command:e,configPath:r,outDir:t,check:s,verbose:f}}i(Io,"parseArgs");function $(o){console.error(`apollion-tokens: ${o}`),process.exit(2)}i($,"fail");async function Fo(o){let n=j(o);if(n.endsWith(".json")){let e=await Eo(n,"utf8");return JSON.parse(e)}return Y({configPath:n})}i(Fo,"loadConfigFromPath");async function Ro(){let o=Io(process.argv),n=await Fo(o.configPath);if(o.check){await K({config:n,outDir:j(o.outDir)})||(console.error("apollion-tokens: dist/ is stale or missing (configHash mismatch). Re-run `apollion-tokens build` without --check."),process.exit(1)),console.log("apollion-tokens: dist/ is up-to-date with config.");return}let{manifest:e}=await X({config:n,outDir:j(o.outDir),verbose:o.verbose});console.log(`apollion-tokens: wrote ${e.files.length} file(s) \u2014 configHash=${e.configHash.slice(0,12)}\u2026`)}i(Ro,"main");Ro().catch(o=>{console.error(`apollion-tokens: ${o instanceof Error?o.message:String(o)}`),process.exit(1)});
|
|
2
|
+
var Re=Object.defineProperty;var s=(e,o)=>Re(e,"name",{value:o,configurable:!0}),Ie=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(o,n)=>(typeof require<"u"?require:o)[n]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});import{readFile as ho}from"node:fs/promises";import{resolve as P}from"node:path";import{mkdir as pe,mkdtemp as to,readFile as ro,rename as io,rm as ue,writeFile as fe}from"node:fs/promises";import{tmpdir as so}from"node:os";import{dirname as de,join as S}from"node:path";import{defaultInputBorder as A}from"@apollion-dsi/core/themes/border";import{createDeth as xe,levelValues as we}from"@apollion-dsi/core/themes/depth";import{defaultInputFont as b}from"@apollion-dsi/core/themes/font";import{converter as De,formatHex as Ce,parse as Te}from"culori";var Se=De("oklch");function k(e){return typeof e.type=="string"&&"value"in e}s(k,"isToken");function I(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}s(I,"kebab");function H(e){return e.split(".").map(I).join(".")}s(H,"kebabPath");function x(e){return e.kind==="ref"?e.resolved.raw:e.raw}s(x,"rawValue");function C(e,o){return Number(e.toFixed(o))||0}s(C,"round");function F(e){let o=Se(Te(e));if(!o)return{kind:"color",raw:e,components:[0,0,0],hex:"#000000"};let n=[C(o.l,4),C(o.c,4),C(o.h??0,2)],t=Ce(o)??"#000000";return o.alpha!==void 0&&o.alpha!==1?{kind:"color",raw:e,components:n,alpha:C(o.alpha,4),hex:t}:{kind:"color",raw:e,components:n,hex:t}}s(F,"toColorValue");var W=/^(-?(?:\d+\.?\d*|\.\d+))(px|rem)$/;function T(e){let o=W.exec(e.trim());if(o)return{kind:"dimension",raw:e,value:Number(o[1]),unit:o[2]};let n=Number.parseFloat(e);return{kind:"dimension",raw:e,value:Number.isFinite(n)?n:0,unit:"rem"}}s(T,"toDimensionValue");function m(e){return{type:"color",value:F(e)}}s(m,"colorToken");function _(e){let o={};for(let[n,t]of Object.entries(e))o[n]=m(t);return o}s(_,"recordColorGroup");var Ve=["main","opposite","complementary","primary","secondary","tertiary","success","danger","warning","information"];function $e(e){let o={};for(let n of Ve){let t=e[n];o[n]={base:m(t.base),dark:m(t.dark),action:m(t.action),active:m(t.active),light:m(t.light)}}return o.grayscale=_(e.grayscale),o.neutral=_(e.neutral),o.baseDark=m(e.baseDark??"#000000"),o.baseLight=m(e.baseLight??"#ffffff"),o.deepDark=m(e.deepDark??"#000000"),o.deepLight=m(e.deepLight??"#ffffff"),o}s($e,"buildPrimitives");var ve={primary:"color.primary.base",secondary:"color.secondary.base",tertiary:"color.tertiary.base",success:"color.success.base",danger:"color.danger.base",warning:"color.warning.base",info:"color.information.base"},Oe={onPrimary:"color.baseLight",onSecondary:"color.baseLight",onTertiary:"color.baseDark",onSuccess:"color.baseLight",onDanger:"color.baseLight",onWarning:"color.baseDark",onInfo:"color.baseLight"};function je(e,o){let n=e;for(let t of o.split(".").slice(1))if(n&&typeof n=="object"&&t in n)n=n[t];else return;return typeof n=="string"?n:void 0}s(je,"lookupPrimitive");function B(e,o,n){let t={};for(let[i,r]of Object.entries(e)){let a=n[i],l=a?je(o,a):void 0;t[i]=a&&l===r?{type:"color",value:{kind:"ref",path:a,resolved:F(r)}}:m(r)}return t}s(B,"colorRefGroup");function Ae(e){let o={};for(let[n,t]of Object.entries(e))o[n]={type:"dimension",value:T(t)};return o}s(Ae,"dimensionGroup");function Fe(){let e={},o={};for(let[i,r]of Object.entries(A.borderRadius))W.test(r)?e[i]={type:"dimension",value:T(r)}:o[i]=r;let n={};for(let[i,r]of Object.entries(A.borderWidth))n[i]={type:"dimension",value:T(r)};let t={};for(let[i,r]of Object.entries(A.borderStyle))t[i]={type:"strokeStyle",value:{kind:"strokeStyle",raw:r,style:r}};return{group:{radius:e,width:n,style:t},extensions:o}}s(Fe,"buildBorder");function Ee(){let e={};for(let[r,a]of Object.entries(b.fontFamily)){let l=a.replace(/;\s*$/,"").trim();e[r]={type:"fontFamily",value:{kind:"fontFamily",raw:l,family:l}}}let o={};for(let[r,a]of Object.entries(b.fontSize))o[r]={type:"dimension",value:{kind:"dimension",raw:`${a}px`,value:a,unit:"px"}};let n={};for(let[r,a]of Object.entries(b.fontWeight))n[r]={type:"fontWeight",value:{kind:"fontWeight",raw:`${a}`,weight:a}};let t={};for(let[r,a]of Object.entries(b.lineHeight))t[r]={type:"number",value:{kind:"number",raw:`${a}`,number:a}};let i={letterSpacing:b.letterSpacing,textTransform:b.textTransform,fontStyle:b.fontStyle};return{group:{family:e,size:o,weight:n,lineHeight:t},extensions:i}}s(Ee,"buildFont");var Ge=/rgba?\([^)]*\)|#[0-9a-fA-F]+|[a-zA-Z]+$/;function Ne(e){let o=e.trim(),n=o.startsWith("inset");n&&(o=o.slice(5).trim());let t=o.match(Ge),i=t?t[0]:"#000000",a=(t?o.slice(0,t.index).trim():o).split(/\s+/).filter(Boolean),l=s(u=>T(a[u]??"0px"),"dim");return{kind:"shadow",raw:e,shadow:{color:F(i),offsetX:l(0),offsetY:l(1),blur:l(2),spread:l(3),inset:n}}}s(Ne,"parseShadow");function Le(e){let o=xe(e),n={};for(let t of Object.keys(we))n[t]={type:"shadow",value:Ne(o(t))};return n}s(Le,"buildShadow");function J(e,o,n){let t={brand:n.brand,mode:n.mode,surface:n.surface,dimension:n.dimension},i=Fe(),r=Ee(),a={"com.apollion.variant":t,"com.apollion.font":r.extensions};return Object.keys(i.extensions).length>0&&(a["com.apollion.border"]={radius:i.extensions}),{description:`Apollion DS tokens \u2014 DTCG 2025.10. Variant: ${t.brand}/${t.mode}/${t.surface}/${t.dimension}.`,meta:t,primitives:$e(o),groups:{bg:B(e.bg,o,ve),text:B(e.text,o,Oe),spacing:Ae(e.spacing),border:i.group,font:r.group,shadow:Le(o)},extensions:a}}s(J,"buildIR");var Me="/* Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs. */";function z(e,o,n){for(let[t,i]of Object.entries(e)){let r=[...o,I(t)];k(i)?n.push(` --apollion-${r.join("-")}: ${x(i.value)};`):z(i,r,n)}}s(z,"emitVars");function U(e,o,n){for(let[t,i]of Object.entries(e)){let r=[...o,I(t)];k(i)?i.type==="color"&&n.push(` @property --apollion-${r.join("-")} { syntax: '<color>'; inherits: true; initial-value: ${x(i.value)}; }`):U(i,r,n)}}s(U,"emitProperties");function X(e){let{brand:o,mode:n,surface:t,dimension:i}=e.meta,r=[Me,""];return r.push(`/* Variant: brand=${o} mode=${n} surface=${t} dimension=${i} */`,"",":root {"),z(e.groups,[],r),r.push("}",""),r.push("@supports (background: paint(squircle)) or (color: oklch(0% 0 0)) {"),U(e.groups,[],r),r.push("}",""),r.join(`
|
|
3
|
+
`)}s(X,"renderCss");function Y(e){return e.alpha!==void 0?{colorSpace:"oklch",components:[...e.components],alpha:e.alpha,hex:e.hex}:{colorSpace:"oklch",components:[...e.components],hex:e.hex}}s(Y,"colorValue");function w(e){return{value:e.value,unit:e.unit}}s(w,"dimensionValue");function Pe(e){let o=e.shadow,n={color:Y(o.color),offsetX:w(o.offsetX),offsetY:w(o.offsetY),blur:w(o.blur),spread:w(o.spread)};return o.inset&&(n.inset=!0),n}s(Pe,"shadowValue");function _e(e){switch(e.kind){case"ref":return`{${H(e.path)}}`;case"color":return Y(e);case"dimension":return w(e);case"fontFamily":return e.family;case"fontWeight":return e.weight;case"number":return e.number;case"strokeStyle":return e.style;case"shadow":return Pe(e)}}s(_e,"projectValue");function Be(e){let o={$value:_e(e.value),$type:e.type};return e.description!==void 0&&(o.$description=e.description),e.deprecated!==void 0&&(o.$deprecated=e.deprecated),o}s(Be,"projectToken");function E(e){let o={};for(let[n,t]of Object.entries(e))o[I(n)]=k(t)?Be(t):E(t);return o}s(E,"projectGroup");function q(e){let o={$description:e.description,color:E(e.primitives),...E(e.groups),$extensions:e.extensions};return`${JSON.stringify(o,null,2)}
|
|
4
|
+
`}s(q,"renderJson");var He="// Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs.";function Z(e,o,n){let t=" ".repeat(o);for(let[i,r]of Object.entries(e))k(r)?n.push(`${t}${JSON.stringify(i)}: ${JSON.stringify(x(r.value))},`):(n.push(`${t}${i}: {`),Z(r,o+1,n),n.push(`${t}},`))}s(Z,"emitGroup");function K(e){let{brand:o,mode:n,surface:t,dimension:i}=e.meta,r=[He,`// Variant: brand=${o} mode=${n} surface=${t} dimension=${i}`,"","export const tokens = {"];return Z(e.groups,1,r),r.push("} as const;","","export type Tokens = typeof tokens;",""),r.join(`
|
|
5
|
+
`)}s(K,"renderTs");var We=["light","dark"],Je=["positive","negative"],ze=["compact","normal","spacious"];function Q(e){let o=e.modes??We,n=e.surfaces??Je,t=e.dimensions??ze,i=Object.entries(e.brands).sort(([a],[l])=>a.localeCompare(l)),r=[];for(let[a,l]of i)for(let u of o)for(let h of n)for(let $ of t)r.push({brand:a,colors:l,mode:u,surface:h,dimension:$});return r}s(Q,"expandVariants");function ee(e){return`${e.brand}.${e.mode}.${e.surface}.${e.dimension}`}s(ee,"variantName");import{createHash as Ue}from"node:crypto";function D(e){return Ue("sha256").update(e).digest("hex")}s(D,"sha256");function N(e){return D(G(e))}s(N,"hashConfig");function G(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(G).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${G(e[t])}`).join(",")}}`}s(G,"stableStringify");function oe(e){try{let{execSync:o}=Ie("node:child_process");return o("git rev-parse --short HEAD",{cwd:e,stdio:["ignore","pipe","ignore"],encoding:"utf8"}).trim()}catch{return""}}s(oe,"detectGitCommit");function ne(){return{node:process.version,platform:`${process.platform}-${process.arch}`}}s(ne,"buildEnv");function te(e){let o=[...e.files].sort((t,i)=>t.path.localeCompare(i.path)),n={configHash:e.configHash,files:o,gitCommit:e.gitCommit,buildEnv:e.buildEnv};return`${JSON.stringify(n,null,2)}
|
|
6
|
+
`}s(te,"serializeManifest");import{createFoundation as ce}from"@apollion-dsi/core/themes/foundation";import{createSemantic as Qe}from"@apollion-dsi/core/themes/semantic";import{defaultInputSpacing as eo}from"@apollion-dsi/core/themes/spacing";import{invertForSurface as oo}from"@apollion-dsi/core/themes/surface";import{converter as Xe,formatHex as Ye,interpolate as qe,parse as re}from"culori";var Bo=Xe("oklch");function c(e,o,n){let t=re(e),i=re(o);if(!t||!i)return e;let r=qe([t,i],"oklch");return Ye(r(n))??e}s(c,"mixOklch");function Ze(e){return e?typeof e=="string"?e:e.base:"#000"}s(Ze,"coerceToHex");function g(e,o){let n=Ze(e),t=o.deepDark,i=o.deepLight;return{base:n,dark:c(n,t,.6),action:c(n,t,.2),active:c(n,i,.6),light:c(n,i,.9)}}s(g,"mountSingleColor");function ie(e){let o=e.deepLight,n=e.deepDark;return{0:o,5:c(o,n,.05),10:c(o,n,.1),20:c(o,n,.2),30:c(o,n,.3),40:c(o,n,.4),50:c(o,n,.5),60:c(o,n,.6),70:c(o,n,.7),80:c(o,n,.8),90:c(o,n,.9),100:c(o,n,1)}}s(ie,"mountGreyscaleColors");function se(e){let o=e.deepLight,n=e.deepDark,t=e.primary,i=s(a=>1/17*(a-1),"interpolation"),r=c(t,n,.5);return{0:c(r,o,i(18)),5:c(r,o,i(17.5)),10:c(r,o,i(17)),20:c(r,o,i(16)),30:c(r,o,i(15)),40:c(r,o,i(14)),50:c(r,o,i(13)),60:c(r,o,i(12)),70:c(r,o,i(11)),80:c(r,o,i(10)),90:c(r,o,i(9)),100:c(r,o,i(8)),110:c(r,o,i(7)),120:c(r,o,i(6)),130:c(r,o,i(5)),140:c(r,o,i(4)),150:c(r,o,i(3)),160:c(r,o,i(2)),170:r,180:c(r,n,.25)}}s(se,"mountNeutralColors");import{DIMENSION_MULTIPLIERS as Ke}from"@apollion-dsi/core/themes/dimension";function ae({multiplier:e=.25,alias:o},n){let t=n!==void 0?Ke[n]:e;return(...i)=>i.map(r=>{let a=typeof r=="string"?o[r]:r;return`${t*a}rem`}).join(" ")}s(ae,"createSpacing");function no(e){return{baseDark:e.baseDark,baseLight:e.baseLight,deepDark:e.deepDark,deepLight:e.deepLight,transparent:e.transparent??"transparent",neutral:se(e),grayscale:ie(e),main:g(e.main,e),opposite:g(e.opposite,e),complementary:g(e.complementary,e),danger:g(e.danger,e),information:g(e.information,e),success:g(e.success,e),warning:g(e.warning,e),primary:g(e.primary,e),secondary:g(e.secondary,e),tertiary:g(e.tertiary,e)}}s(no,"composeColors");function le(e,o=eo){let n=no(e.colors),t=ae(o,e.dimension),i=Qe({colors:n,spacing:t}),r=ce(i);return e.surface==="negative"&&(r=ce(oo({semantic:i,colors:n},"negative").semantic)),{foundation:r,colors:n}}s(le,"buildVariantThemes");async function me(e){let{config:o,outDir:n,cwd:t=process.cwd(),verbose:i=!1}=e,r=Q(o),a=o.output??{};if(r.length===0)throw new Error("apollion-tokens build: no variants (config.brands is empty)");let l=await to(S(so(),"apollion-tokens-"));try{let u=[];for(let v of r){let{foundation:be,colors:ke}=le(v),O=J(be,ke,v),j=ee(v);if(a.css){let y=X(O),d=`css/${j}.css`;await L(l,d,y),u.push({path:d,sha256:D(y)}),i&&console.log(` \u2713 ${d}`)}if(a.json){let y=q(O),d=`json/${j}.json`;await L(l,d,y),u.push({path:d,sha256:D(y)}),i&&console.log(` \u2713 ${d}`)}if(a.ts){let y=K(O),d=`ts/${j}.d.ts`;await L(l,d,y),u.push({path:d,sha256:D(y)}),i&&console.log(` \u2713 ${d}`)}}let h={configHash:N(o),files:u,gitCommit:oe(t),buildEnv:ne()},$=te(h);return await fe(S(l,"manifest.json"),$),await ue(n,{recursive:!0,force:!0}),await pe(de(n),{recursive:!0}),await io(l,n),i&&console.log(`\u2713 ${u.length} files \u2192 ${n}`),{manifest:h,outDir:n}}catch(u){throw await ue(l,{recursive:!0,force:!0}).catch(()=>{}),u}}s(me,"build");async function ge(e){let{config:o,outDir:n}=e,t;try{t=JSON.parse(await ro(S(n,"manifest.json"),"utf8"))}catch{return!1}return t.configHash===N(o)}s(ge,"check");async function L(e,o,n){let t=S(e,o);await pe(de(t),{recursive:!0}),await fe(t,n)}s(L,"writeAt");import{readFile as co}from"node:fs/promises";import{resolve as lo}from"node:path";import{createContext as uo,Script as po}from"node:vm";import{z as p}from"zod";var f=p.string().regex(/^#([0-9a-fA-F]{3}){1,2}$|^transparent$|^rgb\(.*\)$|^oklch\(.*\)$/,{message:'Must be hex, rgb(), oklch(), or "transparent"'}),ao=p.object({baseDark:p.string().optional(),baseLight:p.string().optional(),deepDark:f,deepLight:f,transparent:p.string().optional(),main:f.optional(),opposite:f.optional(),complementary:f.optional(),information:f.optional(),success:f.optional(),danger:f.optional(),warning:f.optional(),primary:f.optional(),secondary:f.optional(),tertiary:f.optional()}).passthrough(),he=p.object({brands:p.record(p.string(),ao).refine(e=>Object.keys(e).length>0,{message:"apollion.config.mjs: brands must contain at least 1 entry"}),modes:p.array(p.enum(["light","dark"])).optional(),surfaces:p.array(p.enum(["positive","negative"])).optional(),dimensions:p.array(p.enum(["compact","normal","spacious"])).optional(),output:p.object({runtime:p.boolean().optional(),css:p.boolean().optional(),json:p.boolean().optional(),ts:p.boolean().optional()}).passthrough().optional()}).passthrough();var fo=[/\brequire\s*\(/,/\bimport\s*\(/,/^\s*import\s+/m],mo=1e3;var M=class M extends Error{constructor(n,t){super(n);this.configPath=t;this.name="ConfigLoadError"}};s(M,"ConfigLoadError");var R=M;async function ye(e){let o=lo(e.configPath),n=await co(o,"utf8");for(let u of fo)if(u.test(n))throw new R(`apollion.config: dynamic require/import not allowed (matched ${u}). Declarative config only \u2014 see ADR-006 \xA73.8 B3.`,o);let t=n.replace(/(^|\n)\s*export\s+default\s+/,(u,h)=>`${h}__apollion_config_export = `);if(t===n)throw new R("apollion.config: missing `export default <object>;` \u2014 config must export a default object literal.",o);let i={__apollion_config_export:void 0,console:go(),Symbol,Number,String,Boolean,Array,Object,JSON,Math,defineConfig:s(u=>u,"defineConfig")},r=uo(i);try{new po(t,{filename:o}).runInContext(r,{timeout:mo})}catch(u){throw new R(`apollion.config: evaluation failed \u2014 ${u instanceof Error?u.message:String(u)}`,o)}let a=i.__apollion_config_export;if(a===void 0)throw new R("apollion.config: `export default` produced undefined.",o);let l=he.safeParse(a);if(!l.success){let u=l.error.issues.map(h=>` ${h.path.join(".")||"(root)"}: ${h.message}`).join(`
|
|
7
|
+
`);throw new R(`apollion.config: schema validation failed \u2014
|
|
8
|
+
${u}`,o)}return l.data}s(ye,"loadConfig");function go(){return{log:s((...e)=>console.log("[apollion.config]",...e),"log"),warn:s((...e)=>console.warn("[apollion.config]",...e),"warn"),error:s((...e)=>console.error("[apollion.config]",...e),"error")}}s(go,"makeReadOnlyConsole");function yo(e){let o=e.slice(2),n=o[0];n!=="build"&&V(`Unknown command "${n??""}". Usage: apollion-tokens build [--check] [--verbose] --config <path> [--out <dir>]`);let t="apollion.config.mjs",i="dist",r=!1,a=!1;for(let l=1;l<o.length;l++){let u=o[l];switch(u){case"--config":t=o[++l]??V("--config needs a path");break;case"--out":i=o[++l]??V("--out needs a path");break;case"--check":r=!0;break;case"--verbose":a=!0;break;default:V(`Unknown flag "${u}"`)}}return{command:n,configPath:t,outDir:i,check:r,verbose:a}}s(yo,"parseArgs");function V(e){console.error(`apollion-tokens: ${e}`),process.exit(2)}s(V,"fail");async function bo(e){let o=P(e);if(o.endsWith(".json")){let n=await ho(o,"utf8");return JSON.parse(n)}return ye({configPath:o})}s(bo,"loadConfigFromPath");async function ko(){let e=yo(process.argv),o=await bo(e.configPath);if(e.check){await ge({config:o,outDir:P(e.outDir)})||(console.error("apollion-tokens: dist/ is stale or missing (configHash mismatch). Re-run `apollion-tokens build` without --check."),process.exit(1)),console.log("apollion-tokens: dist/ is up-to-date with config.");return}let{manifest:n}=await me({config:o,outDir:P(e.outDir),verbose:e.verbose});console.log(`apollion-tokens: wrote ${n.files.length} file(s) \u2014 configHash=${n.configHash.slice(0,12)}\u2026`)}s(ko,"main");ko().catch(e=>{console.error(`apollion-tokens: ${e instanceof Error?e.message:String(e)}`),process.exit(1)});
|
package/package.json
CHANGED
package/src/build.ts
CHANGED
|
@@ -28,9 +28,10 @@ import { renderJson } from './renderers/json';
|
|
|
28
28
|
import { renderTs } from './renderers/ts';
|
|
29
29
|
import type { ApollionConfig, Variant } from './config-schema';
|
|
30
30
|
import { expandVariants, variantName } from './config-schema';
|
|
31
|
+
import { buildIR } from './ir';
|
|
31
32
|
import type { BuildManifest, ManifestFileEntry } from './manifest';
|
|
32
33
|
import { buildEnv, detectGitCommit, hashConfig, serializeManifest, sha256 } from './manifest';
|
|
33
|
-
import {
|
|
34
|
+
import { buildVariantThemes } from './theme-factory';
|
|
34
35
|
|
|
35
36
|
export interface BuildOptions {
|
|
36
37
|
config: ApollionConfig;
|
|
@@ -60,11 +61,12 @@ export async function build(opts: BuildOptions): Promise<BuildResult> {
|
|
|
60
61
|
const files: ManifestFileEntry[] = [];
|
|
61
62
|
|
|
62
63
|
for (const variant of variants) {
|
|
63
|
-
const foundation =
|
|
64
|
+
const { foundation, colors } = buildVariantThemes(variant);
|
|
65
|
+
const ir = buildIR(foundation, colors, variant);
|
|
64
66
|
const baseName = variantName(variant);
|
|
65
67
|
|
|
66
68
|
if (output.css) {
|
|
67
|
-
const content = renderCss(
|
|
69
|
+
const content = renderCss(ir);
|
|
68
70
|
const path = `css/${baseName}.css`;
|
|
69
71
|
await writeAt(tmpRoot, path, content);
|
|
70
72
|
files.push({ path, sha256: sha256(content) });
|
|
@@ -72,7 +74,7 @@ export async function build(opts: BuildOptions): Promise<BuildResult> {
|
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
if (output.json) {
|
|
75
|
-
const content = renderJson(
|
|
77
|
+
const content = renderJson(ir);
|
|
76
78
|
const path = `json/${baseName}.json`;
|
|
77
79
|
await writeAt(tmpRoot, path, content);
|
|
78
80
|
files.push({ path, sha256: sha256(content) });
|
|
@@ -80,7 +82,7 @@ export async function build(opts: BuildOptions): Promise<BuildResult> {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
if (output.ts) {
|
|
83
|
-
const content = renderTs(
|
|
85
|
+
const content = renderTs(ir);
|
|
84
86
|
const path = `ts/${baseName}.d.ts`;
|
|
85
87
|
await writeAt(tmpRoot, path, content);
|
|
86
88
|
files.push({ path, sha256: sha256(content) });
|
package/src/ir.ts
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Intermediate Representation (IR) — the typed, tool-agnostic model that
|
|
3
|
+
* every renderer (JSON/CSS/TS) projects from.
|
|
4
|
+
*
|
|
5
|
+
* **Why (improve-architecture B3):** the IR centralizes token typing so a
|
|
6
|
+
* renderer is a pure projection — a new token type or output format is added in
|
|
7
|
+
* one place.
|
|
8
|
+
*
|
|
9
|
+
* **Layers:**
|
|
10
|
+
* - `primitives` — the structural palette as the colour SSOT (`color.*`, R4).
|
|
11
|
+
* - `groups` — foundation aliases (`bg`/`text`) + scales (`spacing`) +
|
|
12
|
+
* composite/scale layers (`border`/`font`/`shadow`, S4). Foundation colours
|
|
13
|
+
* reference primitives (`{color.x.y}`); diverging values fall back to inline.
|
|
14
|
+
*
|
|
15
|
+
* **DTCG-expressibility (S4):** values DTCG cannot model (font `letterSpacing`
|
|
16
|
+
* in `em`, `textTransform`, `fontStyle`, border `circular: 100%`) are carried in
|
|
17
|
+
* the document `$extensions` under `com.apollion.*` rather than dropped.
|
|
18
|
+
*
|
|
19
|
+
* **Byte-fidelity:** each value keeps its original resolved CSS string (`raw`)
|
|
20
|
+
* so CSS/TS projections (which resolve references) stay faithful, while JSON
|
|
21
|
+
* emits the DTCG-structured + referenced form.
|
|
22
|
+
*
|
|
23
|
+
* @see renderers/json.ts · renderers/css.ts · renderers/ts.ts
|
|
24
|
+
* @see ADR-006 §3.10 · tech-radar R1/R2 (structured) + R4 (references) + R3/B3
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { defaultInputBorder } from '@apollion-dsi/core/themes/border';
|
|
28
|
+
import { createDeth, levelValues } from '@apollion-dsi/core/themes/depth';
|
|
29
|
+
import { defaultInputFont } from '@apollion-dsi/core/themes/font';
|
|
30
|
+
import type { FoundationLayer } from '@apollion-dsi/core/themes/foundation';
|
|
31
|
+
import { converter, formatHex, parse } from 'culori';
|
|
32
|
+
|
|
33
|
+
import type { Variant } from './config-schema';
|
|
34
|
+
import type { ColorsThemeInterface } from './theme-factory';
|
|
35
|
+
|
|
36
|
+
const toOklch = converter('oklch');
|
|
37
|
+
|
|
38
|
+
export type IRTokenType = 'color' | 'dimension' | 'fontFamily' | 'fontWeight' | 'number' | 'strokeStyle' | 'shadow';
|
|
39
|
+
|
|
40
|
+
export interface IRColorValue {
|
|
41
|
+
readonly kind: 'color';
|
|
42
|
+
/** Original resolved CSS color string — preserves CSS/TS byte-fidelity. */
|
|
43
|
+
readonly raw: string;
|
|
44
|
+
/** OKLch components `[L, C, H]` — L ∈ [0,1], C ≥ 0, H in degrees. */
|
|
45
|
+
readonly components: readonly [number, number, number];
|
|
46
|
+
readonly alpha?: number;
|
|
47
|
+
/** sRGB hex fallback (normalized). */
|
|
48
|
+
readonly hex: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface IRDimensionValue {
|
|
52
|
+
readonly kind: 'dimension';
|
|
53
|
+
readonly raw: string;
|
|
54
|
+
readonly value: number;
|
|
55
|
+
readonly unit: 'px' | 'rem';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface IRFontFamilyValue {
|
|
59
|
+
readonly kind: 'fontFamily';
|
|
60
|
+
readonly raw: string;
|
|
61
|
+
readonly family: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface IRFontWeightValue {
|
|
65
|
+
readonly kind: 'fontWeight';
|
|
66
|
+
readonly raw: string;
|
|
67
|
+
readonly weight: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface IRNumberValue {
|
|
71
|
+
readonly kind: 'number';
|
|
72
|
+
readonly raw: string;
|
|
73
|
+
readonly number: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface IRStrokeStyleValue {
|
|
77
|
+
readonly kind: 'strokeStyle';
|
|
78
|
+
readonly raw: string;
|
|
79
|
+
readonly style: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface IRShadowLayer {
|
|
83
|
+
readonly color: IRColorValue;
|
|
84
|
+
readonly offsetX: IRDimensionValue;
|
|
85
|
+
readonly offsetY: IRDimensionValue;
|
|
86
|
+
readonly blur: IRDimensionValue;
|
|
87
|
+
readonly spread: IRDimensionValue;
|
|
88
|
+
readonly inset: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface IRShadowValue {
|
|
92
|
+
readonly kind: 'shadow';
|
|
93
|
+
readonly raw: string;
|
|
94
|
+
readonly shadow: IRShadowLayer;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** A reference to another token (DTCG alias). `resolved` carries the target's
|
|
98
|
+
* value so CSS/TS can project a concrete value without graph traversal. */
|
|
99
|
+
export interface IRReference {
|
|
100
|
+
readonly kind: 'ref';
|
|
101
|
+
readonly path: string;
|
|
102
|
+
readonly resolved: IRColorValue | IRDimensionValue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type IRValue =
|
|
106
|
+
| IRColorValue
|
|
107
|
+
| IRDimensionValue
|
|
108
|
+
| IRFontFamilyValue
|
|
109
|
+
| IRFontWeightValue
|
|
110
|
+
| IRNumberValue
|
|
111
|
+
| IRStrokeStyleValue
|
|
112
|
+
| IRShadowValue
|
|
113
|
+
| IRReference;
|
|
114
|
+
|
|
115
|
+
export interface IRToken {
|
|
116
|
+
readonly type: IRTokenType;
|
|
117
|
+
readonly value: IRValue;
|
|
118
|
+
readonly description?: string;
|
|
119
|
+
readonly deprecated?: boolean | string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface IRGroup {
|
|
123
|
+
readonly [key: string]: IRToken | IRGroup;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface IRVariantMeta {
|
|
127
|
+
readonly brand: string;
|
|
128
|
+
readonly mode: string;
|
|
129
|
+
readonly surface: string;
|
|
130
|
+
readonly dimension: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface IRDocument {
|
|
134
|
+
readonly description: string;
|
|
135
|
+
readonly meta: IRVariantMeta;
|
|
136
|
+
/** Colour SSOT — structural palette primitives (`color.*`). */
|
|
137
|
+
readonly primitives: IRGroup;
|
|
138
|
+
/** Foundation + scale + composite layers (`bg`/`text`/`spacing`/`border`/`font`/`shadow`). */
|
|
139
|
+
readonly groups: IRGroup;
|
|
140
|
+
readonly extensions: Readonly<Record<string, unknown>>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Narrow an IR node to a token (vs a nested group). */
|
|
144
|
+
export function isToken(node: IRToken | IRGroup): node is IRToken {
|
|
145
|
+
return typeof (node as IRToken).type === 'string' && 'value' in node;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** `camelCase` → `kebab-case` token-name normalization (JSON/CSS). */
|
|
149
|
+
export function kebab(key: string): string {
|
|
150
|
+
return key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Kebab-case each segment of a dotted IR path (for DTCG `{...}` aliases). */
|
|
154
|
+
export function kebabPath(path: string): string {
|
|
155
|
+
return path.split('.').map(kebab).join('.');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Concrete value of an IR value — resolving references to their target. */
|
|
159
|
+
export function rawValue(value: IRValue): string {
|
|
160
|
+
return value.kind === 'ref' ? value.resolved.raw : value.raw;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function round(value: number, dp: number): number {
|
|
164
|
+
return Number(value.toFixed(dp)) || 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Build an OKLch IR color value from a resolved CSS color string. */
|
|
168
|
+
export function toColorValue(raw: string): IRColorValue {
|
|
169
|
+
const oklch = toOklch(parse(raw));
|
|
170
|
+
if (!oklch) return { kind: 'color', raw, components: [0, 0, 0], hex: '#000000' };
|
|
171
|
+
|
|
172
|
+
const components: readonly [number, number, number] = [round(oklch.l, 4), round(oklch.c, 4), round(oklch.h ?? 0, 2)];
|
|
173
|
+
const hex = formatHex(oklch) ?? '#000000';
|
|
174
|
+
|
|
175
|
+
return oklch.alpha !== undefined && oklch.alpha !== 1
|
|
176
|
+
? { kind: 'color', raw, components, alpha: round(oklch.alpha, 4), hex }
|
|
177
|
+
: { kind: 'color', raw, components, hex };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const DIMENSION_RE = /^(-?(?:\d+\.?\d*|\.\d+))(px|rem)$/;
|
|
181
|
+
|
|
182
|
+
/** Build an IR dimension value from a resolved CSS length string. */
|
|
183
|
+
export function toDimensionValue(raw: string): IRDimensionValue {
|
|
184
|
+
const match = DIMENSION_RE.exec(raw.trim());
|
|
185
|
+
if (match) return { kind: 'dimension', raw, value: Number(match[1]), unit: match[2] as 'px' | 'rem' };
|
|
186
|
+
|
|
187
|
+
const value = Number.parseFloat(raw);
|
|
188
|
+
return { kind: 'dimension', raw, value: Number.isFinite(value) ? value : 0, unit: 'rem' };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function colorToken(raw: string): IRToken {
|
|
192
|
+
return { type: 'color', value: toColorValue(raw) };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function recordColorGroup(record: Record<string, string>): IRGroup {
|
|
196
|
+
const out: Record<string, IRToken> = {};
|
|
197
|
+
for (const [key, value] of Object.entries(record)) out[key] = colorToken(value);
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const NAMED_PALETTES = [
|
|
202
|
+
'main',
|
|
203
|
+
'opposite',
|
|
204
|
+
'complementary',
|
|
205
|
+
'primary',
|
|
206
|
+
'secondary',
|
|
207
|
+
'tertiary',
|
|
208
|
+
'success',
|
|
209
|
+
'danger',
|
|
210
|
+
'warning',
|
|
211
|
+
'information',
|
|
212
|
+
] as const;
|
|
213
|
+
|
|
214
|
+
/** Structural palette → colour primitives (`color.*`). The SSOT layer. */
|
|
215
|
+
function buildPrimitives(colors: ColorsThemeInterface): IRGroup {
|
|
216
|
+
const out: Record<string, IRToken | IRGroup> = {};
|
|
217
|
+
for (const name of NAMED_PALETTES) {
|
|
218
|
+
const palette = colors[name];
|
|
219
|
+
out[name] = {
|
|
220
|
+
base: colorToken(palette.base),
|
|
221
|
+
dark: colorToken(palette.dark),
|
|
222
|
+
action: colorToken(palette.action),
|
|
223
|
+
active: colorToken(palette.active),
|
|
224
|
+
light: colorToken(palette.light),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
out.grayscale = recordColorGroup(colors.grayscale);
|
|
228
|
+
out.neutral = recordColorGroup(colors.neutral);
|
|
229
|
+
out.baseDark = colorToken(colors.baseDark ?? '#000000');
|
|
230
|
+
out.baseLight = colorToken(colors.baseLight ?? '#ffffff');
|
|
231
|
+
out.deepDark = colorToken(colors.deepDark ?? '#000000');
|
|
232
|
+
out.deepLight = colorToken(colors.deepLight ?? '#ffffff');
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Foundation role → primitive IR path (static mapping, verified vs core). */
|
|
237
|
+
const BG_REF: Readonly<Record<string, string>> = {
|
|
238
|
+
primary: 'color.primary.base',
|
|
239
|
+
secondary: 'color.secondary.base',
|
|
240
|
+
tertiary: 'color.tertiary.base',
|
|
241
|
+
success: 'color.success.base',
|
|
242
|
+
danger: 'color.danger.base',
|
|
243
|
+
warning: 'color.warning.base',
|
|
244
|
+
info: 'color.information.base',
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const TEXT_REF: Readonly<Record<string, string>> = {
|
|
248
|
+
onPrimary: 'color.baseLight',
|
|
249
|
+
onSecondary: 'color.baseLight',
|
|
250
|
+
onTertiary: 'color.baseDark',
|
|
251
|
+
onSuccess: 'color.baseLight',
|
|
252
|
+
onDanger: 'color.baseLight',
|
|
253
|
+
onWarning: 'color.baseDark',
|
|
254
|
+
onInfo: 'color.baseLight',
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/** Navigate the structural palette by IR path (`color.x.y`). */
|
|
258
|
+
function lookupPrimitive(colors: ColorsThemeInterface, path: string): string | undefined {
|
|
259
|
+
let node: unknown = colors;
|
|
260
|
+
for (const segment of path.split('.').slice(1)) {
|
|
261
|
+
if (node && typeof node === 'object' && segment in node) {
|
|
262
|
+
node = (node as Record<string, unknown>)[segment];
|
|
263
|
+
} else {
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return typeof node === 'string' ? node : undefined;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Colour layer: reference the primitive when the resolved value matches, else inline. */
|
|
271
|
+
function colorRefGroup(
|
|
272
|
+
layer: Record<string, string>,
|
|
273
|
+
colors: ColorsThemeInterface,
|
|
274
|
+
refMap: Readonly<Record<string, string>>,
|
|
275
|
+
): IRGroup {
|
|
276
|
+
const out: Record<string, IRToken> = {};
|
|
277
|
+
for (const [key, value] of Object.entries(layer)) {
|
|
278
|
+
const path = refMap[key];
|
|
279
|
+
const primitive = path ? lookupPrimitive(colors, path) : undefined;
|
|
280
|
+
out[key] =
|
|
281
|
+
path && primitive === value
|
|
282
|
+
? { type: 'color', value: { kind: 'ref', path, resolved: toColorValue(value) } }
|
|
283
|
+
: colorToken(value);
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function dimensionGroup(layer: Record<string, string>): IRGroup {
|
|
289
|
+
const out: Record<string, IRToken> = {};
|
|
290
|
+
for (const [key, value] of Object.entries(layer)) {
|
|
291
|
+
out[key] = { type: 'dimension', value: toDimensionValue(value) };
|
|
292
|
+
}
|
|
293
|
+
return out;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Border scales: px → dimension, style → strokeStyle. Non-px (e.g. `100%`)
|
|
297
|
+
* goes to `$extensions`. */
|
|
298
|
+
function buildBorder(): { group: IRGroup; extensions: Record<string, string> } {
|
|
299
|
+
const radius: Record<string, IRToken> = {};
|
|
300
|
+
const extRadius: Record<string, string> = {};
|
|
301
|
+
for (const [key, value] of Object.entries(defaultInputBorder.borderRadius)) {
|
|
302
|
+
if (DIMENSION_RE.test(value)) radius[key] = { type: 'dimension', value: toDimensionValue(value) };
|
|
303
|
+
else extRadius[key] = value;
|
|
304
|
+
}
|
|
305
|
+
const width: Record<string, IRToken> = {};
|
|
306
|
+
for (const [key, value] of Object.entries(defaultInputBorder.borderWidth)) {
|
|
307
|
+
width[key] = { type: 'dimension', value: toDimensionValue(value) };
|
|
308
|
+
}
|
|
309
|
+
const style: Record<string, IRToken> = {};
|
|
310
|
+
for (const [key, value] of Object.entries(defaultInputBorder.borderStyle)) {
|
|
311
|
+
style[key] = { type: 'strokeStyle', value: { kind: 'strokeStyle', raw: value, style: value } };
|
|
312
|
+
}
|
|
313
|
+
return { group: { radius, width, style }, extensions: extRadius };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Font scales: family/size/weight/lineHeight as DTCG types; non-DTCG bits
|
|
317
|
+
* (letterSpacing/textTransform/fontStyle) go to `$extensions`. */
|
|
318
|
+
function buildFont(): { group: IRGroup; extensions: Record<string, unknown> } {
|
|
319
|
+
const family: Record<string, IRToken> = {};
|
|
320
|
+
for (const [key, value] of Object.entries(defaultInputFont.fontFamily)) {
|
|
321
|
+
const clean = value.replace(/;\s*$/, '').trim();
|
|
322
|
+
family[key] = { type: 'fontFamily', value: { kind: 'fontFamily', raw: clean, family: clean } };
|
|
323
|
+
}
|
|
324
|
+
const size: Record<string, IRToken> = {};
|
|
325
|
+
for (const [key, value] of Object.entries(defaultInputFont.fontSize)) {
|
|
326
|
+
size[key] = { type: 'dimension', value: { kind: 'dimension', raw: `${value}px`, value, unit: 'px' } };
|
|
327
|
+
}
|
|
328
|
+
const weight: Record<string, IRToken> = {};
|
|
329
|
+
for (const [key, value] of Object.entries(defaultInputFont.fontWeight)) {
|
|
330
|
+
weight[key] = { type: 'fontWeight', value: { kind: 'fontWeight', raw: `${value}`, weight: value } };
|
|
331
|
+
}
|
|
332
|
+
const lineHeight: Record<string, IRToken> = {};
|
|
333
|
+
for (const [key, value] of Object.entries(defaultInputFont.lineHeight)) {
|
|
334
|
+
lineHeight[key] = { type: 'number', value: { kind: 'number', raw: `${value}`, number: value } };
|
|
335
|
+
}
|
|
336
|
+
const extensions = {
|
|
337
|
+
letterSpacing: defaultInputFont.letterSpacing,
|
|
338
|
+
textTransform: defaultInputFont.textTransform,
|
|
339
|
+
fontStyle: defaultInputFont.fontStyle,
|
|
340
|
+
};
|
|
341
|
+
return { group: { family, size, weight, lineHeight }, extensions };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const SHADOW_COLOR_RE = /rgba?\([^)]*\)|#[0-9a-fA-F]+|[a-zA-Z]+$/;
|
|
345
|
+
|
|
346
|
+
/** Parse a CSS box-shadow string into a DTCG-shaped shadow layer. */
|
|
347
|
+
function parseShadow(css: string): IRShadowValue {
|
|
348
|
+
let body = css.trim();
|
|
349
|
+
const inset = body.startsWith('inset');
|
|
350
|
+
if (inset) body = body.slice('inset'.length).trim();
|
|
351
|
+
|
|
352
|
+
const colorMatch = body.match(SHADOW_COLOR_RE);
|
|
353
|
+
const colorStr = colorMatch ? colorMatch[0] : '#000000';
|
|
354
|
+
const lengthsPart = colorMatch ? body.slice(0, colorMatch.index).trim() : body;
|
|
355
|
+
const lengths = lengthsPart.split(/\s+/).filter(Boolean);
|
|
356
|
+
const dim = (i: number): IRDimensionValue => toDimensionValue(lengths[i] ?? '0px');
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
kind: 'shadow',
|
|
360
|
+
raw: css,
|
|
361
|
+
shadow: { color: toColorValue(colorStr), offsetX: dim(0), offsetY: dim(1), blur: dim(2), spread: dim(3), inset },
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** Depth levels → DTCG shadow composites (geometry from core, colour resolved). */
|
|
366
|
+
function buildShadow(colors: ColorsThemeInterface): IRGroup {
|
|
367
|
+
const depth = createDeth(colors as never);
|
|
368
|
+
const out: Record<string, IRToken> = {};
|
|
369
|
+
for (const level of Object.keys(levelValues)) {
|
|
370
|
+
out[level] = { type: 'shadow', value: parseShadow(depth(level as never)) };
|
|
371
|
+
}
|
|
372
|
+
return out;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Build the IR document for one resolved variant. */
|
|
376
|
+
export function buildIR(foundation: FoundationLayer, colors: ColorsThemeInterface, variant: Variant): IRDocument {
|
|
377
|
+
const meta: IRVariantMeta = {
|
|
378
|
+
brand: variant.brand,
|
|
379
|
+
mode: variant.mode,
|
|
380
|
+
surface: variant.surface,
|
|
381
|
+
dimension: variant.dimension,
|
|
382
|
+
};
|
|
383
|
+
const border = buildBorder();
|
|
384
|
+
const font = buildFont();
|
|
385
|
+
|
|
386
|
+
const extensions: Record<string, unknown> = { 'com.apollion.variant': meta, 'com.apollion.font': font.extensions };
|
|
387
|
+
if (Object.keys(border.extensions).length > 0) {
|
|
388
|
+
extensions['com.apollion.border'] = { radius: border.extensions };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
description: `Apollion DS tokens — DTCG 2025.10. Variant: ${meta.brand}/${meta.mode}/${meta.surface}/${meta.dimension}.`,
|
|
393
|
+
meta,
|
|
394
|
+
primitives: buildPrimitives(colors),
|
|
395
|
+
groups: {
|
|
396
|
+
bg: colorRefGroup(foundation.bg as unknown as Record<string, string>, colors, BG_REF),
|
|
397
|
+
text: colorRefGroup(foundation.text as unknown as Record<string, string>, colors, TEXT_REF),
|
|
398
|
+
spacing: dimensionGroup(foundation.spacing as unknown as Record<string, string>),
|
|
399
|
+
border: border.group,
|
|
400
|
+
font: font.group,
|
|
401
|
+
shadow: buildShadow(colors),
|
|
402
|
+
},
|
|
403
|
+
extensions,
|
|
404
|
+
};
|
|
405
|
+
}
|
package/src/renderers/css.ts
CHANGED
|
@@ -1,64 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CSS renderer —
|
|
2
|
+
* CSS renderer — project the token IR to CSS custom properties (the resolved
|
|
3
|
+
* consumer surface).
|
|
3
4
|
*
|
|
4
|
-
* Format: `--apollion-{
|
|
5
|
+
* Format: `--apollion-{group}-{...path}-{token}` per ADR-006 §3.3. References
|
|
6
|
+
* are resolved to literal values and the primitives layer is omitted — CSS
|
|
7
|
+
* stays fully resolved (JSON is the SSOT surface). Nested groups (border/font/
|
|
8
|
+
* shadow) flatten into dashed paths. Color tokens additionally get `@property`
|
|
9
|
+
* declarations (tech-radar R3) wrapped in `@supports`.
|
|
5
10
|
*
|
|
6
|
-
*
|
|
7
|
-
* (color/length) — enables native CSS transitions (View Transitions API
|
|
8
|
-
* friendly). `@property` is browser-supported 84%+ mid-2024; wrapped in
|
|
9
|
-
* `@supports` for graceful degradation.
|
|
11
|
+
* Output is deterministic: groups/tokens iterate in IR insertion order.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
* FoundationLayer object; no timestamps, no run-id.
|
|
13
|
-
*
|
|
14
|
-
* @see ADR-006 §3.3 + §3.10 + tech-radar R3
|
|
15
|
-
* @see PRD-002 §S5
|
|
13
|
+
* @see ../ir.ts (source of truth) · ADR-006 §3.3 + §3.10 · tech-radar R3
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
|
-
import type {
|
|
19
|
-
|
|
20
|
-
import type { Variant } from '../config-schema';
|
|
16
|
+
import type { IRDocument, IRGroup } from '../ir';
|
|
17
|
+
import { isToken, kebab, rawValue } from '../ir';
|
|
21
18
|
|
|
22
19
|
const HEADER_LINE = '/* Generated by apollion-tokens build — DO NOT EDIT. See apollion.config.mjs. */';
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`/* Variant: brand=${variant.brand} mode=${variant.mode} surface=${variant.surface} dimension=${variant.dimension} */`,
|
|
30
|
-
'',
|
|
31
|
-
':root {',
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
// bg layer
|
|
35
|
-
for (const [key, value] of Object.entries(foundation.bg)) {
|
|
36
|
-
lines.push(` --apollion-bg-${key}: ${value};`);
|
|
21
|
+
function emitVars(group: IRGroup, prefix: string[], lines: string[]): void {
|
|
22
|
+
for (const [key, node] of Object.entries(group)) {
|
|
23
|
+
const path = [...prefix, kebab(key)];
|
|
24
|
+
if (isToken(node)) lines.push(` --apollion-${path.join('-')}: ${rawValue(node.value)};`);
|
|
25
|
+
else emitVars(node, path, lines);
|
|
37
26
|
}
|
|
27
|
+
}
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
for (const [key,
|
|
41
|
-
const
|
|
42
|
-
|
|
29
|
+
function emitProperties(group: IRGroup, prefix: string[], lines: string[]): void {
|
|
30
|
+
for (const [key, node] of Object.entries(group)) {
|
|
31
|
+
const path = [...prefix, kebab(key)];
|
|
32
|
+
if (!isToken(node)) {
|
|
33
|
+
emitProperties(node, path, lines);
|
|
34
|
+
} else if (node.type === 'color') {
|
|
35
|
+
lines.push(
|
|
36
|
+
` @property --apollion-${path.join('-')} { syntax: '<color>'; inherits: true; initial-value: ${rawValue(node.value)}; }`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
43
39
|
}
|
|
40
|
+
}
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
42
|
+
export function renderCss(ir: IRDocument): string {
|
|
43
|
+
const { brand, mode, surface, dimension } = ir.meta;
|
|
44
|
+
const lines: string[] = [HEADER_LINE, ''];
|
|
49
45
|
|
|
46
|
+
// Variant marker as a comment (NOT a var — keep cascade clean).
|
|
47
|
+
lines.push(`/* Variant: brand=${brand} mode=${mode} surface=${surface} dimension=${dimension} */`, '', ':root {');
|
|
48
|
+
emitVars(ir.groups, [], lines);
|
|
50
49
|
lines.push('}', '');
|
|
51
50
|
|
|
52
|
-
// @property declarations for color tokens — opt-in via @supports
|
|
51
|
+
// @property declarations for color tokens — opt-in via @supports.
|
|
53
52
|
lines.push('@supports (background: paint(squircle)) or (color: oklch(0% 0 0)) {');
|
|
54
|
-
|
|
55
|
-
lines.push(` @property --apollion-bg-${key} { syntax: '<color>'; inherits: true; initial-value: ${value}; }`);
|
|
56
|
-
}
|
|
57
|
-
for (const key of Object.keys(foundation.text)) {
|
|
58
|
-
const kebab = key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
59
|
-
const value = (foundation.text as unknown as Record<string, string>)[key];
|
|
60
|
-
lines.push(` @property --apollion-text-${kebab} { syntax: '<color>'; inherits: true; initial-value: ${value}; }`);
|
|
61
|
-
}
|
|
53
|
+
emitProperties(ir.groups, [], lines);
|
|
62
54
|
lines.push('}', '');
|
|
63
55
|
|
|
64
56
|
return lines.join('\n');
|
package/src/renderers/json.ts
CHANGED
|
@@ -1,81 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JSON renderer —
|
|
2
|
+
* JSON renderer — project the token IR to DTCG-compliant JSON.
|
|
3
3
|
*
|
|
4
|
-
* **Spec:** [Design Tokens
|
|
5
|
-
* (W3C draft track
|
|
4
|
+
* **Spec:** [Design Tokens Format Module 2025.10](https://www.designtokens.org/TR/drafts/format/)
|
|
5
|
+
* (DTCG / W3C Community Group draft track).
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Pure projection of the IR (`ir.ts`):
|
|
8
|
+
* - `primitives` → the `color` group (structural SSOT) of OKLch objects.
|
|
9
|
+
* - colour/dimension → `{ colorSpace, components, hex, alpha? }` / `{ value, unit }`.
|
|
10
|
+
* - references → DTCG aliases, e.g. `"$value": "{color.primary.base}"`.
|
|
11
|
+
* - composites/scales (S4) → `fontFamily`/`fontWeight`/`number`/`strokeStyle`
|
|
12
|
+
* primitives and the `shadow` composite `{ color, offsetX, offsetY, blur, spread, inset? }`.
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
* Specify, and any tool consuming the DTCG spec.
|
|
14
|
+
* Non-DTCG values travel in the document `$extensions` (`com.apollion.*`).
|
|
14
15
|
*
|
|
15
|
-
* @see tech-radar R1
|
|
16
|
-
* @see ADR-006 §3.10 + PRD-002 §S5
|
|
16
|
+
* @see ../ir.ts (source of truth) · tech-radar R1/R2/R3/R4 · ADR-006 §3.10
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import type {
|
|
19
|
+
import type { IRColorValue, IRDimensionValue, IRDocument, IRGroup, IRShadowValue, IRToken, IRValue } from '../ir';
|
|
20
|
+
import { isToken, kebab, kebabPath } from '../ir';
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
interface DtcgColorValue {
|
|
23
|
+
colorSpace: 'oklch';
|
|
24
|
+
components: number[];
|
|
25
|
+
alpha?: number;
|
|
26
|
+
hex: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface DtcgDimensionValue {
|
|
30
|
+
value: number;
|
|
31
|
+
unit: 'px' | 'rem';
|
|
32
|
+
}
|
|
22
33
|
|
|
23
|
-
interface
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
interface DtcgShadowValue {
|
|
35
|
+
color: DtcgColorValue;
|
|
36
|
+
offsetX: DtcgDimensionValue;
|
|
37
|
+
offsetY: DtcgDimensionValue;
|
|
38
|
+
blur: DtcgDimensionValue;
|
|
39
|
+
spread: DtcgDimensionValue;
|
|
40
|
+
inset?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type DtcgTokenValue = DtcgColorValue | DtcgDimensionValue | DtcgShadowValue | string | number;
|
|
44
|
+
|
|
45
|
+
interface DtcgToken {
|
|
46
|
+
$value: DtcgTokenValue;
|
|
47
|
+
$type: string;
|
|
48
|
+
$description?: string;
|
|
49
|
+
$deprecated?: boolean | string;
|
|
26
50
|
}
|
|
27
51
|
|
|
28
52
|
interface DtcgGroup {
|
|
29
53
|
[key: string]: DtcgToken | DtcgGroup;
|
|
30
54
|
}
|
|
31
55
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
function colorValue(v: IRColorValue): DtcgColorValue {
|
|
57
|
+
return v.alpha !== undefined
|
|
58
|
+
? { colorSpace: 'oklch', components: [...v.components], alpha: v.alpha, hex: v.hex }
|
|
59
|
+
: { colorSpace: 'oklch', components: [...v.components], hex: v.hex };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function dimensionValue(v: IRDimensionValue): DtcgDimensionValue {
|
|
63
|
+
return { value: v.value, unit: v.unit };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function shadowValue(v: IRShadowValue): DtcgShadowValue {
|
|
67
|
+
const s = v.shadow;
|
|
68
|
+
const out: DtcgShadowValue = {
|
|
69
|
+
color: colorValue(s.color),
|
|
70
|
+
offsetX: dimensionValue(s.offsetX),
|
|
71
|
+
offsetY: dimensionValue(s.offsetY),
|
|
72
|
+
blur: dimensionValue(s.blur),
|
|
73
|
+
spread: dimensionValue(s.spread),
|
|
45
74
|
};
|
|
75
|
+
if (s.inset) out.inset = true;
|
|
76
|
+
return out;
|
|
46
77
|
}
|
|
47
78
|
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
function projectValue(value: IRValue): DtcgTokenValue {
|
|
80
|
+
switch (value.kind) {
|
|
81
|
+
case 'ref':
|
|
82
|
+
return `{${kebabPath(value.path)}}`;
|
|
83
|
+
case 'color':
|
|
84
|
+
return colorValue(value);
|
|
85
|
+
case 'dimension':
|
|
86
|
+
return dimensionValue(value);
|
|
87
|
+
case 'fontFamily':
|
|
88
|
+
return value.family;
|
|
89
|
+
case 'fontWeight':
|
|
90
|
+
return value.weight;
|
|
91
|
+
case 'number':
|
|
92
|
+
return value.number;
|
|
93
|
+
case 'strokeStyle':
|
|
94
|
+
return value.style;
|
|
95
|
+
case 'shadow':
|
|
96
|
+
return shadowValue(value);
|
|
53
97
|
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function projectToken(token: IRToken): DtcgToken {
|
|
101
|
+
const out: DtcgToken = { $value: projectValue(token.value), $type: token.type };
|
|
102
|
+
if (token.description !== undefined) out.$description = token.description;
|
|
103
|
+
if (token.deprecated !== undefined) out.$deprecated = token.deprecated;
|
|
54
104
|
return out;
|
|
55
105
|
}
|
|
56
106
|
|
|
57
|
-
function
|
|
107
|
+
function projectGroup(group: IRGroup): DtcgGroup {
|
|
58
108
|
const out: DtcgGroup = {};
|
|
59
|
-
for (const [key,
|
|
60
|
-
out[key] =
|
|
109
|
+
for (const [key, node] of Object.entries(group)) {
|
|
110
|
+
out[kebab(key)] = isToken(node) ? projectToken(node) : projectGroup(node);
|
|
61
111
|
}
|
|
62
112
|
return out;
|
|
63
113
|
}
|
|
64
114
|
|
|
65
|
-
export function renderJson(
|
|
66
|
-
const doc
|
|
67
|
-
$description:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
$extensions: {
|
|
72
|
-
'com.apollion.variant': {
|
|
73
|
-
brand: variant.brand,
|
|
74
|
-
mode: variant.mode,
|
|
75
|
-
surface: variant.surface,
|
|
76
|
-
dimension: variant.dimension,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
115
|
+
export function renderJson(ir: IRDocument): string {
|
|
116
|
+
const doc = {
|
|
117
|
+
$description: ir.description,
|
|
118
|
+
color: projectGroup(ir.primitives),
|
|
119
|
+
...projectGroup(ir.groups),
|
|
120
|
+
$extensions: ir.extensions,
|
|
79
121
|
};
|
|
80
122
|
return `${JSON.stringify(doc, null, 2)}\n`;
|
|
81
123
|
}
|
package/src/renderers/ts.ts
CHANGED
|
@@ -1,46 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TypeScript renderer —
|
|
2
|
+
* TypeScript renderer — project the token IR to an `as const` literal (the
|
|
3
|
+
* resolved consumer surface).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Consumers importing the generated `.d.ts` get literal types (e.g.
|
|
6
|
+
* `tokens.bg.primary: '#003750'` not `string`) — autocomplete + compile-time
|
|
7
|
+
* invariant checks (tech-radar R4). References resolve to literal values, the
|
|
8
|
+
* primitives layer is omitted, and nested groups (border/font/shadow) nest as
|
|
9
|
+
* nested objects.
|
|
7
10
|
*
|
|
8
|
-
* @see tech-radar R4
|
|
9
|
-
* @see ADR-006 §3.10 + PRD-002 §S5
|
|
11
|
+
* @see ../ir.ts (source of truth) · tech-radar R4 · ADR-006 §3.10
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
|
-
import type {
|
|
13
|
-
|
|
14
|
-
import type { Variant } from '../config-schema';
|
|
14
|
+
import type { IRDocument, IRGroup } from '../ir';
|
|
15
|
+
import { isToken, rawValue } from '../ir';
|
|
15
16
|
|
|
16
17
|
const HEADER_LINE = '// Generated by apollion-tokens build — DO NOT EDIT. See apollion.config.mjs.';
|
|
17
18
|
|
|
18
|
-
function
|
|
19
|
-
const pad = '
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
function emitGroup(group: IRGroup, depth: number, out: string[]): void {
|
|
20
|
+
const pad = ' '.repeat(depth);
|
|
21
|
+
for (const [key, node] of Object.entries(group)) {
|
|
22
|
+
if (isToken(node)) {
|
|
23
|
+
out.push(`${pad}${JSON.stringify(key)}: ${JSON.stringify(rawValue(node.value))},`);
|
|
24
|
+
} else {
|
|
25
|
+
out.push(`${pad}${key}: {`);
|
|
26
|
+
emitGroup(node, depth + 1, out);
|
|
27
|
+
out.push(`${pad}},`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
export function renderTs(
|
|
27
|
-
|
|
32
|
+
export function renderTs(ir: IRDocument): string {
|
|
33
|
+
const { brand, mode, surface, dimension } = ir.meta;
|
|
34
|
+
const out: string[] = [
|
|
28
35
|
HEADER_LINE,
|
|
29
|
-
`// Variant: brand=${
|
|
36
|
+
`// Variant: brand=${brand} mode=${mode} surface=${surface} dimension=${dimension}`,
|
|
30
37
|
'',
|
|
31
38
|
'export const tokens = {',
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
' spacing: {',
|
|
39
|
-
serializeRecord(foundation.spacing as unknown as Record<string, string>, 4),
|
|
40
|
-
' },',
|
|
41
|
-
'} as const;',
|
|
42
|
-
'',
|
|
43
|
-
'export type Tokens = typeof tokens;',
|
|
44
|
-
'',
|
|
45
|
-
].join('\n');
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
emitGroup(ir.groups, 1, out);
|
|
42
|
+
|
|
43
|
+
out.push('} as const;', '', 'export type Tokens = typeof tokens;', '');
|
|
44
|
+
return out.join('\n');
|
|
46
45
|
}
|
package/src/theme-factory.ts
CHANGED
|
@@ -31,7 +31,14 @@ import { mountGreyscaleColors, mountNeutralColors, mountSingleColor } from './bu
|
|
|
31
31
|
import { createSpacing } from './builders/spacing';
|
|
32
32
|
import type { Variant } from './config-schema';
|
|
33
33
|
|
|
34
|
-
type ColorsThemeInterface = ReturnType<typeof composeColors>;
|
|
34
|
+
export type ColorsThemeInterface = ReturnType<typeof composeColors>;
|
|
35
|
+
|
|
36
|
+
/** A variant's resolved themes: the consumer Foundation layer + the structural
|
|
37
|
+
* palette (colour primitives / SSOT). */
|
|
38
|
+
export interface VariantThemes {
|
|
39
|
+
foundation: FoundationLayer;
|
|
40
|
+
colors: ColorsThemeInterface;
|
|
41
|
+
}
|
|
35
42
|
|
|
36
43
|
function composeColors(input: Variant['colors']) {
|
|
37
44
|
return {
|
|
@@ -56,25 +63,33 @@ function composeColors(input: Variant['colors']) {
|
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
/**
|
|
59
|
-
* Build the Foundation layer
|
|
66
|
+
* Build a variant's resolved themes — the Foundation layer plus the structural
|
|
67
|
+
* palette (colour SSOT consumed by the IR for reference emission). Pure +
|
|
68
|
+
* deterministic.
|
|
60
69
|
*/
|
|
61
|
-
export function
|
|
62
|
-
variant: Variant,
|
|
63
|
-
spacingInput: SpacingInput = defaultInputSpacing,
|
|
64
|
-
): FoundationLayer {
|
|
70
|
+
export function buildVariantThemes(variant: Variant, spacingInput: SpacingInput = defaultInputSpacing): VariantThemes {
|
|
65
71
|
const themeColors = composeColors(variant.colors) as unknown as ColorsThemeInterface;
|
|
66
72
|
const themeSpacing = createSpacing(spacingInput, variant.dimension);
|
|
67
73
|
const semantic = createSemantic({ colors: themeColors as never, spacing: themeSpacing as never });
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
let foundation = createFoundation(semantic);
|
|
70
76
|
|
|
71
77
|
if (variant.surface === 'negative') {
|
|
72
78
|
// invertForSurface acts on the full Theme — assemble a minimal shim.
|
|
73
79
|
// S6 will refactor to accept (semantic, surface) directly.
|
|
74
80
|
const fullTheme = { semantic, colors: themeColors as never } as never;
|
|
75
|
-
|
|
76
|
-
return createFoundation(inverted.semantic);
|
|
81
|
+
foundation = createFoundation(invertForSurface(fullTheme, 'negative').semantic);
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
return
|
|
84
|
+
return { foundation, colors: themeColors };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Build the Foundation layer for one variant. Pure function, deterministic.
|
|
89
|
+
*/
|
|
90
|
+
export function buildFoundationForVariant(
|
|
91
|
+
variant: Variant,
|
|
92
|
+
spacingInput: SpacingInput = defaultInputSpacing,
|
|
93
|
+
): FoundationLayer {
|
|
94
|
+
return buildVariantThemes(variant, spacingInput).foundation;
|
|
80
95
|
}
|