@nxgt/material 3.6.0 → 3.7.1

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.
Files changed (97) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +46 -1
  3. package/dist/{button-1Fornjtn.js → button--cF4gY14.js} +1 -1
  4. package/dist/charts.js +1 -1
  5. package/dist/{code-editor-DeZYlIO2.js → code-editor-0CdGwKDg.js} +3 -3
  6. package/dist/code-editor.js +3 -3
  7. package/dist/{code-language-items-Bb7afEj-.js → code-language-items-CGARRbVm.js} +1 -1
  8. package/dist/components/animations/scroll.css +1 -1
  9. package/dist/components/base-content.css +1 -1
  10. package/dist/components/base.css +1 -1
  11. package/dist/components/components.css +2 -0
  12. package/dist/components/editor-field-expressive.css +5 -0
  13. package/dist/components/editor-prose.css +1 -1
  14. package/dist/components/theme-tokens.css +17 -4
  15. package/dist/components/ui/display/period-cards/period-cards.css +2 -2
  16. package/dist/components/ui/media/pdf-viewer/pdf-viewer.css +1 -1
  17. package/dist/components/ui/widgets/drawing/drawing-animate.css +123 -0
  18. package/dist/components/ui/widgets/drawing/drawing.css +459 -0
  19. package/dist/{components-BCYZgeoV.js → components-bYGPzyKd.js} +761 -761
  20. package/dist/components.js +18 -18
  21. package/dist/{dialog-JQT6iETD.js → dialog-DdJ1C4BR.js} +168 -168
  22. package/dist/dnd.js +1 -1
  23. package/dist/draw-animate.js +106 -0
  24. package/dist/draw-png.js +2 -0
  25. package/dist/draw-sketch.js +38 -0
  26. package/dist/draw.js +65 -0
  27. package/dist/drawing-B8VuG7nx.js +390 -0
  28. package/dist/drawing-context-CeKeZE6b.js +10 -0
  29. package/dist/{dropdown-menu-DiZvX4tG.js → dropdown-menu-DWtgUVW7.js} +1 -1
  30. package/dist/editor-viewer.js +1 -1
  31. package/dist/editor.js +120 -120
  32. package/dist/{empty-state-Cpvsmk0R.js → empty-state-CRWLQ1sM.js} +2 -2
  33. package/dist/{icon-badge-C30yKsAM.js → icon-badge-Bb5-73XP.js} +18 -18
  34. package/dist/{icon-button-BXlI3DJ_.js → icon-button-5A6Mrj6w.js} +60 -90
  35. package/dist/lib/components/ui/display/shape/use-shape-morph.d.ts +1 -0
  36. package/dist/lib/components/ui/widgets/drawing/drawing-a11y.d.ts +20 -0
  37. package/dist/lib/components/ui/widgets/drawing/drawing-context.d.ts +23 -0
  38. package/dist/lib/components/ui/widgets/drawing/drawing-types.d.ts +63 -0
  39. package/dist/lib/components/ui/widgets/drawing/drawing.d.ts +17 -0
  40. package/dist/lib/components/ui/widgets/drawing/index.d.ts +5 -0
  41. package/dist/lib/components/ui/widgets/drawing/partials/drawing-root.d.ts +17 -0
  42. package/dist/lib/components/ui/widgets/drawing/partials/interactive-node.d.ts +22 -0
  43. package/dist/lib/components/ui/widgets/drawing/partials/primitives/content.d.ts +20 -0
  44. package/dist/lib/components/ui/widgets/drawing/partials/primitives/path.d.ts +29 -0
  45. package/dist/lib/components/ui/widgets/drawing/partials/primitives/primitive.d.ts +19 -0
  46. package/dist/lib/components/ui/widgets/drawing/partials/primitives/shapes.d.ts +18 -0
  47. package/dist/lib/components/ui/widgets/drawing/partials/react-attrs.d.ts +5 -0
  48. package/dist/lib/components/ui/widgets/drawing/partials/ring.d.ts +9 -0
  49. package/dist/lib/components/ui/widgets/drawing/partials/svg-tree.d.ts +29 -0
  50. package/dist/lib/components/ui/widgets/drawing/use-exact-text.d.ts +16 -0
  51. package/dist/lib/draw/index.d.ts +32 -0
  52. package/dist/lib/draw-animate/index.d.ts +19 -0
  53. package/dist/lib/draw-animate/morph-drawing.d.ts +32 -0
  54. package/dist/lib/draw-animate/reduced-motion.d.ts +2 -0
  55. package/dist/lib/draw-animate/use-drawing-controls.d.ts +27 -0
  56. package/dist/lib/draw-animate/use-scene-morph.d.ts +15 -0
  57. package/dist/lib/draw-png/index.d.ts +11 -0
  58. package/dist/lib/draw-sketch/index.d.ts +17 -0
  59. package/dist/lib/draw-sketch/sketch.d.ts +19 -0
  60. package/dist/lib/print/components/print-drawing.d.ts +16 -0
  61. package/dist/lib/print/index.d.ts +2 -1
  62. package/dist/main.js +23 -23
  63. package/dist/map.js +63 -63
  64. package/dist/material.css +1 -1
  65. package/dist/pdf-reader.js +1 -1
  66. package/dist/print.js +123 -115
  67. package/dist/{public-Cde8_SN8.js → public-SsiazLEv.js} +743 -743
  68. package/dist/{render-json-BwJX1jZ2.js → render-json-DyFYFIkZ.js} +1 -1
  69. package/dist/{rich-code-block-yxMXBuGd.js → rich-code-block-CwNwQs5z.js} +3 -3
  70. package/dist/{rich-text-viewer-Bq-2vTVm.js → rich-text-viewer-LO1IZCWs.js} +1 -1
  71. package/dist/{sortable-provider-DYlpq8lP.js → sortable-provider-BvD98s8Y.js} +19 -19
  72. package/dist/{spinner-D-FE_mTW.js → spinner-CtGCPza-.js} +4 -4
  73. package/dist/styles.css +1 -0
  74. package/dist/{toolbar-items-DnkS3bDY.js → toolbar-items-DsDDD5AY.js} +1 -1
  75. package/dist/{use-chat-composer-BwpSgP_D.js → use-chat-composer-DZ01a16D.js} +923 -922
  76. package/dist/use-shape-morph-BKA_R5P5.js +35 -0
  77. package/docs/README.md +1 -0
  78. package/docs/components/rich-widgets.md +196 -0
  79. package/docs/guides/drawing.md +499 -0
  80. package/docs/guides/printing.md +47 -0
  81. package/docs/guides/theming.md +3 -2
  82. package/docs/previews/rich-widgets-drawing.webp +0 -0
  83. package/docs/roadmap.md +17 -0
  84. package/docs/troubleshooting.md +145 -1
  85. package/lib/components/animations/scroll.css +1 -1
  86. package/lib/components/base-content.css +1 -1
  87. package/lib/components/base.css +1 -1
  88. package/lib/components/components.css +2 -0
  89. package/lib/components/editor-field-expressive.css +5 -0
  90. package/lib/components/editor-prose.css +1 -1
  91. package/lib/components/theme-tokens.css +17 -4
  92. package/lib/components/ui/display/period-cards/period-cards.css +2 -2
  93. package/lib/components/ui/media/pdf-viewer/pdf-viewer.css +1 -1
  94. package/lib/components/ui/widgets/drawing/drawing-animate.css +123 -0
  95. package/lib/components/ui/widgets/drawing/drawing.css +459 -0
  96. package/lib/styles.css +1 -0
  97. package/package.json +22 -2
