@dataimago/tokens 0.1.0-alpha.31 → 0.1.0-alpha.33

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/dist/index.cjs CHANGED
@@ -45,12 +45,27 @@ const tokens = {
45
45
  "scrim": {
46
46
  "light": "rgba(20, 20, 16, 0.85)",
47
47
  "dark": "rgba(20, 20, 16, 0.85)",
48
- "description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03)."
48
+ "description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03). Older browsers (Chrome < 122, Safari < 17.4, Firefox < 120) do not let ::backdrop inherit custom properties from :root, so a consumer painting the scrim on ::backdrop should write a fallback, e.g. var(--color-surface-scrim, rgba(20, 20, 16, 0.85)); the house figure viewer draws its scrim on a div (.di-figure-modal), so it is not affected."
49
49
  },
50
50
  "scrim-solid": {
51
51
  "light": "rgba(20, 20, 16, 0.92)",
52
52
  "dark": "rgba(20, 20, 16, 0.92)",
53
53
  "description": "Figure viewer backdrop where backdrop blur is unavailable (prefers-reduced-transparency, prefers-contrast: more and browsers without backdrop blur): the same ink at 0.92. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Gated by tools/contrast-check.mjs."
54
+ },
55
+ "print": {
56
+ "light": "#f7f7f6",
57
+ "dark": "#f7f7f6",
58
+ "description": "Printed paper: the paper-cover thumbnail, the light slide, the face of a video-play disc. It depicts a physical object (a printed page, a slide), so it is theme-invariant: light and dark carry the same value, and the paper does not darken with the page (the color.border.figure-frame precedent). stone-50, the card colour; the SGPc apps' own #f6f4ee is not a house colour and was mapped onto this step (maintainer decision 2026-11-03). Ink on it is content.on-print, held to 7:1 by tools/contrast-check.mjs. Paper reads as paper by its edge, not its colour: a print surface always carries --shadow-print or a hairline in border.print (the edge rule, maintainer decision 2026-11-03)."
59
+ },
60
+ "play-disc": {
61
+ "light": "rgba(247, 247, 246, 0.92)",
62
+ "dark": "rgba(247, 247, 246, 0.92)",
63
+ "description": "The video-play disc: surface.print at 0.92 alpha, over a poster frame or poster placeholder. It depicts a physical object, so it is theme-invariant: light and dark carry the same value. Its glyph is content.on-print (no separate token), held to 4.5:1 on the disc composited over each mode's page by tools/contrast-check.mjs, so the disc stays legible in dark mode."
64
+ },
65
+ "media": {
66
+ "light": "#000000",
67
+ "dark": "#000000",
68
+ "description": "The media letterbox: the ground behind a <video> and other media (an embedded player, a frame that does not fill its box). Pure black, not the house near-black (stone-900 #14140f, ink-900 #0c0a09): video frames carry true black, so a near-black ground shows the letterbox bars beside the frame's own black, and pure black hides them. It depicts the media frame, not the page, so it is theme-invariant: light and dark carry the same value (the color.surface.print precedent). Nothing is drawn on it, so it has no contrast floor; tools/contrast-check.mjs holds it to theme invariance (maintainer decision 2026-11-03)."
54
69
  }
55
70
  },
56
71
  "border": {
@@ -88,6 +103,11 @@ const tokens = {
88
103
  "light": "#3498DB",
89
104
  "dark": "#3498DB",
90
105
  "description": "Focus ring on the figure viewer scrim. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). The slate accent's dark-mode value in both modes, because the scrim is dark in both. Held to 3:1 (WCAG 1.4.11) over both scrims and both page modes by tools/contrast-check.mjs."
106
+ },
107
+ "print": {
108
+ "light": "rgba(12, 10, 9, 0.45)",
109
+ "dark": "rgba(12, 10, 9, 0.45)",
110
+ "description": "The hairline edge of printed paper (surface.print): the paper-cover thumbnail, the light slide. THE EDGE RULE: paper reads as paper by its edge, not by its colour. surface.print is the card colour, so a print surface always carries either --shadow-print or a hairline in this token (maintainer decision 2026-11-03). It is drawn on the paper, which stays light in both modes, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). It starts from the light value of border.subtle, and as there the alpha is the role and no ratio is quoted on purpose: composited over surface.print it is held to the WCAG 1.4.11 non-text threshold (3:1) by tools/contrast-check.mjs. Read the value, not this sentence."
91
111
  }
92
112
  },
93
113
  "content": {
@@ -115,6 +135,11 @@ const tokens = {
115
135
  "light": "#f7f7f6",
116
136
  "dark": "#f7f7f6",
117
137
  "description": "Text and controls on the figure viewer scrim (stone-50). Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 over both scrims and both page modes by tools/contrast-check.mjs."
138
+ },
139
+ "on-print": {
140
+ "light": "#14140f",
141
+ "dark": "#14140f",
142
+ "description": "Ink on surface.print, and the glyph on surface.play-disc (the play triangle takes this token; it has no separate one). stone-900. It depicts printed ink, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 on surface.print, and 4.5:1 on the play disc composited over each mode's page, by tools/contrast-check.mjs."
118
143
  }
119
144
  },
120
145
  "interactive": {
@@ -308,6 +333,13 @@ const tokens = {
308
333
  "value": "#0072B2",
309
334
  "description": "= okabe-ito.blue. SGPc growth bucket 5 of 5 (very high) on the ordered scale very low → very high. Colour is never the only channel: pair it with a marker and the bucket letter code. Author decision 2026-11-01, matching the LIwLD paper's figures."
310
335
  }
336
+ },
337
+ "map": {
338
+ "ocean": {
339
+ "light": "#808fa7",
340
+ "dark": "#042438",
341
+ "description": "Map-surface ground: the sea on the SGPc globe, behind land, absent countries and bucket-filled shapes. A muted slate blue on light surfaces and a deep blue on dark (maintainer 2026-10-22); not a palette step. The light value moved from #c9e1ec on 2026-11-01, when the Okabe-Ito sky-blue bucket blended into it: #808fa7 sits mid-way in lightness between the two blue buckets and is grey enough to read as sea. Gated by tools/dataviz-check.mjs against the five sgpc-bucket colours and the land neutrals (ink-200 land and ink-100 absent in light; ink-600 land and ink-700 absent in dark) at ΔE2000 ≥ 11.1 under normal vision and deutan, protan and tritan simulation, and the country outline (ink-600 light, ink-300 dark) at ≥ 3:1. Draw country borders in this colour, as gaps between shapes. The dark value moved from the apps' #14232c to #042438 on 2026-11-03 (maintainer): a slightly deeper sea keeps the quiet 'no data' countries (ink-700) distinct from it for protan readers (#14232c was ΔE 6.61 from them). Raised from the SGPc apps (2026-11-03)."
342
+ }
311
343
  }
312
344
  }
313
345
  },
@@ -422,6 +454,52 @@ const tokens = {
422
454
  "description": "Reserved for the dataimago wordmark / brand-mark transitions; do not borrow for general UI"
423
455
  }
424
456
  },
