@nextlyhq/ui 0.0.2-alpha.57 → 0.0.2-alpha.58

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.
@@ -6,8 +6,13 @@ declare const uiPreset: {
6
6
  overlay: string;
7
7
  "overlay-soft": string;
8
8
  "overlay-strong": string;
9
- border: string;
9
+ border: {
10
+ DEFAULT: string;
11
+ subtle: string;
12
+ strong: string;
13
+ };
10
14
  input: string;
15
+ "control-border": string;
11
16
  ring: string;
12
17
  background: string;
13
18
  foreground: string;
@@ -41,6 +46,37 @@ declare const uiPreset: {
41
46
  DEFAULT: string;
42
47
  foreground: string;
43
48
  };
49
+ highlight: {
50
+ DEFAULT: string;
51
+ foreground: string;
52
+ };
53
+ code: {
54
+ bg: string;
55
+ fg: string;
56
+ comment: string;
57
+ keyword: string;
58
+ string: string;
59
+ number: string;
60
+ function: string;
61
+ operator: string;
62
+ punctuation: string;
63
+ variable: string;
64
+ tag: string;
65
+ deleted: string;
66
+ inserted: string;
67
+ };
68
+ sidebar: {
69
+ DEFAULT: string;
70
+ background: string;
71
+ foreground: string;
72
+ primary: string;
73
+ "primary-foreground": string;
74
+ accent: string;
75
+ "accent-background": string;
76
+ "accent-foreground": string;
77
+ border: string;
78
+ ring: string;
79
+ };
44
80
  popover: {
45
81
  DEFAULT: string;
46
82
  foreground: string;
@@ -1,5 +1,5 @@
1
1
  // src/tailwind-preset.ts
2
- var statusScale = (base) => ({
2
+ var tintScale = (base) => ({
3
3
  50: `color-mix(in srgb, var(${base}), white 95%)`,
4
4
  100: `color-mix(in srgb, var(${base}), white 90%)`,
5
5
  200: `color-mix(in srgb, var(${base}), white 70%)`,
@@ -23,14 +23,24 @@ var uiPreset = {
23
23
  overlay: "var(--nx-overlay)",
24
24
  "overlay-soft": "var(--nx-overlay-soft)",
25
25
  "overlay-strong": "var(--nx-overlay-strong)",
26
- border: "var(--nx-border)",
26
+ border: {
27
+ DEFAULT: "var(--nx-border)",
28
+ subtle: "var(--nx-border-subtle)",
29
+ strong: "var(--nx-border-strong)"
30
+ },
27
31
  input: "var(--nx-input)",
32
+ // The boundary of a control that draws no fill of its own -- an
33
+ // unchecked checkbox, radio or switch. Separate from `input` because
34
+ // that one is the field border weight, which sits below the 3:1
35
+ // non-text minimum by design.
36
+ "control-border": "var(--nx-control-border)",
28
37
  ring: "var(--nx-ring)",
29
38
  background: "var(--nx-background)",
30
39
  foreground: "var(--nx-foreground)",
31
40
  primary: {
32
41
  DEFAULT: "var(--nx-primary)",
33
- foreground: "var(--nx-primary-foreground)"
42
+ foreground: "var(--nx-primary-foreground)",
43
+ ...tintScale("--nx-primary")
34
44
  },
35
45
  secondary: {
36
46
  DEFAULT: "var(--nx-secondary)",
@@ -41,18 +51,18 @@ var uiPreset = {
41
51
  // Saturated fill for solid buttons, distinct from the text-tuned base.
42
52
  solid: "var(--nx-destructive-solid)",
43
53
  foreground: "var(--nx-destructive-foreground)",
44
- ...statusScale("--nx-destructive")
54
+ ...tintScale("--nx-destructive")
45
55
  },
46
56
  success: {
47
57
  DEFAULT: "var(--nx-success)",
48
58
  solid: "var(--nx-success-solid)",
49
59
  foreground: "var(--nx-success-foreground)",
50
- ...statusScale("--nx-success")
60
+ ...tintScale("--nx-success")
51
61
  },
52
62
  warning: {
53
63
  DEFAULT: "var(--nx-warning)",
54
64
  foreground: "var(--nx-warning-foreground)",
55
- ...statusScale("--nx-warning")
65
+ ...tintScale("--nx-warning")
56
66
  },
57
67
  muted: {
58
68
  DEFAULT: "var(--nx-muted)",
@@ -60,7 +70,45 @@ var uiPreset = {
60
70
  },
61
71
  accent: {
62
72
  DEFAULT: "var(--nx-accent)",
63
- foreground: "var(--nx-accent-foreground)"
73
+ foreground: "var(--nx-accent-foreground)",
74
+ ...tintScale("--nx-accent")
75
+ },
76
+ // The editor's highlight mark, and the syntax colors, so authored
77
+ // content and code blocks theme like everything else.
78
+ highlight: {
79
+ DEFAULT: "var(--nx-highlight)",
80
+ foreground: "var(--nx-highlight-foreground)"
81
+ },
82
+ code: {
83
+ bg: "var(--nx-code-bg)",
84
+ fg: "var(--nx-code-fg)",
85
+ comment: "var(--nx-code-comment)",
86
+ keyword: "var(--nx-code-keyword)",
87
+ string: "var(--nx-code-string)",
88
+ number: "var(--nx-code-number)",
89
+ function: "var(--nx-code-function)",
90
+ operator: "var(--nx-code-operator)",
91
+ punctuation: "var(--nx-code-punctuation)",
92
+ variable: "var(--nx-code-variable)",
93
+ tag: "var(--nx-code-tag)",
94
+ deleted: "var(--nx-code-deleted)",
95
+ inserted: "var(--nx-code-inserted)"
96
+ },
97
+ // `sidebar` and `sidebar-background` are the same value under two
98
+ // names, and `sidebar-accent` and `sidebar-accent-background` likewise.
99
+ // Both pairs exist in the v4 block and both spellings are in use, so
100
+ // dropping either here would leave a live utility generating nothing.
101
+ sidebar: {
102
+ DEFAULT: "var(--nx-sidebar-background)",
103
+ background: "var(--nx-sidebar-background)",
104
+ foreground: "var(--nx-sidebar-foreground)",
105
+ primary: "var(--nx-sidebar-primary)",
106
+ "primary-foreground": "var(--nx-sidebar-primary-foreground)",
107
+ accent: "var(--nx-sidebar-accent)",
108
+ "accent-background": "var(--nx-sidebar-accent)",
109
+ "accent-foreground": "var(--nx-sidebar-accent-foreground)",
110
+ border: "var(--nx-sidebar-border)",
111
+ ring: "var(--nx-sidebar-ring)"
64
112
  },
65
113
  popover: {
66
114
  DEFAULT: "var(--nx-popover)",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"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","file":"tailwind-preset.mjs","sourcesContent":["/**\n * Tailwind CSS preset for @nextlyhq/ui components.\n *\n * Defines the CSS custom property contract that all UI components expect.\n * Consumers must provide the actual CSS variable values in their stylesheets.\n *\n * Usage (Tailwind v3, ESM):\n * // tailwind.config.ts\n * import uiPreset from \"@nextlyhq/ui/tailwind-preset\";\n * export default { presets: [uiPreset], ... };\n *\n * Usage (Tailwind v3, CommonJS):\n * // tailwind.config.js\n * const { uiPreset } = require(\"@nextlyhq/ui/tailwind-preset\");\n * module.exports = { presets: [uiPreset], ... };\n *\n * Usage (Tailwind v4 with @config):\n * Consumers define the equivalent @theme tokens in their CSS.\n * This file serves as the reference contract.\n */\n/**\n * The numbered shade scale for a status color, mixed from its base the same way\n * the v4 `@theme` block does, so v3 preset consumers get the same utilities the\n * components emit (`bg-destructive-700`, `bg-success-100 text-success-700`, ...).\n */\nconst statusScale = (base: string): Record<number, string> => ({\n 50: `color-mix(in srgb, var(${base}), white 95%)`,\n 100: `color-mix(in srgb, var(${base}), white 90%)`,\n 200: `color-mix(in srgb, var(${base}), white 70%)`,\n 300: `color-mix(in srgb, var(${base}), white 50%)`,\n 400: `color-mix(in srgb, var(${base}), white 30%)`,\n 500: `var(${base})`,\n 600: `color-mix(in srgb, var(${base}), black 10%)`,\n 700: `color-mix(in srgb, var(${base}), black 30%)`,\n 800: `color-mix(in srgb, var(${base}), black 50%)`,\n 900: `color-mix(in srgb, var(${base}), black 70%)`,\n 950: `color-mix(in srgb, var(${base}), black 85%)`,\n});\n\n/** @experimental */\nconst uiPreset = {\n theme: {\n extend: {\n colors: {\n // The modal scrim. Mapped here as well as in the v4 `@theme` block,\n // because this preset is the documented Tailwind v3 path and reads\n // none of that: without these two, `bg-overlay` generates no rule at\n // all and every backdrop is transparent rather than merely unthemed.\n overlay: \"var(--nx-overlay)\",\n \"overlay-soft\": \"var(--nx-overlay-soft)\",\n \"overlay-strong\": \"var(--nx-overlay-strong)\",\n border: \"var(--nx-border)\",\n input: \"var(--nx-input)\",\n ring: \"var(--nx-ring)\",\n background: \"var(--nx-background)\",\n foreground: \"var(--nx-foreground)\",\n primary: {\n DEFAULT: \"var(--nx-primary)\",\n foreground: \"var(--nx-primary-foreground)\",\n },\n secondary: {\n DEFAULT: \"var(--nx-secondary)\",\n foreground: \"var(--nx-secondary-foreground)\",\n },\n destructive: {\n DEFAULT: \"var(--nx-destructive)\",\n // Saturated fill for solid buttons, distinct from the text-tuned base.\n solid: \"var(--nx-destructive-solid)\",\n foreground: \"var(--nx-destructive-foreground)\",\n ...statusScale(\"--nx-destructive\"),\n },\n success: {\n DEFAULT: \"var(--nx-success)\",\n solid: \"var(--nx-success-solid)\",\n foreground: \"var(--nx-success-foreground)\",\n ...statusScale(\"--nx-success\"),\n },\n warning: {\n DEFAULT: \"var(--nx-warning)\",\n foreground: \"var(--nx-warning-foreground)\",\n ...statusScale(\"--nx-warning\"),\n },\n muted: {\n DEFAULT: \"var(--nx-muted)\",\n foreground: \"var(--nx-muted-foreground)\",\n },\n accent: {\n DEFAULT: \"var(--nx-accent)\",\n foreground: \"var(--nx-accent-foreground)\",\n },\n popover: {\n DEFAULT: \"var(--nx-popover)\",\n foreground: \"var(--nx-popover-foreground)\",\n },\n card: {\n DEFAULT: \"var(--nx-card)\",\n foreground: \"var(--nx-card-foreground)\",\n },\n },\n borderRadius: {\n lg: \"var(--radius)\",\n md: \"calc(var(--radius) - 2px)\",\n sm: \"calc(var(--radius) - 4px)\",\n },\n keyframes: {\n \"accordion-down\": {\n from: { height: \"0\" },\n to: { height: \"var(--radix-accordion-content-height)\" },\n },\n \"accordion-up\": {\n from: { height: \"var(--radix-accordion-content-height)\" },\n to: { height: \"0\" },\n },\n },\n animation: {\n \"accordion-down\": \"accordion-down 0.3s cubic-bezier(0.87, 0, 0.13, 1)\",\n \"accordion-up\": \"accordion-up 0.3s cubic-bezier(0.87, 0, 0.13, 1)\",\n },\n },\n },\n};\n\n// Exported both ways on purpose. CommonJS cannot represent a default-only\n// module without `module.exports =`, which makes the emitted declarations\n// disagree with the runtime shape (attw's FalseExportDefault); a named export\n// alongside it keeps `require()` and `import` consistent and typed.\nexport { uiPreset };\nexport default uiPreset;\n"]}
1
+ {"version":3,"sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"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","file":"tailwind-preset.mjs","sourcesContent":["/**\n * Tailwind CSS preset for @nextlyhq/ui components.\n *\n * Defines the CSS custom property contract that all UI components expect.\n * Consumers must provide the actual CSS variable values in their stylesheets.\n *\n * Usage (Tailwind v3, ESM):\n * // tailwind.config.ts\n * import uiPreset from \"@nextlyhq/ui/tailwind-preset\";\n * export default { presets: [uiPreset], ... };\n *\n * Usage (Tailwind v3, CommonJS):\n * // tailwind.config.js\n * const { uiPreset } = require(\"@nextlyhq/ui/tailwind-preset\");\n * module.exports = { presets: [uiPreset], ... };\n *\n * Usage (Tailwind v4 with @config):\n * Consumers define the equivalent @theme tokens in their CSS.\n * This file serves as the reference contract.\n */\n/**\n * The numbered shade scale for a color, mixed from its base the same way the v4\n * `@theme` block does, so v3 preset consumers get the same utilities the\n * components emit (`bg-destructive-700`, `bg-primary-500`, `border-primary-300`).\n *\n * Named for the derivation rather than for one caller: the status colors, the\n * primary and the accent all use it, and `@theme` derives all five identically.\n */\nconst tintScale = (base: string): Record<number, string> => ({\n 50: `color-mix(in srgb, var(${base}), white 95%)`,\n 100: `color-mix(in srgb, var(${base}), white 90%)`,\n 200: `color-mix(in srgb, var(${base}), white 70%)`,\n 300: `color-mix(in srgb, var(${base}), white 50%)`,\n 400: `color-mix(in srgb, var(${base}), white 30%)`,\n 500: `var(${base})`,\n 600: `color-mix(in srgb, var(${base}), black 10%)`,\n 700: `color-mix(in srgb, var(${base}), black 30%)`,\n 800: `color-mix(in srgb, var(${base}), black 50%)`,\n 900: `color-mix(in srgb, var(${base}), black 70%)`,\n 950: `color-mix(in srgb, var(${base}), black 85%)`,\n});\n\n/** @experimental */\nconst uiPreset = {\n theme: {\n extend: {\n colors: {\n // The modal scrim. Mapped here as well as in the v4 `@theme` block,\n // because this preset is the documented Tailwind v3 path and reads\n // none of that: without these two, `bg-overlay` generates no rule at\n // all and every backdrop is transparent rather than merely unthemed.\n overlay: \"var(--nx-overlay)\",\n \"overlay-soft\": \"var(--nx-overlay-soft)\",\n \"overlay-strong\": \"var(--nx-overlay-strong)\",\n border: {\n DEFAULT: \"var(--nx-border)\",\n subtle: \"var(--nx-border-subtle)\",\n strong: \"var(--nx-border-strong)\",\n },\n input: \"var(--nx-input)\",\n // The boundary of a control that draws no fill of its own -- an\n // unchecked checkbox, radio or switch. Separate from `input` because\n // that one is the field border weight, which sits below the 3:1\n // non-text minimum by design.\n \"control-border\": \"var(--nx-control-border)\",\n ring: \"var(--nx-ring)\",\n background: \"var(--nx-background)\",\n foreground: \"var(--nx-foreground)\",\n primary: {\n DEFAULT: \"var(--nx-primary)\",\n foreground: \"var(--nx-primary-foreground)\",\n ...tintScale(\"--nx-primary\"),\n },\n secondary: {\n DEFAULT: \"var(--nx-secondary)\",\n foreground: \"var(--nx-secondary-foreground)\",\n },\n destructive: {\n DEFAULT: \"var(--nx-destructive)\",\n // Saturated fill for solid buttons, distinct from the text-tuned base.\n solid: \"var(--nx-destructive-solid)\",\n foreground: \"var(--nx-destructive-foreground)\",\n ...tintScale(\"--nx-destructive\"),\n },\n success: {\n DEFAULT: \"var(--nx-success)\",\n solid: \"var(--nx-success-solid)\",\n foreground: \"var(--nx-success-foreground)\",\n ...tintScale(\"--nx-success\"),\n },\n warning: {\n DEFAULT: \"var(--nx-warning)\",\n foreground: \"var(--nx-warning-foreground)\",\n ...tintScale(\"--nx-warning\"),\n },\n muted: {\n DEFAULT: \"var(--nx-muted)\",\n foreground: \"var(--nx-muted-foreground)\",\n },\n accent: {\n DEFAULT: \"var(--nx-accent)\",\n foreground: \"var(--nx-accent-foreground)\",\n ...tintScale(\"--nx-accent\"),\n },\n // The editor's highlight mark, and the syntax colors, so authored\n // content and code blocks theme like everything else.\n highlight: {\n DEFAULT: \"var(--nx-highlight)\",\n foreground: \"var(--nx-highlight-foreground)\",\n },\n code: {\n bg: \"var(--nx-code-bg)\",\n fg: \"var(--nx-code-fg)\",\n comment: \"var(--nx-code-comment)\",\n keyword: \"var(--nx-code-keyword)\",\n string: \"var(--nx-code-string)\",\n number: \"var(--nx-code-number)\",\n function: \"var(--nx-code-function)\",\n operator: \"var(--nx-code-operator)\",\n punctuation: \"var(--nx-code-punctuation)\",\n variable: \"var(--nx-code-variable)\",\n tag: \"var(--nx-code-tag)\",\n deleted: \"var(--nx-code-deleted)\",\n inserted: \"var(--nx-code-inserted)\",\n },\n // `sidebar` and `sidebar-background` are the same value under two\n // names, and `sidebar-accent` and `sidebar-accent-background` likewise.\n // Both pairs exist in the v4 block and both spellings are in use, so\n // dropping either here would leave a live utility generating nothing.\n sidebar: {\n DEFAULT: \"var(--nx-sidebar-background)\",\n background: \"var(--nx-sidebar-background)\",\n foreground: \"var(--nx-sidebar-foreground)\",\n primary: \"var(--nx-sidebar-primary)\",\n \"primary-foreground\": \"var(--nx-sidebar-primary-foreground)\",\n accent: \"var(--nx-sidebar-accent)\",\n \"accent-background\": \"var(--nx-sidebar-accent)\",\n \"accent-foreground\": \"var(--nx-sidebar-accent-foreground)\",\n border: \"var(--nx-sidebar-border)\",\n ring: \"var(--nx-sidebar-ring)\",\n },\n popover: {\n DEFAULT: \"var(--nx-popover)\",\n foreground: \"var(--nx-popover-foreground)\",\n },\n card: {\n DEFAULT: \"var(--nx-card)\",\n foreground: \"var(--nx-card-foreground)\",\n },\n },\n borderRadius: {\n lg: \"var(--radius)\",\n md: \"calc(var(--radius) - 2px)\",\n sm: \"calc(var(--radius) - 4px)\",\n },\n keyframes: {\n \"accordion-down\": {\n from: { height: \"0\" },\n to: { height: \"var(--radix-accordion-content-height)\" },\n },\n \"accordion-up\": {\n from: { height: \"var(--radix-accordion-content-height)\" },\n to: { height: \"0\" },\n },\n },\n animation: {\n \"accordion-down\": \"accordion-down 0.3s cubic-bezier(0.87, 0, 0.13, 1)\",\n \"accordion-up\": \"accordion-up 0.3s cubic-bezier(0.87, 0, 0.13, 1)\",\n },\n },\n },\n};\n\n// Exported both ways on purpose. CommonJS cannot represent a default-only\n// module without `module.exports =`, which makes the emitted declarations\n// disagree with the runtime shape (attw's FalseExportDefault); a named export\n// alongside it keeps `require()` and `import` consistent and typed.\nexport { uiPreset };\nexport default uiPreset;\n"]}
package/dist/theme.css CHANGED
@@ -139,6 +139,7 @@
139
139
  --color-border-subtle: var(--nx-border-subtle);
140
140
  --color-border-strong: var(--nx-border-strong);
141
141
  --color-input: var(--nx-input);
142
+ --color-control-border: var(--nx-control-border);
142
143
  --color-ring: var(--nx-ring);
143
144
  --color-background: var(--nx-background);
144
145
  --color-foreground: var(--nx-foreground);
@@ -758,9 +759,12 @@
758
759
  * light enough to read as text on the page is too light for white text, so
759
760
  * success splits the fill and destructive flips its ink to black instead.
760
761
  * The on-color token is what varies, not just the fill. */
761
- /* Darkened from red-500; renders as #d92c32, ~4.8:1 as text on white. */
762
- --nx-destructive: oklch(0.5661 0.19 23.03);
763
- --nx-destructive-solid: oklch(0.5661 0.19 23.03);
762
+ /* red-500 itself, the reference palette's red. It is lighter than the 4.5:1
763
+ * text minimum allows; darkening it to pass reads as a different colour, and
764
+ * the palette is built around this one. `contrast/accepted.ts` records the
765
+ * affected pairings with their measured ratios. */
766
+ --nx-destructive: oklch(0.63 0.19 23.03);
767
+ --nx-destructive-solid: oklch(0.63 0.19 23.03);
764
768
  --nx-destructive-foreground: oklch(1 0 0);
765
769
  /* white */
766
770
  /* Darkened from green-600; renders as #00852c, ~4.8:1 as text on white. */
@@ -828,10 +832,33 @@
828
832
  --nx-overlay-soft: oklch(0 0 0 / 0.4);
829
833
  --nx-border-subtle: color-mix(in srgb, oklch(0.92 0 0), transparent 60%);
830
834
  --nx-border: oklch(0.92 0 0);
831
- /* Alpha raised so the composited strong border clears WCAG 3:1 / ~4:1 (was 2.11:1). */
832
- --nx-border-strong: oklch(0.58 0 0);
833
- /* Darkened so the field border clears WCAG 3:1 on the page (was 2.32:1). */
834
- --nx-input: oklch(0.6154 0 0);
835
+ /* These two sit BELOW the WCAG 1.4.11 3:1 minimum for a control boundary,
836
+ * deliberately, to keep the light border weight the palette is designed
837
+ * around. `contrast/accepted.ts` records each affected pairing with its
838
+ * measured ratio and is what keeps that visible rather than silent; the
839
+ * contrast suite reads that list, so neither token can drift further without
840
+ * failing. */
841
+ --nx-border-strong: color-mix(in srgb, oklch(0.92 0 0), black 25%);
842
+ --nx-input: oklch(0.94 0 0);
843
+ /* The boundary of a control that has NOTHING ELSE to identify it.
844
+ *
845
+ * A text field at --nx-input is identifiable without its edge: it carries a
846
+ * label, a placeholder, a value, and a focus ring the moment it is used. An
847
+ * unchecked checkbox or radio is only the box. Its border is not decoration
848
+ * around the control, it IS the control, so 1.4.11's 3:1 applies with nothing
849
+ * to fall back on -- and at the reference palette's border weight the box is
850
+ * effectively invisible.
851
+ *
852
+ * These were one token, which is why the reference weight could not be taken
853
+ * without losing the checkbox. They are two questions -- "how heavy is a field
854
+ * edge" and "can this control be seen at all" -- and they only shared an
855
+ * answer by accident. Splitting them is what lets the field borders match the
856
+ * reference while the controls stay identifiable.
857
+ *
858
+ * Solved to the LIGHTEST value that still clears 3:1 with the margin the
859
+ * contrast suite requires, against the darkest surface a control lands on, so
860
+ * it sits as close to the reference weight as the criterion permits. */
861
+ --nx-control-border: oklch(0.62 0 0);
835
862
  --nx-ring: var(--nx-primary);
836
863
 
837
864
  /* The base colour of the elevation ramp (--shadow-sm..xl, -inner-subtle,
@@ -871,19 +898,35 @@
871
898
  /* Sidebar specific colors - using Slate scale */
872
899
  --nx-sidebar-background: oklch(0.99 0 0);
873
900
  /* flat with the content surface; nav is separated by borders, not fill */
874
- /* Resting nav ink sits a step back from the active ink, so an active row
875
- * reads as emphasised by its INK as well as its fill. The chosen palette
876
- * used one value for both, which leaves the fill carrying the whole signal
877
- * -- and the fill is the part a low-contrast surface weakens first. */
878
- --nx-sidebar-foreground: oklch(0.44 0 0);
901
+ /* Resting and active nav ink are the same value in light mode: nav labels sit
902
+ * at body-text weight rather than a step back from it, which is the reference
903
+ * palette's arrangement.
904
+ *
905
+ * So the active row is marked by its FILL (--nx-sidebar-accent) TOGETHER WITH
906
+ * a font-weight change, and the pair is load-bearing rather than decorative.
907
+ * The fill alone is about 1.11:1, far under the 3:1 WCAG 1.4.11 asks of
908
+ * information identifying a component's state, and there is no exemption for
909
+ * a large filled region -- that is 1.4.3, and it is about text. The weight
910
+ * change is what satisfies the criterion, being a difference that is not a
911
+ * colour and so carries no ratio at all.
912
+ *
913
+ * Removing `data-[active=true]:font-medium` from the sidebar menu buttons
914
+ * therefore leaves the nav with no conforming active state, and
915
+ * `contrast/__tests__/sidebar-ink-hierarchy.test.ts` fails per variant if
916
+ * either loses it.
917
+ *
918
+ * Dark mode keeps the two-step ink relationship instead, where a near-white
919
+ * active ink on a dark row is what separates the states. */
920
+ --nx-sidebar-foreground: oklch(0 0 0);
879
921
  --nx-sidebar-primary: oklch(0 0 0);
880
922
  --nx-sidebar-primary-foreground: oklch(1 0 0);
881
923
  /* slate-50 */
882
924
  --nx-sidebar-accent: oklch(0.94 0 0);
883
925
  /* slate-100 */
884
926
  --nx-sidebar-accent-foreground: oklch(0 0 0);
885
- /* Darkened so the sidebar border clears WCAG 3:1 on its surface (was 1.23:1). */
886
- --nx-sidebar-border: oklch(0.63 0 0);
927
+ /* Below the 3:1 boundary minimum, deliberately, to match the border weight of
928
+ * the rest of the palette. Recorded in `contrast/accepted.ts`. */
929
+ --nx-sidebar-border: oklch(0.94 0 0);
887
930
  --nx-sidebar-ring: var(--nx-primary);
888
931
 
889
932
  /* Data table specific colors */
@@ -958,7 +1001,11 @@
958
1001
  --nx-destructive-foreground: oklch(0 0 0);
959
1002
  /* Lightened green so it reads as text at WCAG 4.5:1 on the dark page and on
960
1003
  * the lighter popover surface (was 4.17:1 on the page, 4.29:1 on a popover). */
961
- --nx-success: oklch(0.6 0.1921 149.58);
1004
+ /* Lightened a step so success ink clears 4.5:1 on --nx-muted with the margin
1005
+ * the suite requires. Muted is the binding surface here, being the lightest
1006
+ * of the dark surfaces this ink lands on; against card or page it was never
1007
+ * close. */
1008
+ --nx-success: oklch(0.61 0.1921 149.58);
962
1009
  /* Darker green fill so white on-color button text clears 4.5:1. */
963
1010
  --nx-success-solid: oklch(0.5225 0.1921 149.58);
964
1011
  --nx-success-foreground: oklch(1 0 0);
@@ -1010,6 +1057,9 @@
1010
1057
  /* Brightened so the field border clears WCAG 3:1 on the lighter popover
1011
1058
  * surface as well as the page and card (was 1.73:1 on a popover). */
1012
1059
  --nx-input: oklch(0.5445 0 0);
1060
+ /* Dark mode never gave up the boundary minimum, so the control border simply
1061
+ tracks the field border here and the split costs nothing. */
1062
+ --nx-control-border: oklch(0.5445 0 0);
1013
1063
  --nx-ring: var(--nx-primary);
1014
1064
 
1015
1065
  /* Black in dark mode too: the ramp reads as depth against the deep surfaces
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextlyhq/ui",
3
- "version": "0.0.2-alpha.57",
3
+ "version": "0.0.2-alpha.58",
4
4
  "description": "Nextly UI — Reusable React component library for Nextly plugins and custom admin UIs",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -95,13 +95,14 @@
95
95
  "rimraf": "^6.1.3",
96
96
  "tailwindcss": "^4.3.2",
97
97
  "tsup": "^8.5.0",
98
+ "tsx": "^4.20.5",
98
99
  "typescript": "^5.9.3",
99
100
  "vite-tsconfig-paths": "^5.1.4",
100
101
  "vitest": "^4.1.0",
101
- "@nextlyhq/admin-css": "0.0.2-alpha.57",
102
- "@nextlyhq/eslint-config": "0.0.2-alpha.57",
103
- "@nextlyhq/module-specifiers": "0.0.2-alpha.57",
104
- "@nextlyhq/tsconfig": "0.0.2-alpha.57"
102
+ "@nextlyhq/admin-css": "0.0.2-alpha.58",
103
+ "@nextlyhq/eslint-config": "0.0.2-alpha.58",
104
+ "@nextlyhq/module-specifiers": "0.0.2-alpha.58",
105
+ "@nextlyhq/tsconfig": "0.0.2-alpha.58"
105
106
  },
106
107
  "dependencies": {
107
108
  "@radix-ui/react-accordion": "^1.2.12",
@@ -147,9 +148,9 @@
147
148
  "author": "Nextly <contact@nextlyhq.com> (https://nextlyhq.com)",
148
149
  "scripts": {
149
150
  "build": "pnpm build:js && node scripts/build-css.mjs",
150
- "build:js": "rimraf dist && tsup && tsup --config tsup.server-safe.config.ts && node scripts/check-client-directive.mjs && node scripts/check-server-safe-artifacts.mjs",
151
+ "build:js": "rimraf dist && tsup && tsup --config tsup.server-safe.config.ts && tsx scripts/check-client-directive.ts && tsx scripts/check-server-safe-artifacts.ts",
151
152
  "build:css": "node scripts/build-css.mjs",
152
- "dev": "tsup --watch & tsup --config tsup.server-safe.config.ts --watch & wait",
153
+ "dev": "node scripts/dev.mjs",
153
154
  "check-types": "tsc --noEmit && tsc --noEmit -p tsconfig.tests.json",
154
155
  "lint": "eslint . --max-warnings 0",
155
156
  "lint:fix": "eslint . --fix",