okchroma 0.8.1 → 0.9.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 CHANGED
@@ -7,7 +7,7 @@ variables, every token spelled the same way in each.
7
7
 
8
8
  Every family (neutral, brand, brand-alt, critical, warning, positive, info) carries the
9
9
  same scale: 3 papers, 4 chalks, 1 highlighter, 1 pencil, 2 pens, plus the stamp (the
10
- solid fill with its hover and pressed states, its edge, and its text). The neutral adds
10
+ solid fill at rest and at one and two steps, its edge, and its text). The neutral adds
11
11
  the two poles. Two link trios and the two brand seeds complete the set. Light and dark
12
12
  resolve together and ship on the same names, so a token is mapped once and holds for any
13
13
  brand.
@@ -60,18 +60,21 @@ example: [Install and API](https://egerrity.github.io/okchroma/#/docs/install).
60
60
 
61
61
  ## What it emits
62
62
 
63
- Every token has one path, and every output spells it: `brand/stamp/fill`,
64
- `neutral/paper-0`, `link/default/enabled`, `absolute/brand`. Joined with hyphens it is the
65
- CSS custom property (`--brand-stamp-fill`); joined with slashes it is the Figma variable
66
- and the DTCG token path. A consumer whose paths start with the category puts a `color`
67
- group in front: the extended plugin writes `color/brand/stamp/fill`, and the DTCG
68
- documents nest under the same word when asked. The CSS names never carry it.
63
+ Every token has one path, and every output spells it: `brand/stamp-0`,
64
+ `neutral/paper-0`, `link/default-0`, `absolute/brand`. A path is a group and one word, and
65
+ the word is a position: a band at a lightness, the stamp at a step, a link posture at a
66
+ step. No name carries a state word; a semantic layer names the states and aliases the
67
+ steps. Joined with hyphens the path is the CSS custom property (`--brand-stamp-0`); joined
68
+ with slashes it is the Figma variable and the DTCG token path. A consumer whose paths
69
+ start with the category puts a `color` group in front: the extended plugin writes
70
+ `color/brand/stamp-0`, and the DTCG documents nest under the same word when asked. The
71
+ CSS names never carry it.
69
72
 
70
73
  | Group | Tokens |
71
74
  |---|---|
72
- | `neutral` | `paper-0`, `paper-1`, `paper-3`, `paper-5`, `chalk-8`, `chalk-11`, `chalk-15`, `chalk-20`, `highlighter-26`, `pencil-47`, `pen-58`, `pen-70`, `pen-100`, and the stamp: `stamp/fill`, `stamp/fill-hover`, `stamp/fill-pressed`, `stamp/edge`, `stamp/on` |
75
+ | `neutral` | `paper-0`, `paper-1`, `paper-3`, `paper-5`, `chalk-8`, `chalk-11`, `chalk-15`, `chalk-20`, `highlighter-26`, `pencil-47`, `pen-58`, `pen-70`, `pen-100`, and the stamp: `stamp-0`, `stamp-1`, `stamp-2`, `stamp-edge`, `stamp-on` |
73
76
  | `brand`, `brand-alt`, `critical`, `warning`, `positive`, `info` | the same eleven stops and the same five stamp tokens |
74
- | `link` | `default/enabled`, `default/hover`, `default/pressed` for text on the papers; `inverse/enabled`, `inverse/hover`, `inverse/pressed` for text on the pen ground |
77
+ | `link` | `default-0`, `default-1`, `default-2` for text on the papers; `inverse-0`, `inverse-1`, `inverse-2` for text on the pen ground; the link at rest, one step from rest, two steps |
75
78
  | `absolute` | `brand`, `brand-alt`: the seeds as given, reference values |
76
79
 
77
80
  What each name means, which stop to read for which job, and the rules for an agent
@@ -84,9 +87,10 @@ borders and illustration, never text; the highlighter is the non-text contrast s
84
87
  rings, icons, large text, and the one stop to build translucent state layers on); the
85
88
  pencil and the pens are text, and the pencil doubles as the emphasis fill.
86
89
 
87
- **The stamp.** `stamp/fill` is the call-to-action fill, re-solved per family and theme;
88
- `fill-hover` and `fill-pressed` are its states; `stamp/on` is the text over it, the pole
89
- that passes (the pole at alpha on a quiet fill); `stamp/edge` is a stroke that resolves to
90
+ **The stamp.** `stamp-0` is the call-to-action fill at rest, re-solved per family and
91
+ theme; `stamp-1` and `stamp-2` are the fill one and two steps from rest, one apparent step
92
+ each; `stamp-on` is the text over it, the pole that passes (the pole at alpha on a quiet
93
+ fill); `stamp-edge` is a stroke that resolves to
90
94
  a visible offset only where the fill sits close to the page, else transparent, so a
91
95
  component renders the border unconditionally and layout never shifts.
92
96
 
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  LINK_GROUP: () => LINK_GROUP,
30
30
  LINK_POSTURES: () => LINK_POSTURES,
31
31
  LINK_STATES: () => LINK_STATES,
32
+ LINK_STEP: () => LINK_STEP,
32
33
  MODE_SPECS: () => MODE_SPECS,
33
34
  OFFSET_ALPHAS: () => OFFSET_ALPHAS,
34
35
  OUTLINE_HOVER_ALPHA: () => OUTLINE_HOVER_ALPHA,
