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,110 @@
1
+ import type { StyleRule, Modifier } from '../types.ts'
2
+ import { wrapWithMediaQuery } from '../rule.ts'
3
+
4
+ /**
5
+ * Applies styles at the **small** breakpoint and above (`min-width: 640px`).
6
+ *
7
+ * Use with {@link when} to make style rules responsive. Wraps the rule in a
8
+ * `@media (min-width: 640px)` query.
9
+ *
10
+ * @param rule - The style rule to apply at `>=640px`.
11
+ * @returns A new {@link StyleRule} wrapped in the small-breakpoint media query.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { cx, p, when, sm } from 'typewritingclass'
16
+ *
17
+ * cx(p('1rem'), when(sm)(p('2rem')))
18
+ * // CSS: .abc { padding: 1rem; }
19
+ * // @media (min-width: 640px) { .def { padding: 2rem; } }
20
+ * ```
21
+ */
22
+ export const sm: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(min-width: 640px)')
23
+
24
+ /**
25
+ * Applies styles at the **medium** breakpoint and above (`min-width: 768px`).
26
+ *
27
+ * Use with {@link when} to make style rules responsive. Wraps the rule in a
28
+ * `@media (min-width: 768px)` query.
29
+ *
30
+ * @param rule - The style rule to apply at `>=768px`.
31
+ * @returns A new {@link StyleRule} wrapped in the medium-breakpoint media query.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * import { cx, grid, gridCols, when, md } from 'typewritingclass'
36
+ *
37
+ * cx(gridCols('1'), when(md)(gridCols('2')))
38
+ * // CSS: .abc { grid-template-columns: 1; }
39
+ * // @media (min-width: 768px) { .def { grid-template-columns: 2; } }
40
+ * ```
41
+ */
42
+ export const md: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(min-width: 768px)')
43
+
44
+ /**
45
+ * Applies styles at the **large** breakpoint and above (`min-width: 1024px`).
46
+ *
47
+ * Use with {@link when} to make style rules responsive. Wraps the rule in a
48
+ * `@media (min-width: 1024px)` query.
49
+ *
50
+ * @param rule - The style rule to apply at `>=1024px`.
51
+ * @returns A new {@link StyleRule} wrapped in the large-breakpoint media query.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import { cx, maxW, when, lg } from 'typewritingclass'
56
+ *
57
+ * cx(maxW('100%'), when(lg)(maxW('1024px')))
58
+ * // CSS: .abc { max-width: 100%; }
59
+ * // @media (min-width: 1024px) { .def { max-width: 1024px; } }
60
+ * ```
61
+ */
62
+ export const lg: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(min-width: 1024px)')
63
+
64
+ /**
65
+ * Applies styles at the **extra-large** breakpoint and above (`min-width: 1280px`).
66
+ *
67
+ * Use with {@link when} to make style rules responsive. Wraps the rule in a
68
+ * `@media (min-width: 1280px)` query.
69
+ *
70
+ * @param rule - The style rule to apply at `>=1280px`.
71
+ * @returns A new {@link StyleRule} wrapped in the extra-large-breakpoint media query.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * import { cx, maxW, when, xl } from 'typewritingclass'
76
+ *
77
+ * cx(maxW('100%'), when(xl)(maxW('1280px')))
78
+ * // CSS: .abc { max-width: 100%; }
79
+ * // @media (min-width: 1280px) { .def { max-width: 1280px; } }
80
+ * ```
81
+ */
82
+ export const xl: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(min-width: 1280px)')
83
+
84
+ /**
85
+ * Applies styles at the **2x-large** breakpoint and above (`min-width: 1536px`).
86
+ *
87
+ * Use with {@link when} to make style rules responsive. Wraps the rule in a
88
+ * `@media (min-width: 1536px)` query.
89
+ *
90
+ * Exported as `_2xl` because identifiers cannot start with a digit in JavaScript.
91
+ *
92
+ * @param rule - The style rule to apply at `>=1536px`.
93
+ * @returns A new {@link StyleRule} wrapped in the 2xl-breakpoint media query.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * import { cx, maxW, when, _2xl } from 'typewritingclass'
98
+ *
99
+ * cx(maxW('100%'), when(_2xl)(maxW('1536px')))
100
+ * // CSS: .abc { max-width: 100%; }
101
+ * // @media (min-width: 1536px) { .def { max-width: 1536px; } }
102
+ * ```
103
+ */
104
+ export const _2xl: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(min-width: 1536px)')
105
+
106
+ export const maxSm: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(max-width: 639px)')
107
+ export const maxMd: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(max-width: 767px)')
108
+ export const maxLg: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(max-width: 1023px)')
109
+ export const maxXl: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(max-width: 1279px)')
110
+ export const max2xl: Modifier = (rule: StyleRule) => wrapWithMediaQuery(rule, '(max-width: 1535px)')
@@ -0,0 +1,6 @@
1
+ import type { StyleRule, Modifier } from '../types.ts'
2
+ import { wrapWithSupportsQuery } from '../rule.ts'
3
+
4
+ export function supports(query: string): Modifier {
5
+ return (rule: StyleRule) => wrapWithSupportsQuery(rule, query)
6
+ }
@@ -0,0 +1,171 @@
1
+ import type { StyleRule } from './types.ts'
2
+
3
+ /**
4
+ * An entry in the style registry, pairing a {@link StyleRule} with its
5
+ * layer ordering number.
6
+ *
7
+ * @internal
8
+ */
9
+ interface RegistryEntry {
10
+ /** The style rule containing CSS declarations, selectors, and media queries. */
11
+ rule: StyleRule
12
+ /** The layer number determining the order this rule appears in the generated CSS. Lower numbers come first. */
13
+ layer: number
14
+ }
15
+
16
+ /**
17
+ * Global map of generated class names to their {@link RegistryEntry}.
18
+ * Each unique class name is registered at most once.
19
+ *
20
+ * @internal
21
+ */
22
+ const registry = new Map<string, RegistryEntry>()
23
+
24
+ /**
25
+ * List of callbacks invoked whenever a new rule is registered.
26
+ *
27
+ * @internal
28
+ */
29
+ const listeners: Array<() => void> = []
30
+
31
+ /**
32
+ * Registers a style rule under a generated class name.
33
+ *
34
+ * If the class name is already registered, the call is a no-op (first-write-wins).
35
+ * After a successful registration, all listeners registered via {@link onChange}
36
+ * are notified synchronously.
37
+ *
38
+ * @internal
39
+ * @param className - The unique hashed class name (e.g., `'_a1b2c'`).
40
+ * @param rule - The {@link StyleRule} to associate with the class name.
41
+ * @param layer - The layer ordering number from {@link nextLayer}, controlling
42
+ * the position of this rule in the final CSS output.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * register('_a1b2c', createRule({ padding: '1rem' }), 0)
47
+ * ```
48
+ */
49
+ export function register(className: string, rule: StyleRule, layer: number): void {
50
+ if (registry.has(className)) return
51
+ registry.set(className, { rule, layer })
52
+ for (const cb of listeners) cb()
53
+ }
54
+
55
+ /**
56
+ * Subscribes a callback to be invoked whenever a new rule is registered.
57
+ *
58
+ * This is used by the runtime style injection module ({@link inject}) to
59
+ * schedule CSS updates when new rules appear.
60
+ *
61
+ * @internal
62
+ * @param callback - A function to call on each new registration.
63
+ * @returns An unsubscribe function that removes the listener when called.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * const unsubscribe = onChange(() => {
68
+ * console.log('New rule registered, CSS may need updating')
69
+ * })
70
+ *
71
+ * // Later, stop listening:
72
+ * unsubscribe()
73
+ * ```
74
+ */
75
+ export function onChange(callback: () => void): () => void {
76
+ listeners.push(callback)
77
+ return () => {
78
+ const idx = listeners.indexOf(callback)
79
+ if (idx >= 0) listeners.splice(idx, 1)
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Renders a single registry entry into a CSS rule string.
85
+ *
86
+ * Builds the selector from the class name and any pseudo-class/attribute
87
+ * selectors, then wraps the result in `@media` blocks for each media query.
88
+ *
89
+ * @internal
90
+ * @param className - The hashed class name.
91
+ * @param rule - The {@link StyleRule} to render.
92
+ * @returns A complete CSS rule string, potentially wrapped in media queries.
93
+ */
94
+ function renderRule(className: string, rule: StyleRule): string {
95
+ const decls = Object.entries(rule.declarations)
96
+ .map(([prop, val]) => ` ${prop}: ${val};`)
97
+ .join('\n')
98
+
99
+ let selector: string
100
+ if (rule.selectorTemplate) {
101
+ selector = rule.selectorTemplate.replace(/&/g, `.${className}`)
102
+ } else {
103
+ selector = `.${className}`
104
+ if (rule.selectors.length > 0) {
105
+ selector += rule.selectors.join('')
106
+ }
107
+ }
108
+
109
+ let css = `${selector} {\n${decls}\n}`
110
+
111
+ for (const sq of rule.supportsQueries) {
112
+ css = `@supports ${sq} {\n${css}\n}`
113
+ }
114
+
115
+ for (const mq of rule.mediaQueries) {
116
+ css = `@media ${mq} {\n${css}\n}`
117
+ }
118
+
119
+ return css
120
+ }
121
+
122
+ /**
123
+ * Generates a complete CSS stylesheet string from all registered rules.
124
+ *
125
+ * Rules are sorted by their layer number (ascending) so that later-declared
126
+ * utilities naturally override earlier ones in the cascade. Rules are
127
+ * separated by blank lines.
128
+ *
129
+ * @internal
130
+ * @returns The full CSS string for all registered style rules, or an empty
131
+ * string if no rules have been registered.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * register('_a', createRule({ padding: '1rem' }), 0)
136
+ * register('_b', createRule({ margin: '0' }), 1)
137
+ *
138
+ * generateCSS()
139
+ * // "._a {\n padding: 1rem;\n}\n\n._b {\n margin: 0;\n}"
140
+ * ```
141
+ */
142
+ export function generateCSS(): string {
143
+ const entries = [...registry.entries()].sort((a, b) => a[1].layer - b[1].layer)
144
+ const rules = entries.map(([className, { rule }]) => renderRule(className, rule))
145
+ if (rules.length === 0) return ''
146
+ return rules.join('\n\n')
147
+ }
148
+
149
+ /**
150
+ * Returns a read-only view of the internal style registry.
151
+ *
152
+ * Useful for debugging and testing to inspect which rules have been registered.
153
+ *
154
+ * @internal
155
+ * @returns A `ReadonlyMap` mapping class names to their {@link RegistryEntry}.
156
+ */
157
+ export function getRegistry(): ReadonlyMap<string, RegistryEntry> {
158
+ return registry
159
+ }
160
+
161
+ /**
162
+ * Removes all entries from the style registry.
163
+ *
164
+ * Primarily used in tests to reset state between test cases. Does **not**
165
+ * notify change listeners.
166
+ *
167
+ * @internal
168
+ */
169
+ export function clearRegistry(): void {
170
+ registry.clear()
171
+ }
package/src/rule.ts ADDED
@@ -0,0 +1,202 @@
1
+ import type { StyleRule } from './types.ts'
2
+
3
+ /**
4
+ * Creates a static {@link StyleRule} from a set of CSS declarations.
5
+ *
6
+ * This is the lowest-level rule constructor. The returned rule has no
7
+ * selectors, media queries, or dynamic bindings attached.
8
+ *
9
+ * @internal
10
+ * @param declarations - A record of CSS property-value pairs
11
+ * (e.g., `{ 'background-color': '#3b82f6' }`).
12
+ * @returns A new {@link StyleRule} with the given declarations and empty
13
+ * `selectors` and `mediaQueries` arrays.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const rule = createRule({ 'padding': '1rem', 'margin': '0' })
18
+ * // { _tag: 'StyleRule', declarations: { padding: '1rem', margin: '0' }, selectors: [], mediaQueries: [] }
19
+ * ```
20
+ */
21
+ export function createRule(declarations: Record<string, string>): StyleRule {
22
+ return {
23
+ _tag: 'StyleRule',
24
+ declarations,
25
+ selectors: [],
26
+ mediaQueries: [],
27
+ supportsQueries: [],
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Creates a {@link StyleRule} that includes dynamic CSS custom property bindings.
33
+ *
34
+ * Dynamic rules are produced when a utility receives a {@link dynamic} value.
35
+ * The `dynamicBindings` map CSS custom property names to runtime expressions,
36
+ * enabling values that can change without regenerating the class name.
37
+ *
38
+ * @internal
39
+ * @param declarations - A record of CSS property-value pairs, where values may
40
+ * reference CSS custom properties (e.g., `{ color: 'var(--twc-d0)' }`).
41
+ * @param dynamicBindings - A record mapping CSS custom property names to their
42
+ * runtime values (e.g., `{ '--twc-d0': '#ff0000' }`).
43
+ * @returns A new {@link StyleRule} with declarations, empty selectors/mediaQueries,
44
+ * and the given `dynamicBindings`.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const rule = createDynamicRule(
49
+ * { 'color': 'var(--twc-d0)' },
50
+ * { '--twc-d0': '#ff0000' },
51
+ * )
52
+ * // rule.dynamicBindings === { '--twc-d0': '#ff0000' }
53
+ * ```
54
+ */
55
+ export function createDynamicRule(
56
+ declarations: Record<string, string>,
57
+ dynamicBindings: Record<string, string>,
58
+ ): StyleRule {
59
+ return {
60
+ _tag: 'StyleRule',
61
+ declarations,
62
+ selectors: [],
63
+ mediaQueries: [],
64
+ supportsQueries: [],
65
+ dynamicBindings,
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Merges multiple {@link StyleRule} objects into a single combined rule.
71
+ *
72
+ * Declarations are merged left-to-right (later rules override earlier ones for
73
+ * the same property). Selectors and media queries are de-duplicated and
74
+ * concatenated. Dynamic bindings from all rules are merged together.
75
+ *
76
+ * @internal
77
+ * @param rules - An array of {@link StyleRule} objects to merge.
78
+ * @returns A single {@link StyleRule} containing the merged declarations,
79
+ * selectors, media queries, and dynamic bindings from all input rules.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const a = createRule({ padding: '1rem' })
84
+ * const b = createRule({ margin: '0' })
85
+ * const combined = combineRules([a, b])
86
+ * // combined.declarations === { padding: '1rem', margin: '0' }
87
+ * ```
88
+ */
89
+ export function combineRules(rules: StyleRule[]): StyleRule {
90
+ const merged: Record<string, string> = {}
91
+ const selectors: string[] = []
92
+ const mediaQueries: string[] = []
93
+ const supportsQueries: string[] = []
94
+ let dynamicBindings: Record<string, string> | undefined
95
+ for (const rule of rules) {
96
+ Object.assign(merged, rule.declarations)
97
+ for (const s of rule.selectors) {
98
+ if (!selectors.includes(s)) selectors.push(s)
99
+ }
100
+ for (const mq of rule.mediaQueries) {
101
+ if (!mediaQueries.includes(mq)) mediaQueries.push(mq)
102
+ }
103
+ for (const sq of rule.supportsQueries) {
104
+ if (!supportsQueries.includes(sq)) supportsQueries.push(sq)
105
+ }
106
+ if (rule.dynamicBindings) {
107
+ if (!dynamicBindings) dynamicBindings = {}
108
+ Object.assign(dynamicBindings, rule.dynamicBindings)
109
+ }
110
+ }
111
+ const result: StyleRule = { _tag: 'StyleRule', declarations: merged, selectors, mediaQueries, supportsQueries }
112
+ if (dynamicBindings) result.dynamicBindings = dynamicBindings
113
+ return result
114
+ }
115
+
116
+ /**
117
+ * Returns a copy of the given rule with an additional CSS selector appended.
118
+ *
119
+ * The selector is appended directly to the generated class name when the CSS
120
+ * is rendered (e.g., `.abc:hover`). Multiple selectors can be stacked by
121
+ * calling this function repeatedly.
122
+ *
123
+ * @internal
124
+ * @param rule - The source {@link StyleRule} to wrap.
125
+ * @param selector - The CSS selector suffix to append (e.g., `':hover'`, `':first-child'`).
126
+ * @returns A new {@link StyleRule} with the selector added to its `selectors` array.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * const base = createRule({ opacity: '1' })
131
+ * const hovered = wrapWithSelector(base, ':hover')
132
+ * // Rendered CSS: .abc:hover { opacity: 1; }
133
+ * ```
134
+ */
135
+ export function wrapWithSelector(rule: StyleRule, selector: string): StyleRule {
136
+ return {
137
+ ...rule,
138
+ selectors: [...rule.selectors, selector],
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Returns a copy of the given rule wrapped in an additional CSS media query.
144
+ *
145
+ * When rendered, the rule's CSS block is nested inside a `@media` at-rule.
146
+ * Multiple media queries can be stacked by calling this function repeatedly.
147
+ *
148
+ * @internal
149
+ * @param rule - The source {@link StyleRule} to wrap.
150
+ * @param query - The media query condition (e.g., `'(min-width: 768px)'`,
151
+ * `'(prefers-color-scheme: dark)'`).
152
+ * @returns A new {@link StyleRule} with the query added to its `mediaQueries` array.
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * const base = createRule({ padding: '2rem' })
157
+ * const responsive = wrapWithMediaQuery(base, '(min-width: 768px)')
158
+ * // Rendered CSS: @media (min-width: 768px) { .abc { padding: 2rem; } }
159
+ * ```
160
+ */
161
+ export function wrapWithMediaQuery(rule: StyleRule, query: string): StyleRule {
162
+ return {
163
+ ...rule,
164
+ mediaQueries: [...rule.mediaQueries, query],
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Returns a copy of the given rule with a selector template applied.
170
+ *
171
+ * The template uses `&` as a placeholder for the generated class name.
172
+ * At render time, `&` is replaced with `.className`.
173
+ *
174
+ * @internal
175
+ * @param rule - The source {@link StyleRule} to wrap.
176
+ * @param template - The selector template (e.g., `'.group:hover &'`).
177
+ * @returns A new {@link StyleRule} with the `selectorTemplate` set.
178
+ */
179
+ export function wrapWithSelectorTemplate(rule: StyleRule, template: string): StyleRule {
180
+ return {
181
+ ...rule,
182
+ selectorTemplate: template,
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Returns a copy of the given rule wrapped in an additional CSS `@supports` query.
188
+ *
189
+ * When rendered, the rule's CSS block is nested inside a `@supports` at-rule.
190
+ * Multiple supports queries can be stacked by calling this function repeatedly.
191
+ *
192
+ * @internal
193
+ * @param rule - The source {@link StyleRule} to wrap.
194
+ * @param query - The supports query condition (e.g., `'(display: grid)'`).
195
+ * @returns A new {@link StyleRule} with the query added to its `supportsQueries` array.
196
+ */
197
+ export function wrapWithSupportsQuery(rule: StyleRule, query: string): StyleRule {
198
+ return {
199
+ ...rule,
200
+ supportsQueries: [...rule.supportsQueries, query],
201
+ }
202
+ }
package/src/runtime.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { DynamicResult } from './types.ts'
2
+
3
+ /**
4
+ * Runtime helper injected by the compiler when `dynamic()` values are used.
5
+ *
6
+ * Combines a pre-computed static class name with a set of CSS custom property
7
+ * bindings that carry runtime values. The returned {@link DynamicResult} can
8
+ * be spread onto a DOM element (or passed to {@link useStyle} in React) to
9
+ * apply both the `className` and the inline `style` containing the custom
10
+ * property overrides.
11
+ *
12
+ * This function is **not intended to be called manually** -- the typewritingclass
13
+ * compiler generates calls to it when it encounters `dynamic()` in style
14
+ * expressions.
15
+ *
16
+ * @internal
17
+ * @param className - The hashed class name generated at compile time
18
+ * (e.g., `'_a1b2c'`).
19
+ * @param bindings - A record mapping CSS custom property names to their
20
+ * runtime string values (e.g., `{ '--twc-d0': '#ff0000' }`).
21
+ * @returns A {@link DynamicResult} with `className` and `style` properties
22
+ * ready for spreading onto an element.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * // Compiler output (not written by hand):
27
+ * __twcDynamic('_a1b2c', { '--twc-d0': props.color })
28
+ * // => { className: '_a1b2c', style: { '--twc-d0': '#ff0000' } }
29
+ * ```
30
+ */
31
+ export function __twcDynamic(
32
+ className: string,
33
+ bindings: Record<string, string>,
34
+ ): DynamicResult {
35
+ return { className, style: bindings }
36
+ }
@@ -0,0 +1,11 @@
1
+ export const spin = 'spin 1s linear infinite'
2
+ export const ping = 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite'
3
+ export const pulse = 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite'
4
+ export const bounce = 'bounce 1s infinite'
5
+
6
+ export const keyframes = {
7
+ spin: `@keyframes spin { to { transform: rotate(360deg); } }`,
8
+ ping: `@keyframes ping { 75%, 100% { transform: scale(2); opacity: 0; } }`,
9
+ pulse: `@keyframes pulse { 50% { opacity: .5; } }`,
10
+ bounce: `@keyframes bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1); } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1); } }`,
11
+ }
@@ -0,0 +1,9 @@
1
+ export const none = '0px'
2
+ export const sm = '0.125rem'
3
+ export const DEFAULT = '0.25rem'
4
+ export const md = '0.375rem'
5
+ export const lg = '0.5rem'
6
+ export const xl = '0.75rem'
7
+ export const _2xl = '1rem'
8
+ export const _3xl = '1.5rem'
9
+ export const full = '9999px'