457
+ "gradient": {
458
+ "video-poster": {
459
+ "value": "linear-gradient(135deg, #2b3a4a, #1f2a1f 60%, #3b2f1c)",
460
+ "description": "Video-poster placeholder, shown until a real poster frame exists: a slate-to-green-to-umber diagonal. It depicts a video frame, so it is theme-invariant (the same in light and dark). Its three stops (#2b3a4a, #1f2a1f, #3b2f1c) are a one-off and are NOT palette colours: do not reach for them elsewhere. A CSS gradient string has no clean DTCG mapping, so this token is in tokens.css and the JS exports only and is left out of the W3C DTCG and Tokens Studio exports (the legacy dist/tokens.dtcg.json still carries it). Raised from the SGPc apps (2026-11-03)."
461
+ }
462
+ },
463
+ "motion": {
464
+ "enter": {
465
+ "value": "200ms",
466
+ "description": "Something arrives: a page fade, tooltip in, chip in, view switch, drawer open, chart marks. Pair with motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-enter. Within the 300ms motion-dignity ceiling; gated by tools/motion-lint.mjs."
467
+ },
468
+ "exit": {
469
+ "value": "120ms",
470
+ "description": "Something leaves: tooltip out, chip out, drawer close. Shorter than motion.enter, because leaving should not hold the reader. Pair with motion.ease-in; a component that unmounts in script holds the unmount for this long. Raised from the SGPc apps (2026-11-03), which named it --motion-exit."
471
+ },
472
+ "ease-out": {
473
+ "value": "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
474
+ "description": "easeOutQuad, the curve foundations/design/motion-dignity.md names, for entrances: fast at the start, settling gently. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-out."
475
+ },
476
+ "ease-in": {
477
+ "value": "cubic-bezier(0.55, 0.085, 0.68, 0.53)",
478
+ "description": "easeInQuad, for exits: starts gently and leaves quickly. The counterpart of motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-in."
479
+ },
480
+ "duration": {
481
+ "120": {
482
+ "value": "120ms",
483
+ "description": "Duration scale step, 120ms. Equal to motion.exit."
484
+ },
485
+ "160": {
486
+ "value": "160ms",
487
+ "description": "Duration scale step, 160ms: a hover on a small control, a sheet leaving."
488
+ },
489
+ "200": {
490
+ "value": "200ms",
491
+ "description": "Duration scale step, 200ms. Equal to motion.enter."
492
+ },
493
+ "250": {
494
+ "value": "250ms",
495
+ "description": "Duration scale step, 250ms: a sheet arriving, a grid-column change."
496
+ },
497
+ "300": {
498
+ "value": "300ms",
499
+ "description": "Duration scale step, 300ms: the motion-dignity ceiling. Nothing in the scale is longer, and the motion gate holds every motion.* value to it. A slow ambient entrance is not a token: a wait before an animation is a delay, not motion, and the animation itself stays at or under this step."
500
+ }
501
+ }
502
+ },
425
503
  "card": {
426
504
  "background": "surface.elevated",
427
505
  "padding": "spacing.component.padding",
package/dist/index.mjs CHANGED
@@ -45,12 +45,27 @@ const tokens = {
45
45
  "scrim": {
46
46
  "light": "rgba(20, 20, 16, 0.85)",
47
47
  "dark": "rgba(20, 20, 16, 0.85)",
48
- "description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03)."
48
+ "description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03). Older browsers (Chrome < 122, Safari < 17.4, Firefox < 120) do not let ::backdrop inherit custom properties from :root, so a consumer painting the scrim on ::backdrop should write a fallback, e.g. var(--color-surface-scrim, rgba(20, 20, 16, 0.85)); the house figure viewer draws its scrim on a div (.di-figure-modal), so it is not affected."
49
49
  },
50
50
  "scrim-solid": {
51
51
  "light": "rgba(20, 20, 16, 0.92)",
52
52
  "dark": "rgba(20, 20, 16, 0.92)",
53
53
  "description": "Figure viewer backdrop where backdrop blur is unavailable (prefers-reduced-transparency, prefers-contrast: more and browsers without backdrop blur): the same ink at 0.92. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Gated by tools/contrast-check.mjs."
54
+ },
55
+ "print": {
56
+ "light": "#f7f7f6",
57
+ "dark": "#f7f7f6",
58
+ "description": "Printed paper: the paper-cover thumbnail, the light slide, the face of a video-play disc. It depicts a physical object (a printed page, a slide), so it is theme-invariant: light and dark carry the same value, and the paper does not darken with the page (the color.border.figure-frame precedent). stone-50, the card colour; the SGPc apps' own #f6f4ee is not a house colour and was mapped onto this step (maintainer decision 2026-11-03). Ink on it is content.on-print, held to 7:1 by tools/contrast-check.mjs. Paper reads as paper by its edge, not its colour: a print surface always carries --shadow-print or a hairline in border.print (the edge rule, maintainer decision 2026-11-03)."
59
+ },
60
+ "play-disc": {
61
+ "light": "rgba(247, 247, 246, 0.92)",
62
+ "dark": "rgba(247, 247, 246, 0.92)",
63
+ "description": "The video-play disc: surface.print at 0.92 alpha, over a poster frame or poster placeholder. It depicts a physical object, so it is theme-invariant: light and dark carry the same value. Its glyph is content.on-print (no separate token), held to 4.5:1 on the disc composited over each mode's page by tools/contrast-check.mjs, so the disc stays legible in dark mode."
64
+ },
65
+ "media": {
66
+ "light": "#000000",
67
+ "dark": "#000000",
68
+ "description": "The media letterbox: the ground behind a <video> and other media (an embedded player, a frame that does not fill its box). Pure black, not the house near-black (stone-900 #14140f, ink-900 #0c0a09): video frames carry true black, so a near-black ground shows the letterbox bars beside the frame's own black, and pure black hides them. It depicts the media frame, not the page, so it is theme-invariant: light and dark carry the same value (the color.surface.print precedent). Nothing is drawn on it, so it has no contrast floor; tools/contrast-check.mjs holds it to theme invariance (maintainer decision 2026-11-03)."
54
69
  }
55
70
  },
56
71
  "border": {
@@ -88,6 +103,11 @@ const tokens = {
88
103
  "light": "#3498DB",
89
104
  "dark": "#3498DB",
90
105
  "description": "Focus ring on the figure viewer scrim. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). The slate accent's dark-mode value in both modes, because the scrim is dark in both. Held to 3:1 (WCAG 1.4.11) over both scrims and both page modes by tools/contrast-check.mjs."
106
+ },
107
+ "print": {
108
+ "light": "rgba(12, 10, 9, 0.45)",
109
+ "dark": "rgba(12, 10, 9, 0.45)",
110
+ "description": "The hairline edge of printed paper (surface.print): the paper-cover thumbnail, the light slide. THE EDGE RULE: paper reads as paper by its edge, not by its colour. surface.print is the card colour, so a print surface always carries either --shadow-print or a hairline in this token (maintainer decision 2026-11-03). It is drawn on the paper, which stays light in both modes, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). It starts from the light value of border.subtle, and as there the alpha is the role and no ratio is quoted on purpose: composited over surface.print it is held to the WCAG 1.4.11 non-text threshold (3:1) by tools/contrast-check.mjs. Read the value, not this sentence."
91
111
  }
92
112
  },
93
113
  "content": {
@@ -115,6 +135,11 @@ const tokens = {
115
135
  "light": "#f7f7f6",
116
136
  "dark": "#f7f7f6",
117
137
  "description": "Text and controls on the figure viewer scrim (stone-50). Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 over both scrims and both page modes by tools/contrast-check.mjs."
138
+ },
139
+ "on-print": {
140
+ "light": "#14140f",
141
+ "dark": "#14140f",
142
+ "description": "Ink on surface.print, and the glyph on surface.play-disc (the play triangle takes this token; it has no separate one). stone-900. It depicts printed ink, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 on surface.print, and 4.5:1 on the play disc composited over each mode's page, by tools/contrast-check.mjs."
118
143
  }
119
144
  },
120
145
  "interactive": {
@@ -308,6 +333,13 @@ const tokens = {
308
333
  "value": "#0072B2",
309
334
  "description": "= okabe-ito.blue. SGPc growth bucket 5 of 5 (very high) on the ordered scale very low → very high. Colour is never the only channel: pair it with a marker and the bucket letter code. Author decision 2026-11-01, matching the LIwLD paper's figures."
310
335
  }
336
+ },
337
+ "map": {
338
+ "ocean": {
339
+ "light": "#808fa7",
340
+ "dark": "#042438",
341
+ "description": "Map-surface ground: the sea on the SGPc globe, behind land, absent countries and bucket-filled shapes. A muted slate blue on light surfaces and a deep blue on dark (maintainer 2026-10-22); not a palette step. The light value moved from #c9e1ec on 2026-11-01, when the Okabe-Ito sky-blue bucket blended into it: #808fa7 sits mid-way in lightness between the two blue buckets and is grey enough to read as sea. Gated by tools/dataviz-check.mjs against the five sgpc-bucket colours and the land neutrals (ink-200 land and ink-100 absent in light; ink-600 land and ink-700 absent in dark) at ΔE2000 ≥ 11.1 under normal vision and deutan, protan and tritan simulation, and the country outline (ink-600 light, ink-300 dark) at ≥ 3:1. Draw country borders in this colour, as gaps between shapes. The dark value moved from the apps' #14232c to #042438 on 2026-11-03 (maintainer): a slightly deeper sea keeps the quiet 'no data' countries (ink-700) distinct from it for protan readers (#14232c was ΔE 6.61 from them). Raised from the SGPc apps (2026-11-03)."
342
+ }
311
343
  }
312
344
  }
313
345
  },
@@ -422,6 +454,52 @@ const tokens = {
422
454
  "description": "Reserved for the dataimago wordmark / brand-mark transitions; do not borrow for general UI"
423
455
  }
424
456
  },
