typewritingclass 0.2.0
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/README.md +107 -0
- package/package.json +71 -0
- package/src/css.ts +140 -0
- package/src/cx.ts +105 -0
- package/src/dcx.ts +79 -0
- package/src/dynamic.ts +117 -0
- package/src/hash.ts +54 -0
- package/src/index.ts +137 -0
- package/src/inject.ts +86 -0
- package/src/layer.ts +81 -0
- package/src/modifiers/aria.ts +15 -0
- package/src/modifiers/colorScheme.ts +32 -0
- package/src/modifiers/data.ts +6 -0
- package/src/modifiers/direction.ts +5 -0
- package/src/modifiers/group.ts +21 -0
- package/src/modifiers/index.ts +17 -0
- package/src/modifiers/media.ts +11 -0
- package/src/modifiers/peer.ts +24 -0
- package/src/modifiers/pseudo.ts +183 -0
- package/src/modifiers/pseudoElements.ts +26 -0
- package/src/modifiers/responsive.ts +110 -0
- package/src/modifiers/supports.ts +6 -0
- package/src/registry.ts +171 -0
- package/src/rule.ts +202 -0
- package/src/runtime.ts +36 -0
- package/src/theme/animations.ts +11 -0
- package/src/theme/borders.ts +9 -0
- package/src/theme/colors.ts +326 -0
- package/src/theme/createTheme.ts +238 -0
- package/src/theme/filters.ts +20 -0
- package/src/theme/index.ts +9 -0
- package/src/theme/inject-theme.ts +81 -0
- package/src/theme/shadows.ts +8 -0
- package/src/theme/sizes.ts +37 -0
- package/src/theme/spacing.ts +44 -0
- package/src/theme/typography.ts +72 -0
- package/src/types.ts +273 -0
- package/src/utilities/accessibility.ts +33 -0
- package/src/utilities/backgrounds.ts +86 -0
- package/src/utilities/borders.ts +610 -0
- package/src/utilities/colors.ts +127 -0
- package/src/utilities/effects.ts +169 -0
- package/src/utilities/filters.ts +96 -0
- package/src/utilities/index.ts +57 -0
- package/src/utilities/interactivity.ts +253 -0
- package/src/utilities/layout.ts +1149 -0
- package/src/utilities/spacing.ts +681 -0
- package/src/utilities/svg.ts +34 -0
- package/src/utilities/tables.ts +54 -0
- package/src/utilities/transforms.ts +85 -0
- package/src/utilities/transitions.ts +98 -0
- package/src/utilities/typography.ts +380 -0
- package/src/when.ts +63 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
export type ColorScale = {
|
|
2
|
+
50: string
|
|
3
|
+
100: string
|
|
4
|
+
200: string
|
|
5
|
+
300: string
|
|
6
|
+
400: string
|
|
7
|
+
500: string
|
|
8
|
+
600: string
|
|
9
|
+
700: string
|
|
10
|
+
800: string
|
|
11
|
+
900: string
|
|
12
|
+
950: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const slate: ColorScale = {
|
|
16
|
+
50: '#f8fafc',
|
|
17
|
+
100: '#f1f5f9',
|
|
18
|
+
200: '#e2e8f0',
|
|
19
|
+
300: '#cbd5e1',
|
|
20
|
+
400: '#94a3b8',
|
|
21
|
+
500: '#64748b',
|
|
22
|
+
600: '#475569',
|
|
23
|
+
700: '#334155',
|
|
24
|
+
800: '#1e293b',
|
|
25
|
+
900: '#0f172a',
|
|
26
|
+
950: '#020617',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const gray: ColorScale = {
|
|
30
|
+
50: '#f9fafb',
|
|
31
|
+
100: '#f3f4f6',
|
|
32
|
+
200: '#e5e7eb',
|
|
33
|
+
300: '#d1d5db',
|
|
34
|
+
400: '#9ca3af',
|
|
35
|
+
500: '#6b7280',
|
|
36
|
+
600: '#4b5563',
|
|
37
|
+
700: '#374151',
|
|
38
|
+
800: '#1f2937',
|
|
39
|
+
900: '#111827',
|
|
40
|
+
950: '#030712',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const zinc: ColorScale = {
|
|
44
|
+
50: '#fafafa',
|
|
45
|
+
100: '#f4f4f5',
|
|
46
|
+
200: '#e4e4e7',
|
|
47
|
+
300: '#d4d4d8',
|
|
48
|
+
400: '#a1a1aa',
|
|
49
|
+
500: '#71717a',
|
|
50
|
+
600: '#52525b',
|
|
51
|
+
700: '#3f3f46',
|
|
52
|
+
800: '#27272a',
|
|
53
|
+
900: '#18181b',
|
|
54
|
+
950: '#09090b',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const neutral: ColorScale = {
|
|
58
|
+
50: '#fafafa',
|
|
59
|
+
100: '#f5f5f5',
|
|
60
|
+
200: '#e5e5e5',
|
|
61
|
+
300: '#d4d4d4',
|
|
62
|
+
400: '#a3a3a3',
|
|
63
|
+
500: '#737373',
|
|
64
|
+
600: '#525252',
|
|
65
|
+
700: '#404040',
|
|
66
|
+
800: '#262626',
|
|
67
|
+
900: '#171717',
|
|
68
|
+
950: '#0a0a0a',
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const stone: ColorScale = {
|
|
72
|
+
50: '#fafaf9',
|
|
73
|
+
100: '#f5f5f4',
|
|
74
|
+
200: '#e7e5e4',
|
|
75
|
+
300: '#d6d3d1',
|
|
76
|
+
400: '#a8a29e',
|
|
77
|
+
500: '#78716c',
|
|
78
|
+
600: '#57534e',
|
|
79
|
+
700: '#44403c',
|
|
80
|
+
800: '#292524',
|
|
81
|
+
900: '#1c1917',
|
|
82
|
+
950: '#0c0a09',
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const red: ColorScale = {
|
|
86
|
+
50: '#fef2f2',
|
|
87
|
+
100: '#fee2e2',
|
|
88
|
+
200: '#fecaca',
|
|
89
|
+
300: '#fca5a5',
|
|
90
|
+
400: '#f87171',
|
|
91
|
+
500: '#ef4444',
|
|
92
|
+
600: '#dc2626',
|
|
93
|
+
700: '#b91c1c',
|
|
94
|
+
800: '#991b1b',
|
|
95
|
+
900: '#7f1d1d',
|
|
96
|
+
950: '#450a0a',
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const orange: ColorScale = {
|
|
100
|
+
50: '#fff7ed',
|
|
101
|
+
100: '#ffedd5',
|
|
102
|
+
200: '#fed7aa',
|
|
103
|
+
300: '#fdba74',
|
|
104
|
+
400: '#fb923c',
|
|
105
|
+
500: '#f97316',
|
|
106
|
+
600: '#ea580c',
|
|
107
|
+
700: '#c2410c',
|
|
108
|
+
800: '#9a3412',
|
|
109
|
+
900: '#7c2d12',
|
|
110
|
+
950: '#431407',
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const amber: ColorScale = {
|
|
114
|
+
50: '#fffbeb',
|
|
115
|
+
100: '#fef3c7',
|
|
116
|
+
200: '#fde68a',
|
|
117
|
+
300: '#fcd34d',
|
|
118
|
+
400: '#fbbf24',
|
|
119
|
+
500: '#f59e0b',
|
|
120
|
+
600: '#d97706',
|
|
121
|
+
700: '#b45309',
|
|
122
|
+
800: '#92400e',
|
|
123
|
+
900: '#78350f',
|
|
124
|
+
950: '#451a03',
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const yellow: ColorScale = {
|
|
128
|
+
50: '#fefce8',
|
|
129
|
+
100: '#fef9c3',
|
|
130
|
+
200: '#fef08a',
|
|
131
|
+
300: '#fde047',
|
|
132
|
+
400: '#facc15',
|
|
133
|
+
500: '#eab308',
|
|
134
|
+
600: '#ca8a04',
|
|
135
|
+
700: '#a16207',
|
|
136
|
+
800: '#854d0e',
|
|
137
|
+
900: '#713f12',
|
|
138
|
+
950: '#422006',
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const lime: ColorScale = {
|
|
142
|
+
50: '#f7fee7',
|
|
143
|
+
100: '#ecfccb',
|
|
144
|
+
200: '#d9f99d',
|
|
145
|
+
300: '#bef264',
|
|
146
|
+
400: '#a3e635',
|
|
147
|
+
500: '#84cc16',
|
|
148
|
+
600: '#65a30d',
|
|
149
|
+
700: '#4d7c0f',
|
|
150
|
+
800: '#3f6212',
|
|
151
|
+
900: '#365314',
|
|
152
|
+
950: '#1a2e05',
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const green: ColorScale = {
|
|
156
|
+
50: '#f0fdf4',
|
|
157
|
+
100: '#dcfce7',
|
|
158
|
+
200: '#bbf7d0',
|
|
159
|
+
300: '#86efac',
|
|
160
|
+
400: '#4ade80',
|
|
161
|
+
500: '#22c55e',
|
|
162
|
+
600: '#16a34a',
|
|
163
|
+
700: '#15803d',
|
|
164
|
+
800: '#166534',
|
|
165
|
+
900: '#14532d',
|
|
166
|
+
950: '#052e16',
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const emerald: ColorScale = {
|
|
170
|
+
50: '#ecfdf5',
|
|
171
|
+
100: '#d1fae5',
|
|
172
|
+
200: '#a7f3d0',
|
|
173
|
+
300: '#6ee7b7',
|
|
174
|
+
400: '#34d399',
|
|
175
|
+
500: '#10b981',
|
|
176
|
+
600: '#059669',
|
|
177
|
+
700: '#047857',
|
|
178
|
+
800: '#065f46',
|
|
179
|
+
900: '#064e3b',
|
|
180
|
+
950: '#022c22',
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const teal: ColorScale = {
|
|
184
|
+
50: '#f0fdfa',
|
|
185
|
+
100: '#ccfbf1',
|
|
186
|
+
200: '#99f6e4',
|
|
187
|
+
300: '#5eead4',
|
|
188
|
+
400: '#2dd4bf',
|
|
189
|
+
500: '#14b8a6',
|
|
190
|
+
600: '#0d9488',
|
|
191
|
+
700: '#0f766e',
|
|
192
|
+
800: '#115e59',
|
|
193
|
+
900: '#134e4a',
|
|
194
|
+
950: '#042f2e',
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const cyan: ColorScale = {
|
|
198
|
+
50: '#ecfeff',
|
|
199
|
+
100: '#cffafe',
|
|
200
|
+
200: '#a5f3fc',
|
|
201
|
+
300: '#67e8f9',
|
|
202
|
+
400: '#22d3ee',
|
|
203
|
+
500: '#06b6d4',
|
|
204
|
+
600: '#0891b2',
|
|
205
|
+
700: '#0e7490',
|
|
206
|
+
800: '#155e75',
|
|
207
|
+
900: '#164e63',
|
|
208
|
+
950: '#083344',
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export const sky: ColorScale = {
|
|
212
|
+
50: '#f0f9ff',
|
|
213
|
+
100: '#e0f2fe',
|
|
214
|
+
200: '#bae6fd',
|
|
215
|
+
300: '#7dd3fc',
|
|
216
|
+
400: '#38bdf8',
|
|
217
|
+
500: '#0ea5e9',
|
|
218
|
+
600: '#0284c7',
|
|
219
|
+
700: '#0369a1',
|
|
220
|
+
800: '#075985',
|
|
221
|
+
900: '#0c4a6e',
|
|
222
|
+
950: '#082f49',
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export const blue: ColorScale = {
|
|
226
|
+
50: '#eff6ff',
|
|
227
|
+
100: '#dbeafe',
|
|
228
|
+
200: '#bfdbfe',
|
|
229
|
+
300: '#93c5fd',
|
|
230
|
+
400: '#60a5fa',
|
|
231
|
+
500: '#3b82f6',
|
|
232
|
+
600: '#2563eb',
|
|
233
|
+
700: '#1d4ed8',
|
|
234
|
+
800: '#1e40af',
|
|
235
|
+
900: '#1e3a8a',
|
|
236
|
+
950: '#172554',
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export const indigo: ColorScale = {
|
|
240
|
+
50: '#eef2ff',
|
|
241
|
+
100: '#e0e7ff',
|
|
242
|
+
200: '#c7d2fe',
|
|
243
|
+
300: '#a5b4fc',
|
|
244
|
+
400: '#818cf8',
|
|
245
|
+
500: '#6366f1',
|
|
246
|
+
600: '#4f46e5',
|
|
247
|
+
700: '#4338ca',
|
|
248
|
+
800: '#3730a3',
|
|
249
|
+
900: '#312e81',
|
|
250
|
+
950: '#1e1b4b',
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export const violet: ColorScale = {
|
|
254
|
+
50: '#f5f3ff',
|
|
255
|
+
100: '#ede9fe',
|
|
256
|
+
200: '#ddd6fe',
|
|
257
|
+
300: '#c4b5fd',
|
|
258
|
+
400: '#a78bfa',
|
|
259
|
+
500: '#8b5cf6',
|
|
260
|
+
600: '#7c3aed',
|
|
261
|
+
700: '#6d28d9',
|
|
262
|
+
800: '#5b21b6',
|
|
263
|
+
900: '#4c1d95',
|
|
264
|
+
950: '#2e1065',
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const purple: ColorScale = {
|
|
268
|
+
50: '#faf5ff',
|
|
269
|
+
100: '#f3e8ff',
|
|
270
|
+
200: '#e9d5ff',
|
|
271
|
+
300: '#d8b4fe',
|
|
272
|
+
400: '#c084fc',
|
|
273
|
+
500: '#a855f7',
|
|
274
|
+
600: '#9333ea',
|
|
275
|
+
700: '#7e22ce',
|
|
276
|
+
800: '#6b21a8',
|
|
277
|
+
900: '#581c87',
|
|
278
|
+
950: '#3b0764',
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export const fuchsia: ColorScale = {
|
|
282
|
+
50: '#fdf4ff',
|
|
283
|
+
100: '#fae8ff',
|
|
284
|
+
200: '#f5d0fe',
|
|
285
|
+
300: '#f0abfc',
|
|
286
|
+
400: '#e879f9',
|
|
287
|
+
500: '#d946ef',
|
|
288
|
+
600: '#c026d3',
|
|
289
|
+
700: '#a21caf',
|
|
290
|
+
800: '#86198f',
|
|
291
|
+
900: '#701a75',
|
|
292
|
+
950: '#4a044e',
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export const pink: ColorScale = {
|
|
296
|
+
50: '#fdf2f8',
|
|
297
|
+
100: '#fce7f3',
|
|
298
|
+
200: '#fbcfe8',
|
|
299
|
+
300: '#f9a8d4',
|
|
300
|
+
400: '#f472b6',
|
|
301
|
+
500: '#ec4899',
|
|
302
|
+
600: '#db2777',
|
|
303
|
+
700: '#be185d',
|
|
304
|
+
800: '#9d174d',
|
|
305
|
+
900: '#831843',
|
|
306
|
+
950: '#500724',
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export const rose: ColorScale = {
|
|
310
|
+
50: '#fff1f2',
|
|
311
|
+
100: '#ffe4e6',
|
|
312
|
+
200: '#fecdd3',
|
|
313
|
+
300: '#fda4af',
|
|
314
|
+
400: '#fb7185',
|
|
315
|
+
500: '#f43f5e',
|
|
316
|
+
600: '#e11d48',
|
|
317
|
+
700: '#be123c',
|
|
318
|
+
800: '#9f1239',
|
|
319
|
+
900: '#881337',
|
|
320
|
+
950: '#4c0519',
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export const white = '#ffffff'
|
|
324
|
+
export const black = '#000000'
|
|
325
|
+
export const transparent = 'transparent'
|
|
326
|
+
export const currentColor = 'currentColor'
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration object for defining a theme's design tokens.
|
|
3
|
+
*
|
|
4
|
+
* Pass this to {@link createTheme} to generate CSS custom properties and
|
|
5
|
+
* a type-safe `vars` accessor for use in style utilities.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const config: ThemeConfig = {
|
|
10
|
+
* name: 'light',
|
|
11
|
+
* colors: {
|
|
12
|
+
* blue: { 500: '#3b82f6', 600: '#2563eb' },
|
|
13
|
+
* },
|
|
14
|
+
* spacing: { 4: '1rem', 8: '2rem' },
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export interface ThemeConfig {
|
|
19
|
+
/** Optional theme name. Defaults to `'default'` which targets `:root`. Any other name targets `[data-theme="<name>"]`. */
|
|
20
|
+
name?: string
|
|
21
|
+
/** Color palette scales, keyed by color name, then by shade (e.g., `{ blue: { 500: '#3b82f6' } }`). */
|
|
22
|
+
colors?: Record<string, Record<string | number, string>>
|
|
23
|
+
/** Spacing scale, keyed by size token (e.g., `{ 4: '1rem', 8: '2rem' }`). */
|
|
24
|
+
spacing?: Record<string | number, string>
|
|
25
|
+
/** Typography tokens for text sizes and font weights. */
|
|
26
|
+
typography?: {
|
|
27
|
+
/** Named text size presets with `fontSize` and `lineHeight` values. */
|
|
28
|
+
textSizes?: Record<string, { fontSize: string; lineHeight: string }>
|
|
29
|
+
/** Named font weight presets (e.g., `{ bold: '700' }`). */
|
|
30
|
+
fontWeights?: Record<string, string>
|
|
31
|
+
}
|
|
32
|
+
/** Named border style tokens (e.g., `{ default: '1px solid #e5e7eb' }`). */
|
|
33
|
+
borders?: Record<string, string>
|
|
34
|
+
/** Named box-shadow tokens (e.g., `{ md: '0 4px 6px rgba(0,0,0,0.1)' }`). */
|
|
35
|
+
shadows?: Record<string, string>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The result returned by {@link createTheme}.
|
|
40
|
+
*
|
|
41
|
+
* Contains the generated CSS text for injection and a `vars` object whose
|
|
42
|
+
* values are `var(--twc-...)` references ready to pass to style utilities.
|
|
43
|
+
*/
|
|
44
|
+
export interface ThemeResult {
|
|
45
|
+
/** The theme name, matching the `name` from {@link ThemeConfig} (defaults to `'default'`). */
|
|
46
|
+
name: string
|
|
47
|
+
/**
|
|
48
|
+
* A complete CSS rule string containing all custom property declarations.
|
|
49
|
+
*
|
|
50
|
+
* For the default theme this targets `:root`; for named themes it targets
|
|
51
|
+
* `[data-theme="<name>"]`. Inject with {@link injectTheme}.
|
|
52
|
+
*/
|
|
53
|
+
cssText: string
|
|
54
|
+
/** Type-safe accessor whose leaf values are `var(--twc-...)` CSS references. */
|
|
55
|
+
vars: ThemeVars
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Type-safe map of CSS `var()` references generated from a {@link ThemeConfig}.
|
|
60
|
+
*
|
|
61
|
+
* Every leaf value is a string like `var(--twc-color-blue-500)` that can be
|
|
62
|
+
* passed directly to style utilities such as `bg()`, `color()`, and `p()`.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const { vars } = createTheme({ colors: { blue: { 500: '#3b82f6' } } })
|
|
67
|
+
* vars.colors.blue[500] // "var(--twc-color-blue-500)"
|
|
68
|
+
*
|
|
69
|
+
* // Use in style utilities:
|
|
70
|
+
* cx(bg(vars.colors.blue[500]))
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export interface ThemeVars {
|
|
74
|
+
/** Color `var()` references, organized by color name and shade. */
|
|
75
|
+
colors: Record<string, Record<string | number, string>>
|
|
76
|
+
/** Spacing `var()` references, keyed by size token. */
|
|
77
|
+
spacing: Record<string | number, string>
|
|
78
|
+
/** Typography `var()` references for text sizes and font weights. */
|
|
79
|
+
typography: {
|
|
80
|
+
/** Text size `var()` references with `fontSize` and `lineHeight` keys. */
|
|
81
|
+
textSizes: Record<string, { fontSize: string; lineHeight: string }>
|
|
82
|
+
/** Font weight `var()` references keyed by weight name. */
|
|
83
|
+
fontWeights: Record<string, string>
|
|
84
|
+
}
|
|
85
|
+
/** Border `var()` references keyed by border name. */
|
|
86
|
+
borders: Record<string, string>
|
|
87
|
+
/** Shadow `var()` references keyed by shadow name. */
|
|
88
|
+
shadows: Record<string, string>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Builds a CSS custom property name from the given path segments.
|
|
93
|
+
*
|
|
94
|
+
* @internal
|
|
95
|
+
* @param parts - One or more name segments to join with hyphens.
|
|
96
|
+
* @returns A CSS custom property name in the form `--twc-<parts joined by ->`.
|
|
97
|
+
*/
|
|
98
|
+
function varName(...parts: (string | number)[]): string {
|
|
99
|
+
return `--twc-${parts.join('-')}`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Wraps a CSS custom property name in a `var()` reference.
|
|
104
|
+
*
|
|
105
|
+
* @internal
|
|
106
|
+
* @param name - The full custom property name (e.g., `--twc-color-blue-500`).
|
|
107
|
+
* @returns A CSS `var()` expression (e.g., `var(--twc-color-blue-500)`).
|
|
108
|
+
*/
|
|
109
|
+
function varRef(name: string): string {
|
|
110
|
+
return `var(${name})`
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creates a theme from a configuration of design tokens.
|
|
115
|
+
*
|
|
116
|
+
* Converts all token values into CSS custom properties and returns:
|
|
117
|
+
* - `cssText`: a string ready to inject into a `<style>` element via {@link injectTheme}
|
|
118
|
+
* - `vars`: a mirrored object whose leaf values are `var(--twc-...)` references,
|
|
119
|
+
* suitable for passing directly into style utilities like `bg()`, `color()`, `p()`, etc.
|
|
120
|
+
*
|
|
121
|
+
* The default theme (no `name` or `name: 'default'`) targets the `:root` selector.
|
|
122
|
+
* Named themes target `[data-theme="<name>"]` and can be activated with {@link setTheme}.
|
|
123
|
+
*
|
|
124
|
+
* @param config - The theme configuration containing design tokens.
|
|
125
|
+
* @returns A {@link ThemeResult} with `name`, `cssText`, and `vars`.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* import { createTheme, injectTheme } from 'typewritingclass/theme'
|
|
130
|
+
* import { cx, bg, p } from 'typewritingclass'
|
|
131
|
+
*
|
|
132
|
+
* const { cssText, vars } = createTheme({
|
|
133
|
+
* name: 'light',
|
|
134
|
+
* colors: {
|
|
135
|
+
* blue: { 500: '#3b82f6', 600: '#2563eb' },
|
|
136
|
+
* gray: { 100: '#f3f4f6', 900: '#111827' },
|
|
137
|
+
* },
|
|
138
|
+
* spacing: { 4: '1rem', 8: '2rem' },
|
|
139
|
+
* shadows: { md: '0 4px 6px rgba(0,0,0,0.1)' },
|
|
140
|
+
* })
|
|
141
|
+
*
|
|
142
|
+
* // Inject the generated CSS custom properties into the document
|
|
143
|
+
* injectTheme(cssText)
|
|
144
|
+
*
|
|
145
|
+
* // Use vars in style utilities — they resolve to var(--twc-...) references
|
|
146
|
+
* cx(bg(vars.colors.blue[500]), p(vars.spacing[4]))
|
|
147
|
+
* // CSS: .abc { background-color: var(--twc-color-blue-500); padding: var(--twc-spacing-4); }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export function createTheme(config: ThemeConfig): ThemeResult {
|
|
151
|
+
const name = config.name ?? 'default'
|
|
152
|
+
const properties: string[] = []
|
|
153
|
+
|
|
154
|
+
// Build vars proxy objects that resolve to var() references
|
|
155
|
+
const colorVars: Record<string, Record<string | number, string>> = {}
|
|
156
|
+
const spacingVars: Record<string | number, string> = {}
|
|
157
|
+
const typographyVars: ThemeVars['typography'] = { textSizes: {}, fontWeights: {} }
|
|
158
|
+
const borderVars: Record<string, string> = {}
|
|
159
|
+
const shadowVars: Record<string, string> = {}
|
|
160
|
+
|
|
161
|
+
// Colors
|
|
162
|
+
if (config.colors) {
|
|
163
|
+
for (const [colorName, scale] of Object.entries(config.colors)) {
|
|
164
|
+
colorVars[colorName] = {}
|
|
165
|
+
for (const [shade, hex] of Object.entries(scale)) {
|
|
166
|
+
const vn = varName('color', colorName, shade)
|
|
167
|
+
properties.push(` ${vn}: ${hex};`)
|
|
168
|
+
colorVars[colorName][shade] = varRef(vn)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Spacing
|
|
174
|
+
if (config.spacing) {
|
|
175
|
+
for (const [key, value] of Object.entries(config.spacing)) {
|
|
176
|
+
const vn = varName('spacing', key)
|
|
177
|
+
properties.push(` ${vn}: ${value};`)
|
|
178
|
+
spacingVars[key] = varRef(vn)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Typography
|
|
183
|
+
if (config.typography?.textSizes) {
|
|
184
|
+
for (const [sizeName, size] of Object.entries(config.typography.textSizes)) {
|
|
185
|
+
const fsvn = varName('text', sizeName, 'fs')
|
|
186
|
+
const lhvn = varName('text', sizeName, 'lh')
|
|
187
|
+
properties.push(` ${fsvn}: ${size.fontSize};`)
|
|
188
|
+
properties.push(` ${lhvn}: ${size.lineHeight};`)
|
|
189
|
+
typographyVars.textSizes[sizeName] = {
|
|
190
|
+
fontSize: varRef(fsvn),
|
|
191
|
+
lineHeight: varRef(lhvn),
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (config.typography?.fontWeights) {
|
|
196
|
+
for (const [weightName, weight] of Object.entries(config.typography.fontWeights)) {
|
|
197
|
+
const vn = varName('font', weightName)
|
|
198
|
+
properties.push(` ${vn}: ${weight};`)
|
|
199
|
+
typographyVars.fontWeights[weightName] = varRef(vn)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Borders
|
|
204
|
+
if (config.borders) {
|
|
205
|
+
for (const [borderName, value] of Object.entries(config.borders)) {
|
|
206
|
+
const vn = varName('border', borderName)
|
|
207
|
+
properties.push(` ${vn}: ${value};`)
|
|
208
|
+
borderVars[borderName] = varRef(vn)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Shadows
|
|
213
|
+
if (config.shadows) {
|
|
214
|
+
for (const [shadowName, value] of Object.entries(config.shadows)) {
|
|
215
|
+
const vn = varName('shadow', shadowName)
|
|
216
|
+
properties.push(` ${vn}: ${value};`)
|
|
217
|
+
shadowVars[shadowName] = varRef(vn)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Build CSS text
|
|
222
|
+
const selector = name === 'default' ? ':root' : `[data-theme="${name}"]`
|
|
223
|
+
const cssText = properties.length > 0
|
|
224
|
+
? `${selector} {\n${properties.join('\n')}\n}`
|
|
225
|
+
: ''
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
name,
|
|
229
|
+
cssText,
|
|
230
|
+
vars: {
|
|
231
|
+
colors: colorVars,
|
|
232
|
+
spacing: spacingVars,
|
|
233
|
+
typography: typographyVars,
|
|
234
|
+
borders: borderVars,
|
|
235
|
+
shadows: shadowVars,
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const blur = {
|
|
2
|
+
none: '0',
|
|
3
|
+
sm: '4px',
|
|
4
|
+
DEFAULT: '8px',
|
|
5
|
+
md: '12px',
|
|
6
|
+
lg: '16px',
|
|
7
|
+
xl: '24px',
|
|
8
|
+
_2xl: '40px',
|
|
9
|
+
_3xl: '64px',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const dropShadow = {
|
|
13
|
+
sm: '0 1px 1px rgb(0 0 0 / 0.05)',
|
|
14
|
+
DEFAULT: '0 1px 2px rgb(0 0 0 / 0.1), 0 1px 1px rgb(0 0 0 / 0.06)',
|
|
15
|
+
md: '0 4px 3px rgb(0 0 0 / 0.07), 0 2px 2px rgb(0 0 0 / 0.06)',
|
|
16
|
+
lg: '0 10px 8px rgb(0 0 0 / 0.04), 0 4px 3px rgb(0 0 0 / 0.1)',
|
|
17
|
+
xl: '0 20px 13px rgb(0 0 0 / 0.03), 0 8px 5px rgb(0 0 0 / 0.08)',
|
|
18
|
+
_2xl: '0 25px 25px rgb(0 0 0 / 0.15)',
|
|
19
|
+
none: '0 0 #0000',
|
|
20
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * as colors from './colors.ts'
|
|
2
|
+
export * as spacing from './spacing.ts'
|
|
3
|
+
export * as typography from './typography.ts'
|
|
4
|
+
export * as sizes from './sizes.ts'
|
|
5
|
+
export * as shadows from './shadows.ts'
|
|
6
|
+
export * as borders from './borders.ts'
|
|
7
|
+
export { createTheme } from './createTheme.ts'
|
|
8
|
+
export type { ThemeConfig, ThemeResult, ThemeVars } from './createTheme.ts'
|
|
9
|
+
export { injectTheme, setTheme } from './inject-theme.ts'
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** Cached reference to the `<style id="twc-theme">` element used by {@link injectTheme}. */
|
|
2
|
+
let themeStyleEl: HTMLStyleElement | null = null
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Injects theme CSS custom properties into the document.
|
|
6
|
+
*
|
|
7
|
+
* Creates (or reuses) a `<style id="twc-theme">` element in the document head
|
|
8
|
+
* and appends the provided CSS text to it. Each call appends to the existing
|
|
9
|
+
* content, so multiple themes can be injected sequentially.
|
|
10
|
+
*
|
|
11
|
+
* This function is a no-op in non-browser environments (SSR-safe).
|
|
12
|
+
*
|
|
13
|
+
* @param cssText - The CSS string to inject, typically the `cssText` property
|
|
14
|
+
* from a {@link ThemeResult} returned by {@link createTheme}.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { createTheme, injectTheme } from 'typewritingclass/theme'
|
|
19
|
+
*
|
|
20
|
+
* const light = createTheme({
|
|
21
|
+
* name: 'light',
|
|
22
|
+
* colors: { blue: { 500: '#3b82f6' } },
|
|
23
|
+
* })
|
|
24
|
+
*
|
|
25
|
+
* const dark = createTheme({
|
|
26
|
+
* name: 'dark',
|
|
27
|
+
* colors: { blue: { 500: '#60a5fa' } },
|
|
28
|
+
* })
|
|
29
|
+
*
|
|
30
|
+
* // Inject both themes into a single <style> element
|
|
31
|
+
* injectTheme(light.cssText)
|
|
32
|
+
* injectTheme(dark.cssText)
|
|
33
|
+
* // <style id="twc-theme">
|
|
34
|
+
* // [data-theme="light"] { --twc-color-blue-500: #3b82f6; }
|
|
35
|
+
* // [data-theme="dark"] { --twc-color-blue-500: #60a5fa; }
|
|
36
|
+
* // </style>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function injectTheme(cssText: string): void {
|
|
40
|
+
if (typeof document === 'undefined') return
|
|
41
|
+
|
|
42
|
+
if (!themeStyleEl) {
|
|
43
|
+
themeStyleEl = document.createElement('style')
|
|
44
|
+
themeStyleEl.id = 'twc-theme'
|
|
45
|
+
document.head.appendChild(themeStyleEl)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Append to existing theme styles
|
|
49
|
+
themeStyleEl.textContent += (themeStyleEl.textContent ? '\n' : '') + cssText
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Activates a named theme by setting the `data-theme` attribute on the
|
|
54
|
+
* document root element (`<html>`).
|
|
55
|
+
*
|
|
56
|
+
* The matching theme CSS must already be injected via {@link injectTheme}.
|
|
57
|
+
* The active theme's custom properties will take effect for any rules using
|
|
58
|
+
* `[data-theme="<name>"]` selectors generated by {@link createTheme}.
|
|
59
|
+
*
|
|
60
|
+
* This function is a no-op in non-browser environments (SSR-safe).
|
|
61
|
+
*
|
|
62
|
+
* @param name - The theme name to activate, matching the `name` used in
|
|
63
|
+
* {@link ThemeConfig} when calling {@link createTheme}.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* import { setTheme } from 'typewritingclass/theme'
|
|
68
|
+
*
|
|
69
|
+
* // Switch to the dark theme at runtime
|
|
70
|
+
* setTheme('dark')
|
|
71
|
+
* // <html data-theme="dark"> ...
|
|
72
|
+
*
|
|
73
|
+
* // Switch back to light
|
|
74
|
+
* setTheme('light')
|
|
75
|
+
* // <html data-theme="light"> ...
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function setTheme(name: string): void {
|
|
79
|
+
if (typeof document === 'undefined') return
|
|
80
|
+
document.documentElement.setAttribute('data-theme', name)
|
|
81
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const sm = '0 1px 2px 0 rgb(0 0 0 / 0.05)'
|
|
2
|
+
export const DEFAULT = '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)'
|
|
3
|
+
export const md = '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
|
|
4
|
+
export const lg = '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)'
|
|
5
|
+
export const xl = '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)'
|
|
6
|
+
export const _2xl = '0 25px 50px -12px rgb(0 0 0 / 0.25)'
|
|
7
|
+
export const inner = 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)'
|
|
8
|
+
export const none = '0 0 #0000'
|