@nextlyhq/ui 0.0.2-alpha.35 → 0.0.2-alpha.39

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.
@@ -0,0 +1,80 @@
1
+ declare const uiPreset: {
2
+ theme: {
3
+ extend: {
4
+ colors: {
5
+ border: string;
6
+ input: string;
7
+ ring: string;
8
+ background: string;
9
+ foreground: string;
10
+ primary: {
11
+ DEFAULT: string;
12
+ foreground: string;
13
+ };
14
+ secondary: {
15
+ DEFAULT: string;
16
+ foreground: string;
17
+ };
18
+ destructive: {
19
+ DEFAULT: string;
20
+ solid: string;
21
+ foreground: string;
22
+ };
23
+ success: {
24
+ DEFAULT: string;
25
+ solid: string;
26
+ foreground: string;
27
+ };
28
+ warning: {
29
+ DEFAULT: string;
30
+ foreground: string;
31
+ };
32
+ muted: {
33
+ DEFAULT: string;
34
+ foreground: string;
35
+ };
36
+ accent: {
37
+ DEFAULT: string;
38
+ foreground: string;
39
+ };
40
+ popover: {
41
+ DEFAULT: string;
42
+ foreground: string;
43
+ };
44
+ card: {
45
+ DEFAULT: string;
46
+ foreground: string;
47
+ };
48
+ };
49
+ borderRadius: {
50
+ lg: string;
51
+ md: string;
52
+ sm: string;
53
+ };
54
+ keyframes: {
55
+ "accordion-down": {
56
+ from: {
57
+ height: string;
58
+ };
59
+ to: {
60
+ height: string;
61
+ };
62
+ };
63
+ "accordion-up": {
64
+ from: {
65
+ height: string;
66
+ };
67
+ to: {
68
+ height: string;
69
+ };
70
+ };
71
+ };
72
+ animation: {
73
+ "accordion-down": string;
74
+ "accordion-up": string;
75
+ };
76
+ };
77
+ };
78
+ };
79
+
80
+ export { uiPreset as default, uiPreset };
@@ -0,0 +1,80 @@
1
+ declare const uiPreset: {
2
+ theme: {
3
+ extend: {
4
+ colors: {
5
+ border: string;
6
+ input: string;
7
+ ring: string;
8
+ background: string;
9
+ foreground: string;
10
+ primary: {
11
+ DEFAULT: string;
12
+ foreground: string;
13
+ };
14
+ secondary: {
15
+ DEFAULT: string;
16
+ foreground: string;
17
+ };
18
+ destructive: {
19
+ DEFAULT: string;
20
+ solid: string;
21
+ foreground: string;
22
+ };
23
+ success: {
24
+ DEFAULT: string;
25
+ solid: string;
26
+ foreground: string;
27
+ };
28
+ warning: {
29
+ DEFAULT: string;
30
+ foreground: string;
31
+ };
32
+ muted: {
33
+ DEFAULT: string;
34
+ foreground: string;
35
+ };
36
+ accent: {
37
+ DEFAULT: string;
38
+ foreground: string;
39
+ };
40
+ popover: {
41
+ DEFAULT: string;
42
+ foreground: string;
43
+ };
44
+ card: {
45
+ DEFAULT: string;
46
+ foreground: string;
47
+ };
48
+ };
49
+ borderRadius: {
50
+ lg: string;
51
+ md: string;
52
+ sm: string;
53
+ };
54
+ keyframes: {
55
+ "accordion-down": {
56
+ from: {
57
+ height: string;
58
+ };
59
+ to: {
60
+ height: string;
61
+ };
62
+ };
63
+ "accordion-up": {
64
+ from: {
65
+ height: string;
66
+ };
67
+ to: {
68
+ height: string;
69
+ };
70
+ };
71
+ };
72
+ animation: {
73
+ "accordion-down": string;
74
+ "accordion-up": string;
75
+ };
76
+ };
77
+ };
78
+ };
79
+
80
+ export { uiPreset as default, uiPreset };
@@ -0,0 +1,93 @@
1
+ // src/tailwind-preset.ts
2
+ var statusScale = (base) => ({
3
+ 50: `color-mix(in srgb, var(${base}), white 95%)`,
4
+ 100: `color-mix(in srgb, var(${base}), white 90%)`,
5
+ 200: `color-mix(in srgb, var(${base}), white 70%)`,
6
+ 300: `color-mix(in srgb, var(${base}), white 50%)`,
7
+ 400: `color-mix(in srgb, var(${base}), white 30%)`,
8
+ 500: `var(${base})`,
9
+ 600: `color-mix(in srgb, var(${base}), black 10%)`,
10
+ 700: `color-mix(in srgb, var(${base}), black 30%)`,
11
+ 800: `color-mix(in srgb, var(${base}), black 50%)`,
12
+ 900: `color-mix(in srgb, var(${base}), black 70%)`,
13
+ 950: `color-mix(in srgb, var(${base}), black 85%)`
14
+ });
15
+ var uiPreset = {
16
+ theme: {
17
+ extend: {
18
+ colors: {
19
+ border: "var(--nx-border)",
20
+ input: "var(--nx-input)",
21
+ ring: "var(--nx-ring)",
22
+ background: "var(--nx-background)",
23
+ foreground: "var(--nx-foreground)",
24
+ primary: {
25
+ DEFAULT: "var(--nx-primary)",
26
+ foreground: "var(--nx-primary-foreground)"
27
+ },
28
+ secondary: {
29
+ DEFAULT: "var(--nx-secondary)",
30
+ foreground: "var(--nx-secondary-foreground)"
31
+ },
32
+ destructive: {
33
+ DEFAULT: "var(--nx-destructive)",
34
+ // Saturated fill for solid buttons, distinct from the text-tuned base.
35
+ solid: "var(--nx-destructive-solid)",
36
+ foreground: "var(--nx-destructive-foreground)",
37
+ ...statusScale("--nx-destructive")
38
+ },
39
+ success: {
40
+ DEFAULT: "var(--nx-success)",
41
+ solid: "var(--nx-success-solid)",
42
+ foreground: "var(--nx-success-foreground)",
43
+ ...statusScale("--nx-success")
44
+ },
45
+ warning: {
46
+ DEFAULT: "var(--nx-warning)",
47
+ foreground: "var(--nx-warning-foreground)",
48
+ ...statusScale("--nx-warning")
49
+ },
50
+ muted: {
51
+ DEFAULT: "var(--nx-muted)",
52
+ foreground: "var(--nx-muted-foreground)"
53
+ },
54
+ accent: {
55
+ DEFAULT: "var(--nx-accent)",
56
+ foreground: "var(--nx-accent-foreground)"
57
+ },
58
+ popover: {
59
+ DEFAULT: "var(--nx-popover)",
60
+ foreground: "var(--nx-popover-foreground)"
61
+ },
62
+ card: {
63
+ DEFAULT: "var(--nx-card)",
64
+ foreground: "var(--nx-card-foreground)"
65
+ }
66
+ },
67
+ borderRadius: {
68
+ lg: "var(--radius)",
69
+ md: "calc(var(--radius) - 2px)",
70
+ sm: "calc(var(--radius) - 4px)"
71
+ },
72
+ keyframes: {
73
+ "accordion-down": {
74
+ from: { height: "0" },
75
+ to: { height: "var(--radix-accordion-content-height)" }
76
+ },
77
+ "accordion-up": {
78
+ from: { height: "var(--radix-accordion-content-height)" },
79
+ to: { height: "0" }
80
+ }
81
+ },
82
+ animation: {
83
+ "accordion-down": "accordion-down 0.3s cubic-bezier(0.87, 0, 0.13, 1)",
84
+ "accordion-up": "accordion-up 0.3s cubic-bezier(0.87, 0, 0.13, 1)"
85
+ }
86
+ }
87
+ }
88
+ };
89
+ var tailwind_preset_default = uiPreset;
90
+
91
+ export { tailwind_preset_default as default, uiPreset };
92
+ //# sourceMappingURL=tailwind-preset.mjs.map
93
+ //# sourceMappingURL=tailwind-preset.mjs.map
@@ -0,0 +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\nconst uiPreset = {\n theme: {\n extend: {\n colors: {\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"]}
package/dist/theme.css CHANGED
@@ -177,6 +177,9 @@
177
177
  --color-destructive-800: color-mix(in srgb, var(--nx-destructive), black 50%);
178
178
  --color-destructive-900: color-mix(in srgb, var(--nx-destructive), black 70%);
179
179
  --color-destructive-950: color-mix(in srgb, var(--nx-destructive), black 85%);
180
+ /* Saturated fill for solid buttons; distinct from the text-tuned base so
181
+ * white on-color text stays AA in both modes. */
182
+ --color-destructive-solid: var(--nx-destructive-solid);
180
183
  --color-destructive-foreground: var(--nx-destructive-foreground);
181
184
 
182
185
  --color-success: var(--nx-success);
@@ -191,6 +194,8 @@
191
194
  --color-success-800: color-mix(in srgb, var(--nx-success), black 50%);
192
195
  --color-success-900: color-mix(in srgb, var(--nx-success), black 70%);
193
196
  --color-success-950: color-mix(in srgb, var(--nx-success), black 85%);
197
+ /* Saturated fill for solid usage; distinct from the text-tuned base. */
198
+ --color-success-solid: var(--nx-success-solid);
194
199
  --color-success-foreground: var(--nx-success-foreground);
195
200
 
196
201
  --color-warning: var(--nx-warning);
@@ -620,24 +625,34 @@
620
625
 
621
626
  /* Utility colors */
622
627
  --nx-muted: oklch(0.9696 0 0);
623
- --nx-muted-foreground: oklch(0.5547 0.0407 257.44);
628
+ /* Darkened so muted text clears WCAG 4.5:1 on the muted surface (was 4.35:1). */
629
+ --nx-muted-foreground: oklch(0.54 0.0407 257.44);
624
630
  /* slate-500 */
625
631
  --nx-accent: oklch(0.5547 0.0407 257.44);
626
632
  /* Slate-500 per user request #64748b */
627
633
  --nx-accent-foreground: oklch(1 0 0);
628
634
  /* white */
629
635
 
630
- /* State colors - WCAG 2.2 AA Compliant */
631
- --nx-destructive: oklch(0.6368 0.2078 25.33);
632
- /* red-500 (#ef4444) */
636
+ /* State colors - WCAG 2.2 AA Compliant.
637
+ * Each status has two roles kept as separate tokens, the industry-standard
638
+ * split (Radix/Primer): the base token is the readable TEXT color used by
639
+ * `text-destructive` and derived shades, and `-solid` is the saturated FILL
640
+ * used by buttons under white on-color text. One value cannot serve both in
641
+ * dark mode (a fill dark enough for white text is too dark to read as text
642
+ * on the page), so they diverge there. */
643
+ /* Darkened from red-500; renders as #d92c32, ~4.8:1 as text on white. */
644
+ --nx-destructive: oklch(0.5778 0.2078 25.33);
645
+ --nx-destructive-solid: oklch(0.5778 0.2078 25.33);
633
646
  --nx-destructive-foreground: oklch(1 0 0);
634
647
  /* white */
635
- --nx-success: oklch(0.6273 0.17 149.2);
636
- /* green-600 (#16a34a) - AA compliant */
648
+ /* Darkened from green-600; renders as #00852c, ~4.8:1 as text on white. */
649
+ --nx-success: oklch(0.53 0.17 149.2);
650
+ --nx-success-solid: oklch(0.53 0.17 149.2);
637
651
  --nx-success-foreground: oklch(1 0 0);
638
652
  /* white */
639
- --nx-warning: oklch(0.7686 0.1646 70.11);
640
- /* amber-500 (#f59e0b) */
653
+ /* Darkened from amber-500 so it reads as text on white at WCAG 4.5:1 (was
654
+ * 2.15:1); its light shades still tint badge/alert surfaces. */
655
+ --nx-warning: oklch(0.565 0.1646 70.11);
641
656
  --nx-warning-foreground: oklch(0.2079 0.0399 265.73);
642
657
  /* slate-900 - REQUIRED for AA contrast (warning-500 on white = 3.4:1, fails AA) */
643
658
 
@@ -656,13 +671,15 @@
656
671
  * held above 4.5:1 on --nx-code-bg since code is small text. */
657
672
  --nx-code-bg: oklch(0.9761 0.0035 247.86);
658
673
  --nx-code-fg: oklch(0.2079 0.0399 265.73);
659
- --nx-code-comment: oklch(0.5547 0.0407 257.44);
674
+ /* Darkened slightly to clear WCAG 4.5:1 on the code surface (was 4.44:1). */
675
+ --nx-code-comment: oklch(0.541 0.0407 257.44);
660
676
  --nx-code-keyword: oklch(0.4882 0.2172 303.9);
661
677
  --nx-code-string: oklch(0.4478 0.1189 152.1);
662
678
  --nx-code-number: oklch(0.5106 0.1518 44.2);
663
679
  --nx-code-function: oklch(0.4757 0.1444 254.6);
664
680
  --nx-code-operator: oklch(0.4936 0.1418 8.4);
665
- --nx-code-punctuation: oklch(0.5547 0.0407 257.44);
681
+ /* Darkened slightly to clear WCAG 4.5:1 on the code surface (was 4.44:1). */
682
+ --nx-code-punctuation: oklch(0.541 0.0407 257.44);
666
683
  --nx-code-variable: oklch(0.4694 0.1173 62.3);
667
684
  --nx-code-tag: oklch(0.5054 0.1905 27.5);
668
685
  --nx-code-deleted: oklch(0.5054 0.1905 27.5);
@@ -673,11 +690,12 @@
673
690
  * subtle = decorative dividers/cards; default = tables/groups;
674
691
  * strong = interactive control edges (inputs/checkbox) for prominence. */
675
692
  --nx-border-subtle: oklch(0 0 0 / 0.08);
676
- --nx-border: oklch(0 0 0 / 0.13);
677
- --nx-border-strong: oklch(0 0 0 / 0.3);
678
- --nx-input: oklch(
679
- 0.7384 0.0116 286.11
680
- ); /* zinc-400: stronger visible field border (was zinc-300, too faint) */
693
+ /* Alpha raised so the composited border clears WCAG 3:1 on the page (was 1.35:1). */
694
+ --nx-border: oklch(0 0 0 / 0.445);
695
+ /* Alpha raised so the composited strong border clears WCAG 3:1 / ~4:1 (was 2.11:1). */
696
+ --nx-border-strong: oklch(0 0 0 / 0.502);
697
+ /* Darkened so the field border clears WCAG 3:1 on the page (was 2.32:1). */
698
+ --nx-input: oklch(0.6454 0.0116 286.11);
681
699
  --nx-ring: var(--nx-primary);
682
700
 
683
701
  /* Chart colors for data visualization */
@@ -720,7 +738,8 @@
720
738
  /* slate-100 */
721
739
  --nx-sidebar-accent-foreground: oklch(0.2079 0.0399 265.73);
722
740
  /* slate-900 */
723
- --nx-sidebar-border: oklch(0.929 0.0126 255.53);
741
+ /* Darkened so the sidebar border clears WCAG 3:1 on its surface (was 1.23:1). */
742
+ --nx-sidebar-border: oklch(0.6446 0.0126 255.53);
724
743
  /* slate-200 */
725
744
  --nx-sidebar-ring: var(--nx-primary);
726
745
 
@@ -746,7 +765,10 @@
746
765
  * read as distinct surfaces in dark mode instead of merging into the page */
747
766
  --nx-card-foreground: oklch(0.9838 0.0035 247.86);
748
767
  /* slate-50 */
749
- --nx-popover: oklch(0.369 0.0249 257.44);
768
+ /* Darkened toward the card elevation so borders, inputs, and muted text
769
+ * placed on a popover clear their WCAG minimums (border was 2.85:1, input
770
+ * 1.73:1); the surface was too light for its own contents. */
771
+ --nx-popover: oklch(0.24 0.0249 257.44);
750
772
  /* slate-800 */
751
773
  --nx-popover-foreground: oklch(0.9838 0.0035 247.86);
752
774
  /* slate-50 */
@@ -773,13 +795,23 @@
773
795
  --nx-accent-foreground: oklch(1 0 0);
774
796
  /* white */
775
797
 
776
- /* State colors - WCAG 2.2 AA Compliant (Dark Mode) */
777
- --nx-destructive: oklch(0.6368 0.2078 25.33);
778
- /* red-500 */
798
+ /* State colors - WCAG 2.2 AA Compliant (Dark Mode).
799
+ * The base token is lightened from the fill color so it reads as text on the
800
+ * dark page at 4.5:1; `-solid` keeps the darker fill so white on-color text
801
+ * on buttons still clears 4.5:1. Warning is already light enough to read on
802
+ * dark, so its base is unchanged here. */
803
+ /* Lightened red so it reads as text at WCAG 4.5:1 on the dark page and on the
804
+ * lighter popover surface (was 4.17:1 on the page, 4.25:1 on a popover). */
805
+ --nx-destructive: oklch(0.65 0.2078 25.33);
806
+ /* Darker red fill so white on-color button text clears 4.5:1. */
807
+ --nx-destructive-solid: oklch(0.5778 0.2078 25.33);
779
808
  --nx-destructive-foreground: oklch(1 0 0);
780
809
  /* white */
781
- --nx-success: oklch(0.7229 0.1921 149.58);
782
- /* green-500 - brighter for dark mode */
810
+ /* Lightened green so it reads as text at WCAG 4.5:1 on the dark page and on
811
+ * the lighter popover surface (was 4.17:1 on the page, 4.29:1 on a popover). */
812
+ --nx-success: oklch(0.6 0.1921 149.58);
813
+ /* Darker green fill so white on-color button text clears 4.5:1. */
814
+ --nx-success-solid: oklch(0.5225 0.1921 149.58);
783
815
  --nx-success-foreground: oklch(1 0 0);
784
816
  /* white */
785
817
  --nx-warning: oklch(0.7686 0.1646 70.11);
@@ -815,11 +847,13 @@
815
847
  * Recomputed as white-over-dark alpha (not a flipped hex) so dividers read
816
848
  * on the deep background without glowing. Was ~1.2:1 (too faint). */
817
849
  --nx-border-subtle: oklch(1 0 0 / 0.08);
818
- --nx-border: oklch(1 0 0 / 0.13);
819
- --nx-border-strong: oklch(1 0 0 / 0.34);
820
- --nx-input: oklch(
821
- 0.4987 0.0128 285.92
822
- ); /* stronger visible field border (was zinc-700, too faint) */
850
+ /* Alpha raised so the composited border clears WCAG 3:1 on the page (was 1.36:1). */
851
+ --nx-border: oklch(1 0 0 / 0.366);
852
+ /* Alpha raised so the composited strong border clears WCAG 3:1 / ~4:1 (was 3.00:1). */
853
+ --nx-border-strong: oklch(1 0 0 / 0.418);
854
+ /* Brightened so the field border clears WCAG 3:1 on the lighter popover
855
+ * surface as well as the page and card (was 1.73:1 on a popover). */
856
+ --nx-input: oklch(0.54 0.0128 285.92);
823
857
  --nx-ring: var(--nx-primary);
824
858
 
825
859
  /* Chart colors for data visualization (same as light mode) */
@@ -846,8 +880,8 @@
846
880
  /* slate-800 */
847
881
  --nx-sidebar-accent-foreground: oklch(0.9838 0.0035 247.86);
848
882
  /* slate-50 */
849
- --nx-sidebar-border: oklch(0.2321 0.0129 257.43);
850
- /* subtle slate-900 */
883
+ /* Lightened so the sidebar border clears WCAG 3:1 on its surface (was 1.19:1). */
884
+ --nx-sidebar-border: oklch(0.4975 0.0129 257.43);
851
885
  --nx-sidebar-ring: var(--nx-primary);
852
886
 
853
887
  /* Data table specific colors - dark mode */
package/dist/utils.cjs ADDED
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var clsx = require('clsx');
4
+ var tailwindMerge = require('tailwind-merge');
5
+
6
+ // src/lib/utils.ts
7
+ function cn(...inputs) {
8
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
9
+ }
10
+
11
+ exports.cn = cn;
12
+ //# sourceMappingURL=utils.cjs.map
13
+ //# sourceMappingURL=utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/utils.ts"],"names":["twMerge","clsx"],"mappings":";;;;;;AAeO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAOA,qBAAA,CAAQC,SAAA,CAAK,MAAM,CAAC,CAAA;AAC7B","file":"utils.cjs","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge class names with Tailwind CSS support.\n *\n * Combines clsx for conditional class names with tailwind-merge\n * to properly handle Tailwind CSS class conflicts.\n *\n * @example\n * ```typescript\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4'\n * cn('text-muted-foreground', condition && 'text-foreground')\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import { ClassValue } from 'clsx';
2
+
3
+ /**
4
+ * Utility function to merge class names with Tailwind CSS support.
5
+ *
6
+ * Combines clsx for conditional class names with tailwind-merge
7
+ * to properly handle Tailwind CSS class conflicts.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * cn('px-2 py-1', 'px-4') // => 'py-1 px-4'
12
+ * cn('text-muted-foreground', condition && 'text-foreground')
13
+ * ```
14
+ */
15
+ declare function cn(...inputs: ClassValue[]): string;
16
+
17
+ export { cn };
@@ -0,0 +1,17 @@
1
+ import { ClassValue } from 'clsx';
2
+
3
+ /**
4
+ * Utility function to merge class names with Tailwind CSS support.
5
+ *
6
+ * Combines clsx for conditional class names with tailwind-merge
7
+ * to properly handle Tailwind CSS class conflicts.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * cn('px-2 py-1', 'px-4') // => 'py-1 px-4'
12
+ * cn('text-muted-foreground', condition && 'text-foreground')
13
+ * ```
14
+ */
15
+ declare function cn(...inputs: ClassValue[]): string;
16
+
17
+ export { cn };
package/dist/utils.mjs ADDED
@@ -0,0 +1,11 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ // src/lib/utils.ts
5
+ function cn(...inputs) {
6
+ return twMerge(clsx(inputs));
7
+ }
8
+
9
+ export { cn };
10
+ //# sourceMappingURL=utils.mjs.map
11
+ //# sourceMappingURL=utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/utils.ts"],"names":[],"mappings":";;;;AAeO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B","file":"utils.mjs","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge class names with Tailwind CSS support.\n *\n * Combines clsx for conditional class names with tailwind-merge\n * to properly handle Tailwind CSS class conflicts.\n *\n * @example\n * ```typescript\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4'\n * cn('text-muted-foreground', condition && 'text-foreground')\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"]}
@@ -20,8 +20,8 @@ points:
20
20
  | `@nextlyhq/ui/styles.css` | Pre-compiled bundle (Tailwind + tokens + reset). Zero config. | Standalone UI, or a quick embed with no build wiring. |
21
21
 
22
22
  **Inside a Nextly admin plugin you usually need neither.** The admin already loads
23
- `@nextlyhq/admin/style.css`, which defines every token scoped to `.adminapp` (and
24
- `.adminapp.dark` for dark mode). Plugin admin views render inside that scope, so the tokens
23
+ `@nextlyhq/admin/style.css`, which defines every token scoped to `.nextly-admin` (and
24
+ `.nextly-admin.dark` for dark mode). Plugin admin views render inside that scope, so the tokens
25
25
  are already on the page. Your plugin CSS should **consume** those tokens, never redefine
26
26
  them.
27
27
 
@@ -36,52 +36,52 @@ Tokens are **complete color values** (OKLCH). Reference them directly:
36
36
 
37
37
  ```css
38
38
  .my-panel {
39
- background: var(--card);
40
- color: var(--foreground);
41
- border: 1px solid var(--border);
39
+ background: var(--nx-card);
40
+ color: var(--nx-foreground);
41
+ border: 1px solid var(--nx-border);
42
42
  border-radius: var(--radius);
43
43
  }
44
44
  ```
45
45
 
46
46
  ### Never wrap a token in `hsl()` / `rgb()`
47
47
 
48
- Older Nextly builds stored tokens as bare HSL channels (`--primary: 221 83% 53%`) and code
49
- wrapped them: `hsl(var(--primary))`. **That convention is gone.** Tokens are now full colors
50
- (`--primary: oklch(0 0 0)`), so `hsl(var(--primary))` becomes `hsl(oklch(0 0 0))`, which is
48
+ Older Nextly builds stored tokens as bare HSL channels (`--nx-primary: 221 83% 53%`) and code
49
+ wrapped them: `hsl(var(--nx-primary))`. **That convention is gone.** Tokens are now full colors
50
+ (`--nx-primary: oklch(0 0 0)`), so `hsl(var(--nx-primary))` becomes `hsl(oklch(0 0 0))`, which is
51
51
  invalid CSS — the browser drops the declaration and your element loses its color. Always:
52
52
 
53
53
  ```css
54
54
  /* ✗ wrong — produces hsl(oklch(...)), silently dropped */
55
- color: hsl(var(--foreground));
55
+ color: hsl(var(--nx-foreground));
56
56
 
57
57
  /* ✓ right */
58
- color: var(--foreground);
58
+ color: var(--nx-foreground);
59
59
  ```
60
60
 
61
61
  ### Alpha / transparency
62
62
 
63
- You can no longer do `hsl(var(--primary) / 0.1)`. Use `color-mix`:
63
+ You can no longer do `hsl(var(--nx-primary) / 0.1)`. Use `color-mix`:
64
64
 
65
65
  ```css
66
66
  /* 10% primary over transparent */
67
- background: color-mix(in srgb, var(--primary) 10%, transparent);
67
+ background: color-mix(in srgb, var(--nx-primary) 10%, transparent);
68
68
 
69
69
  /* a subtle hover tint from the foreground */
70
- background: color-mix(in srgb, var(--foreground) 6%, transparent);
70
+ background: color-mix(in srgb, var(--nx-foreground) 6%, transparent);
71
71
  ```
72
72
 
73
73
  ### The tokens you may use
74
74
 
75
- Colors: `--background` / `--foreground`, `--card` / `--card-foreground`,
76
- `--popover` / `--popover-foreground`, `--primary` / `--primary-foreground`,
77
- `--secondary` / `--secondary-foreground`, `--muted` / `--muted-foreground`,
78
- `--accent` / `--accent-foreground`, `--destructive`, `--success`, `--warning`,
79
- `--border`, `--border-strong`, `--input`, `--ring`,
80
- `--sidebar-background` and the `--sidebar-*` family.
75
+ Colors: `--nx-background` / `--nx-foreground`, `--nx-card` / `--nx-card-foreground`,
76
+ `--nx-popover` / `--nx-popover-foreground`, `--nx-primary` / `--nx-primary-foreground`,
77
+ `--nx-secondary` / `--nx-secondary-foreground`, `--nx-muted` / `--nx-muted-foreground`,
78
+ `--nx-accent` / `--nx-accent-foreground`, `--nx-destructive`, `--nx-success`, `--nx-warning`,
79
+ `--nx-border`, `--nx-border-strong`, `--nx-input`, `--nx-ring`,
80
+ `--nx-sidebar-background` and the `--nx-sidebar-*` family.
81
81
 
82
82
  Sizing: `--radius` (the system is square — this is `0`; always route corner radius through
83
- it so a future rounding is one edit), and the control-height scale `--control-height`,
84
- `--control-height-sm`, `--control-height-md`, `--control-height-lg` for anything that should
83
+ it so a future rounding is one edit), and the control-height scale `--nx-control-height`,
84
+ `--nx-control-height-sm`, `--nx-control-height-md`, `--nx-control-height-lg` for anything that should
85
85
  line up with admin inputs and buttons.
86
86
 
87
87
  Do not hardcode hex, `rgb()`, `rgba()`, or named colors. The only literals allowed are
@@ -96,7 +96,7 @@ a stylesheet. They are already token-driven, accessible, and dark-mode-correct.
96
96
 
97
97
  ```tsx
98
98
  import { Button, Input, Select, Switch, Badge } from "@nextlyhq/ui";
99
- import { cn } from "@nextlyhq/ui";
99
+ import { cn } from "@nextlyhq/ui/utils";
100
100
 
101
101
  <Button variant="outline" size="sm">Add field</Button>
102
102
  <Input placeholder="Form name" />
@@ -114,13 +114,13 @@ control). When you write it, it must obey the token contract in section 2 and be
114
114
 
115
115
  ## 4. Dark mode is automatic — don't fight it
116
116
 
117
- The admin flips every token under `.adminapp.dark`. If you only ever reference tokens, your
117
+ The admin flips every token under `.nextly-admin.dark`. If you only ever reference tokens, your
118
118
  UI switches with it and you never write a dark rule.
119
119
 
120
120
  - **Never** hardcode a light or dark color.
121
121
  - **Never** gate admin theming on `@media (prefers-color-scheme: dark)` — admin dark mode is
122
- class-based (`.dark` / `.adminapp.dark`), independent of the OS. A media query makes your
123
- UI ignore the admin's own theme toggle. Use Tailwind's `dark:` variant or `.adminapp.dark`
122
+ class-based (`.dark` / `.nextly-admin.dark`), independent of the OS. A media query makes your
123
+ UI ignore the admin's own theme toggle. Use Tailwind's `dark:` variant or `.nextly-admin.dark`
124
124
  descendant selectors if you truly need a mode-specific tweak.
125
125
 
126
126
  ---
@@ -169,7 +169,7 @@ allowlist in the script with a one-line reason — don't silence the whole check
169
169
 
170
170
  - [ ] Colors come from `var(--token)` — no `hsl()`/`rgb()` wrappers, no hardcoded values.
171
171
  - [ ] Alpha uses `color-mix(in srgb, var(--token) N%, transparent)`.
172
- - [ ] Corner radius routes through `var(--radius)`; control heights use `--control-height*`.
172
+ - [ ] Corner radius routes through `var(--radius)`; control heights use `--nx-control-height*`.
173
173
  - [ ] New UI uses `@nextlyhq/ui` components + Tailwind utilities where practical.
174
174
  - [ ] No `@media (prefers-color-scheme)`; dark mode inherited from the admin.
175
175
  - [ ] Class names are prefixed/scoped; no bare element selectors.