457
+ "gradient": {
458
+ "video-poster": {
459
+ "value": "linear-gradient(135deg, #2b3a4a, #1f2a1f 60%, #3b2f1c)",
460
+ "description": "Video-poster placeholder, shown until a real poster frame exists: a slate-to-green-to-umber diagonal. It depicts a video frame, so it is theme-invariant (the same in light and dark). Its three stops (#2b3a4a, #1f2a1f, #3b2f1c) are a one-off and are NOT palette colours: do not reach for them elsewhere. A CSS gradient string has no clean DTCG mapping, so this token is in tokens.css and the JS exports only and is left out of the W3C DTCG and Tokens Studio exports (the legacy dist/tokens.dtcg.json still carries it). Raised from the SGPc apps (2026-11-03)."
461
+ }
462
+ },
463
+ "motion": {
464
+ "enter": {
465
+ "value": "200ms",
466
+ "description": "Something arrives: a page fade, tooltip in, chip in, view switch, drawer open, chart marks. Pair with motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-enter. Within the 300ms motion-dignity ceiling; gated by tools/motion-lint.mjs."
467
+ },
468
+ "exit": {
469
+ "value": "120ms",
470
+ "description": "Something leaves: tooltip out, chip out, drawer close. Shorter than motion.enter, because leaving should not hold the reader. Pair with motion.ease-in; a component that unmounts in script holds the unmount for this long. Raised from the SGPc apps (2026-11-03), which named it --motion-exit."
471
+ },
472
+ "ease-out": {
473
+ "value": "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
474
+ "description": "easeOutQuad, the curve foundations/design/motion-dignity.md names, for entrances: fast at the start, settling gently. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-out."
475
+ },
476
+ "ease-in": {
477
+ "value": "cubic-bezier(0.55, 0.085, 0.68, 0.53)",
478
+ "description": "easeInQuad, for exits: starts gently and leaves quickly. The counterpart of motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-in."
479
+ },
480
+ "duration": {
481
+ "120": {
482
+ "value": "120ms",
483
+ "description": "Duration scale step, 120ms. Equal to motion.exit."
484
+ },
485
+ "160": {
486
+ "value": "160ms",
487
+ "description": "Duration scale step, 160ms: a hover on a small control, a sheet leaving."
488
+ },
489
+ "200": {
490
+ "value": "200ms",
491
+ "description": "Duration scale step, 200ms. Equal to motion.enter."
492
+ },
493
+ "250": {
494
+ "value": "250ms",
495
+ "description": "Duration scale step, 250ms: a sheet arriving, a grid-column change."
496
+ },
497
+ "300": {
498
+ "value": "300ms",
499
+ "description": "Duration scale step, 300ms: the motion-dignity ceiling. Nothing in the scale is longer, and the motion gate holds every motion.* value to it. A slow ambient entrance is not a token: a wait before an animation is a delay, not motion, and the animation itself stays at or under this step."
500
+ }
501
+ }
502
+ },
425
503
  "card": {
426
504
  "background": "surface.elevated",
427
505
  "padding": "spacing.component.padding",
package/dist/tokens.css CHANGED
@@ -63,6 +63,16 @@
63
63
  --transition-standard: 0.25s ease-in-out;
64
64
  --transition-smooth: 0.35s ease;
65
65
  --transition-logo: 0.3s;
66
+ --gradient-video-poster: linear-gradient(135deg, #2b3a4a, #1f2a1f 60%, #3b2f1c);
67
+ --motion-enter: 200ms;
68
+ --motion-exit: 120ms;
69
+ --motion-ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
70
+ --motion-ease-in: cubic-bezier(0.55, 0.085, 0.68, 0.53);
71
+ --motion-duration-120: 120ms;
72
+ --motion-duration-160: 160ms;
73
+ --motion-duration-200: 200ms;
74
+ --motion-duration-250: 250ms;
75
+ --motion-duration-300: 300ms;
66
76
  --font-family-sans: Noto Sans, Noto Sans Math, system-ui, sans-serif;
67
77
  --font-family-mono: Noto Sans Mono, SF Mono, Monaco, Inconsolata, monospace;
68
78
  --font-family-display: var(--font-family-display-editorial);
@@ -158,6 +168,12 @@
158
168
  --color-surface-scrim: color(display-p3 0.0784 0.0784 0.0643 / 0.85);
159
169
  --color-surface-scrim-solid: rgba(20, 20, 16, 0.92);
160
170
  --color-surface-scrim-solid: color(display-p3 0.0784 0.0784 0.0643 / 0.92);
171
+ --color-surface-print: #f7f7f6;
172
+ --color-surface-print: color(display-p3 0.9686 0.9686 0.9651);
173
+ --color-surface-play-disc: rgba(247, 247, 246, 0.92);
174
+ --color-surface-play-disc: color(display-p3 0.9686 0.9686 0.9651 / 0.92);
175
+ --color-surface-media: #000000;
176
+ --color-surface-media: color(display-p3 0 0 0);
161
177
  --color-border-subtle: rgba(12, 10, 9, 0.45);
162
178
  --color-border-subtle: color(display-p3 0.0457 0.0395 0.0358 / 0.45);
163
179
  --color-border-moderate: rgba(12, 10, 9, 0.60);
@@ -172,6 +188,8 @@
172
188
  --color-border-code: color(display-p3 0.8371 0.8278 0.8205);
173
189
  --color-border-on-scrim-focus: #3498db;
174
190
  --color-border-on-scrim-focus: color(display-p3 0.3208 0.588 0.8369);
191
+ --color-border-print: rgba(12, 10, 9, 0.45);
192
+ --color-border-print: color(display-p3 0.0457 0.0395 0.0358 / 0.45);
175
193
  --color-content-primary: #14140f;
176
194
  --color-content-primary: color(display-p3 0.0784 0.0784 0.0608);
177
195
  --color-content-secondary: #6a6a6a;
@@ -182,6 +200,8 @@
182
200
  --color-content-inverse: color(display-p3 0.9894 0.977 0.9518);
183
201
  --color-content-on-scrim: #f7f7f6;
184
202
  --color-content-on-scrim: color(display-p3 0.9686 0.9686 0.9651);
203
+ --color-content-on-print: #14140f;
204
+ --color-content-on-print: color(display-p3 0.0784 0.0784 0.0608);
185
205
  --color-interactive-primary: #434739;
186
206
  --color-interactive-primary: color(display-p3 0.2656 0.2779 0.2287);
187
207
  --color-interactive-hover: #363a2e;
@@ -219,6 +239,10 @@
219
239
  --shadow-prominent: 0 10px 15px -3px rgb(20 20 16 / 0.1);
220
240
  --shadow-figure-plate-hover: 0 10px 15px -3px rgb(20 20 16 / 0.1);
221
241
  --shadow-figure-plate-active: 0 4px 6px -1px rgb(20 20 16 / 0.1);
242
+ --shadow-lift: 0 -8px 24px rgb(20 20 16 / 0.07);
243
+ --shadow-sheet: 0 -8px 24px rgb(0 0 0 / 0.18);
244
+ --shadow-float: 0 1px 2px rgb(20 20 16 / 0.06), 0 4px 14px rgb(20 20 16 / 0.08);
245
+ --shadow-print: 0 1px 3px rgb(0 0 0 / 0.25);
222
246
  --border-subtle: rgba(20, 20, 16, 0.47);
223
247
  --border-moderate: rgba(20, 20, 16, 0.63);
224
248
  --border-focus: #3498DB;
@@ -229,6 +253,7 @@
229
253
  --color-dataviz-uncertainty-outer: #D6D6D6;
230
254
  --color-dataviz-uncertainty-union: #9E9E9E;
231
255
  --color-dataviz-uncertainty-inner: #333333;
256
+ --color-dataviz-map-ocean: #808fa7;
232
257
  }
233
258
 
234
259
  [data-bs-theme="dark"] {
@@ -252,6 +277,12 @@
252
277
  --color-surface-scrim: color(display-p3 0.0784 0.0784 0.0643 / 0.85);
253
278
  --color-surface-scrim-solid: rgba(20, 20, 16, 0.92);
254
279
  --color-surface-scrim-solid: color(display-p3 0.0784 0.0784 0.0643 / 0.92);
280
+ --color-surface-print: #f7f7f6;
281
+ --color-surface-print: color(display-p3 0.9686 0.9686 0.9651);
282
+ --color-surface-play-disc: rgba(247, 247, 246, 0.92);
283
+ --color-surface-play-disc: color(display-p3 0.9686 0.9686 0.9651 / 0.92);
284
+ --color-surface-media: #000000;
285
+ --color-surface-media: color(display-p3 0 0 0);
255
286
  --color-border-subtle: rgba(253, 249, 242, 0.45);
256
287
  --color-border-subtle: color(display-p3 0.9894 0.977 0.9518 / 0.45);
257
288
  --color-border-moderate: rgba(253, 249, 242, 0.60);
@@ -266,6 +297,8 @@
266
297
  --color-border-code: color(display-p3 0.1581 0.1456 0.1418);
267
298
  --color-border-on-scrim-focus: #3498db;
268
299
  --color-border-on-scrim-focus: color(display-p3 0.3208 0.588 0.8369);
300
+ --color-border-print: rgba(12, 10, 9, 0.45);
301
+ --color-border-print: color(display-p3 0.0457 0.0395 0.0358 / 0.45);
269
302
  --color-content-primary: #fdf9f2;
270
303
  --color-content-primary: color(display-p3 0.9894 0.977 0.9518);
271
304
  --color-content-secondary: #a8a29e;
@@ -276,6 +309,8 @@
276
309
  --color-content-inverse: color(display-p3 0.0457 0.0395 0.0358);
277
310
  --color-content-on-scrim: #f7f7f6;
278
311
  --color-content-on-scrim: color(display-p3 0.9686 0.9686 0.9651);
312
+ --color-content-on-print: #14140f;
313
+ --color-content-on-print: color(display-p3 0.0784 0.0784 0.0608);
279
314
  --color-interactive-primary: #98a384;
280
315
  --color-interactive-primary: color(display-p3 0.604 0.6378 0.5291);
281
316
  --color-interactive-hover: #7e8a6b;
@@ -313,6 +348,10 @@
313
348
  --shadow-prominent: 0 10px 15px -3px rgb(237 237 235 / 0.4);
314
349
  --shadow-figure-plate-hover: 0 4px 12px rgb(0 0 0 / 0.5);
315
350
  --shadow-figure-plate-active: 0 2px 8px rgb(0 0 0 / 0.35);
351
+ --shadow-lift: 0 -8px 24px rgb(0 0 0 / 0.30);
352
+ --shadow-sheet: 0 -8px 24px rgb(0 0 0 / 0.18);
353
+ --shadow-float: 0 1px 2px rgb(0 0 0 / 0.40), 0 4px 14px rgb(0 0 0 / 0.35);
354
+ --shadow-print: 0 1px 3px rgb(0 0 0 / 0.25);
316
355
  --border-subtle: rgba(237, 237, 235, 0.47);
317
356
  --border-moderate: rgba(237, 237, 235, 0.63);
318
357
  --border-focus: #4FC3F7;
@@ -323,6 +362,7 @@
323
362
  --color-dataviz-uncertainty-outer: #333333;
324
363
  --color-dataviz-uncertainty-union: #6B6B6B;
325
364
  --color-dataviz-uncertainty-inner: #D6D6D6;
365
+ --color-dataviz-map-ocean: #042438;
326
366
  }
327
367
 
328
368
  @media (prefers-color-scheme: dark) {
@@ -347,6 +387,12 @@
347
387
  --color-surface-scrim: color(display-p3 0.0784 0.0784 0.0643 / 0.85);
348
388
  --color-surface-scrim-solid: rgba(20, 20, 16, 0.92);
349
389
  --color-surface-scrim-solid: color(display-p3 0.0784 0.0784 0.0643 / 0.92);
390
+ --color-surface-print: #f7f7f6;
391
+ --color-surface-print: color(display-p3 0.9686 0.9686 0.9651);
392
+ --color-surface-play-disc: rgba(247, 247, 246, 0.92);
393
+ --color-surface-play-disc: color(display-p3 0.9686 0.9686 0.9651 / 0.92);
394
+ --color-surface-media: #000000;
395
+ --color-surface-media: color(display-p3 0 0 0);
350
396
  --color-border-subtle: rgba(253, 249, 242, 0.45);
351
397
  --color-border-subtle: color(display-p3 0.9894 0.977 0.9518 / 0.45);
352
398
  --color-border-moderate: rgba(253, 249, 242, 0.60);
@@ -361,6 +407,8 @@
361
407
  --color-border-code: color(display-p3 0.1581 0.1456 0.1418);
362
408
  --color-border-on-scrim-focus: #3498db;
363
409
  --color-border-on-scrim-focus: color(display-p3 0.3208 0.588 0.8369);
410
+ --color-border-print: rgba(12, 10, 9, 0.45);
411
+ --color-border-print: color(display-p3 0.0457 0.0395 0.0358 / 0.45);
364
412
  --color-content-primary: #fdf9f2;
365
413
  --color-content-primary: color(display-p3 0.9894 0.977 0.9518);
366
414
  --color-content-secondary: #a8a29e;
@@ -371,6 +419,8 @@
371
419
  --color-content-inverse: color(display-p3 0.0457 0.0395 0.0358);
372
420
  --color-content-on-scrim: #f7f7f6;
373
421
  --color-content-on-scrim: color(display-p3 0.9686 0.9686 0.9651);
422
+ --color-content-on-print: #14140f;
423
+ --color-content-on-print: color(display-p3 0.0784 0.0784 0.0608);
374
424
  --color-interactive-primary: #98a384;
375
425
  --color-interactive-primary: color(display-p3 0.604 0.6378 0.5291);
376
426
  --color-interactive-hover: #7e8a6b;
@@ -408,6 +458,10 @@
408
458
  --shadow-prominent: 0 10px 15px -3px rgb(237 237 235 / 0.4);
409
459
  --shadow-figure-plate-hover: 0 4px 12px rgb(0 0 0 / 0.5);
410
460
  --shadow-figure-plate-active: 0 2px 8px rgb(0 0 0 / 0.35);
461
+ --shadow-lift: 0 -8px 24px rgb(0 0 0 / 0.30);
462
+ --shadow-sheet: 0 -8px 24px rgb(0 0 0 / 0.18);
463
+ --shadow-float: 0 1px 2px rgb(0 0 0 / 0.40), 0 4px 14px rgb(0 0 0 / 0.35);
464
+ --shadow-print: 0 1px 3px rgb(0 0 0 / 0.25);
411
465
  --border-subtle: rgba(237, 237, 235, 0.47);
412
466
  --border-moderate: rgba(237, 237, 235, 0.63);
413
467
  --border-focus: #4FC3F7;
@@ -418,5 +472,6 @@
418
472
  --color-dataviz-uncertainty-outer: #333333;
419
473
  --color-dataviz-uncertainty-union: #6B6B6B;
420
474
  --color-dataviz-uncertainty-inner: #D6D6D6;
475
+ --color-dataviz-map-ocean: #042438;
421
476
  }
422
477
  }
@@ -71,7 +71,7 @@
71
71
  "dark": "rgba(20, 20, 16, 0.85)"
72
72
  },
73
73
  "$type": "color",
74
- "$description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03)."
74
+ "$description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03). Older browsers (Chrome < 122, Safari < 17.4, Firefox < 120) do not let ::backdrop inherit custom properties from :root, so a consumer painting the scrim on ::backdrop should write a fallback, e.g. var(--color-surface-scrim, rgba(20, 20, 16, 0.85)); the house figure viewer draws its scrim on a div (.di-figure-modal), so it is not affected."
75
75
  },
