@arun-dev/tokens 0.35.0 → 0.36.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.
- package/dist/createBrand.cjs +328 -244
- package/dist/createBrand.d.cts +43 -43
- package/dist/createBrand.d.ts +43 -43
- package/dist/createBrand.js +323 -215
- package/package.json +7 -6
- package/src/components/calendar.css +20 -0
- package/src/createBrand.ts +4 -0
package/dist/createBrand.js
CHANGED
|
@@ -1,245 +1,358 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
//#region src/createBrand.ts
|
|
2
|
+
const DEFAULT_NEUTRAL = {
|
|
3
|
+
0: "#ffffff",
|
|
4
|
+
50: "#f8fafc",
|
|
5
|
+
100: "#f1f5f9",
|
|
6
|
+
200: "#e2e8f0",
|
|
7
|
+
300: "#cbd5e1",
|
|
8
|
+
400: "#96a5ba",
|
|
9
|
+
500: "#64748b",
|
|
10
|
+
600: "#475569",
|
|
11
|
+
700: "#334155",
|
|
12
|
+
800: "#1e293b",
|
|
13
|
+
900: "#0f172a",
|
|
14
|
+
950: "#020617"
|
|
15
15
|
};
|
|
16
16
|
function hexToRgb(hex) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
const h = hex.replace("#", "");
|
|
18
|
+
return [
|
|
19
|
+
parseInt(h.slice(0, 2), 16) / 255,
|
|
20
|
+
parseInt(h.slice(2, 4), 16) / 255,
|
|
21
|
+
parseInt(h.slice(4, 6), 16) / 255
|
|
22
|
+
];
|
|
23
23
|
}
|
|
24
24
|
function rgbToHsl(r, g, b) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
25
|
+
const max = Math.max(r, g, b);
|
|
26
|
+
const min = Math.min(r, g, b);
|
|
27
|
+
const l = (max + min) / 2;
|
|
28
|
+
if (max === min) return [
|
|
29
|
+
0,
|
|
30
|
+
0,
|
|
31
|
+
l * 100
|
|
32
|
+
];
|
|
33
|
+
const d = max - min;
|
|
34
|
+
const s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
35
|
+
let h;
|
|
36
|
+
switch (max) {
|
|
37
|
+
case r:
|
|
38
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
39
|
+
break;
|
|
40
|
+
case g:
|
|
41
|
+
h = ((b - r) / d + 2) / 6;
|
|
42
|
+
break;
|
|
43
|
+
default: h = ((r - g) / d + 4) / 6;
|
|
44
|
+
}
|
|
45
|
+
return [
|
|
46
|
+
h * 360,
|
|
47
|
+
s * 100,
|
|
48
|
+
l * 100
|
|
49
|
+
];
|
|
43
50
|
}
|
|
44
51
|
function hslToHex(h, s, l) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const sl = s / 100;
|
|
53
|
+
const ll = l / 100;
|
|
54
|
+
const a = sl * Math.min(ll, 1 - ll);
|
|
55
|
+
const f = (n) => {
|
|
56
|
+
const k = (n + h / 30) % 12;
|
|
57
|
+
const color = ll - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
|
58
|
+
return Math.round(255 * color).toString(16).padStart(2, "0");
|
|
59
|
+
};
|
|
60
|
+
return `#${f(0)}${f(8)}${f(4)}`;
|
|
54
61
|
}
|
|
62
|
+
/** WCAG 2 relative luminance of a #rrggbb colour. */
|
|
55
63
|
function relativeLuminance(hex) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
);
|
|
59
|
-
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
64
|
+
const [r, g, b] = hexToRgb(hex).map((v) => v <= .03928 ? v / 12.92 : ((v + .055) / 1.055) ** 2.4);
|
|
65
|
+
return .2126 * r + .7152 * g + .0722 * b;
|
|
60
66
|
}
|
|
67
|
+
/** WCAG 2 contrast ratio between two #rrggbb colours, from 1 to 21. */
|
|
61
68
|
function contrastRatio(a, b) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
const lighter = Math.max(relativeLuminance(a), relativeLuminance(b));
|
|
70
|
+
const darker = Math.min(relativeLuminance(a), relativeLuminance(b));
|
|
71
|
+
return (lighter + .05) / (darker + .05);
|
|
65
72
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
/** Lightness targets for each shade step */
|
|
74
|
+
const SHADE_LIGHTNESS = {
|
|
75
|
+
50: 97,
|
|
76
|
+
100: 93,
|
|
77
|
+
200: 86,
|
|
78
|
+
300: 75,
|
|
79
|
+
400: 64,
|
|
80
|
+
500: 53,
|
|
81
|
+
600: 44,
|
|
82
|
+
700: 36,
|
|
83
|
+
800: 28,
|
|
84
|
+
900: 20,
|
|
85
|
+
950: 14
|
|
78
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* Generate an 11-step palette from a seed hex color.
|
|
89
|
+
* Uses HSL color space — perceptually approximate but dependency-free.
|
|
90
|
+
*/
|
|
79
91
|
function generatePaletteFromSeed(seed) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
);
|
|
88
|
-
return Object.fromEntries(entries);
|
|
92
|
+
const [r, g, b] = hexToRgb(seed);
|
|
93
|
+
const [h, s] = rgbToHsl(r, g, b);
|
|
94
|
+
const entries = Object.entries(SHADE_LIGHTNESS).map(([shade, targetL]) => {
|
|
95
|
+
const adjustedS = targetL > 80 ? s * .55 : targetL < 25 ? s * .75 : s;
|
|
96
|
+
return [Number(shade), hslToHex(h, Math.min(adjustedS, 100), targetL)];
|
|
97
|
+
});
|
|
98
|
+
return Object.fromEntries(entries);
|
|
89
99
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
const STATUS_LIGHT = {
|
|
101
|
+
"--color-status-success": "#15803d",
|
|
102
|
+
"--color-status-success-bg": "#f0fdf4",
|
|
103
|
+
"--color-status-error": "#b91c1c",
|
|
104
|
+
"--color-status-error-bg": "#fef2f2",
|
|
105
|
+
"--color-status-warning": "#b45309",
|
|
106
|
+
"--color-status-warning-bg": "#fffbeb"
|
|
97
107
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
const STATUS_DARK = {
|
|
109
|
+
"--color-status-success": "#4ade80",
|
|
110
|
+
"--color-status-success-bg": "#052e16",
|
|
111
|
+
"--color-status-error": "#f87171",
|
|
112
|
+
"--color-status-error-bg": "#450a0a",
|
|
113
|
+
"--color-status-warning": "#fbbf24",
|
|
114
|
+
"--color-status-warning-bg": "#451a03"
|
|
105
115
|
};
|
|
106
116
|
function deriveLight() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
};
|
|
117
|
+
return {
|
|
118
|
+
"--color-bg-primary": "var(--color-neutral-0)",
|
|
119
|
+
"--color-bg-secondary": "var(--color-neutral-50)",
|
|
120
|
+
"--color-bg-surface": "var(--color-neutral-100)",
|
|
121
|
+
"--color-bg-accent": "var(--color-brand-50)",
|
|
122
|
+
"--color-bg-inverse": "var(--color-neutral-900)",
|
|
123
|
+
"--color-text-primary": "var(--color-neutral-900)",
|
|
124
|
+
"--color-text-secondary": "var(--color-neutral-700)",
|
|
125
|
+
"--color-text-muted": "var(--color-neutral-600)",
|
|
126
|
+
"--color-text-accent": "var(--color-brand-700)",
|
|
127
|
+
"--color-text-inverse": "var(--color-neutral-50)",
|
|
128
|
+
"--color-text-on-accent": "var(--color-neutral-0)",
|
|
129
|
+
"--color-border-default": "var(--color-neutral-200)",
|
|
130
|
+
"--color-border-subtle": "var(--color-neutral-100)",
|
|
131
|
+
"--color-border-strong": "var(--color-neutral-300)",
|
|
132
|
+
"--color-border-accent": "var(--color-brand-300)",
|
|
133
|
+
...STATUS_LIGHT,
|
|
134
|
+
"--color-status-info": "var(--color-brand-600)",
|
|
135
|
+
"--color-status-info-bg": "var(--color-brand-50)"
|
|
136
|
+
};
|
|
128
137
|
}
|
|
129
138
|
function deriveDark() {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
};
|
|
139
|
+
return {
|
|
140
|
+
"--color-bg-primary": "var(--color-neutral-900)",
|
|
141
|
+
"--color-bg-secondary": "var(--color-neutral-800)",
|
|
142
|
+
"--color-bg-surface": "var(--color-neutral-700)",
|
|
143
|
+
"--color-bg-accent": "var(--color-brand-950)",
|
|
144
|
+
"--color-bg-inverse": "var(--color-neutral-50)",
|
|
145
|
+
"--color-text-primary": "var(--color-neutral-100)",
|
|
146
|
+
"--color-text-secondary": "var(--color-neutral-300)",
|
|
147
|
+
"--color-text-muted": "var(--color-neutral-400)",
|
|
148
|
+
"--color-text-accent": "var(--color-brand-300)",
|
|
149
|
+
"--color-text-inverse": "var(--color-neutral-900)",
|
|
150
|
+
"--color-text-on-accent": "var(--color-neutral-950)",
|
|
151
|
+
"--color-border-default": "var(--color-neutral-700)",
|
|
152
|
+
"--color-border-subtle": "var(--color-neutral-800)",
|
|
153
|
+
"--color-border-strong": "var(--color-neutral-600)",
|
|
154
|
+
"--color-border-accent": "var(--color-brand-700)",
|
|
155
|
+
...STATUS_DARK,
|
|
156
|
+
"--color-status-info": "var(--color-brand-300)",
|
|
157
|
+
"--color-status-info-bg": "var(--color-brand-950)"
|
|
158
|
+
};
|
|
151
159
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Every pairing of semantic tokens that @arun-dev/ui draws one on the other, with the WCAG
|
|
162
|
+
* contrast it needs: 7 where the default brand is audited AAA, 4.5 for other text, 3 for
|
|
163
|
+
* icons, accent bars and focus outlines. A brand that skips createBrand should meet these too.
|
|
164
|
+
*/
|
|
165
|
+
const CONTRAST_REQUIREMENTS = [
|
|
166
|
+
{
|
|
167
|
+
foreground: "--color-text-primary",
|
|
168
|
+
background: "--color-bg-primary",
|
|
169
|
+
min: 7
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
foreground: "--color-text-primary",
|
|
173
|
+
background: "--color-bg-secondary",
|
|
174
|
+
min: 7
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
foreground: "--color-text-primary",
|
|
178
|
+
background: "--color-bg-surface",
|
|
179
|
+
min: 4.5
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
foreground: "--color-text-secondary",
|
|
183
|
+
background: "--color-bg-primary",
|
|
184
|
+
min: 7
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
foreground: "--color-text-muted",
|
|
188
|
+
background: "--color-bg-primary",
|
|
189
|
+
min: 4.5
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
foreground: "--color-text-inverse",
|
|
193
|
+
background: "--color-bg-inverse",
|
|
194
|
+
min: 7
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
foreground: "--color-text-primary",
|
|
198
|
+
background: "--color-bg-accent",
|
|
199
|
+
min: 7
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
foreground: "--color-text-accent",
|
|
203
|
+
background: "--color-bg-primary",
|
|
204
|
+
min: 7
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
foreground: "--color-text-accent",
|
|
208
|
+
background: "--color-bg-secondary",
|
|
209
|
+
min: 4.5
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
foreground: "--color-text-accent",
|
|
213
|
+
background: "--color-bg-surface",
|
|
214
|
+
min: 4.5
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
foreground: "--color-text-accent",
|
|
218
|
+
background: "--color-bg-accent",
|
|
219
|
+
min: 4.5
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
foreground: "--color-text-on-accent",
|
|
223
|
+
background: "--color-text-accent",
|
|
224
|
+
min: 7
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
foreground: "--color-status-success",
|
|
228
|
+
background: "--color-status-success-bg",
|
|
229
|
+
min: 4.5
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
foreground: "--color-status-error",
|
|
233
|
+
background: "--color-status-error-bg",
|
|
234
|
+
min: 4.5
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
foreground: "--color-status-warning",
|
|
238
|
+
background: "--color-status-warning-bg",
|
|
239
|
+
min: 4.5
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
foreground: "--color-status-info",
|
|
243
|
+
background: "--color-status-info-bg",
|
|
244
|
+
min: 4.5
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
foreground: "--color-status-info",
|
|
248
|
+
background: "--color-bg-primary",
|
|
249
|
+
min: 3
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
foreground: "--color-status-error",
|
|
253
|
+
background: "--color-bg-primary",
|
|
254
|
+
min: 4.5
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
foreground: "--color-status-warning",
|
|
258
|
+
background: "--color-bg-primary",
|
|
259
|
+
min: 3
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
foreground: "--color-status-success",
|
|
263
|
+
background: "--color-bg-primary",
|
|
264
|
+
min: 3
|
|
265
|
+
}
|
|
175
266
|
];
|
|
176
|
-
|
|
177
|
-
|
|
267
|
+
const BRAND_VAR = /^var\(--color-brand-(\d+)\)$/;
|
|
268
|
+
const NEUTRAL_VAR = /^var\(--color-neutral-(\d+)\)$/;
|
|
269
|
+
/** The colour a semantic value stands for: a brand shade, a neutral shade or a literal. */
|
|
178
270
|
function resolveColor(value, palette, neutral) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
271
|
+
const brand = BRAND_VAR.exec(value);
|
|
272
|
+
if (brand) return palette[Number(brand[1])];
|
|
273
|
+
const gray = NEUTRAL_VAR.exec(value);
|
|
274
|
+
if (gray) return neutral[Number(gray[1])];
|
|
275
|
+
return value;
|
|
184
276
|
}
|
|
277
|
+
/** The same colour, lighter or darker by `step` HSL points; its hue and saturation kept. */
|
|
185
278
|
function shiftLightness(hex, step) {
|
|
186
|
-
|
|
187
|
-
|
|
279
|
+
const [h, s, l] = rgbToHsl(...hexToRgb(hex));
|
|
280
|
+
return hslToHex(h, s, Math.min(100, Math.max(0, l + step)));
|
|
188
281
|
}
|
|
189
|
-
|
|
282
|
+
const SHADES = Object.keys(SHADE_LIGHTNESS).map(Number);
|
|
283
|
+
/**
|
|
284
|
+
* Moves the shades of a seeded palette until every pairing in CONTRAST_REQUIREMENTS passes in
|
|
285
|
+
* both themes. HSL lightness is not how light a colour looks — a yellow at the 700 step is
|
|
286
|
+
* still bright — so fixed steps alone cannot promise contrast.
|
|
287
|
+
*
|
|
288
|
+
* In each failing pair the brand shade moves, away from the colour it sits on or under: the
|
|
289
|
+
* foreground's when both are brand shades. Hue and saturation stay, so the brand still reads
|
|
290
|
+
* as its seed. The scale is then kept in order, light to dark, by moving its neighbours on.
|
|
291
|
+
*/
|
|
190
292
|
function fitContrast(palette, neutral) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
fitted[shade] = shiftLightness(fitted[shade], 1);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
if (!moved) break;
|
|
225
|
-
}
|
|
226
|
-
return fitted;
|
|
293
|
+
const fitted = { ...palette };
|
|
294
|
+
const themes = [deriveLight(), deriveDark()];
|
|
295
|
+
for (let pass = 0; pass < 4; pass++) {
|
|
296
|
+
let moved = false;
|
|
297
|
+
for (const theme of themes) for (const { foreground, background, min } of CONTRAST_REQUIREMENTS) {
|
|
298
|
+
const fgValue = theme[foreground] ?? "";
|
|
299
|
+
const bgValue = theme[background] ?? "";
|
|
300
|
+
const fgShade = BRAND_VAR.exec(fgValue)?.[1];
|
|
301
|
+
const bgShade = BRAND_VAR.exec(bgValue)?.[1];
|
|
302
|
+
const shade = Number(fgShade ?? bgShade);
|
|
303
|
+
if (!fgShade && !bgShade) continue;
|
|
304
|
+
const other = resolveColor(fgShade ? bgValue : fgValue, fitted, neutral);
|
|
305
|
+
const step = relativeLuminance(other) > .18 ? -1 : 1;
|
|
306
|
+
for (let i = 0; i < 100 && contrastRatio(fitted[shade], other) < min; i++) {
|
|
307
|
+
fitted[shade] = shiftLightness(fitted[shade], step);
|
|
308
|
+
moved = true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const middle = SHADES.indexOf(500);
|
|
312
|
+
for (let i = middle + 1; i < SHADES.length; i++) {
|
|
313
|
+
const [before, shade] = [SHADES[i - 1], SHADES[i]];
|
|
314
|
+
for (let n = 0; n < 100 && relativeLuminance(fitted[shade]) >= relativeLuminance(fitted[before]); n++) fitted[shade] = shiftLightness(fitted[shade], -1);
|
|
315
|
+
}
|
|
316
|
+
for (let i = middle - 1; i >= 0; i--) {
|
|
317
|
+
const [after, shade] = [SHADES[i + 1], SHADES[i]];
|
|
318
|
+
for (let n = 0; n < 100 && relativeLuminance(fitted[shade]) <= relativeLuminance(fitted[after]); n++) fitted[shade] = shiftLightness(fitted[shade], 1);
|
|
319
|
+
}
|
|
320
|
+
if (!moved) break;
|
|
321
|
+
}
|
|
322
|
+
return fitted;
|
|
227
323
|
}
|
|
228
324
|
function toVars(tokens, indent = " ") {
|
|
229
|
-
|
|
325
|
+
return Object.entries(tokens).map(([k, v]) => `${indent}${k}: ${v};`).join("\n");
|
|
230
326
|
}
|
|
231
327
|
function paletteVars(palette) {
|
|
232
|
-
|
|
328
|
+
return Object.keys(SHADE_LIGHTNESS).map((shade) => ` --color-brand-${shade}: ${palette[shade]};`).join("\n");
|
|
233
329
|
}
|
|
234
330
|
function neutralVars(neutral) {
|
|
235
|
-
|
|
331
|
+
return [
|
|
332
|
+
0,
|
|
333
|
+
50,
|
|
334
|
+
100,
|
|
335
|
+
200,
|
|
336
|
+
300,
|
|
337
|
+
400,
|
|
338
|
+
500,
|
|
339
|
+
600,
|
|
340
|
+
700,
|
|
341
|
+
800,
|
|
342
|
+
900,
|
|
343
|
+
950
|
|
344
|
+
].map((shade) => ` --color-neutral-${shade}: ${neutral[shade]};`).join("\n");
|
|
236
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Generate a complete brand CSS string from a palette or seed color.
|
|
348
|
+
* Write the output to a .css file and import it in your globals.css.
|
|
349
|
+
*/
|
|
237
350
|
function createBrand(input) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
351
|
+
const { name, neutral = DEFAULT_NEUTRAL } = input;
|
|
352
|
+
const palette = "seed" in input ? fitContrast(generatePaletteFromSeed(input.seed), neutral) : input.palette;
|
|
353
|
+
const light = deriveLight();
|
|
354
|
+
const dark = deriveDark();
|
|
355
|
+
return `/* ${name} brand — generated by createBrand (@arun-dev/tokens) */
|
|
243
356
|
/* Re-run createBrand to regenerate after palette changes. */
|
|
244
357
|
|
|
245
358
|
:root {
|
|
@@ -265,10 +378,5 @@ ${toVars(light)}
|
|
|
265
378
|
}
|
|
266
379
|
`;
|
|
267
380
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
DEFAULT_NEUTRAL,
|
|
271
|
-
contrastRatio,
|
|
272
|
-
createBrand,
|
|
273
|
-
generatePaletteFromSeed
|
|
274
|
-
};
|
|
381
|
+
//#endregion
|
|
382
|
+
export { CONTRAST_REQUIREMENTS, DEFAULT_NEUTRAL, contrastRatio, createBrand, generatePaletteFromSeed };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arun-dev/tokens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.1",
|
|
4
4
|
"description": "Pure-CSS design tokens with white-label brand generation — primitives, brand palettes, semantic layers, and the createBrand() generator",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -57,14 +57,15 @@
|
|
|
57
57
|
"access": "public"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"eslint": "
|
|
61
|
-
"
|
|
62
|
-
"typescript": "
|
|
63
|
-
"
|
|
60
|
+
"eslint": "10.12.0",
|
|
61
|
+
"tsdown": "0.23.0",
|
|
62
|
+
"typescript": "6.0.3",
|
|
63
|
+
"vite": "8.3.4",
|
|
64
|
+
"vitest": "5.0.3",
|
|
64
65
|
"@arun-dev/config": "0.0.0"
|
|
65
66
|
},
|
|
66
67
|
"scripts": {
|
|
67
|
-
"build": "
|
|
68
|
+
"build": "tsdown",
|
|
68
69
|
"clean": "rm -rf dist .turbo",
|
|
69
70
|
"typecheck": "tsc --noEmit",
|
|
70
71
|
"lint": "eslint .",
|
|
@@ -29,6 +29,26 @@
|
|
|
29
29
|
--calendar-select-border-color: var(--color-border-default);
|
|
30
30
|
--calendar-select-bg: var(--color-bg-primary);
|
|
31
31
|
|
|
32
|
+
/* Day tags — a muted weekend, and one mark per day for its highest-priority tag */
|
|
33
|
+
--calendar-tag-weekend-color: var(--color-text-secondary);
|
|
34
|
+
--calendar-tag-holiday-color: var(--color-status-error);
|
|
35
|
+
--calendar-tag-festival-color: var(--color-status-warning);
|
|
36
|
+
--calendar-tag-birthday-color: var(--color-status-info);
|
|
37
|
+
--calendar-tag-booked-color: var(--color-text-muted);
|
|
38
|
+
/* A day's mark colour: set per tag, or per day by tagStyles; the text's colour otherwise. */
|
|
39
|
+
--calendar-day-mark-color: currentColor;
|
|
40
|
+
--calendar-mark-size: 0.3125rem;
|
|
41
|
+
--calendar-mark-offset: var(--space-3xs);
|
|
42
|
+
|
|
43
|
+
/* The details card */
|
|
44
|
+
--calendar-details-max-width: 16rem;
|
|
45
|
+
--calendar-details-padding: var(--space-2xs);
|
|
46
|
+
--calendar-details-radius: var(--radius-sm);
|
|
47
|
+
--calendar-details-bg: var(--color-bg-inverse);
|
|
48
|
+
--calendar-details-color: var(--color-text-inverse);
|
|
49
|
+
--calendar-details-font-size: var(--text-xs);
|
|
50
|
+
--calendar-details-offset: var(--space-3xs);
|
|
51
|
+
|
|
32
52
|
/* Previous and Next */
|
|
33
53
|
--calendar-nav-size: 2rem;
|
|
34
54
|
--calendar-nav-color: var(--color-text-secondary);
|
package/src/createBrand.ts
CHANGED
|
@@ -285,6 +285,10 @@ export const CONTRAST_REQUIREMENTS: readonly {
|
|
|
285
285
|
{ foreground: '--color-status-warning', background: '--color-status-warning-bg', min: 4.5 },
|
|
286
286
|
{ foreground: '--color-status-info', background: '--color-status-info-bg', min: 4.5 },
|
|
287
287
|
{ foreground: '--color-status-info', background: '--color-bg-primary', min: 3 },
|
|
288
|
+
// Calendar day tags: a holiday's number as text, the other marks as shapes on the page.
|
|
289
|
+
{ foreground: '--color-status-error', background: '--color-bg-primary', min: 4.5 },
|
|
290
|
+
{ foreground: '--color-status-warning', background: '--color-bg-primary', min: 3 },
|
|
291
|
+
{ foreground: '--color-status-success', background: '--color-bg-primary', min: 3 },
|
|
288
292
|
];
|
|
289
293
|
|
|
290
294
|
const BRAND_VAR = /^var\(--color-brand-(\d+)\)$/;
|