@@ -43,9 +44,7 @@ __export(index_exports, {
43
44
  STAMP_FILL: () => STAMP_FILL,
44
45
  STAMP_FILL_HOVER: () => STAMP_FILL_HOVER,
45
46
  STAMP_FILL_PRESSED: () => STAMP_FILL_PRESSED,
46
- STAMP_LEAF: () => STAMP_LEAF,
47
47
  STAMP_ON: () => STAMP_ON,
48
- STAMP_STATE_LEAVES: () => STAMP_STATE_LEAVES,
49
48
  SUBTLE_PASTEL_K: () => SUBTLE_PASTEL_K,
50
49
  SUBTLE_TINT_MULT: () => SUBTLE_TINT_MULT,
51
50
  absolutePath: () => absolutePath,
@@ -66,6 +65,7 @@ __export(index_exports, {
66
65
  generateScale: () => generateScale,
67
66
  generateSubtleSecondary: () => generateSubtleSecondary,
68
67
  groupEntries: () => groupEntries,
68
+ linkLeaf: () => linkLeaf,
69
69
  linkPath: () => linkPath,
70
70
  neutralCss: () => neutralCss,
71
71
  neutralTintHue: () => neutralTintHue,
@@ -838,32 +838,20 @@ function stopTokenName(stop) {
838
838
  }
839
839
  var PAPER_0 = "paper-0";
840
840
  var PEN_100 = "pen-100";
841
- var STAMP_FILL = "stamp-fill";
842
- var STAMP_FILL_HOVER = "stamp-fill-hover";
843
- var STAMP_FILL_PRESSED = "stamp-fill-pressed";
841
+ var STAMP_FILL = "stamp-0";
842
+ var STAMP_FILL_HOVER = "stamp-1";
843
+ var STAMP_FILL_PRESSED = "stamp-2";
844
844
  var STAMP_EDGE = "stamp-edge";
845
845
  var STAMP_ON = "stamp-on";
846
- var STAMP_STATE_LEAVES = {
847
- [STAMP_FILL]: "stamp/fill",
848
- [STAMP_FILL_HOVER]: "stamp/fill-hover",
849
- [STAMP_FILL_PRESSED]: "stamp/fill-pressed",
850
- [STAMP_EDGE]: "stamp/edge",
851
- [STAMP_ON]: "stamp/on"
852
- };
853
- var STAMP_LEAF = {
854
- FILL: STAMP_STATE_LEAVES[STAMP_FILL],
855
- FILL_HOVER: STAMP_STATE_LEAVES[STAMP_FILL_HOVER],
856
- FILL_PRESSED: STAMP_STATE_LEAVES[STAMP_FILL_PRESSED],
857
- EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
858
- ON: STAMP_STATE_LEAVES[STAMP_ON]
859
- };
860
846
  var cssVarName = (path) => `--${path.join("-")}`;
861
847
  var figmaPathOf = (path) => path.join("/");
862
- var familyPath = (family, leaf) => [family, ...(STAMP_STATE_LEAVES[leaf] ?? leaf).split("/")];
848
+ var familyPath = (family, leaf) => [family, leaf];
863
849
  var LINK_GROUP = "link";
864
850
  var LINK_POSTURES = ["default", "inverse"];
865
851
  var LINK_STATES = ["enabled", "hover", "pressed"];
866
- var linkPath = (posture, state) => [LINK_GROUP, posture, state];
852
+ var LINK_STEP = { enabled: 0, hover: 1, pressed: 2 };
853
+ var linkLeaf = (posture, state) => `${posture}-${LINK_STEP[state]}`;
854
+ var linkPath = (posture, state) => [LINK_GROUP, linkLeaf(posture, state)];
867
855
  var ABSOLUTE_GROUP = "absolute";
868
856
  var absolutePath = (family) => [ABSOLUTE_GROUP, family];
869
857
  var COLOR_GROUP = "color";
@@ -2854,20 +2842,21 @@ var SCALE = {
2854
2842
  // ("heavy-emphasis": "high" and "low" are plane words in the community plugin and
2855
2843
  // "strong" carries "on")
2856
2844
  "pen-70": { req: "heavy-emphasis text, inverted backgrounds", contrast: AA_BODY, claim: claimOf("pen"), theming: solved, collides: true },
2857
- // the stamp family. "CTA" stays in these bodies on purpose: it is not a token label, so
2845
+ // the stamp rows, each a position: the fill at rest and at one and two steps, the edge,
2846
+ // the on-text. No body names a state; a consumer's semantic layer names the states and
2847
+ // aliases the steps. "CTA" stays in these bodies on purpose: it is not a token label, so
2858
2848
  // it floods nothing, and a designer's "cta" query lands on these rows.
2859
2849
  // the usage lines are the owner's guidance, for the document rendering: the fill is
2860
2850
  // misused as a standalone color in the wild
2861
- "stamp/fill": {
2862
- req: "CTAs",
2851
+ "stamp-0": {
2852
+ req: "CTAs, the fill at rest",
2863
2853
  theming: (f) => `fully re-solved per theme and family${COLOR_WORD[f]}`,
2864
- use: "primary button fills; optional for avatars, badges and other elements that carry text. Not a standalone color: it has no contrast guarantee of its own, only its on-text has one. Use it alone or with its hover and pressed states. Always bordered with its edge"
2854
+ use: "primary button fills; optional for avatars, badges and other elements that carry text. Not a standalone color: it has no contrast guarantee of its own, only its on-text has one. Use it alone or with the two steps above it. Always bordered with its edge"
2865
2855
  },
2866
- "stamp/fill-hover": { req: "CTA pointer-over state", theming: (f) => `follows its rest fill${COLOR_WORD[f]}`, use: "the pointer-over state of the fill, with the fill; for nothing else. Always bordered with the edge" },
2867
- "stamp/fill-pressed": { req: "CTA pressed state", theming: (f) => `follows its rest fill${COLOR_WORD[f]}`, use: "the pressed state of the fill, with the fill; for nothing else. Always bordered with the edge" },
2868
- "stamp/edge": { req: "min APCA visibility", theming: (f) => `draws for CTAs that sit close to the page; strength per family tier${COLOR_WORD[f]}`, use: "the border of the fill and its states, always rendered with them and never without them (it resolves to transparent unless the fill sits close to the page); for nothing else" },
2869
- // ("fill" is a label word and foreign here)
2870
- "stamp/on": { req: "text over the CTA color", contrast: `${AA_BODY} over its CTA`, theming: (f) => `whichever pole passes; quiet CTAs take the soft pole${COLOR_WORD[f]}`, use: "the only text color over the fill; never elsewhere" }
2856
+ "stamp-1": { req: "CTAs, the fill one step from rest", theming: (f) => `follows the fill at rest${COLOR_WORD[f]}`, use: "the fill one step from rest, with the fill at rest; for nothing else. Always bordered with the edge" },
2857
+ "stamp-2": { req: "CTAs, the fill two steps from rest", theming: (f) => `follows the fill at rest${COLOR_WORD[f]}`, use: "the fill two steps from rest, with the fill at rest; for nothing else. Always bordered with the edge" },
2858
+ "stamp-edge": { req: "min APCA visibility", theming: (f) => `draws for CTAs that sit close to the page; strength per family tier${COLOR_WORD[f]}`, use: "the border of the fill at every step, always rendered with it and never without it (it resolves to transparent unless the fill sits close to the page); for nothing else" },
2859
+ "stamp-on": { req: "text over the CTA color", contrast: `${AA_BODY} over its CTA`, theming: (f) => `whichever pole passes; quiet CTAs take the soft pole${COLOR_WORD[f]}`, use: "the only text color over the fill at every step; never elsewhere" }
2871
2860
  };
2872
2861
  var NEUTRAL_ONLY = {
2873
2862
  // the pole is measured against the neutral's own stops only; as text, it is the on-color
@@ -2876,15 +2865,15 @@ var NEUTRAL_ONLY = {
2876
2865
  // the literal pole carries no tint, so no theming line
2877
2866
  "pen-100": { req: "max-emphasis text", contrast: AA_BODY, claim: () => `${AA_BODY} on every paper and chalk of every family` }
2878
2867
  };
2879
- var LINK = (state, contrast) => ({
2880
- req: "links" + state,
2868
+ var LINK = (step, contrast) => ({
2869
+ req: "links" + step,
2881
2870
  contrast,
2882
2871
  claim: () => `${contrast} on every paper of the neutral`,
2883
2872
  theming: "rides the theme\u2019s link color; custom seed re-solves; overridable per theme",
2884
2873
  use: "hyperlinks; not a text-style CTA, which is the text stops read as states"
2885
2874
  });
2886
- var LINK_INVERSE = (state, contrast) => ({
2887
- req: "links over inverted backgrounds" + state,
2875
+ var LINK_INVERSE = (step, contrast) => ({
2876
+ req: "links over inverted backgrounds" + step,
2888
2877
  contrast,
2889
2878
  claim: () => `${contrast} on any family's pen-70`,
2890
2879
  theming: "same seed as the link, re-solved for inverted backgrounds; overridable per theme",
@@ -2893,12 +2882,12 @@ var LINK_INVERSE = (state, contrast) => ({
2893
2882
  var SYSTEM = {
2894
2883
  "absolute/brand": { req: "identity seed reference", theming: "the theme\u2019s own input, as given", use: "a reference value (logos, swatches of the input); never a UI color, it carries no contrast guarantee" },
2895
2884
  "absolute/brand-alt": { req: "identity seed reference", theming: "the theme\u2019s paired input, as given", use: "a reference value (logos, swatches of the input); never a UI color, it carries no contrast guarantee" },
2896
- "link/default/enabled": LINK("", AA_BODY),
2897
- "link/default/hover": LINK(" pointer-over", AA_BODY),
2898
- "link/default/pressed": LINK(" pressed", AA_BODY),
2899
- "link/inverse/enabled": LINK_INVERSE("", AA_BODY),
2900
- "link/inverse/hover": LINK_INVERSE(" pointer-over", AA_BODY),
2901
- "link/inverse/pressed": LINK_INVERSE(" pressed", AA_BODY)
2885
+ "link/default-0": LINK("", AA_BODY),
2886
+ "link/default-1": LINK(", one step from rest", AA_BODY),
2887
+ "link/default-2": LINK(", two steps from rest", AA_BODY),
2888
+ "link/inverse-0": LINK_INVERSE("", AA_BODY),
2889
+ "link/inverse-1": LINK_INVERSE(", one step from rest", AA_BODY),
2890
+ "link/inverse-2": LINK_INVERSE(", two steps from rest", AA_BODY)
2902
2891
  };
2903
2892
  var PREFIXES = [
2904
2893
  // community spellings first: the family word is a prefix of the community shapes
@@ -3889,9 +3878,6 @@ function colorFromHexString(hex) {
3889
3878
  const h = hex.replace("#", "");
3890
3879
  return color(parseInt(h.slice(0, 2), 16) / 255, parseInt(h.slice(2, 4), 16) / 255, parseInt(h.slice(4, 6), 16) / 255);
3891
3880
  }
3892
- function bandedLeaf(flat) {
3893
- return STAMP_STATE_LEAVES[flat] ?? flat;
3894
- }
3895
3881
  function groupEntries(g2) {
3896
3882
  const entries = Object.entries(g2);
3897
3883
  const digitLeading = (k) => /^\d/.test(k);
@@ -3899,24 +3885,15 @@ function groupEntries(g2) {
3899
3885
  return entries.sort((a, b) => parseInt(b[0], 10) - parseInt(a[0], 10));
3900
3886
  return entries;
3901
3887
  }
3902
- function putLeaf(g2, flat, tok) {
3903
- const path = bandedLeaf(flat).split("/");
3904
- let cur = g2;
3905
- for (const seg of path.slice(0, -1)) {
3906
- if (!(seg in cur) || "$type" in cur[seg]) cur[seg] = {};
3907
- cur = cur[seg];
3908
- }
3909
- cur[path[path.length - 1]] = tok;
3910
- }
3911
3888
  function rampGroup(stops, onFillWhite, extra) {
3912
3889
  const g2 = {};
3913
3890
  const leaves = stops.map((s) => ({ name: stopTokenName(s.stop), tok: colorFromStop(s) })).sort((a, b) => tokenOrder(a.name) - tokenOrder(b.name));
3914
- for (const l of leaves) putLeaf(g2, l.name, l.tok);
3915
- if (extra?.cta) putLeaf(g2, STAMP_FILL, colorFromStop(extra.cta));
3916
- if (extra?.ctaHover) putLeaf(g2, STAMP_FILL_HOVER, colorFromStop(extra.ctaHover));
3917
- if (extra?.ctaPressed) putLeaf(g2, STAMP_FILL_PRESSED, colorFromStop(extra.ctaPressed));
3918
- if (extra?.cta) putLeaf(g2, STAMP_EDGE, extra.ctaBorder ?? TRANSPARENT_TOKEN);
3919
- putLeaf(g2, STAMP_ON, pole(onFillWhite));
3891
+ for (const l of leaves) g2[l.name] = l.tok;
3892
+ if (extra?.cta) g2[STAMP_FILL] = colorFromStop(extra.cta);
3893
+ if (extra?.ctaHover) g2[STAMP_FILL_HOVER] = colorFromStop(extra.ctaHover);
3894
+ if (extra?.ctaPressed) g2[STAMP_FILL_PRESSED] = colorFromStop(extra.ctaPressed);
3895
+ if (extra?.cta) g2[STAMP_EDGE] = extra.ctaBorder ?? TRANSPARENT_TOKEN;
3896
+ g2[STAMP_ON] = pole(onFillWhite);
3920
3897
  return g2;
3921
3898
  }
3922
3899
  function themeToFigma(r, input) {
@@ -3955,16 +3932,18 @@ function themeToFigma(r, input) {
3955
3932
  if (input.secondaryStyle === "outline" && input.secondary) {
3956
3933
  const s8 = secondary[mode].find((s) => s.stop === 8);
3957
3934
  const s9 = secondary[mode].find((s) => s.stop === 9);
3958
- putLeaf(secondaryGroup, STAMP_FILL, TRANSPARENT_TOKEN);
3935
+ secondaryGroup[STAMP_FILL] = TRANSPARENT_TOKEN;
3959
3936
  if (s8) {
3960
3937
  const e = srgbEmitChannels(s8);
3961
- putLeaf(secondaryGroup, STAMP_FILL_HOVER, color(e.r, e.g, e.b, OUTLINE_HOVER_ALPHA));
3962
- putLeaf(secondaryGroup, STAMP_FILL_PRESSED, color(e.r, e.g, e.b, OUTLINE_PRESSED_ALPHA));
3963
- putLeaf(secondaryGroup, STAMP_EDGE, colorFromStop(s8));
3938
+ secondaryGroup[STAMP_FILL_HOVER] = color(e.r, e.g, e.b, OUTLINE_HOVER_ALPHA);
3939
+ secondaryGroup[STAMP_FILL_PRESSED] = color(e.r, e.g, e.b, OUTLINE_PRESSED_ALPHA);
3940
+ secondaryGroup[STAMP_EDGE] = colorFromStop(s8);
3964
3941
  }
3965
- if (s9) putLeaf(secondaryGroup, STAMP_ON, colorFromStop(s9));
3942
+ if (s9) secondaryGroup[STAMP_ON] = colorFromStop(s9);
3966
3943
  }
3967
- const softOnCta = (g3, white) => putLeaf(g3, STAMP_ON, pole(white, SOFT_ON_CTA_ALPHA[mode]));
3944
+ const softOnCta = (g3, white) => {
3945
+ g3[STAMP_ON] = pole(white, SOFT_ON_CTA_ALPHA[mode]);
3946
+ };
3968
3947
  softOnCta(neutralGroup, mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark);
3969
3948
  if (input.secondary && input.secondaryStyle !== "outline" && softOnCtaPasses(input.secondary, mode))
3970
3949
  softOnCta(secondaryGroup, mode === "light" ? secondaryOnFillLight : secondaryOnFillDark);
@@ -3972,10 +3951,10 @@ function themeToFigma(r, input) {
3972
3951
  const esc = input.ctaEscape ? escapeCtaFamily(nScale, mode, input.contrastProfile) : null;
3973
3952
  if (esc) {
3974
3953
  for (const g3 of input.secondary ? [brandGroup] : [brandGroup, secondaryGroup]) {
3975
- putLeaf(g3, STAMP_FILL, colorFromStop(esc.cta));
3976
- putLeaf(g3, STAMP_FILL_HOVER, colorFromStop(esc.ctaHover));
3977
- putLeaf(g3, STAMP_FILL_PRESSED, colorFromStop(esc.ctaPressed));
3978
- putLeaf(g3, STAMP_ON, pole(esc.onFillIsWhite));
3954
+ g3[STAMP_FILL] = colorFromStop(esc.cta);
3955
+ g3[STAMP_FILL_HOVER] = colorFromStop(esc.ctaHover);
3956
+ g3[STAMP_FILL_PRESSED] = colorFromStop(esc.ctaPressed);
3957
+ g3[STAMP_ON] = pole(esc.onFillIsWhite);
3979
3958
  }
3980
3959
  }
3981
3960
  const scaleTextAt = (n) => {
@@ -3985,7 +3964,7 @@ function themeToFigma(r, input) {
3985
3964
  };
3986
3965
  const trio = (posture) => {
3987
3966
  const t = posture === "default" ? lt ? mode === "light" ? [lt.link, lt.linkHover, lt.linkPressed] : [lt.linkDark, lt.linkHoverDark, lt.linkPressedDark] : [scaleTextAt(9), scaleTextAt(10), scaleTextAt(11)] : mode === "light" ? [invLt.link, invLt.linkHover, invLt.linkPressed] : [invLt.linkDark, invLt.linkHoverDark, invLt.linkPressedDark];
3988
- return Object.fromEntries(LINK_STATES.map((state, i) => [state, colorFromStop(t[i])]));
3967
+ return Object.fromEntries(LINK_STATES.map((state, i) => [linkLeaf(posture, state), colorFromStop(t[i])]));
3989
3968
  };
3990
3969
  const g2 = {
3991
3970
  [CSS_FAMILY.neutral]: neutralGroup,
@@ -4000,7 +3979,7 @@ function themeToFigma(r, input) {
4000
3979
  ctaFamily(sig.scale, mode, role)
4001
3980
  );
4002
3981
  }
4003
- g2[LINK_GROUP] = { default: trio("default"), inverse: trio("inverse") };
3982
+ g2[LINK_GROUP] = { ...trio("default"), ...trio("inverse") };
4004
3983
  g2[ABSOLUTE_GROUP] = {
4005
3984
  [CSS_FAMILY.brandPrimary]: colorFromHexString(scale.identityHex ?? invSeed),
4006
3985
  [CSS_FAMILY.brandSecondary]: colorFromHexString((input.secondary ?? scale).identityHex ?? invSeed)
@@ -745,32 +745,20 @@ function stopTokenName(stop) {
745
745
  }
746
746
  var PAPER_0 = "paper-0";
747
747
  var PEN_100 = "pen-100";
748
- var STAMP_FILL = "stamp-fill";
749
- var STAMP_FILL_HOVER = "stamp-fill-hover";
750
- var STAMP_FILL_PRESSED = "stamp-fill-pressed";
748
+ var STAMP_FILL = "stamp-0";
749
+ var STAMP_FILL_HOVER = "stamp-1";
750
+ var STAMP_FILL_PRESSED = "stamp-2";
751
751
  var STAMP_EDGE = "stamp-edge";
752
752
  var STAMP_ON = "stamp-on";
753
- var STAMP_STATE_LEAVES = {
754
- [STAMP_FILL]: "stamp/fill",
755
- [STAMP_FILL_HOVER]: "stamp/fill-hover",
756
- [STAMP_FILL_PRESSED]: "stamp/fill-pressed",
757
- [STAMP_EDGE]: "stamp/edge",
758
- [STAMP_ON]: "stamp/on"
759
- };
760
- var STAMP_LEAF = {
761
- FILL: STAMP_STATE_LEAVES[STAMP_FILL],
762
- FILL_HOVER: STAMP_STATE_LEAVES[STAMP_FILL_HOVER],
763
- FILL_PRESSED: STAMP_STATE_LEAVES[STAMP_FILL_PRESSED],
764
- EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
765
- ON: STAMP_STATE_LEAVES[STAMP_ON]
766
- };
767
753
  var cssVarName = (path) => `--${path.join("-")}`;
768
754
  var figmaPathOf = (path) => path.join("/");
769
- var familyPath = (family, leaf) => [family, ...(STAMP_STATE_LEAVES[leaf] ?? leaf).split("/")];
755
+ var familyPath = (family, leaf) => [family, leaf];
770
756
  var LINK_GROUP = "link";
771
757
  var LINK_POSTURES = ["default", "inverse"];
772
758
  var LINK_STATES = ["enabled", "hover", "pressed"];
773
- var linkPath = (posture, state) => [LINK_GROUP, posture, state];
759
+ var LINK_STEP = { enabled: 0, hover: 1, pressed: 2 };
760
+ var linkLeaf = (posture, state) => `${posture}-${LINK_STEP[state]}`;
761
+ var linkPath = (posture, state) => [LINK_GROUP, linkLeaf(posture, state)];
774
762
  var ABSOLUTE_GROUP = "absolute";
775
763
  var absolutePath = (family) => [ABSOLUTE_GROUP, family];
776
764
  var COLOR_GROUP = "color";
@@ -2761,20 +2749,21 @@ var SCALE = {
2761
2749
  // ("heavy-emphasis": "high" and "low" are plane words in the community plugin and
2762
2750
  // "strong" carries "on")
2763
2751
  "pen-70": { req: "heavy-emphasis text, inverted backgrounds", contrast: AA_BODY, claim: claimOf("pen"), theming: solved, collides: true },
2764
- // the stamp family. "CTA" stays in these bodies on purpose: it is not a token label, so
2752
+ // the stamp rows, each a position: the fill at rest and at one and two steps, the edge,
2753
+ // the on-text. No body names a state; a consumer's semantic layer names the states and
2754
+ // aliases the steps. "CTA" stays in these bodies on purpose: it is not a token label, so
2765
2755
  // it floods nothing, and a designer's "cta" query lands on these rows.
2766
2756
  // the usage lines are the owner's guidance, for the document rendering: the fill is
2767
2757
  // misused as a standalone color in the wild
2768
- "stamp/fill": {
2769
- req: "CTAs",
2758
+ "stamp-0": {
2759
+ req: "CTAs, the fill at rest",
2770
2760
  theming: (f) => `fully re-solved per theme and family${COLOR_WORD[f]}`,
2771
- use: "primary button fills; optional for avatars, badges and other elements that carry text. Not a standalone color: it has no contrast guarantee of its own, only its on-text has one. Use it alone or with its hover and pressed states. Always bordered with its edge"
2761
+ use: "primary button fills; optional for avatars, badges and other elements that carry text. Not a standalone color: it has no contrast guarantee of its own, only its on-text has one. Use it alone or with the two steps above it. Always bordered with its edge"
2772
2762
  },
2773
- "stamp/fill-hover": { req: "CTA pointer-over state", theming: (f) => `follows its rest fill${COLOR_WORD[f]}`, use: "the pointer-over state of the fill, with the fill; for nothing else. Always bordered with the edge" },
2774
- "stamp/fill-pressed": { req: "CTA pressed state", theming: (f) => `follows its rest fill${COLOR_WORD[f]}`, use: "the pressed state of the fill, with the fill; for nothing else. Always bordered with the edge" },
2775
- "stamp/edge": { req: "min APCA visibility", theming: (f) => `draws for CTAs that sit close to the page; strength per family tier${COLOR_WORD[f]}`, use: "the border of the fill and its states, always rendered with them and never without them (it resolves to transparent unless the fill sits close to the page); for nothing else" },
2776
- // ("fill" is a label word and foreign here)
2777
- "stamp/on": { req: "text over the CTA color", contrast: `${AA_BODY} over its CTA`, theming: (f) => `whichever pole passes; quiet CTAs take the soft pole${COLOR_WORD[f]}`, use: "the only text color over the fill; never elsewhere" }
2763
+ "stamp-1": { req: "CTAs, the fill one step from rest", theming: (f) => `follows the fill at rest${COLOR_WORD[f]}`, use: "the fill one step from rest, with the fill at rest; for nothing else. Always bordered with the edge" },
2764
+ "stamp-2": { req: "CTAs, the fill two steps from rest", theming: (f) => `follows the fill at rest${COLOR_WORD[f]}`, use: "the fill two steps from rest, with the fill at rest; for nothing else. Always bordered with the edge" },
2765
+ "stamp-edge": { req: "min APCA visibility", theming: (f) => `draws for CTAs that sit close to the page; strength per family tier${COLOR_WORD[f]}`, use: "the border of the fill at every step, always rendered with it and never without it (it resolves to transparent unless the fill sits close to the page); for nothing else" },
2766
+ "stamp-on": { req: "text over the CTA color", contrast: `${AA_BODY} over its CTA`, theming: (f) => `whichever pole passes; quiet CTAs take the soft pole${COLOR_WORD[f]}`, use: "the only text color over the fill at every step; never elsewhere" }
2778
2767
  };
2779
2768
  var NEUTRAL_ONLY = {
2780
2769
  // the pole is measured against the neutral's own stops only; as text, it is the on-color
@@ -2783,15 +2772,15 @@ var NEUTRAL_ONLY = {
2783
2772
  // the literal pole carries no tint, so no theming line
2784
2773
  "pen-100": { req: "max-emphasis text", contrast: AA_BODY, claim: () => `${AA_BODY} on every paper and chalk of every family` }
2785
2774
  };
2786
- var LINK = (state, contrast) => ({
2787
- req: "links" + state,
2775
+ var LINK = (step, contrast) => ({
2776
+ req: "links" + step,
2788
2777
  contrast,
2789
2778
  claim: () => `${contrast} on every paper of the neutral`,
2790
2779
  theming: "rides the theme\u2019s link color; custom seed re-solves; overridable per theme",
2791
2780
  use: "hyperlinks; not a text-style CTA, which is the text stops read as states"
2792
2781
  });
2793
- var LINK_INVERSE = (state, contrast) => ({
2794
- req: "links over inverted backgrounds" + state,
2782
+ var LINK_INVERSE = (step, contrast) => ({
2783
+ req: "links over inverted backgrounds" + step,
2795
2784
  contrast,
2796
2785
  claim: () => `${contrast} on any family's pen-70`,
2797
2786
  theming: "same seed as the link, re-solved for inverted backgrounds; overridable per theme",
@@ -2800,12 +2789,12 @@ var LINK_INVERSE = (state, contrast) => ({
2800
2789
  var SYSTEM = {
2801
2790
  "absolute/brand": { req: "identity seed reference", theming: "the theme\u2019s own input, as given", use: "a reference value (logos, swatches of the input); never a UI color, it carries no contrast guarantee" },
2802
2791
  "absolute/brand-alt": { req: "identity seed reference", theming: "the theme\u2019s paired input, as given", use: "a reference value (logos, swatches of the input); never a UI color, it carries no contrast guarantee" },
2803
- "link/default/enabled": LINK("", AA_BODY),
2804
- "link/default/hover": LINK(" pointer-over", AA_BODY),
2805
- "link/default/pressed": LINK(" pressed", AA_BODY),
2806
- "link/inverse/enabled": LINK_INVERSE("", AA_BODY),
2807
- "link/inverse/hover": LINK_INVERSE(" pointer-over", AA_BODY),
2808
- "link/inverse/pressed": LINK_INVERSE(" pressed", AA_BODY)
2792
+ "link/default-0": LINK("", AA_BODY),
2793
+ "link/default-1": LINK(", one step from rest", AA_BODY),
2794
+ "link/default-2": LINK(", two steps from rest", AA_BODY),
2795
+ "link/inverse-0": LINK_INVERSE("", AA_BODY),
2796
+ "link/inverse-1": LINK_INVERSE(", one step from rest", AA_BODY),
2797
+ "link/inverse-2": LINK_INVERSE(", two steps from rest", AA_BODY)
2809
2798
  };
2810
2799
  var PREFIXES = [
2811
2800
  // community spellings first: the family word is a prefix of the community shapes
@@ -3796,9 +3785,6 @@ function colorFromHexString(hex) {
3796
3785
  const h = hex.replace("#", "");
3797
3786
  return color(parseInt(h.slice(0, 2), 16) / 255, parseInt(h.slice(2, 4), 16) / 255, parseInt(h.slice(4, 6), 16) / 255);
3798
3787
  }
3799
- function bandedLeaf(flat) {
3800
- return STAMP_STATE_LEAVES[flat] ?? flat;
3801
- }
3802
3788
  function groupEntries(g2) {
3803
3789
  const entries = Object.entries(g2);
3804
3790
  const digitLeading = (k) => /^\d/.test(k);
@@ -3806,24 +3792,15 @@ function groupEntries(g2) {
3806
3792
  return entries.sort((a, b) => parseInt(b[0], 10) - parseInt(a[0], 10));
3807
3793
  return entries;
3808
3794
  }
3809
- function putLeaf(g2, flat, tok) {
3810
- const path = bandedLeaf(flat).split("/");
3811
- let cur = g2;
3812
- for (const seg of path.slice(0, -1)) {
3813
- if (!(seg in cur) || "$type" in cur[seg]) cur[seg] = {};
3814
- cur = cur[seg];
3815
- }
3816
- cur[path[path.length - 1]] = tok;
3817
- }
3818
3795
  function rampGroup(stops, onFillWhite, extra) {
3819
3796
  const g2 = {};
3820
3797
  const leaves = stops.map((s) => ({ name: stopTokenName(s.stop), tok: colorFromStop(s) })).sort((a, b) => tokenOrder(a.name) - tokenOrder(b.name));
3821
- for (const l of leaves) putLeaf(g2, l.name, l.tok);
3822
- if (extra?.cta) putLeaf(g2, STAMP_FILL, colorFromStop(extra.cta));
3823
- if (extra?.ctaHover) putLeaf(g2, STAMP_FILL_HOVER, colorFromStop(extra.ctaHover));
3824
- if (extra?.ctaPressed) putLeaf(g2, STAMP_FILL_PRESSED, colorFromStop(extra.ctaPressed));
3825
- if (extra?.cta) putLeaf(g2, STAMP_EDGE, extra.ctaBorder ?? TRANSPARENT_TOKEN);
3826
- putLeaf(g2, STAMP_ON, pole(onFillWhite));
3798
+ for (const l of leaves) g2[l.name] = l.tok;
3799
+ if (extra?.cta) g2[STAMP_FILL] = colorFromStop(extra.cta);
3800
+ if (extra?.ctaHover) g2[STAMP_FILL_HOVER] = colorFromStop(extra.ctaHover);
3801
+ if (extra?.ctaPressed) g2[STAMP_FILL_PRESSED] = colorFromStop(extra.ctaPressed);
3802
+ if (extra?.cta) g2[STAMP_EDGE] = extra.ctaBorder ?? TRANSPARENT_TOKEN;
3803
+ g2[STAMP_ON] = pole(onFillWhite);
3827
3804
  return g2;
3828
3805
  }
3829
3806
  function themeToFigma(r, input) {
@@ -3862,16 +3839,18 @@ function themeToFigma(r, input) {
3862
3839
  if (input.secondaryStyle === "outline" && input.secondary) {
3863
3840
  const s8 = secondary[mode].find((s) => s.stop === 8);
3864
3841
  const s9 = secondary[mode].find((s) => s.stop === 9);
3865
- putLeaf(secondaryGroup, STAMP_FILL, TRANSPARENT_TOKEN);
3842
+ secondaryGroup[STAMP_FILL] = TRANSPARENT_TOKEN;
3866
3843
  if (s8) {
3867
3844
  const e = srgbEmitChannels(s8);
3868
- putLeaf(secondaryGroup, STAMP_FILL_HOVER, color(e.r, e.g, e.b, OUTLINE_HOVER_ALPHA));
3869
- putLeaf(secondaryGroup, STAMP_FILL_PRESSED, color(e.r, e.g, e.b, OUTLINE_PRESSED_ALPHA));
3870
- putLeaf(secondaryGroup, STAMP_EDGE, colorFromStop(s8));
3845
+ secondaryGroup[STAMP_FILL_HOVER] = color(e.r, e.g, e.b, OUTLINE_HOVER_ALPHA);
3846
+ secondaryGroup[STAMP_FILL_PRESSED] = color(e.r, e.g, e.b, OUTLINE_PRESSED_ALPHA);
3847
+ secondaryGroup[STAMP_EDGE] = colorFromStop(s8);
3871
3848
  }
3872
- if (s9) putLeaf(secondaryGroup, STAMP_ON, colorFromStop(s9));
3849
+ if (s9) secondaryGroup[STAMP_ON] = colorFromStop(s9);
3873
3850
  }
3874
- const softOnCta = (g3, white) => putLeaf(g3, STAMP_ON, pole(white, SOFT_ON_CTA_ALPHA[mode]));
3851
+ const softOnCta = (g3, white) => {
3852
+ g3[STAMP_ON] = pole(white, SOFT_ON_CTA_ALPHA[mode]);
3853
+ };
3875
3854
  softOnCta(neutralGroup, mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark);
3876
3855
  if (input.secondary && input.secondaryStyle !== "outline" && softOnCtaPasses(input.secondary, mode))
3877
3856
  softOnCta(secondaryGroup, mode === "light" ? secondaryOnFillLight : secondaryOnFillDark);
@@ -3879,10 +3858,10 @@ function themeToFigma(r, input) {
3879
3858
  const esc = input.ctaEscape ? escapeCtaFamily(nScale, mode, input.contrastProfile) : null;
3880
3859
  if (esc) {
3881
3860
  for (const g3 of input.secondary ? [brandGroup] : [brandGroup, secondaryGroup]) {
3882
- putLeaf(g3, STAMP_FILL, colorFromStop(esc.cta));
3883
- putLeaf(g3, STAMP_FILL_HOVER, colorFromStop(esc.ctaHover));
3884
- putLeaf(g3, STAMP_FILL_PRESSED, colorFromStop(esc.ctaPressed));
3885
- putLeaf(g3, STAMP_ON, pole(esc.onFillIsWhite));
3861
+ g3[STAMP_FILL] = colorFromStop(esc.cta);
3862
+ g3[STAMP_FILL_HOVER] = colorFromStop(esc.ctaHover);
3863
+ g3[STAMP_FILL_PRESSED] = colorFromStop(esc.ctaPressed);
3864
+ g3[STAMP_ON] = pole(esc.onFillIsWhite);
3886
3865
  }
3887
3866
  }
3888
3867
  const scaleTextAt = (n) => {
@@ -3892,7 +3871,7 @@ function themeToFigma(r, input) {
3892
3871
  };
3893
3872
  const trio = (posture) => {
3894
3873
  const t = posture === "default" ? lt ? mode === "light" ? [lt.link, lt.linkHover, lt.linkPressed] : [lt.linkDark, lt.linkHoverDark, lt.linkPressedDark] : [scaleTextAt(9), scaleTextAt(10), scaleTextAt(11)] : mode === "light" ? [invLt.link, invLt.linkHover, invLt.linkPressed] : [invLt.linkDark, invLt.linkHoverDark, invLt.linkPressedDark];
3895
- return Object.fromEntries(LINK_STATES.map((state, i) => [state, colorFromStop(t[i])]));
3874
+ return Object.fromEntries(LINK_STATES.map((state, i) => [linkLeaf(posture, state), colorFromStop(t[i])]));
3896
3875
  };
3897
3876
  const g2 = {
3898
3877
  [CSS_FAMILY.neutral]: neutralGroup,
@@ -3907,7 +3886,7 @@ function themeToFigma(r, input) {
3907
3886
  ctaFamily(sig.scale, mode, role)
3908
3887
  );
3909
3888
  }
3910
- g2[LINK_GROUP] = { default: trio("default"), inverse: trio("inverse") };
3889
+ g2[LINK_GROUP] = { ...trio("default"), ...trio("inverse") };
3911
3890
  g2[ABSOLUTE_GROUP] = {
3912
3891
  [CSS_FAMILY.brandPrimary]: colorFromHexString(scale.identityHex ?? invSeed),
3913
3892
  [CSS_FAMILY.brandSecondary]: colorFromHexString((input.secondary ?? scale).identityHex ?? invSeed)
@@ -4008,6 +3987,7 @@ export {
4008
3987
  LINK_GROUP,
4009
3988
  LINK_POSTURES,
4010
3989
  LINK_STATES,
3990
+ LINK_STEP,
4011
3991
  MODE_SPECS,
4012
3992
  OFFSET_ALPHAS,
4013
3993
  OUTLINE_HOVER_ALPHA,
@@ -4022,9 +4002,7 @@ export {
4022
4002
  STAMP_FILL,
4023
4003
  STAMP_FILL_HOVER,
4024
4004
  STAMP_FILL_PRESSED,
4025
- STAMP_LEAF,
4026
4005
  STAMP_ON,
4027
- STAMP_STATE_LEAVES,
4028
4006
  SUBTLE_PASTEL_K,
4029
4007
  SUBTLE_TINT_MULT,
4030
4008
  absolutePath,
@@ -4045,6 +4023,7 @@ export {
4045
4023
  generateScale,
4046
4024
  generateSubtleSecondary,
4047
4025
  groupEntries,
4026
+ linkLeaf,
4048
4027
  linkPath,
4049
4028
  neutralCss,
4050
4029
  neutralTintHue,
@@ -18,11 +18,11 @@ export type DtcgGroup = {
18
18
  export type DtcgDocument = DtcgGroup;
19
19
  /**
20
20
  * Every primitive's path, in document order: the neutral with its poles, the brand, the
21
- * alt, the four signals by role, the link trios, the seed absolutes. This roster is the
21
+ * alt, the four signals by role, the link rows, the seed absolutes. This roster is the
22
22
  * definition of what the engine emits; the emitters and the audit ride it.
23
23
  */
24
24
  export declare function tokenPaths(): TokenPath[];
25
- /** the path of a structured-emit name (`brand-stamp-fill`), or undefined when it is not a primitive */
25
+ /** the path of a structured-emit name (`brand-stamp-0`), or undefined when it is not a primitive */
26
26
  export declare function tokenPathOf(name: string): TokenPath | undefined;
27
27
  /** the description key of a structured-emit name: its slash-joined path */
28
28
  export declare function descriptionPathOf(name: string): string | undefined;
@@ -14,7 +14,6 @@ export type FigmaGroup = {
14
14
  [key: string]: FigmaLeaf | FigmaGroup;
15
15
  };
16
16
  export declare function groupEntries(g: FigmaGroup): Array<[string, FigmaLeaf | FigmaGroup]>;
17
- export declare function putLeaf(g: FigmaGroup, flat: string, tok: FigmaLeaf): void;
18
17
  export interface ThemeInput {
19
18
  secondary?: GeneratedScale | null;
20
19
  secondaryStyle?: SecondaryStyle;
@@ -2,29 +2,25 @@ export declare const SCALE_STOP_COUNT: number;
2
2
  export declare function stopTokenName(stop: number): string;
3
3
  export declare const PAPER_0 = "paper-0";
4
4
  export declare const PEN_100 = "pen-100";
5
- export declare const STAMP_FILL = "stamp-fill";
6
- export declare const STAMP_FILL_HOVER = "stamp-fill-hover";
7
- export declare const STAMP_FILL_PRESSED = "stamp-fill-pressed";
5
+ export declare const STAMP_FILL = "stamp-0";
6
+ export declare const STAMP_FILL_HOVER = "stamp-1";
7
+ export declare const STAMP_FILL_PRESSED = "stamp-2";
8
8
  export declare const STAMP_EDGE = "stamp-edge";
9
9
  export declare const STAMP_ON = "stamp-on";
10
- export declare const STAMP_STATE_LEAVES: Record<string, string>;
11
- export declare const STAMP_LEAF: {
12
- readonly FILL: string;
13
- readonly FILL_HOVER: string;
14
- readonly FILL_PRESSED: string;
15
- readonly EDGE: string;
16
- readonly ON: string;
17
- };
18
10
  export type TokenPath = readonly string[];
19
11
  export declare const cssVarName: (path: TokenPath) => string;
20
12
  export declare const figmaPathOf: (path: TokenPath) => string;
21
- /** a family row: a scale stop, a pole, or a stamp token (nested under stamp/) */
13
+ /** a family row: a scale stop, a pole, or a stamp row, flat in the family group */
22
14
  export declare const familyPath: (family: string, leaf: string) => string[];
23
15
  export declare const LINK_GROUP = "link";
24
16
  export declare const LINK_POSTURES: readonly ["default", "inverse"];
25
17
  export type LinkPosture = (typeof LINK_POSTURES)[number];
26
18
  export declare const LINK_STATES: readonly ["enabled", "hover", "pressed"];
27
19
  export type LinkState = (typeof LINK_STATES)[number];
20
+ /** the step each state is emitted as: rest 0, one step 1, two steps 2 */
21
+ export declare const LINK_STEP: Record<LinkState, number>;
22
+ /** the one word a link row has: its posture at its step (`default-1`) */
23
+ export declare const linkLeaf: (posture: LinkPosture, state: LinkState) => string;
28
24
  export declare const linkPath: (posture: LinkPosture, state: LinkState) => string[];
29
25
  export declare const ABSOLUTE_GROUP = "absolute";
30
26
  export declare const absolutePath: (family: string) => string[];
@@ -6,7 +6,7 @@ export { themeTokens, tokensFromEmission, readEmission, resolveReferences, type
6
6
  export { themeToFigma, groupEntries, type FigmaGroup, type FigmaColorToken, type FigmaLeaf, type ThemeInput, } from './engine/figmaRender';
7
7
  export { tokensToDtcg, tokenPathOf, descriptionPathOf, type DtcgDocument, type DtcgGroup, type DtcgToken, type DtcgColorValue, type DtcgOptions, } from './engine/dtcgRender';
8
8
  export { MODE_SPECS, type ModeSpec } from './engine/requirements/spec';
9
- export { stopTokenName, tokenOrder, SCALE_STOP_COUNT, PAPER_0, PEN_100, STAMP_FILL, STAMP_FILL_HOVER, STAMP_FILL_PRESSED, STAMP_EDGE, STAMP_ON, STAMP_STATE_LEAVES, STAMP_LEAF, cssVarName, figmaPathOf, familyPath, linkPath, absolutePath, LINK_GROUP, LINK_POSTURES, LINK_STATES, ABSOLUTE_GROUP, COLOR_GROUP, type TokenPath, type LinkPosture, type LinkState, } from './engine/tokenNames';
9
+ export { stopTokenName, tokenOrder, SCALE_STOP_COUNT, PAPER_0, PEN_100, STAMP_FILL, STAMP_FILL_HOVER, STAMP_FILL_PRESSED, STAMP_EDGE, STAMP_ON, cssVarName, figmaPathOf, familyPath, linkLeaf, linkPath, LINK_STEP, absolutePath, LINK_GROUP, LINK_POSTURES, LINK_STATES, ABSOLUTE_GROUP, COLOR_GROUP, type TokenPath, type LinkPosture, type LinkState, } from './engine/tokenNames';
10
10
  export { SIGNAL_EMIT_NAME } from './engine/signals';
11
11
  export { classifyArchetype, type Archetype } from './engine/archetypes';
12
12
  export { SIGNALS, type SignalDef } from './engine/signals';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okchroma",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "license": "MIT",
5
5
  "description": "Color-system engine: one brand hex in, a light and dark set of color primitives out, with the contrast requirements solved during generation; emitted as CSS custom properties, DTCG documents, and Figma variables",
6
6
  "repository": {
@@ -23,7 +23,7 @@
23
23
  "sideEffects": false,
24
24
  "scripts": {
25
25
  "build:lib": "node esbuild.config.js --lib && tsc -p tsconfig.lib.json",
26
- "prepack": "npm run build:lib",
26
+ "prepack": "npm run build:lib && npm run audit:lib",
27
27
  "generate": "node dist/build-script.js",
28
28
  "demo:build": "node esbuild.config.js",
29
29
  "build": "node esbuild.config.js",
@@ -56,6 +56,7 @@
56
56
  "docs:lint": "esbuild scripts/docs-lint.ts --bundle --platform=node --outfile=dist/docs-lint.js && node dist/docs-lint.js",
57
57
  "audit:tokens": "esbuild scripts/tokens-audit.ts --bundle --platform=node --outfile=dist/tokens-audit.js && node dist/tokens-audit.js",
58
58
  "audit:dtcg": "esbuild scripts/dtcg-audit.ts --bundle --platform=node --outfile=dist/dtcg-audit.js && node dist/dtcg-audit.js",
59
+ "audit:lib": "esbuild scripts/lib-audit.ts --bundle --platform=node --outfile=dist/lib-audit.js && node dist/lib-audit.js",
59
60
  "tokens:emit": "esbuild scripts/dtcg-emit.ts --bundle --platform=node --outfile=dist/dtcg-emit.js && node dist/dtcg-emit.js"
60
61
  },
61
62
  "devDependencies": {
@@ -1,35 +0,0 @@
1
- import { type OpacityRung } from './cssRender';
2
- import { type ThemeTokens, type TokenMode } from './tokensRender';
3
- export declare const INTERACTION_POLE_FAMILY: {
4
- readonly strong: "neutral-strong";
5
- readonly inverse: "neutral-inverse";
6
- };
7
- export type InteractionPoleFamily = (typeof INTERACTION_POLE_FAMILY)[keyof typeof INTERACTION_POLE_FAMILY];
8
- /** every family the register emits, color families first, the two pole families last */
9
- export declare const INTERACTION_FAMILIES: readonly string[];
10
- export type InteractionState = 'enabled' | 'hover' | 'pressed' | 'selected';
11
- export declare const INTERACTION_STATES: readonly InteractionState[];
12
- /** the rung each translucent tier takes per state; null is the transparent rest */
13
- export declare const INTERACTION_LADDER: Record<'subtle' | 'hint', Record<InteractionState, OpacityRung | null>>;
14
- export declare const INTERACTION_ROWS: readonly ["fg", "fg-strong", "fg-on-hint", "solid-bg-enabled", "solid-bg-hover", "solid-bg-pressed", "solid-border", "solid-fg", "subtle-bg-enabled", "subtle-bg-hover", "subtle-bg-pressed", "subtle-bg-selected", "hint-bg-enabled", "hint-bg-hover", "hint-bg-pressed", "hint-bg-selected"];
15
- export type InteractionRow = (typeof INTERACTION_ROWS)[number];
16
- /** the CSS custom property a family's row lives under */
17
- export declare const interactionVarName: (family: string, row: InteractionRow) => string;
18
- /**
19
- * The name whose channels a family's translucent rows ride: the family's
20
- * highlighter-26, or the pole for the pole families.
21
- */
22
- export declare function interactionTintName(family: string): string;
23
- /** One family's rows for one mode as [name, value] pairs, aliases as var() and rungs as rgba. */
24
- export declare function interactionRows(family: string, mode: TokenMode, tokens: ThemeTokens): Array<[InteractionRow, string]>;
25
- /**
26
- * The register as CSS for one brand: every family's rows under the brand scope, the
27
- * rung rows per mode, then one scope block per family that maps the family-agnostic
28
- * names (`--solid-bg-enabled`, `--fg`) onto that family, so a component reads one set
29
- * of names and `data-family` picks the family, the way a mode does on a Figma instance.
30
- */
31
- export declare function interactionCss(slug: string, tokens: ThemeTokens): string;
32
- /** The base tokens plus the register's rows, resolved to literals per mode. */
33
- export declare function interactionTokens(tokens: ThemeTokens): ThemeTokens;
34
- /** every rung the register uses is one of the engine's interaction rungs */
35
- export declare const interactionLadderIsLegal: () => boolean;
@@ -1,44 +0,0 @@
1
- import type { StopReq, RoleReq, ModeSpec } from './spec';
2
- import { type ResolvedRamp } from './resolve';
3
- export declare const EXT_KEY = "org.okchroma.requirement";
4
- export declare const RESOLVER_ID = "okchroma-reqtoken@2";
5
- export type DtcgColorValue = {
6
- colorSpace: 'srgb';
7
- components: [number, number, number];
8
- alpha: 1;
9
- hex: string;
10
- };
11
- type ExtCommon = {
12
- resolver: string;
13
- seed: string;
14
- mode: 'light' | 'dark';
15
- };
16
- export type DtcgRequirementToken = {
17
- $type: 'color';
18
- $value: DtcgColorValue;
19
- $extensions: {
20
- [EXT_KEY]: ExtCommon & (StopReq | RoleReq);
21
- };
22
- };
23
- export type DtcgSeedToken = {
24
- $type: 'color';
25
- $value: DtcgColorValue;
26
- };
27
- export type DtcgRampGroup = {
28
- seed: DtcgSeedToken;
29
- $extensions?: {
30
- [EXT_KEY]: {
31
- resolver: string;
32
- ons: ModeSpec['ons'];
33
- };
34
- };
35
- [key: string]: unknown;
36
- };
37
- export declare function emitDtcgRamp(hex: string, mode: 'light' | 'dark', groupName: string, spec?: ModeSpec): DtcgRampGroup;
38
- export declare function parseToken(token: DtcgRequirementToken): {
39
- req: StopReq | RoleReq;
40
- seedRef: string;
41
- mode: 'light' | 'dark';
42
- };
43
- export declare function resolveDtcgRamp(group: DtcgRampGroup): ResolvedRamp;
44
- export {};