76
76
  "scrim-solid": {
77
77
  "$value": {
@@ -80,6 +80,30 @@
80
80
  },
81
81
  "$type": "color",
82
82
  "$description": "Figure viewer backdrop where backdrop blur is unavailable (prefers-reduced-transparency, prefers-contrast: more and browsers without backdrop blur): the same ink at 0.92. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Gated by tools/contrast-check.mjs."
83
+ },
84
+ "print": {
85
+ "$value": {
86
+ "light": "#f7f7f6",
87
+ "dark": "#f7f7f6"
88
+ },
89
+ "$type": "color",
90
+ "$description": "Printed paper: the paper-cover thumbnail, the light slide, the face of a video-play disc. It depicts a physical object (a printed page, a slide), so it is theme-invariant: light and dark carry the same value, and the paper does not darken with the page (the color.border.figure-frame precedent). stone-50, the card colour; the SGPc apps' own #f6f4ee is not a house colour and was mapped onto this step (maintainer decision 2026-11-03). Ink on it is content.on-print, held to 7:1 by tools/contrast-check.mjs. Paper reads as paper by its edge, not its colour: a print surface always carries --shadow-print or a hairline in border.print (the edge rule, maintainer decision 2026-11-03)."
91
+ },
92
+ "play-disc": {
93
+ "$value": {
94
+ "light": "rgba(247, 247, 246, 0.92)",
95
+ "dark": "rgba(247, 247, 246, 0.92)"
96
+ },
97
+ "$type": "color",
98
+ "$description": "The video-play disc: surface.print at 0.92 alpha, over a poster frame or poster placeholder. It depicts a physical object, so it is theme-invariant: light and dark carry the same value. Its glyph is content.on-print (no separate token), held to 4.5:1 on the disc composited over each mode's page by tools/contrast-check.mjs, so the disc stays legible in dark mode."
99
+ },
100
+ "media": {
101
+ "$value": {
102
+ "light": "#000000",
103
+ "dark": "#000000"
104
+ },
105
+ "$type": "color",
106
+ "$description": "The media letterbox: the ground behind a <video> and other media (an embedded player, a frame that does not fill its box). Pure black, not the house near-black (stone-900 #14140f, ink-900 #0c0a09): video frames carry true black, so a near-black ground shows the letterbox bars beside the frame's own black, and pure black hides them. It depicts the media frame, not the page, so it is theme-invariant: light and dark carry the same value (the color.surface.print precedent). Nothing is drawn on it, so it has no contrast floor; tools/contrast-check.mjs holds it to theme invariance (maintainer decision 2026-11-03)."
83
107
  }
84
108
  },
85
109
  "border": {
@@ -138,6 +162,14 @@
138
162
  },
139
163
  "$type": "color",
140
164
  "$description": "Focus ring on the figure viewer scrim. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). The slate accent's dark-mode value in both modes, because the scrim is dark in both. Held to 3:1 (WCAG 1.4.11) over both scrims and both page modes by tools/contrast-check.mjs."
165
+ },
166
+ "print": {
167
+ "$value": {
168
+ "light": "rgba(12, 10, 9, 0.45)",
169
+ "dark": "rgba(12, 10, 9, 0.45)"
170
+ },
171
+ "$type": "color",
172
+ "$description": "The hairline edge of printed paper (surface.print): the paper-cover thumbnail, the light slide. THE EDGE RULE: paper reads as paper by its edge, not by its colour. surface.print is the card colour, so a print surface always carries either --shadow-print or a hairline in this token (maintainer decision 2026-11-03). It is drawn on the paper, which stays light in both modes, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). It starts from the light value of border.subtle, and as there the alpha is the role and no ratio is quoted on purpose: composited over surface.print it is held to the WCAG 1.4.11 non-text threshold (3:1) by tools/contrast-check.mjs. Read the value, not this sentence."
141
173
  }
142
174
  },
143
175
  "content": {
@@ -180,6 +212,14 @@
180
212
  },
181
213
  "$type": "color",
182
214
  "$description": "Text and controls on the figure viewer scrim (stone-50). Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 over both scrims and both page modes by tools/contrast-check.mjs."
215
+ },
216
+ "on-print": {
217
+ "$value": {
218
+ "light": "#14140f",
219
+ "dark": "#14140f"
220
+ },
221
+ "$type": "color",
222
+ "$description": "Ink on surface.print, and the glyph on surface.play-disc (the play triangle takes this token; it has no separate one). stone-900. It depicts printed ink, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 on surface.print, and 4.5:1 on the play disc composited over each mode's page, by tools/contrast-check.mjs."
183
223
  }
184
224
  },
185
225
  "interactive": {
@@ -465,6 +505,62 @@
465
505
  "$description": "Reserved for the dataimago wordmark / brand-mark transitions; do not borrow for general UI"
466
506
  }
467
507
  },
