@apollion-dsi/tokens 4.0.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 +46 -0
- package/lib/build.cjs +6 -0
- package/lib/build.esm.js +6 -0
- package/lib/cli.mjs +9 -0
- package/lib/config-loader.cjs +3 -0
- package/lib/config-loader.esm.js +3 -0
- package/lib/config-schema.cjs +1 -0
- package/lib/config-schema.esm.js +1 -0
- package/lib/runtime/v1.cjs +1 -0
- package/lib/runtime/v1.esm.js +1 -0
- package/package.json +84 -0
- package/src/build.ts +136 -0
- package/src/builders/colors.ts +135 -0
- package/src/builders/foundation.ts +19 -0
- package/src/builders/spacing.ts +27 -0
- package/src/builders/surface.ts +16 -0
- package/src/cli.ts +128 -0
- package/src/config-loader.ts +167 -0
- package/src/config-schema-runtime.ts +64 -0
- package/src/config-schema.ts +110 -0
- package/src/manifest.ts +103 -0
- package/src/renderers/css.ts +65 -0
- package/src/renderers/json.ts +81 -0
- package/src/renderers/ts.ts +46 -0
- package/src/runtime/v1.ts +40 -0
- package/src/theme-factory.ts +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# `@apollion-dsi/tokens`
|
|
2
|
+
|
|
3
|
+
Framework-agnostic design tokens for Apollion Design System v4+.
|
|
4
|
+
|
|
5
|
+
**Status:** 🟡 `0.0.0-alpha.0` — S1 scaffold. Public surface stabilizes at `4.0.0` GA.
|
|
6
|
+
|
|
7
|
+
## Versioned API
|
|
8
|
+
|
|
9
|
+
Single public entry: `@apollion-dsi/tokens/runtime/v1`.
|
|
10
|
+
|
|
11
|
+
Future major surface changes go to `runtime/v2`, `runtime/v3`, etc. — `v1` is preserved indefinitely (Strangler Fig per ADR-006 §3.3).
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { TOKENS_RUNTIME_API_VERSION } from '@apollion-dsi/tokens/runtime/v1';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Scope
|
|
18
|
+
|
|
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
|
+
- **Out:** Runtime React components (lives in `@apollion-dsi/core`), framework adapters (Vue/Flutter consumers consume `dist/json/*.json` directly).
|
|
21
|
+
|
|
22
|
+
## Coupling to `@apollion-dsi/core`
|
|
23
|
+
|
|
24
|
+
Hard rule per ADR-006 §3.1:
|
|
25
|
+
|
|
26
|
+
- `core` declares `@apollion-dsi/tokens` in `optionalDependencies` (NOT `dependencies`).
|
|
27
|
+
- Consumer that does not opt into `apollion.config.mjs#output.runtime: true` never installs tokens.
|
|
28
|
+
- Lockstep version enforced via `.changeset/config.json#fixed` array from `4.0.0` (PRD-002 §S7).
|
|
29
|
+
|
|
30
|
+
## Slicing (ADR-006 §6)
|
|
31
|
+
|
|
32
|
+
| Slice | Status | Deliverable |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| S1 — scaffold + versioned API | ✅ this commit | `runtime/v1` namespace, esbuild ESM/CJS dual emit, smoke test |
|
|
35
|
+
| S2 — culori vendor + OKLch lerp (in core) | pending | spike `spike/culori-oklch-lerp-parity` validated 0 regressions |
|
|
36
|
+
| S3 — Dimension axis | pending | `compact/normal/spacious` multipliers |
|
|
37
|
+
| S4 — builders duplicated here via Strangler Fig | pending | parity tests vs core |
|
|
38
|
+
| S5 — atomic+idempotent build CLI | pending | `apollion-tokens build` + `dist/manifest.json` |
|
|
39
|
+
| S6 — Config-First + sandboxed loader | pending | `node:vm` + zod schema |
|
|
40
|
+
| S7 — release `4.0.0` | pending | first major + lockstep changesets |
|
|
41
|
+
|
|
42
|
+
## Refs
|
|
43
|
+
|
|
44
|
+
- [ADR-006 — v4 epic](../core/docs/adr/ADR-006-v4-oklch-dimension-token-split.md)
|
|
45
|
+
- [PRD-002 — v4 epic execution](../core/docs/prd/PRD-002-v4-epic-execution.md)
|
|
46
|
+
- [`packages/core/docs/v4-baseline-review.md`](../core/docs/v4-baseline-review.md)
|
package/lib/build.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";var k=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var Q=Object.prototype.hasOwnProperty;var s=(o,e)=>k(o,"name",{value:e,configurable:!0});var W=(o,e)=>{for(var n in e)k(o,n,{get:e[n],enumerable:!0})},X=(o,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of K(e))!Q.call(o,t)&&t!==n&&k(o,t,{get:()=>e[t],enumerable:!(r=q(e,t))||r.enumerable});return o};var Y=o=>X(k({},"__esModule",{value:!0}),o);var lo={};W(lo,{build:()=>co,check:()=>po});module.exports=Y(lo);var c=require("node:fs/promises"),Z=require("node:os"),y=require("node:path");var oo="/* Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs. */";function I(o,e){let n=[oo,""];n.push(`/* Variant: brand=${e.brand} mode=${e.mode} surface=${e.surface} dimension=${e.dimension} */`,"",":root {");for(let[r,t]of Object.entries(o.bg))n.push(` --apollion-bg-${r}: ${t};`);for(let[r,t]of Object.entries(o.text)){let i=r.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();n.push(` --apollion-text-${i}: ${t};`)}for(let[r,t]of Object.entries(o.spacing))n.push(` --apollion-spacing-${r}: ${t};`);n.push("}",""),n.push("@supports (background: paint(squircle)) or (color: oklch(0% 0 0)) {");for(let[r,t]of Object.entries(o.bg))n.push(` @property --apollion-bg-${r} { syntax: '<color>'; inherits: true; initial-value: ${t}; }`);for(let r of Object.keys(o.text)){let t=r.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),i=o.text[r];n.push(` @property --apollion-text-${t} { syntax: '<color>'; inherits: true; initial-value: ${i}; }`)}return n.push("}",""),n.join(`
|
|
2
|
+
`)}s(I,"renderCss");function A(o){let e={};for(let[n,r]of Object.entries(o)){let t=n.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();e[t]={$value:r,$type:"color"}}return e}s(A,"toColorGroup");function eo(o){let e={};for(let[n,r]of Object.entries(o))e[n]={$value:r,$type:"dimension"};return e}s(eo,"toDimensionGroup");function F(o,e){let n={$description:`Apollion DS tokens \u2014 DTCG v1. Variant: ${e.brand}/${e.mode}/${e.surface}/${e.dimension}.`,bg:A(o.bg),text:A(o.text),spacing:eo(o.spacing),$extensions:{"com.apollion.variant":{brand:e.brand,mode:e.mode,surface:e.surface,dimension:e.dimension}}};return`${JSON.stringify(n,null,2)}
|
|
3
|
+
`}s(F,"renderJson");var no="// Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs.";function S(o,e){let n=" ".repeat(e);return Object.entries(o).map(([t,i])=>`${n}${JSON.stringify(t)}: ${JSON.stringify(i)},`).join(`
|
|
4
|
+
`)}s(S,"serializeRecord");function j(o,e){return[no,`// Variant: brand=${e.brand} mode=${e.mode} surface=${e.surface} dimension=${e.dimension}`,"","export const tokens = {"," bg: {",S(o.bg,4)," },"," text: {",S(o.text,4)," },"," spacing: {",S(o.spacing,4)," },","} as const;","","export type Tokens = typeof tokens;",""].join(`
|
|
5
|
+
`)}s(j,"renderTs");var to=["light","dark"],ro=["positive","negative"],io=["compact","normal","spacious"];function v(o){let e=o.modes??to,n=o.surfaces??ro,r=o.dimensions??io,t=Object.entries(o.brands).sort(([p],[l])=>p.localeCompare(l)),i=[];for(let[p,l]of t)for(let m of e)for(let $ of n)for(let x of r)i.push({brand:p,colors:l,mode:m,surface:$,dimension:x});return i}s(v,"expandVariants");function L(o){return`${o.brand}.${o.mode}.${o.surface}.${o.dimension}`}s(L,"variantName");var R=require("node:crypto");function b(o){return(0,R.createHash)("sha256").update(o).digest("hex")}s(b,"sha256");function T(o){return b(w(o))}s(T,"hashConfig");function w(o){return o===null||typeof o!="object"?JSON.stringify(o):Array.isArray(o)?`[${o.map(w).join(",")}]`:`{${Object.keys(o).sort().map(r=>`${JSON.stringify(r)}:${w(o[r])}`).join(",")}}`}s(w,"stableStringify");function M(o){try{let{execSync:e}=require("node:child_process");return e("git rev-parse --short HEAD",{cwd:o,stdio:["ignore","pipe","ignore"],encoding:"utf8"}).trim()}catch{return""}}s(M,"detectGitCommit");function N(){return{node:process.version,platform:`${process.platform}-${process.arch}`}}s(N,"buildEnv");function V(o){let e=[...o.files].sort((r,t)=>r.path.localeCompare(t.path)),n={configHash:o.configHash,files:e,gitCommit:o.gitCommit,buildEnv:o.buildEnv};return`${JSON.stringify(n,null,2)}
|
|
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});
|
package/lib/build.esm.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
var P=Object.defineProperty;var s=(o,e)=>P(o,"name",{value:e,configurable:!0}),_=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(e,n)=>(typeof require<"u"?require:e)[n]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});import{mkdir as H,mkdtemp as co,readFile as po,rename as lo,rm as B,writeFile as J}from"node:fs/promises";import{tmpdir as fo}from"node:os";import{dirname as z,join as h}from"node:path";var U="/* Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs. */";function w(o,e){let n=[U,""];n.push(`/* Variant: brand=${e.brand} mode=${e.mode} surface=${e.surface} dimension=${e.dimension} */`,"",":root {");for(let[r,t]of Object.entries(o.bg))n.push(` --apollion-bg-${r}: ${t};`);for(let[r,t]of Object.entries(o.text)){let i=r.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();n.push(` --apollion-text-${i}: ${t};`)}for(let[r,t]of Object.entries(o.spacing))n.push(` --apollion-spacing-${r}: ${t};`);n.push("}",""),n.push("@supports (background: paint(squircle)) or (color: oklch(0% 0 0)) {");for(let[r,t]of Object.entries(o.bg))n.push(` @property --apollion-bg-${r} { syntax: '<color>'; inherits: true; initial-value: ${t}; }`);for(let r of Object.keys(o.text)){let t=r.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),i=o.text[r];n.push(` @property --apollion-text-${t} { syntax: '<color>'; inherits: true; initial-value: ${i}; }`)}return n.push("}",""),n.join(`
|
|
2
|
+
`)}s(w,"renderCss");function T(o){let e={};for(let[n,r]of Object.entries(o)){let t=n.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();e[t]={$value:r,$type:"color"}}return e}s(T,"toColorGroup");function Z(o){let e={};for(let[n,r]of Object.entries(o))e[n]={$value:r,$type:"dimension"};return e}s(Z,"toDimensionGroup");function O(o,e){let n={$description:`Apollion DS tokens \u2014 DTCG v1. Variant: ${e.brand}/${e.mode}/${e.surface}/${e.dimension}.`,bg:T(o.bg),text:T(o.text),spacing:Z(o.spacing),$extensions:{"com.apollion.variant":{brand:e.brand,mode:e.mode,surface:e.surface,dimension:e.dimension}}};return`${JSON.stringify(n,null,2)}
|
|
3
|
+
`}s(O,"renderJson");var q="// Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs.";function x(o,e){let n=" ".repeat(e);return Object.entries(o).map(([t,i])=>`${n}${JSON.stringify(t)}: ${JSON.stringify(i)},`).join(`
|
|
4
|
+
`)}s(x,"serializeRecord");function E(o,e){return[q,`// Variant: brand=${e.brand} mode=${e.mode} surface=${e.surface} dimension=${e.dimension}`,"","export const tokens = {"," bg: {",x(o.bg,4)," },"," text: {",x(o.text,4)," },"," spacing: {",x(o.spacing,4)," },","} as const;","","export type Tokens = typeof tokens;",""].join(`
|
|
5
|
+
`)}s(E,"renderTs");var K=["light","dark"],Q=["positive","negative"],W=["compact","normal","spacious"];function I(o){let e=o.modes??K,n=o.surfaces??Q,r=o.dimensions??W,t=Object.entries(o.brands).sort(([c],[p])=>c.localeCompare(p)),i=[];for(let[c,p]of t)for(let u of e)for(let y of n)for(let b of r)i.push({brand:c,colors:p,mode:u,surface:y,dimension:b});return i}s(I,"expandVariants");function A(o){return`${o.brand}.${o.mode}.${o.surface}.${o.dimension}`}s(A,"variantName");import{createHash as X}from"node:crypto";function d(o){return X("sha256").update(o).digest("hex")}s(d,"sha256");function D(o){return d(C(o))}s(D,"hashConfig");function C(o){return o===null||typeof o!="object"?JSON.stringify(o):Array.isArray(o)?`[${o.map(C).join(",")}]`:`{${Object.keys(o).sort().map(r=>`${JSON.stringify(r)}:${C(o[r])}`).join(",")}}`}s(C,"stableStringify");function F(o){try{let{execSync:e}=_("node:child_process");return e("git rev-parse --short HEAD",{cwd:o,stdio:["ignore","pipe","ignore"],encoding:"utf8"}).trim()}catch{return""}}s(F,"detectGitCommit");function j(){return{node:process.version,platform:`${process.platform}-${process.arch}`}}s(j,"buildEnv");function v(o){let e=[...o.files].sort((r,t)=>r.path.localeCompare(t.path)),n={configHash:o.configHash,files:e,gitCommit:o.gitCommit,buildEnv:o.buildEnv};return`${JSON.stringify(n,null,2)}
|
|
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};
|
package/lib/cli.mjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var oo=Object.defineProperty;var i=(o,n)=>oo(o,"name",{value:n,configurable:!0}),no=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(n,e)=>(typeof require<"u"?require:n)[e]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});import{readFile as Eo}from"node:fs/promises";import{resolve as j}from"node:path";import{mkdir as q,mkdtemp as ko,readFile as xo,rename as $o,rm as U,writeFile as Z}from"node:fs/promises";import{tmpdir as Co}from"node:os";import{dirname as W,join as x}from"node:path";var eo="/* Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs. */";function E(o,n){let e=[eo,""];e.push(`/* Variant: brand=${n.brand} mode=${n.mode} surface=${n.surface} dimension=${n.dimension} */`,"",":root {");for(let[r,t]of Object.entries(o.bg))e.push(` --apollion-bg-${r}: ${t};`);for(let[r,t]of Object.entries(o.text)){let s=r.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();e.push(` --apollion-text-${s}: ${t};`)}for(let[r,t]of Object.entries(o.spacing))e.push(` --apollion-spacing-${r}: ${t};`);e.push("}",""),e.push("@supports (background: paint(squircle)) or (color: oklch(0% 0 0)) {");for(let[r,t]of Object.entries(o.bg))e.push(` @property --apollion-bg-${r} { syntax: '<color>'; inherits: true; initial-value: ${t}; }`);for(let r of Object.keys(o.text)){let t=r.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),s=o.text[r];e.push(` @property --apollion-text-${t} { syntax: '<color>'; inherits: true; initial-value: ${s}; }`)}return e.push("}",""),e.join(`
|
|
3
|
+
`)}i(E,"renderCss");function I(o){let n={};for(let[e,r]of Object.entries(o)){let t=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();n[t]={$value:r,$type:"color"}}return n}i(I,"toColorGroup");function to(o){let n={};for(let[e,r]of Object.entries(o))n[e]={$value:r,$type:"dimension"};return n}i(to,"toDimensionGroup");function F(o,n){let e={$description:`Apollion DS tokens \u2014 DTCG v1. Variant: ${n.brand}/${n.mode}/${n.surface}/${n.dimension}.`,bg:I(o.bg),text:I(o.text),spacing:to(o.spacing),$extensions:{"com.apollion.variant":{brand:n.brand,mode:n.mode,surface:n.surface,dimension:n.dimension}}};return`${JSON.stringify(e,null,2)}
|
|
4
|
+
`}i(F,"renderJson");var ro="// Generated by apollion-tokens build \u2014 DO NOT EDIT. See apollion.config.mjs.";function S(o,n){let e=" ".repeat(n);return Object.entries(o).map(([t,s])=>`${e}${JSON.stringify(t)}: ${JSON.stringify(s)},`).join(`
|
|
5
|
+
`)}i(S,"serializeRecord");function R(o,n){return[ro,`// Variant: brand=${n.brand} mode=${n.mode} surface=${n.surface} dimension=${n.dimension}`,"","export const tokens = {"," bg: {",S(o.bg,4)," },"," text: {",S(o.text,4)," },"," spacing: {",S(o.spacing,4)," },","} as const;","","export type Tokens = typeof tokens;",""].join(`
|
|
6
|
+
`)}i(R,"renderTs");var io=["light","dark"],so=["positive","negative"],ao=["compact","normal","spacious"];function L(o){let n=o.modes??io,e=o.surfaces??so,r=o.dimensions??ao,t=Object.entries(o.brands).sort(([f],[l])=>f.localeCompare(l)),s=[];for(let[f,l]of t)for(let c of n)for(let d of e)for(let C of r)s.push({brand:f,colors:l,mode:c,surface:d,dimension:C});return s}i(L,"expandVariants");function M(o){return`${o.brand}.${o.mode}.${o.surface}.${o.dimension}`}i(M,"variantName");import{createHash as co}from"node:crypto";function k(o){return co("sha256").update(o).digest("hex")}i(k,"sha256");function v(o){return k(A(o))}i(v,"hashConfig");function A(o){return o===null||typeof o!="object"?JSON.stringify(o):Array.isArray(o)?`[${o.map(A).join(",")}]`:`{${Object.keys(o).sort().map(r=>`${JSON.stringify(r)}:${A(o[r])}`).join(",")}}`}i(A,"stableStringify");function N(o){try{let{execSync:n}=no("node:child_process");return n("git rev-parse --short HEAD",{cwd:o,stdio:["ignore","pipe","ignore"],encoding:"utf8"}).trim()}catch{return""}}i(N,"detectGitCommit");function P(){return{node:process.version,platform:`${process.platform}-${process.arch}`}}i(P,"buildEnv");function V(o){let n=[...o.files].sort((r,t)=>r.path.localeCompare(t.path)),e={configHash:o.configHash,files:n,gitCommit:o.gitCommit,buildEnv:o.buildEnv};return`${JSON.stringify(e,null,2)}
|
|
7
|
+
`}i(V,"serializeManifest");import{createFoundation as J}from"@apollion-dsi/core/themes/foundation";import{createSemantic as go}from"@apollion-dsi/core/themes/semantic";import{defaultInputSpacing as ho}from"@apollion-dsi/core/themes/spacing";import{invertForSurface as yo}from"@apollion-dsi/core/themes/surface";import{converter as lo,formatHex as po,interpolate as fo,parse as _}from"culori";var Zo=lo("oklch");function a(o,n,e){let r=_(o),t=_(n);if(!r||!t)return o;let s=fo([r,t],"oklch");return po(s(e))??o}i(a,"mixOklch");function mo(o){return o?typeof o=="string"?o:o.base:"#000"}i(mo,"coerceToHex");function g(o,n){let e=mo(o),r=n.deepDark,t=n.deepLight;return{base:e,dark:a(e,r,.6),action:a(e,r,.2),active:a(e,t,.6),light:a(e,t,.9)}}i(g,"mountSingleColor");function G(o){let n=o.deepLight,e=o.deepDark;return{0:n,5:a(n,e,.05),10:a(n,e,.1),20:a(n,e,.2),30:a(n,e,.3),40:a(n,e,.4),50:a(n,e,.5),60:a(n,e,.6),70:a(n,e,.7),80:a(n,e,.8),90:a(n,e,.9),100:a(n,e,1)}}i(G,"mountGreyscaleColors");function B(o){let n=o.deepLight,e=o.deepDark,r=o.primary,t=i(f=>1/17*(f-1),"interpolation"),s=a(r,e,.5);return{0:a(s,n,t(18)),5:a(s,n,t(17.5)),10:a(s,n,t(17)),20:a(s,n,t(16)),30:a(s,n,t(15)),40:a(s,n,t(14)),50:a(s,n,t(13)),60:a(s,n,t(12)),70:a(s,n,t(11)),80:a(s,n,t(10)),90:a(s,n,t(9)),100:a(s,n,t(8)),110:a(s,n,t(7)),120:a(s,n,t(6)),130:a(s,n,t(5)),140:a(s,n,t(4)),150:a(s,n,t(3)),160:a(s,n,t(2)),170:s,180:a(s,e,.25)}}i(B,"mountNeutralColors");import{DIMENSION_MULTIPLIERS as uo}from"@apollion-dsi/core/themes/dimension";function H({multiplier:o=.25,alias:n},e){let r=e!==void 0?uo[e]:o;return(...t)=>t.map(s=>{let f=typeof s=="string"?n[s]:s;return`${r*f}rem`}).join(" ")}i(H,"createSpacing");function bo(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:g(o.main,o),opposite:g(o.opposite,o),complementary:g(o.complementary,o),danger:g(o.danger,o),information:g(o.information,o),success:g(o.success,o),warning:g(o.warning,o),primary:g(o.primary,o),secondary:g(o.secondary,o),tertiary:g(o.tertiary,o)}}i(bo,"composeColors");function z(o,n=ho){let e=bo(o.colors),r=H(n,o.dimension),t=go({colors:e,spacing:r}),s=J(t);if(o.surface==="negative"){let l=yo({semantic:t,colors:e},"negative");return J(l.semantic)}return s}i(z,"buildFoundationForVariant");async function X(o){let{config:n,outDir:e,cwd:r=process.cwd(),verbose:t=!1}=o,s=L(n),f=n.output??{};if(s.length===0)throw new Error("apollion-tokens build: no variants (config.brands is empty)");let l=await ko(x(Co(),"apollion-tokens-"));try{let c=[];for(let b of s){let w=z(b),D=M(b);if(f.css){let h=E(w,b),u=`css/${D}.css`;await O(l,u,h),c.push({path:u,sha256:k(h)}),t&&console.log(` \u2713 ${u}`)}if(f.json){let h=F(w,b),u=`json/${D}.json`;await O(l,u,h),c.push({path:u,sha256:k(h)}),t&&console.log(` \u2713 ${u}`)}if(f.ts){let h=R(w,b),u=`ts/${D}.d.ts`;await O(l,u,h),c.push({path:u,sha256:k(h)}),t&&console.log(` \u2713 ${u}`)}}let d={configHash:v(n),files:c,gitCommit:N(r),buildEnv:P()},C=V(d);return await Z(x(l,"manifest.json"),C),await U(e,{recursive:!0,force:!0}),await q(W(e),{recursive:!0}),await $o(l,e),t&&console.log(`\u2713 ${c.length} files \u2192 ${e}`),{manifest:d,outDir:e}}catch(c){throw await U(l,{recursive:!0,force:!0}).catch(()=>{}),c}}i(X,"build");async function K(o){let{config:n,outDir:e}=o,r;try{r=JSON.parse(await xo(x(e,"manifest.json"),"utf8"))}catch{return!1}return r.configHash===v(n)}i(K,"check");async function O(o,n,e){let r=x(o,n);await q(W(r),{recursive:!0}),await Z(r,e)}i(O,"writeAt");import{readFile as Do}from"node:fs/promises";import{resolve as So}from"node:path";import{createContext as Ao,Script as vo}from"node:vm";import{z as p}from"zod";var m=p.string().regex(/^#([0-9a-fA-F]{3}){1,2}$|^transparent$|^rgb\(.*\)$|^oklch\(.*\)$/,{message:'Must be hex, rgb(), oklch(), or "transparent"'}),wo=p.object({baseDark:p.string().optional(),baseLight:p.string().optional(),deepDark:m,deepLight:m,transparent:p.string().optional(),main:m.optional(),opposite:m.optional(),complementary:m.optional(),information:m.optional(),success:m.optional(),danger:m.optional(),warning:m.optional(),primary:m.optional(),secondary:m.optional(),tertiary:m.optional()}).passthrough(),Q=p.object({brands:p.record(p.string(),wo).refine(o=>Object.keys(o).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 Oo=[/\brequire\s*\(/,/\bimport\s*\(/,/^\s*import\s+/m],To=1e3;var T=class T extends Error{constructor(e,r){super(e);this.configPath=r;this.name="ConfigLoadError"}};i(T,"ConfigLoadError");var y=T;async function Y(o){let n=So(o.configPath),e=await Do(n,"utf8");for(let c of Oo)if(c.test(e))throw new y(`apollion.config: dynamic require/import not allowed (matched ${c}). Declarative config only \u2014 see ADR-006 \xA73.8 B3.`,n);let r=e.replace(/(^|\n)\s*export\s+default\s+/,(c,d)=>`${d}__apollion_config_export = `);if(r===e)throw new y("apollion.config: missing `export default <object>;` \u2014 config must export a default object literal.",n);let t={__apollion_config_export:void 0,console:jo(),Symbol,Number,String,Boolean,Array,Object,JSON,Math,defineConfig:i(c=>c,"defineConfig")},s=Ao(t);try{new vo(r,{filename:n}).runInContext(s,{timeout:To})}catch(c){throw new y(`apollion.config: evaluation failed \u2014 ${c instanceof Error?c.message:String(c)}`,n)}let f=t.__apollion_config_export;if(f===void 0)throw new y("apollion.config: `export default` produced undefined.",n);let l=Q.safeParse(f);if(!l.success){let c=l.error.issues.map(d=>` ${d.path.join(".")||"(root)"}: ${d.message}`).join(`
|
|
8
|
+
`);throw new y(`apollion.config: schema validation failed \u2014
|
|
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)});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";var f=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var a=(o,n)=>f(o,"name",{value:n,configurable:!0});var k=(o,n)=>{for(var r in n)f(o,r,{get:n[r],enumerable:!0})},j=(o,n,r,p)=>{if(n&&typeof n=="object"||typeof n=="function")for(let s of C(n))!_.call(o,s)&&s!==r&&f(o,s,{get:()=>n[s],enumerable:!(p=y(n,s))||p.enumerable});return o};var A=o=>j(f({},"__esModule",{value:!0}),o);var v={};k(v,{ConfigLoadError:()=>l,defineConfig:()=>P,loadConfig:()=>D});module.exports=A(v);var b=require("node:fs/promises"),w=require("node:path"),g=require("node:vm");var t=require("zod"),i=t.z.string().regex(/^#([0-9a-fA-F]{3}){1,2}$|^transparent$|^rgb\(.*\)$|^oklch\(.*\)$/,{message:'Must be hex, rgb(), oklch(), or "transparent"'}),S=t.z.object({baseDark:t.z.string().optional(),baseLight:t.z.string().optional(),deepDark:i,deepLight:i,transparent:t.z.string().optional(),main:i.optional(),opposite:i.optional(),complementary:i.optional(),information:i.optional(),success:i.optional(),danger:i.optional(),warning:i.optional(),primary:i.optional(),secondary:i.optional(),tertiary:i.optional()}).passthrough(),h=t.z.object({brands:t.z.record(t.z.string(),S).refine(o=>Object.keys(o).length>0,{message:"apollion.config.mjs: brands must contain at least 1 entry"}),modes:t.z.array(t.z.enum(["light","dark"])).optional(),surfaces:t.z.array(t.z.enum(["positive","negative"])).optional(),dimensions:t.z.array(t.z.enum(["compact","normal","spacious"])).optional(),output:t.z.object({runtime:t.z.boolean().optional(),css:t.z.boolean().optional(),json:t.z.boolean().optional(),ts:t.z.boolean().optional()}).passthrough().optional()}).passthrough();var $=[/\brequire\s*\(/,/\bimport\s*\(/,/^\s*import\s+/m],O=1e3;function P(o){return o}a(P,"defineConfig");var u=class u extends Error{constructor(r,p){super(r);this.configPath=p;this.name="ConfigLoadError"}};a(u,"ConfigLoadError");var l=u;async function D(o){let n=(0,w.resolve)(o.configPath),r=await(0,b.readFile)(n,"utf8");for(let e of $)if(e.test(r))throw new l(`apollion.config: dynamic require/import not allowed (matched ${e}). Declarative config only \u2014 see ADR-006 \xA73.8 B3.`,n);let p=r.replace(/(^|\n)\s*export\s+default\s+/,(e,c)=>`${c}__apollion_config_export = `);if(p===r)throw new l("apollion.config: missing `export default <object>;` \u2014 config must export a default object literal.",n);let s={__apollion_config_export:void 0,console:T(),Symbol,Number,String,Boolean,Array,Object,JSON,Math,defineConfig:a(e=>e,"defineConfig")},x=(0,g.createContext)(s);try{new g.Script(p,{filename:n}).runInContext(x,{timeout:O})}catch(e){throw new l(`apollion.config: evaluation failed \u2014 ${e instanceof Error?e.message:String(e)}`,n)}let d=s.__apollion_config_export;if(d===void 0)throw new l("apollion.config: `export default` produced undefined.",n);let m=h.safeParse(d);if(!m.success){let e=m.error.issues.map(c=>` ${c.path.join(".")||"(root)"}: ${c.message}`).join(`
|
|
2
|
+
`);throw new l(`apollion.config: schema validation failed \u2014
|
|
3
|
+
${e}`,n)}return m.data}a(D,"loadConfig");function T(){return{log:a((...o)=>console.log("[apollion.config]",...o),"log"),warn:a((...o)=>console.warn("[apollion.config]",...o),"warn"),error:a((...o)=>console.error("[apollion.config]",...o),"error")}}a(T,"makeReadOnlyConsole");0&&(module.exports={ConfigLoadError,defineConfig,loadConfig});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var h=Object.defineProperty;var r=(n,i)=>h(n,"name",{value:i,configurable:!0});import{readFile as w}from"node:fs/promises";import{resolve as x}from"node:path";import{createContext as y,Script as C}from"node:vm";import{z as o}from"zod";var e=o.string().regex(/^#([0-9a-fA-F]{3}){1,2}$|^transparent$|^rgb\(.*\)$|^oklch\(.*\)$/,{message:'Must be hex, rgb(), oklch(), or "transparent"'}),b=o.object({baseDark:o.string().optional(),baseLight:o.string().optional(),deepDark:e,deepLight:e,transparent:o.string().optional(),main:e.optional(),opposite:e.optional(),complementary:e.optional(),information:e.optional(),success:e.optional(),danger:e.optional(),warning:e.optional(),primary:e.optional(),secondary:e.optional(),tertiary:e.optional()}).passthrough(),u=o.object({brands:o.record(o.string(),b).refine(n=>Object.keys(n).length>0,{message:"apollion.config.mjs: brands must contain at least 1 entry"}),modes:o.array(o.enum(["light","dark"])).optional(),surfaces:o.array(o.enum(["positive","negative"])).optional(),dimensions:o.array(o.enum(["compact","normal","spacious"])).optional(),output:o.object({runtime:o.boolean().optional(),css:o.boolean().optional(),json:o.boolean().optional(),ts:o.boolean().optional()}).passthrough().optional()}).passthrough();var _=[/\brequire\s*\(/,/\bimport\s*\(/,/^\s*import\s+/m],k=1e3;function v(n){return n}r(v,"defineConfig");var f=class f extends Error{constructor(s,p){super(s);this.configPath=p;this.name="ConfigLoadError"}};r(f,"ConfigLoadError");var a=f;async function I(n){let i=x(n.configPath),s=await w(i,"utf8");for(let t of _)if(t.test(s))throw new a(`apollion.config: dynamic require/import not allowed (matched ${t}). Declarative config only \u2014 see ADR-006 \xA73.8 B3.`,i);let p=s.replace(/(^|\n)\s*export\s+default\s+/,(t,l)=>`${l}__apollion_config_export = `);if(p===s)throw new a("apollion.config: missing `export default <object>;` \u2014 config must export a default object literal.",i);let g={__apollion_config_export:void 0,console:j(),Symbol,Number,String,Boolean,Array,Object,JSON,Math,defineConfig:r(t=>t,"defineConfig")},d=y(g);try{new C(p,{filename:i}).runInContext(d,{timeout:k})}catch(t){throw new a(`apollion.config: evaluation failed \u2014 ${t instanceof Error?t.message:String(t)}`,i)}let m=g.__apollion_config_export;if(m===void 0)throw new a("apollion.config: `export default` produced undefined.",i);let c=u.safeParse(m);if(!c.success){let t=c.error.issues.map(l=>` ${l.path.join(".")||"(root)"}: ${l.message}`).join(`
|
|
2
|
+
`);throw new a(`apollion.config: schema validation failed \u2014
|
|
3
|
+
${t}`,i)}return c.data}r(I,"loadConfig");function j(){return{log:r((...n)=>console.log("[apollion.config]",...n),"log"),warn:r((...n)=>console.warn("[apollion.config]",...n),"warn"),error:r((...n)=>console.error("[apollion.config]",...n),"error")}}r(j,"makeReadOnlyConsole");export{a as ConfigLoadError,v as defineConfig,I as loadConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var t=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var c=(o,n)=>t(o,"name",{value:n,configurable:!0});var D=(o,n)=>{for(var s in n)t(o,s,{get:n[s],enumerable:!0})},S=(o,n,s,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let e of f(n))!b.call(o,e)&&e!==s&&t(o,e,{get:()=>n[e],enumerable:!(r=u(n,e))||r.enumerable});return o};var g=o=>S(t({},"__esModule",{value:!0}),o);var F={};D(F,{expandVariants:()=>C,variantName:()=>E});module.exports=g(F);var x=["light","dark"],A=["positive","negative"],y=["compact","normal","spacious"];function C(o){let n=o.modes??x,s=o.surfaces??A,r=o.dimensions??y,e=Object.entries(o.brands).sort(([i],[a])=>i.localeCompare(a)),d=[];for(let[i,a]of e)for(let l of n)for(let p of s)for(let m of r)d.push({brand:i,colors:a,mode:l,surface:p,dimension:m});return d}c(C,"expandVariants");function E(o){return`${o.brand}.${o.mode}.${o.surface}.${o.dimension}`}c(E,"variantName");0&&(module.exports={expandVariants,variantName});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var m=Object.defineProperty;var r=(o,n)=>m(o,"name",{value:n,configurable:!0});var u=["light","dark"],f=["positive","negative"],b=["compact","normal","spacious"];function S(o){let n=o.modes??u,i=o.surfaces??f,a=o.dimensions??b,c=Object.entries(o.brands).sort(([e],[s])=>e.localeCompare(s)),t=[];for(let[e,s]of c)for(let d of n)for(let l of i)for(let p of a)t.push({brand:e,colors:s,mode:d,surface:l,dimension:p});return t}r(S,"expandVariants");function g(o){return`${o.brand}.${o.mode}.${o.surface}.${o.dimension}`}r(g,"variantName");export{S as expandVariants,g as variantName};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var s=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var a=(n,e)=>s(n,"name",{value:e,configurable:!0});var k=(n,e)=>{for(var r in e)s(n,r,{get:e[r],enumerable:!0})},N=(n,e,r,c)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of S(e))!T.call(n,t)&&t!==r&&s(n,t,{get:()=>e[t],enumerable:!(c=I(e,t))||c.enumerable});return n};var b=n=>N(s({},"__esModule",{value:!0}),n);var A={};k(A,{TOKENS_RUNTIME_API_VERSION:()=>O,createFoundation:()=>u.createFoundation,createSpacing:()=>x,getOppositeColor:()=>d,invertForSurface:()=>f.invertForSurface,mountGreyscaleColors:()=>y,mountNeutralColors:()=>h,mountSingleColor:()=>g});module.exports=b(A);var p=require("culori");var L=(0,p.converter)("oklch");function o(n,e,r){let c=(0,p.parse)(n),t=(0,p.parse)(e);if(!c||!t)return n;let i=(0,p.interpolate)([c,t],"oklch");return(0,p.formatHex)(i(r))??n}a(o,"mixOklch");function m(n){return n?typeof n=="string"?n:n.base:"#000"}a(m,"coerceToHex");function d(n){let e=m(n),r=L((0,p.parse)(e));if(!r)return e;let c={...r,h:((r.h??0)+180)%360};return(0,p.formatHex)(c)??e}a(d,"getOppositeColor");function g(n,e){let r=m(n),c=e.deepDark,t=e.deepLight;return{base:r,dark:o(r,c,.6),action:o(r,c,.2),active:o(r,t,.6),light:o(r,t,.9)}}a(g,"mountSingleColor");function y(n){let e=n.deepLight,r=n.deepDark;return{0:e,5:o(e,r,.05),10:o(e,r,.1),20:o(e,r,.2),30:o(e,r,.3),40:o(e,r,.4),50:o(e,r,.5),60:o(e,r,.6),70:o(e,r,.7),80:o(e,r,.8),90:o(e,r,.9),100:o(e,r,1)}}a(y,"mountGreyscaleColors");function h(n){let e=n.deepLight,r=n.deepDark,c=n.primary,t=a(l=>1/17*(l-1),"interpolation"),i=o(c,r,.5);return{0:o(i,e,t(18)),5:o(i,e,t(17.5)),10:o(i,e,t(17)),20:o(i,e,t(16)),30:o(i,e,t(15)),40:o(i,e,t(14)),50:o(i,e,t(13)),60:o(i,e,t(12)),70:o(i,e,t(11)),80:o(i,e,t(10)),90:o(i,e,t(9)),100:o(i,e,t(8)),110:o(i,e,t(7)),120:o(i,e,t(6)),130:o(i,e,t(5)),140:o(i,e,t(4)),150:o(i,e,t(3)),160:o(i,e,t(2)),170:i,180:o(i,r,.25)}}a(h,"mountNeutralColors");var C=require("@apollion-dsi/core/themes/dimension");function x({multiplier:n=.25,alias:e},r){let c=r!==void 0?C.DIMENSION_MULTIPLIERS[r]:n;return(...t)=>t.map(i=>{let l=typeof i=="string"?e[i]:i;return`${c*l}rem`}).join(" ")}a(x,"createSpacing");var u=require("@apollion-dsi/core/themes/foundation");var f=require("@apollion-dsi/core/themes/surface");var O="v1";0&&(module.exports={TOKENS_RUNTIME_API_VERSION,createFoundation,createSpacing,getOppositeColor,invertForSurface,mountGreyscaleColors,mountNeutralColors,mountSingleColor});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var f=Object.defineProperty;var p=(i,e)=>f(i,"name",{value:e,configurable:!0});import{converter as m,formatHex as l,interpolate as d,parse as s}from"culori";var g=m("oklch");function o(i,e,r){let c=s(i),t=s(e);if(!c||!t)return i;let n=d([c,t],"oklch");return l(n(r))??i}p(o,"mixOklch");function u(i){return i?typeof i=="string"?i:i.base:"#000"}p(u,"coerceToHex");function y(i){let e=u(i),r=g(s(e));if(!r)return e;let c={...r,h:((r.h??0)+180)%360};return l(c)??e}p(y,"getOppositeColor");function h(i,e){let r=u(i),c=e.deepDark,t=e.deepLight;return{base:r,dark:o(r,c,.6),action:o(r,c,.2),active:o(r,t,.6),light:o(r,t,.9)}}p(h,"mountSingleColor");function C(i){let e=i.deepLight,r=i.deepDark;return{0:e,5:o(e,r,.05),10:o(e,r,.1),20:o(e,r,.2),30:o(e,r,.3),40:o(e,r,.4),50:o(e,r,.5),60:o(e,r,.6),70:o(e,r,.7),80:o(e,r,.8),90:o(e,r,.9),100:o(e,r,1)}}p(C,"mountGreyscaleColors");function x(i){let e=i.deepLight,r=i.deepDark,c=i.primary,t=p(a=>1/17*(a-1),"interpolation"),n=o(c,r,.5);return{0:o(n,e,t(18)),5:o(n,e,t(17.5)),10:o(n,e,t(17)),20:o(n,e,t(16)),30:o(n,e,t(15)),40:o(n,e,t(14)),50:o(n,e,t(13)),60:o(n,e,t(12)),70:o(n,e,t(11)),80:o(n,e,t(10)),90:o(n,e,t(9)),100:o(n,e,t(8)),110:o(n,e,t(7)),120:o(n,e,t(6)),130:o(n,e,t(5)),140:o(n,e,t(4)),150:o(n,e,t(3)),160:o(n,e,t(2)),170:n,180:o(n,r,.25)}}p(x,"mountNeutralColors");import{DIMENSION_MULTIPLIERS as I}from"@apollion-dsi/core/themes/dimension";function S({multiplier:i=.25,alias:e},r){let c=r!==void 0?I[r]:i;return(...t)=>t.map(n=>{let a=typeof n=="string"?e[n]:n;return`${c*a}rem`}).join(" ")}p(S,"createSpacing");import{createFoundation as T}from"@apollion-dsi/core/themes/foundation";import{invertForSurface as k}from"@apollion-dsi/core/themes/surface";var G="v1";export{G as TOKENS_RUNTIME_API_VERSION,T as createFoundation,S as createSpacing,y as getOppositeColor,k as invertForSurface,C as mountGreyscaleColors,x as mountNeutralColors,h as mountSingleColor};
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@apollion-dsi/tokens",
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "Apollion Design System — Framework-agnostic design tokens",
|
|
5
|
+
"homepage": "https://github.com/apollion-ds/apollion",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "lib/runtime/v1.cjs",
|
|
9
|
+
"module": "lib/runtime/v1.esm.js",
|
|
10
|
+
"types": "lib/runtime/v1.d.ts",
|
|
11
|
+
"bin": {
|
|
12
|
+
"apollion-tokens": "./lib/cli.mjs"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
"./runtime/v1": {
|
|
16
|
+
"types": "./lib/runtime/v1.d.ts",
|
|
17
|
+
"import": "./lib/runtime/v1.esm.js",
|
|
18
|
+
"require": "./lib/runtime/v1.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./build": {
|
|
21
|
+
"types": "./lib/build.d.ts",
|
|
22
|
+
"import": "./lib/build.esm.js",
|
|
23
|
+
"require": "./lib/build.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./config-schema": {
|
|
26
|
+
"types": "./lib/config-schema.d.ts",
|
|
27
|
+
"import": "./lib/config-schema.esm.js",
|
|
28
|
+
"require": "./lib/config-schema.cjs"
|
|
29
|
+
},
|
|
30
|
+
"./config-loader": {
|
|
31
|
+
"types": "./lib/config-loader.d.ts",
|
|
32
|
+
"import": "./lib/config-loader.esm.js",
|
|
33
|
+
"require": "./lib/config-loader.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./dist/css/*": "./dist/css/*",
|
|
36
|
+
"./dist/json/*": "./dist/json/*",
|
|
37
|
+
"./dist/ts/*": "./dist/ts/*",
|
|
38
|
+
"./dist/manifest.json": "./dist/manifest.json"
|
|
39
|
+
},
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"files": [
|
|
42
|
+
"lib",
|
|
43
|
+
"src"
|
|
44
|
+
],
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/fedbalves/apollion-ds.git",
|
|
48
|
+
"directory": "packages/tokens"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "node ./esbuild.cjs",
|
|
52
|
+
"postbuild": "tsc --emitDeclarationOnly",
|
|
53
|
+
"check-types": "tsc --noEmit",
|
|
54
|
+
"lint": "eslint src --quiet",
|
|
55
|
+
"lint:full": "eslint src",
|
|
56
|
+
"lint:fix": "eslint src --fix",
|
|
57
|
+
"prettier": "prettier --check **/*.{ts,tsx} --ignore-path .gitignore --no-error-on-unmatched-pattern",
|
|
58
|
+
"format": "yarn prettier --write",
|
|
59
|
+
"test": "jest --passWithNoTests",
|
|
60
|
+
"coverage": "jest --coverage --passWithNoTests",
|
|
61
|
+
"validate": "./scripts/validate.sh",
|
|
62
|
+
"release": "yarn build && changeset publish"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"@apollion-dsi/core": ">=4.0.0"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"culori": "4.0.2",
|
|
69
|
+
"zod": "4.4.3"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@apollion-dsi/core": "workspace:*",
|
|
73
|
+
"@apollion-dsi/eslint-config": "0.7.0",
|
|
74
|
+
"@types/culori": "4.0.1",
|
|
75
|
+
"@types/jest": "30.0.0",
|
|
76
|
+
"@types/node": "25.7.0",
|
|
77
|
+
"esbuild": "0.28.0",
|
|
78
|
+
"eslint": "9.29.0",
|
|
79
|
+
"jest": "30.4.2",
|
|
80
|
+
"prettier": "3.8.3",
|
|
81
|
+
"ts-jest": "^29.4.11",
|
|
82
|
+
"typescript": "6.0.3"
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/build.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `apollion-tokens build` orchestrator — atomic + idempotent (PRD-002 §S5).
|
|
3
|
+
*
|
|
4
|
+
* **Atomicity contract (B1 mitigation from improve-architecture):**
|
|
5
|
+
* 1. `mkdtemp` → write all variants to `.apollion-tmp/{uuid}/`.
|
|
6
|
+
* 2. Build manifest (sha256 of each file + configHash + gitCommit + env).
|
|
7
|
+
* 3. Atomic `rename(tmpdir, outDir)` — if step 1 or 2 throws, `dist/` keeps
|
|
8
|
+
* its previous state. Never partial.
|
|
9
|
+
*
|
|
10
|
+
* **Idempotency contract:** rerunning with the same `(config, gitHead,
|
|
11
|
+
* nodeVersion, platform)` produces byte-identical `dist/` + `manifest.json`.
|
|
12
|
+
* Verified by `__tests__/build-idempotent.test.ts`.
|
|
13
|
+
*
|
|
14
|
+
* **`--check` mode (CI gate):** read existing `manifest.json`, recompute
|
|
15
|
+
* the expected manifest from current config, compare. Exit 1 on drift.
|
|
16
|
+
* Never writes anything. Suitable for pre-push hook or local lint.
|
|
17
|
+
*
|
|
18
|
+
* @see ADR-006 §3.5 (atomic+idempotent build)
|
|
19
|
+
* @see PRD-002 §S5 acceptance (rerun byte-identical, --check blocks drift)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
23
|
+
import { tmpdir } from 'node:os';
|
|
24
|
+
import { dirname, join } from 'node:path';
|
|
25
|
+
|
|
26
|
+
import { renderCss } from './renderers/css';
|
|
27
|
+
import { renderJson } from './renderers/json';
|
|
28
|
+
import { renderTs } from './renderers/ts';
|
|
29
|
+
import type { ApollionConfig, Variant } from './config-schema';
|
|
30
|
+
import { expandVariants, variantName } from './config-schema';
|
|
31
|
+
import type { BuildManifest, ManifestFileEntry } from './manifest';
|
|
32
|
+
import { buildEnv, detectGitCommit, hashConfig, serializeManifest, sha256 } from './manifest';
|
|
33
|
+
import { buildFoundationForVariant } from './theme-factory';
|
|
34
|
+
|
|
35
|
+
export interface BuildOptions {
|
|
36
|
+
config: ApollionConfig;
|
|
37
|
+
outDir: string;
|
|
38
|
+
cwd?: string;
|
|
39
|
+
verbose?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface BuildResult {
|
|
43
|
+
manifest: BuildManifest;
|
|
44
|
+
outDir: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Atomic build — see file header for full contract. */
|
|
48
|
+
export async function build(opts: BuildOptions): Promise<BuildResult> {
|
|
49
|
+
const { config, outDir, cwd = process.cwd(), verbose = false } = opts;
|
|
50
|
+
const variants = expandVariants(config);
|
|
51
|
+
const output = config.output ?? {};
|
|
52
|
+
|
|
53
|
+
if (variants.length === 0) {
|
|
54
|
+
throw new Error('apollion-tokens build: no variants (config.brands is empty)');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const tmpRoot = await mkdtemp(join(tmpdir(), 'apollion-tokens-'));
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const files: ManifestFileEntry[] = [];
|
|
61
|
+
|
|
62
|
+
for (const variant of variants) {
|
|
63
|
+
const foundation = buildFoundationForVariant(variant);
|
|
64
|
+
const baseName = variantName(variant);
|
|
65
|
+
|
|
66
|
+
if (output.css) {
|
|
67
|
+
const content = renderCss(foundation, variant);
|
|
68
|
+
const path = `css/${baseName}.css`;
|
|
69
|
+
await writeAt(tmpRoot, path, content);
|
|
70
|
+
files.push({ path, sha256: sha256(content) });
|
|
71
|
+
if (verbose) console.log(` ✓ ${path}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (output.json) {
|
|
75
|
+
const content = renderJson(foundation, variant);
|
|
76
|
+
const path = `json/${baseName}.json`;
|
|
77
|
+
await writeAt(tmpRoot, path, content);
|
|
78
|
+
files.push({ path, sha256: sha256(content) });
|
|
79
|
+
if (verbose) console.log(` ✓ ${path}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (output.ts) {
|
|
83
|
+
const content = renderTs(foundation, variant);
|
|
84
|
+
const path = `ts/${baseName}.d.ts`;
|
|
85
|
+
await writeAt(tmpRoot, path, content);
|
|
86
|
+
files.push({ path, sha256: sha256(content) });
|
|
87
|
+
if (verbose) console.log(` ✓ ${path}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const manifest: BuildManifest = {
|
|
92
|
+
configHash: hashConfig(config),
|
|
93
|
+
files,
|
|
94
|
+
gitCommit: detectGitCommit(cwd),
|
|
95
|
+
buildEnv: buildEnv(),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const manifestText = serializeManifest(manifest);
|
|
99
|
+
await writeFile(join(tmpRoot, 'manifest.json'), manifestText);
|
|
100
|
+
|
|
101
|
+
// Atomic rename — replaces outDir entirely. If outDir exists, remove first
|
|
102
|
+
// (rename behavior across platforms for non-empty target is undefined).
|
|
103
|
+
await rm(outDir, { recursive: true, force: true });
|
|
104
|
+
await mkdir(dirname(outDir), { recursive: true });
|
|
105
|
+
await rename(tmpRoot, outDir);
|
|
106
|
+
|
|
107
|
+
if (verbose) console.log(`✓ ${files.length} files → ${outDir}`);
|
|
108
|
+
|
|
109
|
+
return { manifest, outDir };
|
|
110
|
+
} catch (err) {
|
|
111
|
+
// Best-effort cleanup of tmpdir on error — never propagate to user dist.
|
|
112
|
+
await rm(tmpRoot, { recursive: true, force: true }).catch(() => {});
|
|
113
|
+
throw err;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* `--check` mode: read existing manifest + recompute expected. Exit 1 on drift.
|
|
119
|
+
* Returns true if in sync.
|
|
120
|
+
*/
|
|
121
|
+
export async function check(opts: { config: ApollionConfig; outDir: string }): Promise<boolean> {
|
|
122
|
+
const { config, outDir } = opts;
|
|
123
|
+
let current: BuildManifest;
|
|
124
|
+
try {
|
|
125
|
+
current = JSON.parse(await readFile(join(outDir, 'manifest.json'), 'utf8'));
|
|
126
|
+
} catch {
|
|
127
|
+
return false; // no manifest = needs build
|
|
128
|
+
}
|
|
129
|
+
return current.configHash === hashConfig(config);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function writeAt(root: string, relativePath: string, content: string): Promise<void> {
|
|
133
|
+
const full = join(root, relativePath);
|
|
134
|
+
await mkdir(dirname(full), { recursive: true });
|
|
135
|
+
await writeFile(full, content);
|
|
136
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OKLch palette builders — **Strangler Fig duplicate** of
|
|
3
|
+
* `@apollion-dsi/core/themes/colors` (ADR-006 §6 S4, PRD-002 §S4).
|
|
4
|
+
*
|
|
5
|
+
* **Strangler Fig contract:** this file DUPLICATES the implementation from
|
|
6
|
+
* `packages/core/src/themes/Colors/Colors.helpers.ts` (S2 OKLch migration).
|
|
7
|
+
* It does NOT delegate to core. Rationale:
|
|
8
|
+
*
|
|
9
|
+
* - Core remains SSOT for v4.x release line.
|
|
10
|
+
* - Tokens v4.0.0 ships standalone — consumer that opts into
|
|
11
|
+
* `@apollion-dsi/tokens` (via `optionalDependencies` in core) gets a
|
|
12
|
+
* self-contained palette derivation without runtime dep on core internals.
|
|
13
|
+
* - Future PRD-003 (post-v4) consolidates by deleting either copy after
|
|
14
|
+
* real consumer adoption validates the API surface.
|
|
15
|
+
*
|
|
16
|
+
* Parity is enforced via `__tests__/parity.test.ts`: given identical input,
|
|
17
|
+
* `core.mountSingleColor(...) === tokens.mountSingleColor(...)` for every
|
|
18
|
+
* exported function. Drift = build fail.
|
|
19
|
+
*
|
|
20
|
+
* Types are imported from core via the stable subpath
|
|
21
|
+
* `@apollion-dsi/core/themes/colors` (NOT the root barrel that gets removed
|
|
22
|
+
* in E7) — types ARE shared SSOT; only logic is duplicated.
|
|
23
|
+
*
|
|
24
|
+
* @see ../../docs/adr/ADR-006-v4-oklch-dimension-token-split.md §3.1 §6 S4
|
|
25
|
+
* @see ../../docs/prd/PRD-002-v4-epic-execution.md §S4
|
|
26
|
+
*/
|
|
27
|
+
import type {
|
|
28
|
+
AcceptColorType,
|
|
29
|
+
ColorPalleteInterface,
|
|
30
|
+
ColorsInput,
|
|
31
|
+
GrayscaleColorsType,
|
|
32
|
+
NeutralColorsType,
|
|
33
|
+
} from '@apollion-dsi/core/themes/colors';
|
|
34
|
+
import { converter, formatHex, interpolate, parse } from 'culori';
|
|
35
|
+
|
|
36
|
+
const toOklch = converter('oklch');
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* OKLch perceptual lerp wrapper. See core's S2 docs for rationale.
|
|
40
|
+
*
|
|
41
|
+
* Validation: `spike/culori-oklch-lerp-parity` (preservada local — ADR-006
|
|
42
|
+
* §11.5) confirmou 0 unexpected regressions em 68 deltas; greyscale ramp
|
|
43
|
+
* 19× mais uniforme em OKLAB L space.
|
|
44
|
+
*/
|
|
45
|
+
function mixOklch(a: string, b: string, t: number): string {
|
|
46
|
+
const colorA = parse(a);
|
|
47
|
+
const colorB = parse(b);
|
|
48
|
+
if (!colorA || !colorB) return a;
|
|
49
|
+
const fn = interpolate([colorA, colorB], 'oklch');
|
|
50
|
+
return formatHex(fn(t)) ?? a;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function coerceToHex(color: AcceptColorType): string {
|
|
54
|
+
if (!color) return '#000';
|
|
55
|
+
if (typeof color === 'string') return color;
|
|
56
|
+
return color.base;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Rotate hue 180° in OKLch space. Semantic shift vs HSL — see core for details. */
|
|
60
|
+
export function getOppositeColor(color: AcceptColorType): string {
|
|
61
|
+
const hex = coerceToHex(color);
|
|
62
|
+
const oklch = toOklch(parse(hex));
|
|
63
|
+
if (!oklch) return hex;
|
|
64
|
+
const rotated = { ...oklch, h: ((oklch.h ?? 0) + 180) % 360 };
|
|
65
|
+
return formatHex(rotated) ?? hex;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Derive `{ base, dark, action, active, light }` via OKLch lerp. */
|
|
69
|
+
export function mountSingleColor(color: AcceptColorType, colors: ColorsInput): ColorPalleteInterface {
|
|
70
|
+
const base = coerceToHex(color);
|
|
71
|
+
const darken = colors.deepDark;
|
|
72
|
+
const lighten = colors.deepLight;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
base,
|
|
76
|
+
dark: mixOklch(base, darken!, 0.6),
|
|
77
|
+
action: mixOklch(base, darken!, 0.2),
|
|
78
|
+
active: mixOklch(base, lighten!, 0.6),
|
|
79
|
+
light: mixOklch(base, lighten!, 0.9),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** 12-step greyscale ramp — perceptually uniform via OKLch lerp. */
|
|
84
|
+
export function mountGreyscaleColors(colors: ColorsInput): Record<GrayscaleColorsType, string> {
|
|
85
|
+
const lighter = colors.deepLight!;
|
|
86
|
+
const darken = colors.deepDark!;
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
'0': lighter,
|
|
90
|
+
'5': mixOklch(lighter, darken, 0.05),
|
|
91
|
+
'10': mixOklch(lighter, darken, 0.1),
|
|
92
|
+
'20': mixOklch(lighter, darken, 0.2),
|
|
93
|
+
'30': mixOklch(lighter, darken, 0.3),
|
|
94
|
+
'40': mixOklch(lighter, darken, 0.4),
|
|
95
|
+
'50': mixOklch(lighter, darken, 0.5),
|
|
96
|
+
'60': mixOklch(lighter, darken, 0.6),
|
|
97
|
+
'70': mixOklch(lighter, darken, 0.7),
|
|
98
|
+
'80': mixOklch(lighter, darken, 0.8),
|
|
99
|
+
'90': mixOklch(lighter, darken, 0.9),
|
|
100
|
+
'100': mixOklch(lighter, darken, 1),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** 20-step neutral ramp anchored at n170 (primary mixed with darker). */
|
|
105
|
+
export function mountNeutralColors(colors: ColorsInput): Record<NeutralColorsType, string> {
|
|
106
|
+
const lighter = colors.deepLight!;
|
|
107
|
+
const darker = colors.deepDark!;
|
|
108
|
+
const primary = colors.primary!;
|
|
109
|
+
|
|
110
|
+
const interpolation = (n: number): number => (1 / 17) * (n - 1);
|
|
111
|
+
const n170 = mixOklch(primary, darker, 0.5);
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
'0': mixOklch(n170, lighter, interpolation(18)),
|
|
115
|
+
'5': mixOklch(n170, lighter, interpolation(17.5)),
|
|
116
|
+
'10': mixOklch(n170, lighter, interpolation(17)),
|
|
117
|
+
'20': mixOklch(n170, lighter, interpolation(16)),
|
|
118
|
+
'30': mixOklch(n170, lighter, interpolation(15)),
|
|
119
|
+
'40': mixOklch(n170, lighter, interpolation(14)),
|
|
120
|
+
'50': mixOklch(n170, lighter, interpolation(13)),
|
|
121
|
+
'60': mixOklch(n170, lighter, interpolation(12)),
|
|
122
|
+
'70': mixOklch(n170, lighter, interpolation(11)),
|
|
123
|
+
'80': mixOklch(n170, lighter, interpolation(10)),
|
|
124
|
+
'90': mixOklch(n170, lighter, interpolation(9)),
|
|
125
|
+
'100': mixOklch(n170, lighter, interpolation(8)),
|
|
126
|
+
'110': mixOklch(n170, lighter, interpolation(7)),
|
|
127
|
+
'120': mixOklch(n170, lighter, interpolation(6)),
|
|
128
|
+
'130': mixOklch(n170, lighter, interpolation(5)),
|
|
129
|
+
'140': mixOklch(n170, lighter, interpolation(4)),
|
|
130
|
+
'150': mixOklch(n170, lighter, interpolation(3)),
|
|
131
|
+
'160': mixOklch(n170, lighter, interpolation(2)),
|
|
132
|
+
'170': n170,
|
|
133
|
+
'180': mixOklch(n170, darker, 0.25),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foundation builder — **interim re-export** from
|
|
3
|
+
* `@apollion-dsi/core/themes/foundation` (ADR-006 §6 S4, PRD-002 §S4).
|
|
4
|
+
*
|
|
5
|
+
* **Interim contract:** Foundation is complex (ADR-002 §3) and ships
|
|
6
|
+
* unchanged in v4. Tokens re-exports `createFoundation` from core via the
|
|
7
|
+
* stable subpath. Strangler Fig consolidation to standalone implementation
|
|
8
|
+
* is deferred to PRD-003 post-v4 if/when real consumer adoption justifies
|
|
9
|
+
* the duplication cost.
|
|
10
|
+
*
|
|
11
|
+
* Unlike `./colors.ts` and `./spacing.ts` (full duplicates because v4 logic
|
|
12
|
+
* is new), Foundation logic exists pre-v4 and parity is enforced trivially
|
|
13
|
+
* by re-export identity.
|
|
14
|
+
*
|
|
15
|
+
* @see ./colors.ts (Strangler Fig contract notes)
|
|
16
|
+
* @see ../../docs/adr/ADR-002-layered-theme-aliases-and-surface.md §3
|
|
17
|
+
*/
|
|
18
|
+
export { createFoundation } from '@apollion-dsi/core/themes/foundation';
|
|
19
|
+
export type { FoundationLayer } from '@apollion-dsi/core/themes/foundation';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spacing builder — **Strangler Fig duplicate** of
|
|
3
|
+
* `@apollion-dsi/core/themes/spacing` (ADR-006 §6 S4).
|
|
4
|
+
*
|
|
5
|
+
* Same dimension override semantics as core: when `dimension` arg
|
|
6
|
+
* provided, its calibrated multiplier wins over `input.multiplier`.
|
|
7
|
+
*
|
|
8
|
+
* @see ./colors.ts (Strangler Fig contract notes)
|
|
9
|
+
*/
|
|
10
|
+
import type { Dimension } from '@apollion-dsi/core/themes/dimension';
|
|
11
|
+
import { DIMENSION_MULTIPLIERS } from '@apollion-dsi/core/themes/dimension';
|
|
12
|
+
import type { SpacingInput, SpacingInterface, SpacingThemeInterface } from '@apollion-dsi/core/themes/spacing';
|
|
13
|
+
|
|
14
|
+
export function createSpacing(
|
|
15
|
+
{ multiplier = 0.25, alias }: SpacingInput,
|
|
16
|
+
dimension?: Dimension,
|
|
17
|
+
): SpacingThemeInterface {
|
|
18
|
+
const effectiveMultiplier = dimension !== undefined ? DIMENSION_MULTIPLIERS[dimension] : multiplier;
|
|
19
|
+
|
|
20
|
+
return (...values: SpacingInterface[]) =>
|
|
21
|
+
values
|
|
22
|
+
.map((value) => {
|
|
23
|
+
const spacingValue = typeof value === 'string' ? alias[value] : value;
|
|
24
|
+
return `${effectiveMultiplier * spacingValue}rem`;
|
|
25
|
+
})
|
|
26
|
+
.join(' ');
|
|
27
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surface builder — **interim re-export** from
|
|
3
|
+
* `@apollion-dsi/core/themes/surface` (ADR-006 §6 S4, PRD-002 §S4).
|
|
4
|
+
*
|
|
5
|
+
* **Interim contract:** Surface ships unchanged in v4 S4. Tokens re-exports
|
|
6
|
+
* `invertForSurface` from core via the stable subpath. Surface-within-mode
|
|
7
|
+
* extension (E6 in S6) will REPLACE the signature to
|
|
8
|
+
* `invertForSurface(theme, surface, mode)`; tokens will update its re-export
|
|
9
|
+
* accordingly in S6.
|
|
10
|
+
*
|
|
11
|
+
* @see ./colors.ts (Strangler Fig contract notes)
|
|
12
|
+
* @see ../../docs/adr/ADR-002-layered-theme-aliases-and-surface.md §3
|
|
13
|
+
* @see ../../docs/adr/ADR-006-v4-oklch-dimension-token-split.md §3.6 (E6 in S6)
|
|
14
|
+
*/
|
|
15
|
+
export { invertForSurface } from '@apollion-dsi/core/themes/surface';
|
|
16
|
+
export type { SurfaceMode } from '@apollion-dsi/core/themes/surface';
|