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.
Files changed (53) hide show
  1. package/README.md +107 -0
  2. package/package.json +71 -0
  3. package/src/css.ts +140 -0
  4. package/src/cx.ts +105 -0
  5. package/src/dcx.ts +79 -0
  6. package/src/dynamic.ts +117 -0
  7. package/src/hash.ts +54 -0
  8. package/src/index.ts +137 -0
  9. package/src/inject.ts +86 -0
  10. package/src/layer.ts +81 -0
  11. package/src/modifiers/aria.ts +15 -0
  12. package/src/modifiers/colorScheme.ts +32 -0
  13. package/src/modifiers/data.ts +6 -0
  14. package/src/modifiers/direction.ts +5 -0
  15. package/src/modifiers/group.ts +21 -0
  16. package/src/modifiers/index.ts +17 -0
  17. package/src/modifiers/media.ts +11 -0
  18. package/src/modifiers/peer.ts +24 -0
  19. package/src/modifiers/pseudo.ts +183 -0
  20. package/src/modifiers/pseudoElements.ts +26 -0
  21. package/src/modifiers/responsive.ts +110 -0
  22. package/src/modifiers/supports.ts +6 -0
  23. package/src/registry.ts +171 -0
  24. package/src/rule.ts +202 -0
  25. package/src/runtime.ts +36 -0
  26. package/src/theme/animations.ts +11 -0
  27. package/src/theme/borders.ts +9 -0
  28. package/src/theme/colors.ts +326 -0
  29. package/src/theme/createTheme.ts +238 -0
  30. package/src/theme/filters.ts +20 -0
  31. package/src/theme/index.ts +9 -0
  32. package/src/theme/inject-theme.ts +81 -0
  33. package/src/theme/shadows.ts +8 -0
  34. package/src/theme/sizes.ts +37 -0
  35. package/src/theme/spacing.ts +44 -0
  36. package/src/theme/typography.ts +72 -0
  37. package/src/types.ts +273 -0
  38. package/src/utilities/accessibility.ts +33 -0
  39. package/src/utilities/backgrounds.ts +86 -0
  40. package/src/utilities/borders.ts +610 -0
  41. package/src/utilities/colors.ts +127 -0
  42. package/src/utilities/effects.ts +169 -0
  43. package/src/utilities/filters.ts +96 -0
  44. package/src/utilities/index.ts +57 -0
  45. package/src/utilities/interactivity.ts +253 -0
  46. package/src/utilities/layout.ts +1149 -0
  47. package/src/utilities/spacing.ts +681 -0
  48. package/src/utilities/svg.ts +34 -0
  49. package/src/utilities/tables.ts +54 -0
  50. package/src/utilities/transforms.ts +85 -0
  51. package/src/utilities/transitions.ts +98 -0
  52. package/src/utilities/typography.ts +380 -0
  53. 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'