508
+ "gradient": {
509
+ "video-poster": {
510
+ "$value": "linear-gradient(135deg, #2b3a4a, #1f2a1f 60%, #3b2f1c)",
511
+ "$type": "other",
512
+ "$description": "Video-poster placeholder, shown until a real poster frame exists: a slate-to-green-to-umber diagonal. It depicts a video frame, so it is theme-invariant (the same in light and dark). Its three stops (#2b3a4a, #1f2a1f, #3b2f1c) are a one-off and are NOT palette colours: do not reach for them elsewhere. A CSS gradient string has no clean DTCG mapping, so this token is in tokens.css and the JS exports only and is left out of the W3C DTCG and Tokens Studio exports (the legacy dist/tokens.dtcg.json still carries it). Raised from the SGPc apps (2026-11-03)."
513
+ }
514
+ },
515
+ "motion": {
516
+ "enter": {
517
+ "$value": "200ms",
518
+ "$type": "duration",
519
+ "$description": "Something arrives: a page fade, tooltip in, chip in, view switch, drawer open, chart marks. Pair with motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-enter. Within the 300ms motion-dignity ceiling; gated by tools/motion-lint.mjs."
520
+ },
521
+ "exit": {
522
+ "$value": "120ms",
523
+ "$type": "duration",
524
+ "$description": "Something leaves: tooltip out, chip out, drawer close. Shorter than motion.enter, because leaving should not hold the reader. Pair with motion.ease-in; a component that unmounts in script holds the unmount for this long. Raised from the SGPc apps (2026-11-03), which named it --motion-exit."
525
+ },
526
+ "ease-out": {
527
+ "$value": "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
528
+ "$type": "other",
529
+ "$description": "easeOutQuad, the curve foundations/design/motion-dignity.md names, for entrances: fast at the start, settling gently. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-out."
530
+ },
531
+ "ease-in": {
532
+ "$value": "cubic-bezier(0.55, 0.085, 0.68, 0.53)",
533
+ "$type": "other",
534
+ "$description": "easeInQuad, for exits: starts gently and leaves quickly. The counterpart of motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-in."
535
+ },
536
+ "duration": {
537
+ "120": {
538
+ "$value": "120ms",
539
+ "$type": "duration",
540
+ "$description": "Duration scale step, 120ms. Equal to motion.exit."
541
+ },
542
+ "160": {
543
+ "$value": "160ms",
544
+ "$type": "duration",
545
+ "$description": "Duration scale step, 160ms: a hover on a small control, a sheet leaving."
546
+ },
547
+ "200": {
548
+ "$value": "200ms",
549
+ "$type": "duration",
550
+ "$description": "Duration scale step, 200ms. Equal to motion.enter."
551
+ },
552
+ "250": {
553
+ "$value": "250ms",
554
+ "$type": "duration",
555
+ "$description": "Duration scale step, 250ms: a sheet arriving, a grid-column change."
556
+ },
557
+ "300": {
558
+ "$value": "300ms",
559
+ "$type": "duration",
560
+ "$description": "Duration scale step, 300ms: the motion-dignity ceiling. Nothing in the scale is longer, and the motion gate holds every motion.* value to it. A slow ambient entrance is not a token: a wait before an animation is a delay, not motion, and the animation itself stays at or under this step."
561
+ }
562
+ }
563
+ },
468
564
  "card": {
469
565
  "background": {
470
566
  "$value": "surface.elevated",
package/dist/tokens.scss CHANGED
@@ -63,6 +63,16 @@
63
63
  --transition-standard: 0.25s ease-in-out;
64
64
  --transition-smooth: 0.35s ease;
65
65
  --transition-logo: 0.3s;
66
+ --gradient-video-poster: linear-gradient(135deg, #2b3a4a, #1f2a1f 60%, #3b2f1c);
67
+ --motion-enter: 200ms;
68
+ --motion-exit: 120ms;
69
+ --motion-ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
70
+ --motion-ease-in: cubic-bezier(0.55, 0.085, 0.68, 0.53);
71
+ --motion-duration-120: 120ms;
72
+ --motion-duration-160: 160ms;
73
+ --motion-duration-200: 200ms;
74
+ --motion-duration-250: 250ms;
75
+ --motion-duration-300: 300ms;
66
76
  --font-family-sans: Noto Sans, Noto Sans Math, system-ui, sans-serif;
67
77
  --font-family-mono: Noto Sans Mono, SF Mono, Monaco, Inconsolata, monospace;
68
78
  --font-family-display: var(--font-family-display-editorial);
@@ -158,6 +168,12 @@
158
168
  --color-surface-scrim: color(display-p3 0.0784 0.0784 0.0643 / 0.85);
159
169
  --color-surface-scrim-solid: rgba(20, 20, 16, 0.92);
160
170
  --color-surface-scrim-solid: color(display-p3 0.0784 0.0784 0.0643 / 0.92);
171
+ --color-surface-print: #f7f7f6;
172
+ --color-surface-print: color(display-p3 0.9686 0.9686 0.9651);
173
+ --color-surface-play-disc: rgba(247, 247, 246, 0.92);
174
+ --color-surface-play-disc: color(display-p3 0.9686 0.9686 0.9651 / 0.92);
175
+ --color-surface-media: #000000;
176
+ --color-surface-media: color(display-p3 0 0 0);
161
177
  --color-border-subtle: rgba(12, 10, 9, 0.45);
162
178
  --color-border-subtle: color(display-p3 0.0457 0.0395 0.0358 / 0.45);
163
179
  --color-border-moderate: rgba(12, 10, 9, 0.60);
@@ -172,6 +188,8 @@
172
188
  --color-border-code: color(display-p3 0.8371 0.8278 0.8205);
173
189
  --color-border-on-scrim-focus: #3498db;
174
190
  --color-border-on-scrim-focus: color(display-p3 0.3208 0.588 0.8369);
191
+ --color-border-print: rgba(12, 10, 9, 0.45);
192
+ --color-border-print: color(display-p3 0.0457 0.0395 0.0358 / 0.45);
175
193
  --color-content-primary: #14140f;
176
194
  --color-content-primary: color(display-p3 0.0784 0.0784 0.0608);
177
195
  --color-content-secondary: #6a6a6a;
@@ -182,6 +200,8 @@
182
200
  --color-content-inverse: color(display-p3 0.9894 0.977 0.9518);
183
201
  --color-content-on-scrim: #f7f7f6;
184
202
  --color-content-on-scrim: color(display-p3 0.9686 0.9686 0.9651);
203
+ --color-content-on-print: #14140f;
204
+ --color-content-on-print: color(display-p3 0.0784 0.0784 0.0608);
185
205
  --color-interactive-primary: #434739;
186
206
  --color-interactive-primary: color(display-p3 0.2656 0.2779 0.2287);
187
207
  --color-interactive-hover: #363a2e;
@@ -219,6 +239,10 @@
219
239
  --shadow-prominent: 0 10px 15px -3px rgb(20 20 16 / 0.1);
220
240
  --shadow-figure-plate-hover: 0 10px 15px -3px rgb(20 20 16 / 0.1);
221
241
  --shadow-figure-plate-active: 0 4px 6px -1px rgb(20 20 16 / 0.1);
242
+ --shadow-lift: 0 -8px 24px rgb(20 20 16 / 0.07);
243
+ --shadow-sheet: 0 -8px 24px rgb(0 0 0 / 0.18);
244
+ --shadow-float: 0 1px 2px rgb(20 20 16 / 0.06), 0 4px 14px rgb(20 20 16 / 0.08);
245
+ --shadow-print: 0 1px 3px rgb(0 0 0 / 0.25);
222
246
  --border-subtle: rgba(20, 20, 16, 0.47);
223
247
  --border-moderate: rgba(20, 20, 16, 0.63);
224
248
  --border-focus: #3498DB;
@@ -229,6 +253,7 @@
229
253
  --color-dataviz-uncertainty-outer: #D6D6D6;
230
254
  --color-dataviz-uncertainty-union: #9E9E9E;
231
255
  --color-dataviz-uncertainty-inner: #333333;
256
+ --color-dataviz-map-ocean: #808fa7;
232
257
  }
233
258
 
234
259
  [data-bs-theme="dark"] {
@@ -252,6 +277,12 @@
252
277
  --color-surface-scrim: color(display-p3 0.0784 0.0784 0.0643 / 0.85);
253
278
  --color-surface-scrim-solid: rgba(20, 20, 16, 0.92);
254
279
  --color-surface-scrim-solid: color(display-p3 0.0784 0.0784 0.0643 / 0.92);
280
+ --color-surface-print: #f7f7f6;
281
+ --color-surface-print: color(display-p3 0.9686 0.9686 0.9651);
282
+ --color-surface-play-disc: rgba(247, 247, 246, 0.92);
283
+ --color-surface-play-disc: color(display-p3 0.9686 0.9686 0.9651 / 0.92);
284
+ --color-surface-media: #000000;
285
+ --color-surface-media: color(display-p3 0 0 0);
255
286
  --color-border-subtle: rgba(253, 249, 242, 0.45);
256
287
  --color-border-subtle: color(display-p3 0.9894 0.977 0.9518 / 0.45);
257
288
  --color-border-moderate: rgba(253, 249, 242, 0.60);
@@ -266,6 +297,8 @@
266
297
  --color-border-code: color(display-p3 0.1581 0.1456 0.1418);
267
298
  --color-border-on-scrim-focus: #3498db;
268
299
  --color-border-on-scrim-focus: color(display-p3 0.3208 0.588 0.8369);
300
+ --color-border-print: rgba(12, 10, 9, 0.45);
301
+ --color-border-print: color(display-p3 0.0457 0.0395 0.0358 / 0.45);
269
302
  --color-content-primary: #fdf9f2;
270
303
  --color-content-primary: color(display-p3 0.9894 0.977 0.9518);
271
304
  --color-content-secondary: #a8a29e;
@@ -276,6 +309,8 @@
276
309
  --color-content-inverse: color(display-p3 0.0457 0.0395 0.0358);
277
310
  --color-content-on-scrim: #f7f7f6;
278
311
  --color-content-on-scrim: color(display-p3 0.9686 0.9686 0.9651);
312
+ --color-content-on-print: #14140f;
313
+ --color-content-on-print: color(display-p3 0.0784 0.0784 0.0608);
279
314
  --color-interactive-primary: #98a384;
280
315
  --color-interactive-primary: color(display-p3 0.604 0.6378 0.5291);
281
316
  --color-interactive-hover: #7e8a6b;
@@ -313,6 +348,10 @@
313
348
  --shadow-prominent: 0 10px 15px -3px rgb(237 237 235 / 0.4);
314
349
  --shadow-figure-plate-hover: 0 4px 12px rgb(0 0 0 / 0.5);
315
350
  --shadow-figure-plate-active: 0 2px 8px rgb(0 0 0 / 0.35);
351
+ --shadow-lift: 0 -8px 24px rgb(0 0 0 / 0.30);
352
+ --shadow-sheet: 0 -8px 24px rgb(0 0 0 / 0.18);
353
+ --shadow-float: 0 1px 2px rgb(0 0 0 / 0.40), 0 4px 14px rgb(0 0 0 / 0.35);
354
+ --shadow-print: 0 1px 3px rgb(0 0 0 / 0.25);
316
355
  --border-subtle: rgba(237, 237, 235, 0.47);
317
356
  --border-moderate: rgba(237, 237, 235, 0.63);
318
357
  --border-focus: #4FC3F7;
@@ -323,6 +362,7 @@
323
362
  --color-dataviz-uncertainty-outer: #333333;
324
363
  --color-dataviz-uncertainty-union: #6B6B6B;
325
364
  --color-dataviz-uncertainty-inner: #D6D6D6;
365
+ --color-dataviz-map-ocean: #042438;
326
366
  }
327
367
 
328
368
  @media (prefers-color-scheme: dark) {
@@ -347,6 +387,12 @@
347
387
  --color-surface-scrim: color(display-p3 0.0784 0.0784 0.0643 / 0.85);
348
388
  --color-surface-scrim-solid: rgba(20, 20, 16, 0.92);
349
389
  --color-surface-scrim-solid: color(display-p3 0.0784 0.0784 0.0643 / 0.92);
390
+ --color-surface-print: #f7f7f6;
391
+ --color-surface-print: color(display-p3 0.9686 0.9686 0.9651);
392
+ --color-surface-play-disc: rgba(247, 247, 246, 0.92);
393
+ --color-surface-play-disc: color(display-p3 0.9686 0.9686 0.9651 / 0.92);
394
+ --color-surface-media: #000000;
395
+ --color-surface-media: color(display-p3 0 0 0);
350
396
  --color-border-subtle: rgba(253, 249, 242, 0.45);
351
397
  --color-border-subtle: color(display-p3 0.9894 0.977 0.9518 / 0.45);
352
398
  --color-border-moderate: rgba(253, 249, 242, 0.60);
@@ -361,6 +407,8 @@
361
407
  --color-border-code: color(display-p3 0.1581 0.1456 0.1418);
362
408
  --color-border-on-scrim-focus: #3498db;
363
409
  --color-border-on-scrim-focus: color(display-p3 0.3208 0.588 0.8369);
410
+ --color-border-print: rgba(12, 10, 9, 0.45);
411
+ --color-border-print: color(display-p3 0.0457 0.0395 0.0358 / 0.45);
364
412
  --color-content-primary: #fdf9f2;
365
413
  --color-content-primary: color(display-p3 0.9894 0.977 0.9518);
366
414
  --color-content-secondary: #a8a29e;
@@ -371,6 +419,8 @@
371
419
  --color-content-inverse: color(display-p3 0.0457 0.0395 0.0358);
372
420
  --color-content-on-scrim: #f7f7f6;
373
421
  --color-content-on-scrim: color(display-p3 0.9686 0.9686 0.9651);
422
+ --color-content-on-print: #14140f;
423
+ --color-content-on-print: color(display-p3 0.0784 0.0784 0.0608);
374
424
  --color-interactive-primary: #98a384;
375
425
  --color-interactive-primary: color(display-p3 0.604 0.6378 0.5291);
376
426
  --color-interactive-hover: #7e8a6b;
@@ -408,6 +458,10 @@
408
458
  --shadow-prominent: 0 10px 15px -3px rgb(237 237 235 / 0.4);
409
459
  --shadow-figure-plate-hover: 0 4px 12px rgb(0 0 0 / 0.5);
410
460
  --shadow-figure-plate-active: 0 2px 8px rgb(0 0 0 / 0.35);
461
+ --shadow-lift: 0 -8px 24px rgb(0 0 0 / 0.30);
462
+ --shadow-sheet: 0 -8px 24px rgb(0 0 0 / 0.18);
463
+ --shadow-float: 0 1px 2px rgb(0 0 0 / 0.40), 0 4px 14px rgb(0 0 0 / 0.35);
464
+ --shadow-print: 0 1px 3px rgb(0 0 0 / 0.25);
411
465
  --border-subtle: rgba(237, 237, 235, 0.47);
412
466
  --border-moderate: rgba(237, 237, 235, 0.63);
413
467
  --border-focus: #4FC3F7;
@@ -418,5 +472,6 @@
418
472
  --color-dataviz-uncertainty-outer: #333333;
419
473
  --color-dataviz-uncertainty-union: #6B6B6B;
420
474
  --color-dataviz-uncertainty-inner: #D6D6D6;
475
+ --color-dataviz-map-ocean: #042438;
421
476
  }
422
477
  }
@@ -41,6 +41,18 @@
41
41
  "scrim-solid": {
42
42
  "value": "rgba(20, 20, 16, 0.92)",
43
43
  "type": "color"
44
+ },
45
+ "print": {
46
+ "value": "#f7f7f6",
47
+ "type": "color"
48
+ },
49
+ "play-disc": {
50
+ "value": "rgba(247, 247, 246, 0.92)",
51
+ "type": "color"
52
+ },
53
+ "media": {
54
+ "value": "#000000",
55
+ "type": "color"
44
56
  }
45
57
  },
46
58
  "border": {
@@ -71,6 +83,10 @@
71
83
  "on-scrim-focus": {
72
84
  "value": "#3498DB",
73
85
  "type": "color"
86
+ },
87
+ "print": {
88
+ "value": "rgba(12, 10, 9, 0.45)",
89
+ "type": "color"
74
90
  }
75
91
  },
76
92
  "content": {
@@ -93,6 +109,10 @@
93
109
  "on-scrim": {
94
110
  "value": "#f7f7f6",
95
111
  "type": "color"
112
+ },
113
+ "on-print": {
114
+ "value": "#14140f",
115
+ "type": "color"
96
116
  }
97
117
  },
98
118
  "interactive": {
@@ -273,6 +293,12 @@
273
293
  "value": "#0072B2",
274
294
  "type": "color"
275
295
  }
296
+ },
297
+ "map": {
298
+ "ocean": {
299
+ "value": "#808fa7",
300
+ "type": "color"
301
+ }
276
302
  }
277
303
  }
278
304
  },
@@ -326,6 +352,46 @@
326
352
  "type": "duration"
327
353
  }
328
354
  },
