@vsreact/core 0.0.10 → 0.0.12

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/src/theme.ts CHANGED
@@ -1,159 +1,159 @@
1
- export type ThemeColors = Record<string, string>;
2
-
3
- // The full Tailwind v3 color palette.
4
- const palettes: Record<string, Record<string, string>> = {
5
- slate: {
6
- "50": "#f8fafc", "100": "#f1f5f9", "200": "#e2e8f0", "300": "#cbd5e1",
7
- "400": "#94a3b8", "500": "#64748b", "600": "#475569", "700": "#334155",
8
- "800": "#1e293b", "900": "#0f172a", "950": "#020617",
9
- },
10
- gray: {
11
- "50": "#f9fafb", "100": "#f3f4f6", "200": "#e5e7eb", "300": "#d1d5db",
12
- "400": "#9ca3af", "500": "#6b7280", "600": "#4b5563", "700": "#374151",
13
- "800": "#1f2937", "900": "#111827", "950": "#030712",
14
- },
15
- stone: {
16
- "50": "#fafaf9", "100": "#f5f5f4", "200": "#e7e5e4", "300": "#d6d3d1",
17
- "400": "#a8a29e", "500": "#78716c", "600": "#57534e", "700": "#44403c",
18
- "800": "#292524", "900": "#1c1917", "950": "#0c0a09",
19
- },
20
- orange: {
21
- "50": "#fff7ed", "100": "#ffedd5", "200": "#fed7aa", "300": "#fdba74",
22
- "400": "#fb923c", "500": "#f97316", "600": "#ea580c", "700": "#c2410c",
23
- "800": "#9a3412", "900": "#7c2d12", "950": "#431407",
24
- },
25
- yellow: {
26
- "50": "#fefce8", "100": "#fef9c3", "200": "#fef08a", "300": "#fde047",
27
- "400": "#facc15", "500": "#eab308", "600": "#ca8a04", "700": "#a16207",
28
- "800": "#854d0e", "900": "#713f12", "950": "#422006",
29
- },
30
- green: {
31
- "50": "#f0fdf4", "100": "#dcfce7", "200": "#bbf7d0", "300": "#86efac",
32
- "400": "#4ade80", "500": "#22c55e", "600": "#16a34a", "700": "#15803d",
33
- "800": "#166534", "900": "#14532d", "950": "#052e16",
34
- },
35
- teal: {
36
- "50": "#f0fdfa", "100": "#ccfbf1", "200": "#99f6e4", "300": "#5eead4",
37
- "400": "#2dd4bf", "500": "#14b8a6", "600": "#0d9488", "700": "#0f766e",
38
- "800": "#115e59", "900": "#134e4a", "950": "#042f2e",
39
- },
40
- cyan: {
41
- "50": "#ecfeff", "100": "#cffafe", "200": "#a5f3fc", "300": "#67e8f9",
42
- "400": "#22d3ee", "500": "#06b6d4", "600": "#0891b2", "700": "#0e7490",
43
- "800": "#155e75", "900": "#164e63", "950": "#083344",
44
- },
45
- blue: {
46
- "50": "#eff6ff", "100": "#dbeafe", "200": "#bfdbfe", "300": "#93c5fd",
47
- "400": "#60a5fa", "500": "#3b82f6", "600": "#2563eb", "700": "#1d4ed8",
48
- "800": "#1e40af", "900": "#1e3a8a", "950": "#172554",
49
- },
50
- indigo: {
51
- "50": "#eef2ff", "100": "#e0e7ff", "200": "#c7d2fe", "300": "#a5b4fc",
52
- "400": "#818cf8", "500": "#6366f1", "600": "#4f46e5", "700": "#4338ca",
53
- "800": "#3730a3", "900": "#312e81", "950": "#1e1b4b",
54
- },
55
- violet: {
56
- "50": "#f5f3ff", "100": "#ede9fe", "200": "#ddd6fe", "300": "#c4b5fd",
57
- "400": "#a78bfa", "500": "#8b5cf6", "600": "#7c3aed", "700": "#6d28d9",
58
- "800": "#5b21b6", "900": "#4c1d95", "950": "#2e1065",
59
- },
60
- purple: {
61
- "50": "#faf5ff", "100": "#f3e8ff", "200": "#e9d5ff", "300": "#d8b4fe",
62
- "400": "#c084fc", "500": "#a855f7", "600": "#9333ea", "700": "#7e22ce",
63
- "800": "#6b21a8", "900": "#581c87", "950": "#3b0764",
64
- },
65
- fuchsia: {
66
- "50": "#fdf4ff", "100": "#fae8ff", "200": "#f5d0fe", "300": "#f0abfc",
67
- "400": "#e879f9", "500": "#d946ef", "600": "#c026d3", "700": "#a21caf",
68
- "800": "#86198f", "900": "#701a75", "950": "#4a044e",
69
- },
70
- pink: {
71
- "50": "#fdf2f8", "100": "#fce7f3", "200": "#fbcfe8", "300": "#f9a8d4",
72
- "400": "#f472b6", "500": "#ec4899", "600": "#db2777", "700": "#be185d",
73
- "800": "#9d174d", "900": "#831843", "950": "#500724",
74
- },
75
- rose: {
76
- "50": "#fff1f2", "100": "#ffe4e6", "200": "#fecdd3", "300": "#fda4af",
77
- "400": "#fb7185", "500": "#f43f5e", "600": "#e11d48", "700": "#be123c",
78
- "800": "#9f1239", "900": "#881337", "950": "#4c0519",
79
- },
80
- zinc: {
81
- "50": "#fafafa", "100": "#f4f4f5", "200": "#e4e4e7", "300": "#d4d4d8",
82
- "400": "#a1a1aa", "500": "#71717a", "600": "#52525b", "700": "#3f3f46",
83
- "800": "#27272a", "900": "#18181b", "950": "#09090b",
84
- },
85
- neutral: {
86
- "50": "#fafafa", "100": "#f5f5f5", "200": "#e5e5e5", "300": "#d4d4d4",
87
- "400": "#a3a3a3", "500": "#737373", "600": "#525252", "700": "#404040",
88
- "800": "#262626", "900": "#171717", "950": "#0a0a0a",
89
- },
90
- lime: {
91
- "50": "#f7fee7", "100": "#ecfccb", "200": "#d9f99d", "300": "#bef264",
92
- "400": "#a3e635", "500": "#84cc16", "600": "#65a30d", "700": "#4d7c0f",
93
- "800": "#3f6212", "900": "#365314", "950": "#1a2e05",
94
- },
95
- red: {
96
- "50": "#fef2f2", "100": "#fee2e2", "200": "#fecaca", "300": "#fca5a5",
97
- "400": "#f87171", "500": "#ef4444", "600": "#dc2626", "700": "#b91c1c",
98
- "800": "#991b1b", "900": "#7f1d1d", "950": "#450a0a",
99
- },
100
- amber: {
101
- "50": "#fffbeb", "100": "#fef3c7", "200": "#fde68a", "300": "#fcd34d",
102
- "400": "#fbbf24", "500": "#f59e0b", "600": "#d97706", "700": "#b45309",
103
- "800": "#92400e", "900": "#78350f", "950": "#451a03",
104
- },
105
- emerald: {
106
- "50": "#ecfdf5", "100": "#d1fae5", "200": "#a7f3d0", "300": "#6ee7b7",
107
- "400": "#34d399", "500": "#10b981", "600": "#059669", "700": "#047857",
108
- "800": "#065f46", "900": "#064e3b", "950": "#022c22",
109
- },
110
- sky: {
111
- "50": "#f0f9ff", "100": "#e0f2fe", "200": "#bae6fd", "300": "#7dd3fc",
112
- "400": "#38bdf8", "500": "#0ea5e9", "600": "#0284c7", "700": "#0369a1",
113
- "800": "#075985", "900": "#0c4a6e", "950": "#082f49",
114
- },
115
- };
116
-
117
- let themeColors: ThemeColors = {};
118
-
119
- export function setThemeColors(colors: ThemeColors): void {
120
- themeColors = { ...themeColors };
121
- for (const [name, value] of Object.entries(colors))
122
- themeColors[name] = value.toLowerCase();
123
- }
124
-
125
- /** Resolves a color name ("zinc-900", "white", "accent", "lime-400/20",
126
- "[#c6f135]") to "#rrggbb"/"#rrggbbaa", or undefined if unknown. */
127
- export function resolveColor(name: string): string | undefined {
128
- let base = name;
129
- let alpha: string | undefined;
130
-
131
- const slash = name.lastIndexOf("/");
132
- if (slash > 0) {
133
- const pct = Number(name.slice(slash + 1));
134
- if (Number.isFinite(pct)) {
135
- base = name.slice(0, slash);
136
- alpha = Math.round((pct / 100) * 255)
137
- .toString(16)
138
- .padStart(2, "0");
139
- }
140
- }
141
-
142
- let hex: string | undefined;
143
-
144
- if (base.startsWith("[#") && base.endsWith("]")) hex = base.slice(1, -1).toLowerCase();
145
- else if (base === "white") hex = "#ffffff";
146
- else if (base === "black") hex = "#000000";
147
- else if (base === "transparent") hex = "#00000000";
148
- else if (themeColors[base]) hex = themeColors[base];
149
- else {
150
- const dash = base.lastIndexOf("-");
151
- if (dash > 0) {
152
- const palette = palettes[base.slice(0, dash)];
153
- hex = palette?.[base.slice(dash + 1)];
154
- }
155
- }
156
-
157
- if (hex === undefined) return undefined;
158
- return alpha !== undefined && hex.length === 7 ? hex + alpha : hex;
159
- }
1
+ export type ThemeColors = Record<string, string>;
2
+
3
+ // The full Tailwind v3 color palette.
4
+ const palettes: Record<string, Record<string, string>> = {
5
+ slate: {
6
+ "50": "#f8fafc", "100": "#f1f5f9", "200": "#e2e8f0", "300": "#cbd5e1",
7
+ "400": "#94a3b8", "500": "#64748b", "600": "#475569", "700": "#334155",
8
+ "800": "#1e293b", "900": "#0f172a", "950": "#020617",
9
+ },
10
+ gray: {
11
+ "50": "#f9fafb", "100": "#f3f4f6", "200": "#e5e7eb", "300": "#d1d5db",
12
+ "400": "#9ca3af", "500": "#6b7280", "600": "#4b5563", "700": "#374151",
13
+ "800": "#1f2937", "900": "#111827", "950": "#030712",
14
+ },
15
+ stone: {
16
+ "50": "#fafaf9", "100": "#f5f5f4", "200": "#e7e5e4", "300": "#d6d3d1",
17
+ "400": "#a8a29e", "500": "#78716c", "600": "#57534e", "700": "#44403c",
18
+ "800": "#292524", "900": "#1c1917", "950": "#0c0a09",
19
+ },
20
+ orange: {
21
+ "50": "#fff7ed", "100": "#ffedd5", "200": "#fed7aa", "300": "#fdba74",
22
+ "400": "#fb923c", "500": "#f97316", "600": "#ea580c", "700": "#c2410c",
23
+ "800": "#9a3412", "900": "#7c2d12", "950": "#431407",
24
+ },
25
+ yellow: {
26
+ "50": "#fefce8", "100": "#fef9c3", "200": "#fef08a", "300": "#fde047",
27
+ "400": "#facc15", "500": "#eab308", "600": "#ca8a04", "700": "#a16207",
28
+ "800": "#854d0e", "900": "#713f12", "950": "#422006",
29
+ },
30
+ green: {
31
+ "50": "#f0fdf4", "100": "#dcfce7", "200": "#bbf7d0", "300": "#86efac",
32
+ "400": "#4ade80", "500": "#22c55e", "600": "#16a34a", "700": "#15803d",
33
+ "800": "#166534", "900": "#14532d", "950": "#052e16",
34
+ },
35
+ teal: {
36
+ "50": "#f0fdfa", "100": "#ccfbf1", "200": "#99f6e4", "300": "#5eead4",
37
+ "400": "#2dd4bf", "500": "#14b8a6", "600": "#0d9488", "700": "#0f766e",
38
+ "800": "#115e59", "900": "#134e4a", "950": "#042f2e",
39
+ },
40
+ cyan: {
41
+ "50": "#ecfeff", "100": "#cffafe", "200": "#a5f3fc", "300": "#67e8f9",
42
+ "400": "#22d3ee", "500": "#06b6d4", "600": "#0891b2", "700": "#0e7490",
43
+ "800": "#155e75", "900": "#164e63", "950": "#083344",
44
+ },
45
+ blue: {
46
+ "50": "#eff6ff", "100": "#dbeafe", "200": "#bfdbfe", "300": "#93c5fd",
47
+ "400": "#60a5fa", "500": "#3b82f6", "600": "#2563eb", "700": "#1d4ed8",
48
+ "800": "#1e40af", "900": "#1e3a8a", "950": "#172554",
49
+ },
50
+ indigo: {
51
+ "50": "#eef2ff", "100": "#e0e7ff", "200": "#c7d2fe", "300": "#a5b4fc",
52
+ "400": "#818cf8", "500": "#6366f1", "600": "#4f46e5", "700": "#4338ca",
53
+ "800": "#3730a3", "900": "#312e81", "950": "#1e1b4b",
54
+ },
55
+ violet: {
56
+ "50": "#f5f3ff", "100": "#ede9fe", "200": "#ddd6fe", "300": "#c4b5fd",
57
+ "400": "#a78bfa", "500": "#8b5cf6", "600": "#7c3aed", "700": "#6d28d9",
58
+ "800": "#5b21b6", "900": "#4c1d95", "950": "#2e1065",
59
+ },
60
+ purple: {
61
+ "50": "#faf5ff", "100": "#f3e8ff", "200": "#e9d5ff", "300": "#d8b4fe",
62
+ "400": "#c084fc", "500": "#a855f7", "600": "#9333ea", "700": "#7e22ce",
63
+ "800": "#6b21a8", "900": "#581c87", "950": "#3b0764",
64
+ },
65
+ fuchsia: {
66
+ "50": "#fdf4ff", "100": "#fae8ff", "200": "#f5d0fe", "300": "#f0abfc",
67
+ "400": "#e879f9", "500": "#d946ef", "600": "#c026d3", "700": "#a21caf",
68
+ "800": "#86198f", "900": "#701a75", "950": "#4a044e",
69
+ },
70
+ pink: {
71
+ "50": "#fdf2f8", "100": "#fce7f3", "200": "#fbcfe8", "300": "#f9a8d4",
72
+ "400": "#f472b6", "500": "#ec4899", "600": "#db2777", "700": "#be185d",
73
+ "800": "#9d174d", "900": "#831843", "950": "#500724",
74
+ },
75
+ rose: {
76
+ "50": "#fff1f2", "100": "#ffe4e6", "200": "#fecdd3", "300": "#fda4af",
77
+ "400": "#fb7185", "500": "#f43f5e", "600": "#e11d48", "700": "#be123c",
78
+ "800": "#9f1239", "900": "#881337", "950": "#4c0519",
79
+ },
80
+ zinc: {
81
+ "50": "#fafafa", "100": "#f4f4f5", "200": "#e4e4e7", "300": "#d4d4d8",
82
+ "400": "#a1a1aa", "500": "#71717a", "600": "#52525b", "700": "#3f3f46",
83
+ "800": "#27272a", "900": "#18181b", "950": "#09090b",
84
+ },
85
+ neutral: {
86
+ "50": "#fafafa", "100": "#f5f5f5", "200": "#e5e5e5", "300": "#d4d4d4",
87
+ "400": "#a3a3a3", "500": "#737373", "600": "#525252", "700": "#404040",
88
+ "800": "#262626", "900": "#171717", "950": "#0a0a0a",
89
+ },
90
+ lime: {
91
+ "50": "#f7fee7", "100": "#ecfccb", "200": "#d9f99d", "300": "#bef264",
92
+ "400": "#a3e635", "500": "#84cc16", "600": "#65a30d", "700": "#4d7c0f",
93
+ "800": "#3f6212", "900": "#365314", "950": "#1a2e05",
94
+ },
95
+ red: {
96
+ "50": "#fef2f2", "100": "#fee2e2", "200": "#fecaca", "300": "#fca5a5",
97
+ "400": "#f87171", "500": "#ef4444", "600": "#dc2626", "700": "#b91c1c",
98
+ "800": "#991b1b", "900": "#7f1d1d", "950": "#450a0a",
99
+ },
100
+ amber: {
101
+ "50": "#fffbeb", "100": "#fef3c7", "200": "#fde68a", "300": "#fcd34d",
102
+ "400": "#fbbf24", "500": "#f59e0b", "600": "#d97706", "700": "#b45309",
103
+ "800": "#92400e", "900": "#78350f", "950": "#451a03",
104
+ },
105
+ emerald: {
106
+ "50": "#ecfdf5", "100": "#d1fae5", "200": "#a7f3d0", "300": "#6ee7b7",
107
+ "400": "#34d399", "500": "#10b981", "600": "#059669", "700": "#047857",
108
+ "800": "#065f46", "900": "#064e3b", "950": "#022c22",
109
+ },
110
+ sky: {
111
+ "50": "#f0f9ff", "100": "#e0f2fe", "200": "#bae6fd", "300": "#7dd3fc",
112
+ "400": "#38bdf8", "500": "#0ea5e9", "600": "#0284c7", "700": "#0369a1",
113
+ "800": "#075985", "900": "#0c4a6e", "950": "#082f49",
114
+ },
115
+ };
116
+
117
+ let themeColors: ThemeColors = {};
118
+
119
+ export function setThemeColors(colors: ThemeColors): void {
120
+ themeColors = { ...themeColors };
121
+ for (const [name, value] of Object.entries(colors))
122
+ themeColors[name] = value.toLowerCase();
123
+ }
124
+
125
+ /** Resolves a color name ("zinc-900", "white", "accent", "lime-400/20",
126
+ "[#c6f135]") to "#rrggbb"/"#rrggbbaa", or undefined if unknown. */
127
+ export function resolveColor(name: string): string | undefined {
128
+ let base = name;
129
+ let alpha: string | undefined;
130
+
131
+ const slash = name.lastIndexOf("/");
132
+ if (slash > 0) {
133
+ const pct = Number(name.slice(slash + 1));
134
+ if (Number.isFinite(pct)) {
135
+ base = name.slice(0, slash);
136
+ alpha = Math.round((pct / 100) * 255)
137
+ .toString(16)
138
+ .padStart(2, "0");
139
+ }
140
+ }
141
+
142
+ let hex: string | undefined;
143
+
144
+ if (base.startsWith("[#") && base.endsWith("]")) hex = base.slice(1, -1).toLowerCase();
145
+ else if (base === "white") hex = "#ffffff";
146
+ else if (base === "black") hex = "#000000";
147
+ else if (base === "transparent") hex = "#00000000";
148
+ else if (themeColors[base]) hex = themeColors[base];
149
+ else {
150
+ const dash = base.lastIndexOf("-");
151
+ if (dash > 0) {
152
+ const palette = palettes[base.slice(0, dash)];
153
+ hex = palette?.[base.slice(dash + 1)];
154
+ }
155
+ }
156
+
157
+ if (hex === undefined) return undefined;
158
+ return alpha !== undefined && hex.length === 7 ? hex + alpha : hex;
159
+ }
package/src/tw.test.ts CHANGED
@@ -1,116 +1,122 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { configureTheme, tw } from "./tw";
3
-
4
- describe("tw resolver", () => {
5
- test("layout, palette color, radius, border with opacity suffix", () => {
6
- const r = tw("flex-1 flex-row bg-zinc-950 rounded-xl border border-lime-400/20 p-4");
7
- expect(r.style).toEqual({
8
- flex: 1,
9
- flexDirection: "row",
10
- backgroundColor: "#09090b",
11
- borderRadius: 12,
12
- borderWidth: 1,
13
- borderColor: "#a3e63533",
14
- padding: 16,
15
- });
16
- expect(r.hoverStyle).toBeUndefined();
17
- });
18
-
19
- test("hover variant routes to hoverStyle", () => {
20
- const r = tw("bg-zinc-900 hover:bg-lime-300 active:bg-lime-500 focus:border-lime-400");
21
- expect(r.style).toEqual({ backgroundColor: "#18181b" });
22
- expect(r.hoverStyle).toEqual({ backgroundColor: "#bef264" });
23
- expect(r.activeStyle).toEqual({ backgroundColor: "#84cc16" });
24
- expect(r.focusStyle).toEqual({ borderColor: "#a3e635" });
25
- });
26
-
27
- test("theme tokens", () => {
28
- configureTheme({ colors: { accent: "#C6F135", well: "#0C0E0C" } });
29
- const r = tw("bg-well text-accent border-accent/35");
30
- expect(r.style.backgroundColor).toBe("#0c0e0c");
31
- expect(r.style.color).toBe("#c6f135");
32
- expect(r.style.borderColor).toBe("#c6f13559");
33
- });
34
-
35
- test("arbitrary values", () => {
36
- const r = tw("w-[123] h-[45%] bg-[#C6F135] p-[7]");
37
- expect(r.style).toEqual({
38
- width: 123,
39
- height: "45%",
40
- backgroundColor: "#c6f135",
41
- padding: 7,
42
- });
43
- });
44
-
45
- test("spacing scale, fractions, full, directional padding/margin", () => {
46
- const r = tw("w-full h-1/2 px-3 py-1.5 mt-2 -mb-1 gap-2 gap-x-4");
47
- expect(r.style).toEqual({
48
- width: "100%",
49
- height: "50%",
50
- paddingLeft: 12,
51
- paddingRight: 12,
52
- paddingTop: 6,
53
- paddingBottom: 6,
54
- marginTop: 8,
55
- marginBottom: -4,
56
- gap: 8,
57
- columnGap: 16,
58
- });
59
- });
60
-
61
- test("text utilities", () => {
62
- const r = tw("text-sm font-bold text-center text-zinc-400 leading-5 tracking-wide");
63
- expect(r.style).toEqual({
64
- fontSize: 14,
65
- fontWeight: 700,
66
- textAlign: "center",
67
- color: "#a1a1aa",
68
- lineHeight: 20,
69
- letterSpacing: 0.4,
70
- });
71
- expect(tw("font-mono").style).toEqual({ fontFamily: "monospace" });
72
- });
73
-
74
- test("alignment, position, shadow, opacity, overflow, cursor", () => {
75
- const r = tw(
76
- "items-center justify-between absolute inset-0 top-2 shadow-lg opacity-50 overflow-hidden cursor-pointer",
77
- );
78
- expect(r.style).toEqual({
79
- alignItems: "center",
80
- justifyContent: "space-between",
81
- position: "absolute",
82
- left: 0,
83
- right: 0,
84
- bottom: 0,
85
- top: 8,
86
- shadowColor: "#00000066",
87
- shadowRadius: 12,
88
- shadowOffsetY: 4,
89
- opacity: 0.5,
90
- overflow: "hidden",
91
- cursor: "pointer",
92
- });
93
- });
94
-
95
- test("per-corner radius", () => {
96
- const r = tw("rounded-t-lg rounded-br-full");
97
- expect(r.style).toEqual({
98
- borderTopLeftRadius: 8,
99
- borderTopRightRadius: 8,
100
- borderBottomRightRadius: 9999,
101
- });
102
- });
103
-
104
- test("unknown classes are ignored", () => {
105
- const r = tw("bg-zinc-900 not-a-real-class");
106
- expect(r.style).toEqual({ backgroundColor: "#18181b" });
107
- });
108
-
109
- test("later classes win", () => {
110
- const r = tw("bg-zinc-900 bg-zinc-800");
111
- expect(r.style.backgroundColor).toBe("#27272a");
112
- });
113
- });
1
+ import { describe, expect, test } from "bun:test";
2
+ import { configureTheme, tw } from "./tw";
3
+
4
+ describe("tw resolver", () => {
5
+ test("layout, palette color, radius, border with opacity suffix", () => {
6
+ const r = tw("flex-1 flex-row bg-zinc-950 rounded-xl border border-lime-400/20 p-4");
7
+ expect(r.style).toEqual({
8
+ flex: 1,
9
+ flexDirection: "row",
10
+ backgroundColor: "#09090b",
11
+ borderRadius: 12,
12
+ borderWidth: 1,
13
+ borderColor: "#a3e63533",
14
+ padding: 16,
15
+ });
16
+ expect(r.hoverStyle).toBeUndefined();
17
+ });
18
+
19
+ test("hover variant routes to hoverStyle", () => {
20
+ const r = tw("bg-zinc-900 hover:bg-lime-300 active:bg-lime-500 focus:border-lime-400");
21
+ expect(r.style).toEqual({ backgroundColor: "#18181b" });
22
+ expect(r.hoverStyle).toEqual({ backgroundColor: "#bef264" });
23
+ expect(r.activeStyle).toEqual({ backgroundColor: "#84cc16" });
24
+ expect(r.focusStyle).toEqual({ borderColor: "#a3e635" });
25
+ });
26
+
27
+ test("theme tokens", () => {
28
+ configureTheme({ colors: { accent: "#C6F135", well: "#0C0E0C" } });
29
+ const r = tw("bg-well text-accent border-accent/35");
30
+ expect(r.style.backgroundColor).toBe("#0c0e0c");
31
+ expect(r.style.color).toBe("#c6f135");
32
+ expect(r.style.borderColor).toBe("#c6f13559");
33
+ });
34
+
35
+ test("arbitrary values", () => {
36
+ const r = tw("w-[123] h-[45%] bg-[#C6F135] p-[7]");
37
+ expect(r.style).toEqual({
38
+ width: 123,
39
+ height: "45%",
40
+ backgroundColor: "#c6f135",
41
+ padding: 7,
42
+ });
43
+ });
44
+
45
+ test("spacing scale, fractions, full, directional padding/margin", () => {
46
+ const r = tw("w-full h-1/2 px-3 py-1.5 mt-2 -mb-1 gap-2 gap-x-4");
47
+ expect(r.style).toEqual({
48
+ width: "100%",
49
+ height: "50%",
50
+ paddingLeft: 12,
51
+ paddingRight: 12,
52
+ paddingTop: 6,
53
+ paddingBottom: 6,
54
+ marginTop: 8,
55
+ marginBottom: -4,
56
+ gap: 8,
57
+ columnGap: 16,
58
+ });
59
+ });
60
+
61
+ test("text utilities", () => {
62
+ const r = tw("text-sm font-bold text-center text-zinc-400 leading-5 tracking-wide");
63
+ expect(r.style).toEqual({
64
+ fontSize: 14,
65
+ fontWeight: 700,
66
+ textAlign: "center",
67
+ color: "#a1a1aa",
68
+ lineHeight: 20,
69
+ letterSpacing: 0.4,
70
+ });
71
+ expect(tw("font-mono").style).toEqual({ fontFamily: "monospace" });
72
+ });
73
+
74
+ test("arbitrary text sizes resolve; bracket hex stays a color", () => {
75
+ expect(tw("text-[13]").style).toEqual({ fontSize: 13 });
76
+ expect(tw("text-[8]").style).toEqual({ fontSize: 8 });
77
+ expect(tw("text-[#ff4a38]").style).toEqual({ color: "#ff4a38" });
78
+ });
79
+
80
+ test("alignment, position, shadow, opacity, overflow, cursor", () => {
81
+ const r = tw(
82
+ "items-center justify-between absolute inset-0 top-2 shadow-lg opacity-50 overflow-hidden cursor-pointer",
83
+ );
84
+ expect(r.style).toEqual({
85
+ alignItems: "center",
86
+ justifyContent: "space-between",
87
+ position: "absolute",
88
+ left: 0,
89
+ right: 0,
90
+ bottom: 0,
91
+ top: 8,
92
+ shadowColor: "#00000066",
93
+ shadowRadius: 12,
94
+ shadowOffsetY: 4,
95
+ opacity: 0.5,
96
+ overflow: "hidden",
97
+ cursor: "pointer",
98
+ });
99
+ });
100
+
101
+ test("per-corner radius", () => {
102
+ const r = tw("rounded-t-lg rounded-br-full");
103
+ expect(r.style).toEqual({
104
+ borderTopLeftRadius: 8,
105
+ borderTopRightRadius: 8,
106
+ borderBottomRightRadius: 9999,
107
+ });
108
+ });
109
+
110
+ test("unknown classes are ignored", () => {
111
+ const r = tw("bg-zinc-900 not-a-real-class");
112
+ expect(r.style).toEqual({ backgroundColor: "#18181b" });
113
+ });
114
+
115
+ test("later classes win", () => {
116
+ const r = tw("bg-zinc-900 bg-zinc-800");
117
+ expect(r.style.backgroundColor).toBe("#27272a");
118
+ });
119
+ });
114
120
 
115
121
  describe("tw 0.0.3 additions", () => {
116
122
  test("full Tailwind palette resolves", () => {