@@ -389,6 +389,19 @@ words in your error.
389
389
  - [My gradient colour override stopped working](#my-gradient-colour-override-stopped-working)
390
390
  - [A preset's items use class names my own CSS also uses (`fl`, `d1`, `k1`, `mo`)](#a-presets-items-use-class-names-my-own-css-also-uses-fl-d1-k1-mo)
391
391
 
392
+ **Drawing**
393
+
394
+ - [`A decorative drawing has an interactive node: give the Drawing a title instead`](#a-decorative-drawing-has-an-interactive-node-give-the-drawing-a-title-instead)
395
+ - [`Unsafe drawing at <tag>: <reason>` from a scene or a JSX drawing](#unsafe-drawing-at-tag-reason-from-a-scene-or-a-jsx-drawing)
396
+ - [`Tones "a" and "b" meet on one element: use two nodes in a group`](#tones-a-and-b-meet-on-one-element-use-two-nodes-in-a-group)
397
+ - [`No colour for "brand": pass palette: { "brand": "#rrggbb" }`](#no-colour-for-brand-pass-palette--brand-rrggbb-)
398
+ - [`shapeOutline: unknown shape "x"; expected one of SHAPE_NAMES (…)`](#shapeoutline-unknown-shape-x-expected-one-of-shape_names-)
399
+ - [`drawingToPng: cannot read the image "<url>" (<reason>)…`](#drawingtopng-cannot-read-the-image-url-reason)
400
+ - [`drawingToPng: the canvas gave no PNG.` and its siblings](#drawingtopng-the-canvas-gave-no-png-and-its-siblings)
401
+ - [A drawing is black, or its role colours do nothing](#a-drawing-is-black-or-its-role-colours-do-nothing)
402
+ - [A Server Component that renders `Drawing` fails](#a-server-component-that-renders-drawing-fails)
403
+ - [`Duplicate identifier 'text'` when importing from `/draw` and `/print`](#duplicate-identifier-text-when-importing-from-draw-and-print)
404
+
392
405
  **Content Security Policy**
393
406
 
394
407
  - [`[nxgt-material] html.cspNonce is set, but the CSP is in hash mode (the default): set nxgtMaterial({ csp: { mode: 'nonce' } }) or drop html.cspNonce.`](#nxgt-material-htmlcspnonce-is-set-but-the-csp-is-in-hash-mode-the-default-set-nxgtmaterial-csp--mode-nonce---or-drop-htmlcspnonce)
@@ -906,7 +919,7 @@ catalogue's own field messages pass.
906
919
  **Why:** it is drawn with `text-error`, the fill colour. The dark `--error` is
907
920
  kept dark enough for near-white text on a filled error surface, which makes it
908
921
  too dark to read as text on the dark surfaces. Error text has its own token,
909
- `--error-text`, lightened in the dark theme (5.4:1 on `--background`, 5.0:1 on
922
+ `--error-text`, lightened in the dark theme (5.9:1 on `--background`, 5.3:1 on
910
923
  `--card`).
911
924
  **Fix:** draw text with `text-error-text`; keep `text-error` for fills,
912
925
  borders and icons that stand alone. On a `Button` or `IconButton` drop the
@@ -947,6 +960,15 @@ An app that overrides `--primary` under `:root` and `.dark, [data-theme="dark"]`
947
960
  gets `--primary-text` for free; set `--primary-text` after it to choose another
948
961
  colour. On an element below the root, set `--color-primary-text`.
949
962
 
963
+ The same goes for a primary mark that is not text: a bar, a dot, a selected or
964
+ focused border, a ring or a spinner arc. In dark, `--primary` is only 2.1 to
965
+ 2.5:1 on the surfaces, under the 3:1 of WCAG 1.4.11. Draw it with
966
+ `bg-primary-indicator`, `border-primary-indicator`, `ring-primary-indicator` or
967
+ `stroke-primary-indicator`: `--primary-indicator` is `--primary` in light and
968
+ `--primary` mixed 25 % with white in dark (3.7:1 or more on `--background`,
969
+ `--card` and `--muted`). It follows an app's own `--primary`; set
970
+ `--primary-indicator` after it to choose another dark colour.
971
+
950
972
  ### `expectTapTargets` reports a checkbox, switch or chip remove button under 44px
951
973
 
952
974
  **When:** a phone project's `expectTapTargets` (`@nxgt/playwright`) lists a
@@ -6788,6 +6810,128 @@ import '@nxgt/material/backgrounds.css'; // every preset
6788
6810
 
6789
6811
  ---
6790
6812
 
6813
+ ## Drawing
6814
+
6815
+ How to use it is in the [Drawing guide](guides/drawing.md); the engine's own
6816
+ errors (`Invalid colour`, `Invalid path data`, `Unsupported scene version`,
6817
+ `[pdf] A drawing mask is not supported in a PDF: dropped.`) are in the
6818
+ [core troubleshooting](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/troubleshooting.md#drawing).
6819
+
6820
+ ### `A decorative drawing has an interactive node: give the Drawing a title instead`
6821
+
6822
+ **When:** a `Drawing` with `decorative` renders, or contains, a node with `onClick` (or any event), `interactive`, or an entry in `handlers`. It throws on the render.
6823
+ **Why:** `decorative` is `aria-hidden`, and a focusable node inside it would be reachable by keyboard and invisible to assistive technology.
6824
+ **Fix:** give the drawing a `title` (and each interactive node a `label`), or drop the events:
6825
+
6826
+ ```tsx
6827
+ <Drawing scene={board} title="Board columns" onNodeClick={open} />
6828
+ ```
6829
+
6830
+ ### `Unsafe drawing at <tag>: <reason>` from a scene or a JSX drawing
6831
+
6832
+ **When:** `Drawing`, `toSVG`, `compileScene` or `assertSafeDrawing` throws `Unsafe drawing at <image>: href must be http(s), relative or data:image (png, jpeg, gif, webp)` or another reason (`tag not allowed`, `style not allowed`, `<attribute> not finite`, `id used twice`...).
6833
+ **Why:** a strict CSP could not accept what the scene would write. From a scene loaded as JSON the cause is usually an `image` `href` (`javascript:`, `file:` or `data:image/svg+xml`), a `className` or node `id` with unusual characters, or a coordinate that is `NaN`.
6834
+ **Fix:** correct the value the message names. Check untrusted JSON before drawing it:
6835
+
6836
+ ```ts
6837
+ import { assertSafeDrawing } from '@nxgt/material/draw';
6838
+
6839
+ assertSafeDrawing(scene); // throws with the element and the reason
6840
+ ```
6841
+
6842
+ The reasons are listed in the [core troubleshooting](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/troubleshooting.md#unsafe-drawing-at-tag-reason).
6843
+
6844
+ ### `Tones "a" and "b" meet on one element: use two nodes in a group`
6845
+
6846
+ **When:** a node has `fill: tone('brand')` and `stroke: tone('accent')` where both are app tones.
6847
+ **Why:** an app tone is carried by one `data-tone` attribute on the element, so an element can have only one.
6848
+ **Fix:** one node per tone, in a group, or name a theme tone for one of them:
6849
+
6850
+ ```tsx
6851
+ group({}, [
6852
+ circle({ cx: 20, cy: 20, r: 14, fill: tone('brand', 'container') }),
6853
+ circle({ cx: 20, cy: 20, r: 14, fill: 'transparent', stroke: tone('accent') }),
6854
+ ]);
6855
+ ```
6856
+
6857
+ ### `No colour for "brand": pass palette: { "brand": "#rrggbb" }`
6858
+
6859
+ **When:** `toSVG` (the default `colors: 'literal'`) or a PDF meets an app tone or token that has no literal colour.
6860
+ **Why:** a standalone SVG has no theme to read, so every token needs a hex; only the theme's own roles have one.
6861
+ **Fix:** pass it, or use `colors: 'vars'` for an inline SVG:
6862
+
6863
+ ```ts
6864
+ toSVG(art, { palette: { brand: '#0a7c59' } });
6865
+ ```
6866
+
6867
+ ### `shapeOutline: unknown shape "x"; expected one of SHAPE_NAMES (…)`
6868
+
6869
+ **When:** `shape('cookie-99', …)` or `morph` is given a name the registry does not have.
6870
+ **Why:** a typo, or a name from another library. The message lists the valid ones.
6871
+ **Fix:** use a name of `SHAPE_NAMES`, with or without the `m3-` prefix:
6872
+
6873
+ ```ts
6874
+ import { SHAPE_NAMES, shape } from '@nxgt/material/draw';
6875
+
6876
+ shape('cookie-9', { x: 0, y: 0, w: 80, h: 80 }); // or 'm3-cookie-9'
6877
+ console.log(SHAPE_NAMES);
6878
+ ```
6879
+
6880
+ ### `drawingToPng: cannot read the image "<url>" (<reason>)…`
6881
+
6882
+ **When:** `drawingToPng` or `downloadDrawingPng` rejects, and the drawing holds an `image` with a URL.
6883
+ **Why:** an SVG drawn as an image loads nothing external, so the function fetches each image and inlines its bytes; the browser refused the fetch, almost always CORS. Nothing is left to taint the canvas: the failure is reported before it can happen.
6884
+ **Fix:** serve the image with `Access-Control-Allow-Origin`, or put a `data:image/png|jpeg|gif|webp` URL in the scene:
6885
+
6886
+ ```ts
6887
+ image({ href: 'data:image/png;base64,…', x: 0, y: 0, w: 64, h: 64 });
6888
+ ```
6889
+
6890
+ ### `drawingToPng: the canvas gave no PNG.` and its siblings
6891
+
6892
+ **When:** a PNG fails with one of `drawingToPng: the canvas gave no PNG.`, `drawingToPng: no 2D canvas in this browser.`, `drawingToPng: scale must be above 0.`, `drawingToPng: the drawing has no size.` or `drawingToPng: the browser could not draw the SVG.`.
6893
+ **Why:** respectively a canvas the browser refused to encode (usually far too large: `scale` times the drawing's size), no canvas support, a zero or negative `scale`, a live `<svg>` with no `width`, `height` or `viewBox`, and an SVG that does not load as an image.
6894
+ **Fix:** lower the `scale`, pass a positive one, give the `<svg>` a `viewBox`, or draw from the scene instead of the live element:
6895
+
6896
+ ```ts
6897
+ await drawingToPng(scene, { scale: 1 });
6898
+ ```
6899
+
6900
+ ### A drawing is black, or its role colours do nothing
6901
+
6902
+ **When:** a `Drawing` renders in black (or the browser's default colours), whatever its `fill="primary"`.
6903
+ **Why:** role colours are classes of `styles.css` (`nxgt-draw-f-primary`...); without the stylesheet nothing maps them. A standalone `toSVG` file does not use classes: it writes hex.
6904
+ **Fix:** import the stylesheet once in the app:
6905
+
6906
+ ```ts
6907
+ import '@nxgt/material/styles.css';
6908
+ ```
6909
+
6910
+ ### A Server Component that renders `Drawing` fails
6911
+
6912
+ **When:** a Next.js or RSC page imports `Drawing`, a primitive, `Sketch` or the animate hooks and the build reports that a hook or a context only works in a Client Component.
6913
+ **Why:** `@nxgt/material/draw` is its own entry whose built files carry no `'use client'` directive (the components use hooks).
6914
+ **Fix:** render it from a file of yours that starts with `'use client'`; the builders and `toSVG` can stay in the Server Component and pass the scene as a prop:
6915
+
6916
+ ```tsx
6917
+ // drawing-client.tsx
6918
+ 'use client';
6919
+ export { Drawing } from '@nxgt/material/draw';
6920
+ ```
6921
+
6922
+ ### `Duplicate identifier 'text'` when importing from `/draw` and `/print`
6923
+
6924
+ **When:** TypeScript reports `Duplicate identifier 'text'` (or `Import declaration conflicts with local declaration of 'text'`, or the same for `image`) in a file that imports from both entries.
6925
+ **Why:** `@nxgt/material/draw` and `@nxgt/material/print` each export a `text` and an `image`: the first builds a drawing node, the second a PDF block.
6926
+ **Fix:** rename one of them:
6927
+
6928
+ ```ts
6929
+ import { text as drawText } from '@nxgt/material/draw';
6930
+ import { text } from '@nxgt/material/print';
6931
+ ```
6932
+
6933
+ ---
6934
+
6791
6935
  ## Content Security Policy
6792
6936
 
6793
6937
  How to set up the policy is in the [Content Security Policy guide](guides/csp.md).
@@ -132,7 +132,7 @@
132
132
  z-index: 50;
133
133
  height: 0.25rem;
134
134
  pointer-events: none;
135
- background-color: var(--primary);
135
+ background-color: var(--primary-indicator);
136
136
  }
137
137
  transform-origin: 0 50%;
138
138
  animation: nx-scroll-progress linear both;
@@ -43,7 +43,7 @@
43
43
  }
44
44
 
45
45
  .column-resize-handle {
46
- background-color: var(--color-primary);
46
+ background-color: var(--color-primary-indicator);
47
47
  bottom: -2px;
48
48
  pointer-events: none;
49
49
  position: absolute;
@@ -124,7 +124,7 @@
124
124
  height: 0.55em;
125
125
  transform: scale(0);
126
126
  transition: 120ms transform ease-in-out;
127
- box-shadow: inset 1em 1em var(--color-primary);
127
+ box-shadow: inset 1em 1em var(--color-primary-indicator);
128
128
  }
129
129
  input[type="checkbox"]:checked::before {
130
130
  transform: scale(1);
@@ -196,3 +196,5 @@
196
196
  @import "../backgrounds/css/base.css";
197
197
  @import "../backgrounds/css/gradient.css";
198
198
  @import "../backgrounds/css/motion.css";
199
+ @import "./ui/widgets/drawing/drawing.css"; /* @nxgt/material/draw: role colours, focus ring */
200
+ @import "./ui/widgets/drawing/drawing-animate.css";
@@ -37,6 +37,11 @@
37
37
  border-color: color-mix(in oklab, var(--primary) 60%, transparent);
38
38
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary) 50%, transparent);
39
39
  }
40
+ /* Dark: the 60 % border is under 3:1 on the card (WCAG 1.4.11). */
41
+ :is(.dark, [data-theme="dark"])
42
+ [data-slot="compact-editor"][data-look="expressive"]:focus-within {
43
+ border-color: var(--primary-indicator);
44
+ }
40
45
  [data-slot="editor-field"][data-invalid]
41
46
  :is(
42
47
  [data-slot="compact-editor"],
@@ -115,7 +115,7 @@
115
115
  }
116
116
 
117
117
  blockquote {
118
- border-inline-start: 3px solid var(--primary);
118
+ border-inline-start: 3px solid var(--primary-indicator);
119
119
  padding-inline-start: 1.1em;
120
120
  color: var(--muted-foreground);
121
121
  font-family: var(--nxgt-prose-heading-font);
@@ -17,6 +17,11 @@
17
17
  the violet page (7.9:1 on a Card; the colour itself gives 6.8:1 on the
18
18
  page); the dark theme lifts it. */
19
19
  --primary-text: color-mix(in oklab, var(--primary) 96%, black);
20
+ /* Primary drawn as a non-text mark (WCAG 1.4.11, 3:1): an indicator bar or dot,
21
+ a selected or focused border, a ring, a spinner arc, a progress fill
22
+ (`bg-primary-indicator`, `border-primary-indicator`...). Light is --primary;
23
+ the dark theme lifts it. */
24
+ --primary-indicator: var(--primary);
20
25
  /* --secondary, --success and --info are 0.02 darker than before: on the
21
26
  violet page tint (--background, 97.3 %) their own 15 % tonal tint gave the
22
27
  text 4.3 to 4.5:1; now 4.7:1 or more on the page and on a Card. */
@@ -53,7 +58,9 @@
53
58
  --destructive: var(--error);
54
59
  --border: oklch(0.912 0.016 293.7);
55
60
  --input: oklch(0.912 0.016 293.7);
56
- --ring: oklch(0.704 0.04 291);
61
+ /* The focus indicator (WCAG 1.4.11, 3:1): 3.40:1 on --background, 3.69:1 on
62
+ --card, 3.23:1 on --muted. */
63
+ --ring: oklch(0.62 0.04 291);
57
64
  --chart-1: oklch(0.646 0.222 41.116);
58
65
  --chart-2: oklch(0.6 0.118 184.704);
59
66
  --chart-3: oklch(0.398 0.07 227.392);
@@ -86,7 +93,7 @@
86
93
  --sidebar-accent: oklch(0.955 0.012 292);
87
94
  --sidebar-accent-foreground: oklch(0.227 0.017 290);
88
95
  --sidebar-border: oklch(0.912 0.016 293.7);
89
- --sidebar-ring: oklch(0.704 0.04 291);
96
+ --sidebar-ring: oklch(0.62 0.04 291);
90
97
  }
91
98
 
92
99
  .dark,
@@ -102,6 +109,9 @@
102
109
  /* Mixed 60 % toward white (the tonal Button labels' recipe): readable as text
103
110
  on --background and --card. */
104
111
  --primary-text: color-mix(in oklab, var(--primary) 40%, white);
112
+ /* --primary itself is 2.5:1 on --background, 2.3:1 on --card and 2.1:1 on
113
+ --muted. Mixed 25 % toward white: 4.5:1, 4.1:1 and 3.7:1. */
114
+ --primary-indicator: color-mix(in oklab, var(--primary) 75%, white);
105
115
  /* Brighter than the light one: it is read as text on the dark surfaces (6.3:1
106
116
  on --background, 5.8:1 on --card; 4.6:1 on its own 15 % tonal tint over a
107
117
  Card). A filled secondary surface mixes it toward --background (the opaque
@@ -120,8 +130,10 @@
120
130
  --error: oklch(0.577 0.245 27.325);
121
131
  --error-foreground: oklch(0.995 0.002 291.3);
122
132
  /* --error stays the fill under near-white text; as text it would give only
123
- 3.9:1 on --background and 3.5:1 on --card. Lightened, 5.4 and 5.0:1. */
124
- --error-text: color-mix(in oklab, var(--error) 80%, white);
133
+ 3.9:1 on --background and 3.5:1 on --card. Lightened (76 % --error), 5.85
134
+ and 5.35:1, and 4.78:1 on a focused error menu item (bg-error/20 over the
135
+ popover; 4.45:1 at the former 80 %, which missed 4.5:1 once rendered). */
136
+ --error-text: color-mix(in oklab, var(--error) 76%, white);
125
137
  /* The light brights: on the dark surfaces they read as text and as the
126
138
  badge's 60% tint, which keeps its contrast under near-white text (success
127
139
  7.1:1, info 5.6:1, error 9.1:1). */
@@ -137,6 +149,7 @@
137
149
  --destructive: oklch(0.704 0.191 22.216);
138
150
  --border: oklch(0.301 0.025 291.4);
139
151
  --input: oklch(0.34 0.027 291.4);
152
+ /* 3.78:1 on --background, 3.45:1 on --card, 3.09:1 on --muted. */
140
153
  --ring: oklch(0.551 0.03 290);
141
154
  --chart-1: oklch(0.488 0.243 264.376);
142
155
  --chart-2: oklch(0.696 0.17 162.48);
@@ -49,8 +49,8 @@
49
49
  pointer-events: none;
50
50
  }
51
51
  [data-slot="period-card"][data-state="current"] {
52
- --card-bar: var(--primary);
53
- border-color: var(--primary);
52
+ --card-bar: var(--primary-indicator);
53
+ border-color: var(--primary-indicator);
54
54
  background: color-mix(in oklab, var(--primary) 7%, var(--card));
55
55
  }
56
56
  [data-slot="period-card"][data-state="past"] {
@@ -9,7 +9,7 @@
9
9
  }
10
10
 
11
11
  .pdf-highlight[data-current] {
12
- outline: 2px solid var(--color-primary);
12
+ outline: 2px solid var(--color-primary-indicator);
13
13
  outline-offset: 1px;
14
14
  }
15
15
 
@@ -0,0 +1,123 @@
1
+ /* Entry animations (`drawOn` and `appear` of @nxgt/material/draw/animate).
2
+ * A node carries `nxgt-draw-on` or `nxgt-draw-appear`, a delay step
3
+ * `nxgt-draw-d-<0..19>` and a duration `nxgt-draw-t-<s|m|l>`: never an inline
4
+ * style. The delay is the step times `--nxgt-draw-stagger` (60ms), set on the
5
+ * drawing or an ancestor to speed it up or slow it down. The animations fill
6
+ * both ways, so a replay (`useDrawingControls`) can find them and the last
7
+ * frame is the drawing itself: a stroke is traced with a dash as long as its
8
+ * path (`pathLength` 1), a fill fades in, a node fades and grows in. */
9
+ .nxgt-draw-t-s {
10
+ --nxgt-draw-dur: 300ms;
11
+ }
12
+ .nxgt-draw-t-m {
13
+ --nxgt-draw-dur: 600ms;
14
+ }
15
+ .nxgt-draw-t-l {
16
+ --nxgt-draw-dur: 1000ms;
17
+ }
18
+ .nxgt-draw-d-0 {
19
+ --nxgt-draw-i: 0;
20
+ }
21
+ .nxgt-draw-d-1 {
22
+ --nxgt-draw-i: 1;
23
+ }
24
+ .nxgt-draw-d-2 {
25
+ --nxgt-draw-i: 2;
26
+ }
27
+ .nxgt-draw-d-3 {
28
+ --nxgt-draw-i: 3;
29
+ }
30
+ .nxgt-draw-d-4 {
31
+ --nxgt-draw-i: 4;
32
+ }
33
+ .nxgt-draw-d-5 {
34
+ --nxgt-draw-i: 5;
35
+ }
36
+ .nxgt-draw-d-6 {
37
+ --nxgt-draw-i: 6;
38
+ }
39
+ .nxgt-draw-d-7 {
40
+ --nxgt-draw-i: 7;
41
+ }
42
+ .nxgt-draw-d-8 {
43
+ --nxgt-draw-i: 8;
44
+ }
45
+ .nxgt-draw-d-9 {
46
+ --nxgt-draw-i: 9;
47
+ }
48
+ .nxgt-draw-d-10 {
49
+ --nxgt-draw-i: 10;
50
+ }
51
+ .nxgt-draw-d-11 {
52
+ --nxgt-draw-i: 11;
53
+ }
54
+ .nxgt-draw-d-12 {
55
+ --nxgt-draw-i: 12;
56
+ }
57
+ .nxgt-draw-d-13 {
58
+ --nxgt-draw-i: 13;
59
+ }
60
+ .nxgt-draw-d-14 {
61
+ --nxgt-draw-i: 14;
62
+ }
63
+ .nxgt-draw-d-15 {
64
+ --nxgt-draw-i: 15;
65
+ }
66
+ .nxgt-draw-d-16 {
67
+ --nxgt-draw-i: 16;
68
+ }
69
+ .nxgt-draw-d-17 {
70
+ --nxgt-draw-i: 17;
71
+ }
72
+ .nxgt-draw-d-18 {
73
+ --nxgt-draw-i: 18;
74
+ }
75
+ .nxgt-draw-d-19 {
76
+ --nxgt-draw-i: 19;
77
+ }
78
+ .nxgt-draw-on,
79
+ .nxgt-draw-appear {
80
+ animation-duration: var(--nxgt-draw-dur, 600ms);
81
+ animation-delay: calc(var(--nxgt-draw-i, 0) * var(--nxgt-draw-stagger, 60ms));
82
+ animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
83
+ animation-fill-mode: both;
84
+ }
85
+ .nxgt-draw-on {
86
+ animation-name: nxgt-draw-trace, nxgt-draw-fill;
87
+ }
88
+ .nxgt-draw-appear {
89
+ animation-name: nxgt-draw-fade, nxgt-draw-grow;
90
+ transform-box: fill-box;
91
+ transform-origin: center;
92
+ }
93
+ @keyframes nxgt-draw-trace {
94
+ from {
95
+ stroke-dasharray: 1;
96
+ stroke-dashoffset: 1;
97
+ }
98
+ to {
99
+ stroke-dasharray: 1;
100
+ stroke-dashoffset: 0;
101
+ }
102
+ }
103
+ @keyframes nxgt-draw-fill {
104
+ from {
105
+ fill-opacity: 0;
106
+ }
107
+ }
108
+ @keyframes nxgt-draw-fade {
109
+ from {
110
+ opacity: 0;
111
+ }
112
+ }
113
+ @keyframes nxgt-draw-grow {
114
+ from {
115
+ scale: 0.92;
116
+ }
117
+ }
118
+ @media (prefers-reduced-motion: reduce) {
119
+ .nxgt-draw-on,
120
+ .nxgt-draw-appear {
121
+ animation: none;
122
+ }
123
+ }