355
+ "motion": {
356
+ "enter": {
357
+ "value": "200ms",
358
+ "type": "duration"
359
+ },
360
+ "exit": {
361
+ "value": "120ms",
362
+ "type": "duration"
363
+ },
364
+ "ease-out": {
365
+ "value": "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
366
+ "type": "other"
367
+ },
368
+ "ease-in": {
369
+ "value": "cubic-bezier(0.55, 0.085, 0.68, 0.53)",
370
+ "type": "other"
371
+ },
372
+ "duration": {
373
+ "120": {
374
+ "value": "120ms",
375
+ "type": "duration"
376
+ },
377
+ "160": {
378
+ "value": "160ms",
379
+ "type": "duration"
380
+ },
381
+ "200": {
382
+ "value": "200ms",
383
+ "type": "duration"
384
+ },
385
+ "250": {
386
+ "value": "250ms",
387
+ "type": "duration"
388
+ },
389
+ "300": {
390
+ "value": "300ms",
391
+ "type": "duration"
392
+ }
393
+ }
394
+ },
329
395
  "font": {
330
396
  "family": {
331
397
  "sans": {
@@ -593,6 +659,18 @@
593
659
  "scrim-solid": {
594
660
  "value": "rgba(20, 20, 16, 0.92)",
595
661
  "type": "color"
662
+ },
663
+ "print": {
664
+ "value": "#f7f7f6",
665
+ "type": "color"
666
+ },
667
+ "play-disc": {
668
+ "value": "rgba(247, 247, 246, 0.92)",
669
+ "type": "color"
670
+ },
671
+ "media": {
672
+ "value": "#000000",
673
+ "type": "color"
596
674
  }
597
675
  },
598
676
  "border": {
@@ -623,6 +701,10 @@
623
701
  "on-scrim-focus": {
624
702
  "value": "#3498DB",
625
703
  "type": "color"
704
+ },
705
+ "print": {
706
+ "value": "rgba(12, 10, 9, 0.45)",
707
+ "type": "color"
626
708
  }
627
709
  },
628
710
  "content": {
@@ -645,6 +727,10 @@
645
727
  "on-scrim": {
646
728
  "value": "#f7f7f6",
647
729
  "type": "color"
730
+ },
731
+ "on-print": {
732
+ "value": "#14140f",
733
+ "type": "color"
648
734
  }
649
735
  },
650
736
  "interactive": {
@@ -739,6 +825,12 @@
739
825
  "value": "#D6D6D6",
740
826
  "type": "color"
741
827
  }
828
+ },
829
+ "map": {
830
+ "ocean": {
831
+ "value": "#042438",
832
+ "type": "color"
833
+ }
742
834
  }
743
835
  }
744
836
  },
@@ -89,7 +89,7 @@
89
89
  "dark": "rgba(20, 20, 16, 0.85)"
90
90
  }
91
91
  },
92
- "$description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03)."
92
+ "$description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03). Older browsers (Chrome < 122, Safari < 17.4, Firefox < 120) do not let ::backdrop inherit custom properties from :root, so a consumer painting the scrim on ::backdrop should write a fallback, e.g. var(--color-surface-scrim, rgba(20, 20, 16, 0.85)); the house figure viewer draws its scrim on a div (.di-figure-modal), so it is not affected."
93
93
  },
94
94
  "scrim-solid": {
95
95
  "$value": "rgba(20, 20, 16, 0.92)",
@@ -100,6 +100,36 @@
100
100
  }
101
101
  },
102
102
  "$description": "Figure viewer backdrop where backdrop blur is unavailable (prefers-reduced-transparency, prefers-contrast: more and browsers without backdrop blur): the same ink at 0.92. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Gated by tools/contrast-check.mjs."
103
+ },
104
+ "print": {
105
+ "$value": "#f7f7f6",
106
+ "$type": "color",
107
+ "$extensions": {
108
+ "com.dataimago.theme": {
109
+ "dark": "#f7f7f6"
110
+ }
111
+ },
112
+ "$description": "Printed paper: the paper-cover thumbnail, the light slide, the face of a video-play disc. It depicts a physical object (a printed page, a slide), so it is theme-invariant: light and dark carry the same value, and the paper does not darken with the page (the color.border.figure-frame precedent). stone-50, the card colour; the SGPc apps' own #f6f4ee is not a house colour and was mapped onto this step (maintainer decision 2026-11-03). Ink on it is content.on-print, held to 7:1 by tools/contrast-check.mjs. Paper reads as paper by its edge, not its colour: a print surface always carries --shadow-print or a hairline in border.print (the edge rule, maintainer decision 2026-11-03)."
113
+ },
114
+ "play-disc": {
115
+ "$value": "rgba(247, 247, 246, 0.92)",
116
+ "$type": "color",
117
+ "$extensions": {
118
+ "com.dataimago.theme": {
119
+ "dark": "rgba(247, 247, 246, 0.92)"
120
+ }
121
+ },
122
+ "$description": "The video-play disc: surface.print at 0.92 alpha, over a poster frame or poster placeholder. It depicts a physical object, so it is theme-invariant: light and dark carry the same value. Its glyph is content.on-print (no separate token), held to 4.5:1 on the disc composited over each mode's page by tools/contrast-check.mjs, so the disc stays legible in dark mode."
123
+ },
124
+ "media": {
125
+ "$value": "#000000",
126
+ "$type": "color",
127
+ "$extensions": {
128
+ "com.dataimago.theme": {
129
+ "dark": "#000000"
130
+ }
131
+ },
132
+ "$description": "The media letterbox: the ground behind a <video> and other media (an embedded player, a frame that does not fill its box). Pure black, not the house near-black (stone-900 #14140f, ink-900 #0c0a09): video frames carry true black, so a near-black ground shows the letterbox bars beside the frame's own black, and pure black hides them. It depicts the media frame, not the page, so it is theme-invariant: light and dark carry the same value (the color.surface.print precedent). Nothing is drawn on it, so it has no contrast floor; tools/contrast-check.mjs holds it to theme invariance (maintainer decision 2026-11-03)."
103
133
  }
104
134
  },
105
135
  "border": {
@@ -172,6 +202,16 @@
172
202
  }
173
203
  },
174
204
  "$description": "Focus ring on the figure viewer scrim. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). The slate accent's dark-mode value in both modes, because the scrim is dark in both. Held to 3:1 (WCAG 1.4.11) over both scrims and both page modes by tools/contrast-check.mjs."
205
+ },
206
+ "print": {
207
+ "$value": "rgba(12, 10, 9, 0.45)",
208
+ "$type": "color",
209
+ "$extensions": {
210
+ "com.dataimago.theme": {
211
+ "dark": "rgba(12, 10, 9, 0.45)"
212
+ }
213
+ },
214
+ "$description": "The hairline edge of printed paper (surface.print): the paper-cover thumbnail, the light slide. THE EDGE RULE: paper reads as paper by its edge, not by its colour. surface.print is the card colour, so a print surface always carries either --shadow-print or a hairline in this token (maintainer decision 2026-11-03). It is drawn on the paper, which stays light in both modes, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). It starts from the light value of border.subtle, and as there the alpha is the role and no ratio is quoted on purpose: composited over surface.print it is held to the WCAG 1.4.11 non-text threshold (3:1) by tools/contrast-check.mjs. Read the value, not this sentence."
175
215
  }
176
216
  },
177
217
  "content": {
@@ -224,6 +264,16 @@
224
264
  }
225
265
  },
226
266
  "$description": "Text and controls on the figure viewer scrim (stone-50). Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 over both scrims and both page modes by tools/contrast-check.mjs."
267
+ },
268
+ "on-print": {
269
+ "$value": "#14140f",
270
+ "$type": "color",
271
+ "$extensions": {
272
+ "com.dataimago.theme": {
273
+ "dark": "#14140f"
274
+ }
275
+ },
276
+ "$description": "Ink on surface.print, and the glyph on surface.play-disc (the play triangle takes this token; it has no separate one). stone-900. It depicts printed ink, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 on surface.print, and 4.5:1 on the play disc composited over each mode's page, by tools/contrast-check.mjs."
227
277
  }
228
278
  },
229
279
  "interactive": {
@@ -535,6 +585,18 @@
535
585
  "$type": "color",
536
586
  "$description": "= okabe-ito.blue. SGPc growth bucket 5 of 5 (very high) on the ordered scale very low → very high. Colour is never the only channel: pair it with a marker and the bucket letter code. Author decision 2026-11-01, matching the LIwLD paper's figures."
537
587
  }
588
+ },
589
+ "map": {
590
+ "ocean": {
591
+ "$value": "#808fa7",
592
+ "$type": "color",
593
+ "$extensions": {
594
+ "com.dataimago.theme": {
595
+ "dark": "#042438"
596
+ }
597
+ },
598
+ "$description": "Map-surface ground: the sea on the SGPc globe, behind land, absent countries and bucket-filled shapes. A muted slate blue on light surfaces and a deep blue on dark (maintainer 2026-10-22); not a palette step. The light value moved from #c9e1ec on 2026-11-01, when the Okabe-Ito sky-blue bucket blended into it: #808fa7 sits mid-way in lightness between the two blue buckets and is grey enough to read as sea. Gated by tools/dataviz-check.mjs against the five sgpc-bucket colours and the land neutrals (ink-200 land and ink-100 absent in light; ink-600 land and ink-700 absent in dark) at ΔE2000 ≥ 11.1 under normal vision and deutan, protan and tritan simulation, and the country outline (ink-600 light, ink-300 dark) at ≥ 3:1. Draw country borders in this colour, as gaps between shapes. The dark value moved from the apps' #14232c to #042438 on 2026-11-03 (maintainer): a slightly deeper sea keeps the quiet 'no data' countries (ink-700) distinct from it for protan readers (#14232c was ΔE 6.61 from them). Raised from the SGPc apps (2026-11-03)."
599
+ }
538
600
  }
539
601
  }
540
602
  },
@@ -606,6 +668,65 @@
606
668
  "$description": "Reserved for the dataimago wordmark / brand-mark transitions; do not borrow for general UI"
607
669
  }
608
670
  },
671
+ "motion": {
672
+ "enter": {
673
+ "$value": "200ms",
674
+ "$type": "duration",
675
+ "$description": "Something arrives: a page fade, tooltip in, chip in, view switch, drawer open, chart marks. Pair with motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-enter. Within the 300ms motion-dignity ceiling; gated by tools/motion-lint.mjs."
676
+ },
677
+ "exit": {
678
+ "$value": "120ms",
679
+ "$type": "duration",
680
+ "$description": "Something leaves: tooltip out, chip out, drawer close. Shorter than motion.enter, because leaving should not hold the reader. Pair with motion.ease-in; a component that unmounts in script holds the unmount for this long. Raised from the SGPc apps (2026-11-03), which named it --motion-exit."
681
+ },
682
+ "ease-out": {
683
+ "$value": [
684
+ 0.25,
685
+ 0.46,
686
+ 0.45,
687
+ 0.94
688
+ ],
689
+ "$type": "cubicBezier",
690
+ "$description": "easeOutQuad, the curve foundations/design/motion-dignity.md names, for entrances: fast at the start, settling gently. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-out."
691
+ },
692
+ "ease-in": {
693
+ "$value": [
694
+ 0.55,
695
+ 0.085,
696
+ 0.68,
697
+ 0.53
698
+ ],
699
+ "$type": "cubicBezier",
700
+ "$description": "easeInQuad, for exits: starts gently and leaves quickly. The counterpart of motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-in."
701
+ },
702
+ "duration": {
703
+ "120": {
704
+ "$value": "120ms",
705
+ "$type": "duration",
706
+ "$description": "Duration scale step, 120ms. Equal to motion.exit."
707
+ },
708
+ "160": {
709
+ "$value": "160ms",
710
+ "$type": "duration",
711
+ "$description": "Duration scale step, 160ms: a hover on a small control, a sheet leaving."
712
+ },
713
+ "200": {
714
+ "$value": "200ms",
715
+ "$type": "duration",
716
+ "$description": "Duration scale step, 200ms. Equal to motion.enter."
717
+ },
718
+ "250": {
719
+ "$value": "250ms",
720
+ "$type": "duration",
721
+ "$description": "Duration scale step, 250ms: a sheet arriving, a grid-column change."
722
+ },
723
+ "300": {
724
+ "$value": "300ms",
725
+ "$type": "duration",
726
+ "$description": "Duration scale step, 300ms: the motion-dignity ceiling. Nothing in the scale is longer, and the motion gate holds every motion.* value to it. A slow ambient entrance is not a token: a wait before an animation is a delay, not motion, and the animation itself stays at or under this step."
727
+ }
728
+ }
729
+ },
609
730
  "font": {
610
731
  "family": {
611
732
  "sans": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dataimago/tokens",
3
- "version": "0.1.0-alpha.31",
3
+ "version": "0.1.0-alpha.33",
4
4
  "description": "dataimago design tokens — canonical JSON sources plus generated TS/CSS artifacts",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/dataviz.json CHANGED
@@ -109,6 +109,13 @@
109
109
  "value": "#0072B2",
110
110
  "description": "= okabe-ito.blue. SGPc growth bucket 5 of 5 (very high) on the ordered scale very low → very high. Colour is never the only channel: pair it with a marker and the bucket letter code. Author decision 2026-11-01, matching the LIwLD paper's figures."
111
111
  }
112
+ },
113
+ "map": {
114
+ "ocean": {
115
+ "light": "#808fa7",
116
+ "dark": "#042438",
117
+ "description": "Map-surface ground: the sea on the SGPc globe, behind land, absent countries and bucket-filled shapes. A muted slate blue on light surfaces and a deep blue on dark (maintainer 2026-10-22); not a palette step. The light value moved from #c9e1ec on 2026-11-01, when the Okabe-Ito sky-blue bucket blended into it: #808fa7 sits mid-way in lightness between the two blue buckets and is grey enough to read as sea. Gated by tools/dataviz-check.mjs against the five sgpc-bucket colours and the land neutrals (ink-200 land and ink-100 absent in light; ink-600 land and ink-700 absent in dark) at ΔE2000 ≥ 11.1 under normal vision and deutan, protan and tritan simulation, and the country outline (ink-600 light, ink-300 dark) at ≥ 3:1. Draw country borders in this colour, as gaps between shapes. The dark value moved from the apps' #14232c to #042438 on 2026-11-03 (maintainer): a slightly deeper sea keeps the quiet 'no data' countries (ink-700) distinct from it for protan readers (#14232c was ΔE 6.61 from them). Raised from the SGPc apps (2026-11-03)."
118
+ }
112
119
  }
113
120
  }
114
121
  }
package/src/effects.json CHANGED
@@ -26,6 +26,26 @@
26
26
  "dark": "0 2px 8px rgb(0 0 0 / 0.35)",
27
27
  "description": "Figure plate while pressed. Light mode is the moderate shadow; dark mode is a plain black shadow for the same reason as figure-plate.hover. Not mode-invariant."
28
28
  }
29
+ },
30
+ "lift": {
31
+ "light": "0 -8px 24px rgb(20 20 16 / 0.07)",
32
+ "dark": "0 -8px 24px rgb(0 0 0 / 0.30)",
33
+ "description": "Upward panel lift: a lower panel rising over the section above it, e.g. the landing's lower panel over the globe during the section wipe. Every other shadow here falls downward. Light is ink-900-ish at 0.07 (the apps wrote rgb(28 25 23), normalised to the house base rgb(20 20 16)); dark is plain black at 0.30 because a light-ink glow (as in shadow.prominent) would haze the dark page above the panel. The dark value is a controller call, not measured. Raised from the SGPc apps (2026-11-03)."
34
+ },
35
+ "sheet": {
36
+ "light": "0 -8px 24px rgb(0 0 0 / 0.18)",
37
+ "dark": "0 -8px 24px rgb(0 0 0 / 0.18)",
38
+ "description": "Upward shadow for a bottom sheet (the map details panel on a narrow screen). Plain black at 0.18 in both modes: a sheet is a physical layer over the page, so light and dark carry the same value. Raised from the SGPc apps (2026-11-03)."
39
+ },
40
+ "float": {
41
+ "light": "0 1px 2px rgb(20 20 16 / 0.06), 0 4px 14px rgb(20 20 16 / 0.08)",
42
+ "dark": "0 1px 2px rgb(0 0 0 / 0.40), 0 4px 14px rgb(0 0 0 / 0.35)",
43
+ "description": "Floating control: a small disc standing off the page, e.g. the landing's scroll chevron. Two layers, a tight contact shadow and a soft spread. Pair it with a hairline outline, because a shadow alone does not read in dark mode. Light is the apps' value normalised to rgb(20 20 16); dark is plain black, stronger (0.40 and 0.35) so the layers still read on a dark page, a controller call, not measured. Raised from the SGPc apps (2026-11-03)."
44
+ },
45
+ "print": {
46
+ "light": "0 1px 3px rgb(0 0 0 / 0.25)",
47
+ "dark": "0 1px 3px rgb(0 0 0 / 0.25)",
48
+ "description": "Paper drop shadow under a printed-paper object, e.g. the paper-cover thumbnail on color.surface.print. It depicts a physical object, so it is theme-invariant: the same in light and dark. Raised from the SGPc apps (2026-11-03)."
29
49
  }
30
50
  },
31
51
  "border": {
@@ -62,5 +82,51 @@
62
82
  "value": "0.3s",
63
83
  "description": "Reserved for the dataimago wordmark / brand-mark transitions; do not borrow for general UI"
64
84
  }
85
+ },
86
+ "gradient": {
87
+ "video-poster": {
88
+ "value": "linear-gradient(135deg, #2b3a4a, #1f2a1f 60%, #3b2f1c)",
89
+ "description": "Video-poster placeholder, shown until a real poster frame exists: a slate-to-green-to-umber diagonal. It depicts a video frame, so it is theme-invariant (the same in light and dark). Its three stops (#2b3a4a, #1f2a1f, #3b2f1c) are a one-off and are NOT palette colours: do not reach for them elsewhere. A CSS gradient string has no clean DTCG mapping, so this token is in tokens.css and the JS exports only and is left out of the W3C DTCG and Tokens Studio exports (the legacy dist/tokens.dtcg.json still carries it). Raised from the SGPc apps (2026-11-03)."
90
+ }
91
+ },
92
+ "motion": {
93
+ "enter": {
94
+ "value": "200ms",
95
+ "description": "Something arrives: a page fade, tooltip in, chip in, view switch, drawer open, chart marks. Pair with motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-enter. Within the 300ms motion-dignity ceiling; gated by tools/motion-lint.mjs."
96
+ },
97
+ "exit": {
98
+ "value": "120ms",
99
+ "description": "Something leaves: tooltip out, chip out, drawer close. Shorter than motion.enter, because leaving should not hold the reader. Pair with motion.ease-in; a component that unmounts in script holds the unmount for this long. Raised from the SGPc apps (2026-11-03), which named it --motion-exit."
100
+ },
101
+ "ease-out": {
102
+ "value": "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
103
+ "description": "easeOutQuad, the curve foundations/design/motion-dignity.md names, for entrances: fast at the start, settling gently. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-out."
104
+ },
105
+ "ease-in": {
106
+ "value": "cubic-bezier(0.55, 0.085, 0.68, 0.53)",
107
+ "description": "easeInQuad, for exits: starts gently and leaves quickly. The counterpart of motion.ease-out. Raised from the SGPc apps (2026-11-03), which named it --motion-ease-in."
108
+ },
109
+ "duration": {
110
+ "120": {
111
+ "value": "120ms",
112
+ "description": "Duration scale step, 120ms. Equal to motion.exit."
113
+ },
114
+ "160": {
115
+ "value": "160ms",
116
+ "description": "Duration scale step, 160ms: a hover on a small control, a sheet leaving."
117
+ },
118
+ "200": {
119
+ "value": "200ms",
120
+ "description": "Duration scale step, 200ms. Equal to motion.enter."
121
+ },
122
+ "250": {
123
+ "value": "250ms",
124
+ "description": "Duration scale step, 250ms: a sheet arriving, a grid-column change."
125
+ },
126
+ "300": {
127
+ "value": "300ms",
128
+ "description": "Duration scale step, 300ms: the motion-dignity ceiling. Nothing in the scale is longer, and the motion gate holds every motion.* value to it. A slow ambient entrance is not a token: a wait before an animation is a delay, not motion, and the animation itself stays at or under this step."
129
+ }
130
+ }
65
131
  }
66
132
  }
@@ -116,7 +116,7 @@
116
116
  "oklch": "oklch(18.97% 0.0078 107.13 / 0.85)",
117
117
  "p3": "color(display-p3 0.0784 0.0784 0.0643 / 0.85)"
118
118
  },
119
- "description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03)."
119
+ "description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03). Older browsers (Chrome < 122, Safari < 17.4, Firefox < 120) do not let ::backdrop inherit custom properties from :root, so a consumer painting the scrim on ::backdrop should write a fallback, e.g. var(--color-surface-scrim, rgba(20, 20, 16, 0.85)); the house figure viewer draws its scrim on a div (.di-figure-modal), so it is not affected."
120
120
  },
121
121
  "scrim-solid": {
122
122
  "light": {
@@ -130,6 +130,45 @@
130
130
  "p3": "color(display-p3 0.0784 0.0784 0.0643 / 0.92)"
131
131
  },
132
132
  "description": "Figure viewer backdrop where backdrop blur is unavailable (prefers-reduced-transparency, prefers-contrast: more and browsers without backdrop blur): the same ink at 0.92. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Gated by tools/contrast-check.mjs."
133
+ },
134
+ "print": {
135
+ "light": {
136
+ "srgb": "#f7f7f6",
137
+ "oklch": "oklch(97.59% 0.0013 106.42)",
138
+ "p3": "color(display-p3 0.9686 0.9686 0.9651)"
139
+ },
140
+ "dark": {
141
+ "srgb": "#f7f7f6",
142
+ "oklch": "oklch(97.59% 0.0013 106.42)",
143
+ "p3": "color(display-p3 0.9686 0.9686 0.9651)"
144
+ },
145
+ "description": "Printed paper: the paper-cover thumbnail, the light slide, the face of a video-play disc. It depicts a physical object (a printed page, a slide), so it is theme-invariant: light and dark carry the same value, and the paper does not darken with the page (the color.border.figure-frame precedent). stone-50, the card colour; the SGPc apps' own #f6f4ee is not a house colour and was mapped onto this step (maintainer decision 2026-11-03). Ink on it is content.on-print, held to 7:1 by tools/contrast-check.mjs. Paper reads as paper by its edge, not its colour: a print surface always carries --shadow-print or a hairline in border.print (the edge rule, maintainer decision 2026-11-03)."
146
+ },
147
+ "play-disc": {
148
+ "light": {
149
+ "srgb": "rgba(247, 247, 246, 0.92)",
150
+ "oklch": "oklch(97.59% 0.0013 106.42 / 0.92)",
151
+ "p3": "color(display-p3 0.9686 0.9686 0.9651 / 0.92)"
152
+ },
153
+ "dark": {
154
+ "srgb": "rgba(247, 247, 246, 0.92)",
155
+ "oklch": "oklch(97.59% 0.0013 106.42 / 0.92)",
156
+ "p3": "color(display-p3 0.9686 0.9686 0.9651 / 0.92)"
157
+ },
158
+ "description": "The video-play disc: surface.print at 0.92 alpha, over a poster frame or poster placeholder. It depicts a physical object, so it is theme-invariant: light and dark carry the same value. Its glyph is content.on-print (no separate token), held to 4.5:1 on the disc composited over each mode's page by tools/contrast-check.mjs, so the disc stays legible in dark mode."
159
+ },
160
+ "media": {
161
+ "light": {
162
+ "srgb": "#000000",
163
+ "oklch": "oklch(0% 0 0)",
164
+ "p3": "color(display-p3 0 0 0)"
165
+ },
166
+ "dark": {
167
+ "srgb": "#000000",
168
+ "oklch": "oklch(0% 0 0)",
169
+ "p3": "color(display-p3 0 0 0)"
170
+ },
171
+ "description": "The media letterbox: the ground behind a <video> and other media (an embedded player, a frame that does not fill its box). Pure black, not the house near-black (stone-900 #14140f, ink-900 #0c0a09): video frames carry true black, so a near-black ground shows the letterbox bars beside the frame's own black, and pure black hides them. It depicts the media frame, not the page, so it is theme-invariant: light and dark carry the same value (the color.surface.print precedent). Nothing is drawn on it, so it has no contrast floor; tools/contrast-check.mjs holds it to theme invariance (maintainer decision 2026-11-03)."
133
172
  }
134
173
  },
135
174
  "border": {
@@ -223,6 +262,19 @@
223
262
  "p3": "color(display-p3 0.3208 0.588 0.8369)"
224
263
  },
225
264
  "description": "Focus ring on the figure viewer scrim. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). The slate accent's dark-mode value in both modes, because the scrim is dark in both. Held to 3:1 (WCAG 1.4.11) over both scrims and both page modes by tools/contrast-check.mjs."
265
+ },
266
+ "print": {
267
+ "light": {
268
+ "srgb": "rgba(12, 10, 9, 0.45)",
269
+ "oklch": "oklch(14.69% 0.0041 49.25 / 0.45)",
270
+ "p3": "color(display-p3 0.0457 0.0395 0.0358 / 0.45)"
271
+ },
272
+ "dark": {
273
+ "srgb": "rgba(12, 10, 9, 0.45)",
274
+ "oklch": "oklch(14.69% 0.0041 49.25 / 0.45)",
275
+ "p3": "color(display-p3 0.0457 0.0395 0.0358 / 0.45)"
276
+ },
277
+ "description": "The hairline edge of printed paper (surface.print): the paper-cover thumbnail, the light slide. THE EDGE RULE: paper reads as paper by its edge, not by its colour. surface.print is the card colour, so a print surface always carries either --shadow-print or a hairline in this token (maintainer decision 2026-11-03). It is drawn on the paper, which stays light in both modes, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). It starts from the light value of border.subtle, and as there the alpha is the role and no ratio is quoted on purpose: composited over surface.print it is held to the WCAG 1.4.11 non-text threshold (3:1) by tools/contrast-check.mjs. Read the value, not this sentence."
226
278
  }
227
279
  },
228
280
  "content": {
@@ -290,6 +342,19 @@
290
342
  "p3": "color(display-p3 0.9686 0.9686 0.9651)"
291
343
  },
292
344
  "description": "Text and controls on the figure viewer scrim (stone-50). Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 over both scrims and both page modes by tools/contrast-check.mjs."
345
+ },
346
+ "on-print": {
347
+ "light": {
348
+ "srgb": "#14140f",
349
+ "oklch": "oklch(18.93% 0.0098 107.3)",
350
+ "p3": "color(display-p3 0.0784 0.0784 0.0608)"
351
+ },
352
+ "dark": {
353
+ "srgb": "#14140f",
354
+ "oklch": "oklch(18.93% 0.0098 107.3)",
355
+ "p3": "color(display-p3 0.0784 0.0784 0.0608)"
356
+ },
357
+ "description": "Ink on surface.print, and the glyph on surface.play-disc (the play triangle takes this token; it has no separate one). stone-900. It depicts printed ink, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 on surface.print, and 4.5:1 on the play disc composited over each mode's page, by tools/contrast-check.mjs."
293
358
  }
294
359
  },
295
360
  "interactive": {
@@ -44,12 +44,27 @@
44
44
  "scrim": {
45
45
  "light": "rgba(20, 20, 16, 0.85)",
46
46
  "dark": "rgba(20, 20, 16, 0.85)",
47
- "description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03)."
47
+ "description": "Figure viewer backdrop. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Ink-900-ish at 0.85, drawn under a 3px blur; measured composited over the page, on-scrim text is 11.5:1 on the light page and 17.2:1 on the dark (gated by tools/contrast-check.mjs, 2026-11-03). Older browsers (Chrome < 122, Safari < 17.4, Firefox < 120) do not let ::backdrop inherit custom properties from :root, so a consumer painting the scrim on ::backdrop should write a fallback, e.g. var(--color-surface-scrim, rgba(20, 20, 16, 0.85)); the house figure viewer draws its scrim on a div (.di-figure-modal), so it is not affected."
48
48
  },
49
49
  "scrim-solid": {
50
50
  "light": "rgba(20, 20, 16, 0.92)",
51
51
  "dark": "rgba(20, 20, 16, 0.92)",
52
52
  "description": "Figure viewer backdrop where backdrop blur is unavailable (prefers-reduced-transparency, prefers-contrast: more and browsers without backdrop blur): the same ink at 0.92. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Gated by tools/contrast-check.mjs."
53
+ },
54
+ "print": {
55
+ "light": "#f7f7f6",
56
+ "dark": "#f7f7f6",
57
+ "description": "Printed paper: the paper-cover thumbnail, the light slide, the face of a video-play disc. It depicts a physical object (a printed page, a slide), so it is theme-invariant: light and dark carry the same value, and the paper does not darken with the page (the color.border.figure-frame precedent). stone-50, the card colour; the SGPc apps' own #f6f4ee is not a house colour and was mapped onto this step (maintainer decision 2026-11-03). Ink on it is content.on-print, held to 7:1 by tools/contrast-check.mjs. Paper reads as paper by its edge, not its colour: a print surface always carries --shadow-print or a hairline in border.print (the edge rule, maintainer decision 2026-11-03)."
58
+ },
59
+ "play-disc": {
60
+ "light": "rgba(247, 247, 246, 0.92)",
61
+ "dark": "rgba(247, 247, 246, 0.92)",
62
+ "description": "The video-play disc: surface.print at 0.92 alpha, over a poster frame or poster placeholder. It depicts a physical object, so it is theme-invariant: light and dark carry the same value. Its glyph is content.on-print (no separate token), held to 4.5:1 on the disc composited over each mode's page by tools/contrast-check.mjs, so the disc stays legible in dark mode."
63
+ },
64
+ "media": {
65
+ "light": "#000000",
66
+ "dark": "#000000",
67
+ "description": "The media letterbox: the ground behind a <video> and other media (an embedded player, a frame that does not fill its box). Pure black, not the house near-black (stone-900 #14140f, ink-900 #0c0a09): video frames carry true black, so a near-black ground shows the letterbox bars beside the frame's own black, and pure black hides them. It depicts the media frame, not the page, so it is theme-invariant: light and dark carry the same value (the color.surface.print precedent). Nothing is drawn on it, so it has no contrast floor; tools/contrast-check.mjs holds it to theme invariance (maintainer decision 2026-11-03)."
53
68
  }
54
69
  },
55
70
  "border": {
@@ -87,6 +102,11 @@
87
102
  "light": "#3498DB",
88
103
  "dark": "#3498DB",
89
104
  "description": "Focus ring on the figure viewer scrim. Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). The slate accent's dark-mode value in both modes, because the scrim is dark in both. Held to 3:1 (WCAG 1.4.11) over both scrims and both page modes by tools/contrast-check.mjs."
105
+ },
106
+ "print": {
107
+ "light": "rgba(12, 10, 9, 0.45)",
108
+ "dark": "rgba(12, 10, 9, 0.45)",
109
+ "description": "The hairline edge of printed paper (surface.print): the paper-cover thumbnail, the light slide. THE EDGE RULE: paper reads as paper by its edge, not by its colour. surface.print is the card colour, so a print surface always carries either --shadow-print or a hairline in this token (maintainer decision 2026-11-03). It is drawn on the paper, which stays light in both modes, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). It starts from the light value of border.subtle, and as there the alpha is the role and no ratio is quoted on purpose: composited over surface.print it is held to the WCAG 1.4.11 non-text threshold (3:1) by tools/contrast-check.mjs. Read the value, not this sentence."
90
110
  }
91
111
  },
92
112
  "content": {
@@ -114,6 +134,11 @@
114
134
  "light": "#f7f7f6",
115
135
  "dark": "#f7f7f6",
116
136
  "description": "Text and controls on the figure viewer scrim (stone-50). Mode-invariant: the scrim is dark in both modes, so light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 over both scrims and both page modes by tools/contrast-check.mjs."
137
+ },
138
+ "on-print": {
139
+ "light": "#14140f",
140
+ "dark": "#14140f",
141
+ "description": "Ink on surface.print, and the glyph on surface.play-disc (the play triangle takes this token; it has no separate one). stone-900. It depicts printed ink, so it is theme-invariant: light and dark carry the same value (the color.border.figure-frame precedent). Held to 7:1 on surface.print, and 4.5:1 on the play disc composited over each mode's page, by tools/contrast-check.mjs."
117
142
  }
118
143
  },
119
144
  "interactive": {