@stacksjs/ts-css 0.1.4 → 0.3.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/ENGINE.md +677 -0
- package/PLUGIN.md +217 -0
- package/dist/bin/cli.js +29 -38
- package/dist/bin/cssx.js +405 -0
- package/dist/chunk-0h48763m.js +2 -0
- package/dist/chunk-br9jax0b.js +2 -0
- package/dist/chunk-f9gzb0a5.js +3 -0
- package/dist/chunk-genwkevp.js +3 -0
- package/dist/chunk-jj3s4ctf.js +62 -0
- package/dist/chunk-mz87cw2e.js +2 -0
- package/dist/engine/build.d.ts +26 -0
- package/dist/engine/color-modifier.d.ts +8 -0
- package/dist/engine/config.d.ts +5 -0
- package/dist/engine/format.d.ts +20 -0
- package/dist/engine/generator.d.ts +14 -0
- package/dist/engine/index.d.ts +13 -0
- package/dist/engine/index.js +653 -0
- package/dist/engine/parser.d.ts +68 -0
- package/dist/engine/plugin.d.ts +22 -0
- package/dist/engine/preflight-forms.d.ts +6 -0
- package/dist/engine/preflight.d.ts +3 -0
- package/dist/engine/rules-advanced.d.ts +27 -0
- package/dist/engine/rules-effects.d.ts +35 -0
- package/dist/engine/rules-forms.d.ts +8 -0
- package/dist/engine/rules-grid.d.ts +13 -0
- package/dist/engine/rules-icons.d.ts +2 -0
- package/dist/engine/rules-interactivity.d.ts +41 -0
- package/dist/engine/rules-layout.d.ts +28 -0
- package/dist/engine/rules-transforms.d.ts +35 -0
- package/dist/engine/rules-typography.d.ts +47 -0
- package/dist/engine/rules.d.ts +56 -0
- package/dist/engine/scanner.d.ts +16 -0
- package/dist/engine/style/api.d.ts +95 -0
- package/dist/engine/style/collect.d.ts +10 -0
- package/dist/engine/style/evaluate.d.ts +11 -0
- package/dist/engine/style/hash.d.ts +15 -0
- package/dist/engine/style/index.d.ts +56 -0
- package/dist/engine/style/plugin.d.ts +27 -0
- package/dist/engine/style/priority.d.ts +12 -0
- package/dist/engine/style/registry.d.ts +56 -0
- package/dist/engine/style/types.d.ts +54 -0
- package/dist/engine/style/value.d.ts +15 -0
- package/dist/engine/transformer-compile-class.d.ts +34 -0
- package/dist/engine/types.d.ts +156 -0
- package/dist/index.js +1 -60
- package/dist/optimize/index.js +1 -1
- package/dist/parse/index.js +1 -1
- package/dist/parse/list.d.ts +1 -1
- package/dist/select/index.js +1 -1
- package/dist/what/index.js +1 -1
- package/package.json +35 -19
- package/dist/chunk-1kmkypmr.js +0 -3
- package/dist/chunk-2wsah70r.js +0 -2
- package/dist/chunk-9ep6bwwb.js +0 -2
- package/dist/chunk-edwg811g.js +0 -2
- package/dist/chunk-thy3p95k.js +0 -3
package/ENGINE.md
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
# ts-css
|
|
2
|
+
|
|
3
|
+
A CSS engine with two front ends and one atomic output: Tailwind-compatible **utility classes** for laying out markup, and StyleX-style **typed style objects** for styles that have to be computed, tokenised, or merged across a component boundary. Both compile into the same deduplicated atomic CSS, with nothing left at runtime.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @ts-css/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### Programmatic API
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { CSSGenerator, defaultConfig } from '@ts-css/core'
|
|
17
|
+
|
|
18
|
+
const gen = new CSSGenerator(defaultConfig)
|
|
19
|
+
gen.generate('flex')
|
|
20
|
+
gen.generate('items-center')
|
|
21
|
+
gen.generate('gap-4')
|
|
22
|
+
gen.generate('p-8')
|
|
23
|
+
gen.generate('bg-blue-500')
|
|
24
|
+
gen.generate('text-white')
|
|
25
|
+
gen.generate('rounded-lg')
|
|
26
|
+
gen.generate('hover:bg-blue-600')
|
|
27
|
+
gen.generate('dark:bg-gray-900')
|
|
28
|
+
|
|
29
|
+
const css = gen.toCSS(true) // true = include preflight
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Build API
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { build } from '@ts-css/core'
|
|
36
|
+
|
|
37
|
+
const result = await build({
|
|
38
|
+
content: ['./src/**/*.html', './src/**/*.tsx'],
|
|
39
|
+
output: './dist/styles.css',
|
|
40
|
+
minify: true,
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Style API
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { css } from '@ts-css/core'
|
|
48
|
+
|
|
49
|
+
const theme = css.defineVars({
|
|
50
|
+
accent: '#0b7',
|
|
51
|
+
surface: { default: '#fff', '@media (prefers-color-scheme: dark)': '#111' },
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const styles = css.create({
|
|
55
|
+
card: {
|
|
56
|
+
padding: 16,
|
|
57
|
+
backgroundColor: theme.surface,
|
|
58
|
+
':hover': { transform: 'translateY(-2px)' },
|
|
59
|
+
'@media (min-width: 768px)': { padding: 24 },
|
|
60
|
+
},
|
|
61
|
+
danger: { color: 'red' },
|
|
62
|
+
sized: (width: number) => ({ width }),
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// { className: 'tc9faijott tcze3ap6z …', style: { '--tc2hxvo7bz': '120px' } }
|
|
66
|
+
css.props(styles.card, isDangerous && styles.danger, styles.sized(120))
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`css.props()` merges **per property**, not per class: a later argument replaces
|
|
70
|
+
an earlier one's `color` class outright rather than relying on the cascade to
|
|
71
|
+
outrank it. `null` removes a property, and falsy arguments are skipped.
|
|
72
|
+
|
|
73
|
+
Style factories turn their arguments into inline custom properties, so a
|
|
74
|
+
component rendered at a thousand widths still emits one rule.
|
|
75
|
+
|
|
76
|
+
| | |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `css.create(styles)` | compile named style objects into atomic classes |
|
|
79
|
+
| `css.props(...styles)` | merge compiled styles into `className` / `style` |
|
|
80
|
+
| `css.defineVars(vars)` | declare custom properties, get typed `var()` references |
|
|
81
|
+
| `css.createTheme(vars, overrides)` | redeclare a variable group under a generated class |
|
|
82
|
+
| `css.keyframes(frames)` | declare an animation, get its generated name |
|
|
83
|
+
| `css.firstThatWorks(...values)` | progressive-enhancement fallbacks, in CSS order |
|
|
84
|
+
| `css.defineConsts(consts)` | build-time constants that never become CSS |
|
|
85
|
+
|
|
86
|
+
Point `styles` at the modules that declare them and `cssx build` emits their
|
|
87
|
+
CSS alongside the utilities. Outside the CLI, `collectStyles(patterns)` returns
|
|
88
|
+
the stylesheet directly and `stylePlugin()` collects styles during a
|
|
89
|
+
`Bun.build()`.
|
|
90
|
+
|
|
91
|
+
### CLI
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cssx build
|
|
95
|
+
cssx build --watch
|
|
96
|
+
cssx build --minify
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Configuration
|
|
100
|
+
|
|
101
|
+
Create a `css.config.ts` in your project root:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import type { TsCssConfig } from '@ts-css/core'
|
|
105
|
+
|
|
106
|
+
export default {
|
|
107
|
+
content: ['./src/**/*.{html,tsx,stx}'],
|
|
108
|
+
styles: ['./src/**/*.styles.ts'],
|
|
109
|
+
output: './dist/styles.css',
|
|
110
|
+
minify: false,
|
|
111
|
+
|
|
112
|
+
theme: {
|
|
113
|
+
extend: {
|
|
114
|
+
colors: {
|
|
115
|
+
brand: {
|
|
116
|
+
50: '#eff6ff',
|
|
117
|
+
500: '#3b82f6',
|
|
118
|
+
900: '#1e3a5a',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
spacing: {
|
|
122
|
+
'18': '4.5rem',
|
|
123
|
+
'112': '28rem',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
safelist: ['bg-brand-500', 'text-white'],
|
|
129
|
+
blocklist: ['opacity-0'],
|
|
130
|
+
} satisfies Partial<TsCssConfig>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Theme
|
|
134
|
+
|
|
135
|
+
The default theme includes Tailwind-compatible values for:
|
|
136
|
+
|
|
137
|
+
- **colors** - Full color palette (slate, gray, zinc, red, orange, yellow, green, blue, indigo, purple, pink, etc.) with 50-950 shades in oklch
|
|
138
|
+
- **spacing** - 0 through 96, plus `px` (1px), fractional values
|
|
139
|
+
- **fontSize** - xs through 9xl with line-height pairs
|
|
140
|
+
- **fontFamily** - sans, serif, mono
|
|
141
|
+
- **screens** - sm (640px), md (768px), lg (1024px), xl (1280px), 2xl (1536px)
|
|
142
|
+
- **borderRadius** - none, sm, DEFAULT, md, lg, xl, 2xl, 3xl, full
|
|
143
|
+
- **boxShadow** - sm, DEFAULT, md, lg, xl, 2xl, inner, none
|
|
144
|
+
|
|
145
|
+
All theme values can be extended or overridden via `theme.extend`.
|
|
146
|
+
|
|
147
|
+
## Utilities
|
|
148
|
+
|
|
149
|
+
### Layout
|
|
150
|
+
|
|
151
|
+
| Utility | CSS |
|
|
152
|
+
|---------|-----|
|
|
153
|
+
| `block`, `inline-block`, `flex`, `grid`, `hidden` | `display: *` |
|
|
154
|
+
| `static`, `fixed`, `absolute`, `relative`, `sticky` | `position: *` |
|
|
155
|
+
| `top-*`, `right-*`, `bottom-*`, `left-*`, `inset-*` | Positioning |
|
|
156
|
+
| `z-*` | `z-index` |
|
|
157
|
+
| `overflow-*`, `overflow-x-*`, `overflow-y-*` | Overflow |
|
|
158
|
+
| `float-*`, `clear-*` | Float & clear (includes logical `start`/`end`) |
|
|
159
|
+
| `isolate`, `isolation-auto` | Isolation |
|
|
160
|
+
| `object-cover`, `object-contain`, `object-fill`, `object-none` | Object fit |
|
|
161
|
+
| `object-top`, `object-center`, `object-bottom`, etc. | Object position |
|
|
162
|
+
| `columns-*` | Multi-column layout |
|
|
163
|
+
| `break-before-*`, `break-after-*`, `break-inside-*` | Break behavior |
|
|
164
|
+
| `box-border`, `box-content` | Box sizing |
|
|
165
|
+
| `aspect-auto`, `aspect-square`, `aspect-video` | Aspect ratio |
|
|
166
|
+
| `visible`, `invisible`, `collapse` | Visibility |
|
|
167
|
+
|
|
168
|
+
### Flexbox
|
|
169
|
+
|
|
170
|
+
| Utility | CSS |
|
|
171
|
+
|---------|-----|
|
|
172
|
+
| `flex-row`, `flex-col`, `flex-row-reverse`, `flex-col-reverse` | Direction |
|
|
173
|
+
| `flex-wrap`, `flex-nowrap`, `flex-wrap-reverse` | Wrap |
|
|
174
|
+
| `flex-1`, `flex-auto`, `flex-initial`, `flex-none` | Flex shorthand |
|
|
175
|
+
| `grow`, `grow-0`, `shrink`, `shrink-0` | Grow & shrink |
|
|
176
|
+
| `basis-*` | Flex basis (supports fractions: `basis-1/2`) |
|
|
177
|
+
| `justify-*`, `items-*`, `self-*` | Alignment |
|
|
178
|
+
| `justify-items-*`, `justify-self-*` | Justify items/self |
|
|
179
|
+
| `content-*` | Align content |
|
|
180
|
+
| `order-*`, `order-first`, `order-last`, `order-none` | Order |
|
|
181
|
+
|
|
182
|
+
### Grid
|
|
183
|
+
|
|
184
|
+
| Utility | CSS |
|
|
185
|
+
|---------|-----|
|
|
186
|
+
| `grid-cols-*`, `grid-rows-*` | Template columns/rows (1-12, none, subgrid) |
|
|
187
|
+
| `col-span-*`, `col-start-*`, `col-end-*` | Column placement |
|
|
188
|
+
| `row-span-*`, `row-start-*`, `row-end-*` | Row placement |
|
|
189
|
+
| `grid-flow-row`, `grid-flow-col`, `grid-flow-dense` | Auto flow |
|
|
190
|
+
| `auto-cols-*`, `auto-rows-*` | Auto columns/rows |
|
|
191
|
+
| `gap-*`, `gap-x-*`, `gap-y-*` | Gap |
|
|
192
|
+
| `place-content-*`, `place-items-*`, `place-self-*` | Place shortcuts |
|
|
193
|
+
|
|
194
|
+
Arbitrary grid templates use underscore-to-space conversion:
|
|
195
|
+
|
|
196
|
+
```html
|
|
197
|
+
<div class="grid grid-cols-[120px_1fr_200px]">
|
|
198
|
+
<!-- grid-template-columns: 120px 1fr 200px -->
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Spacing
|
|
202
|
+
|
|
203
|
+
| Utility | CSS |
|
|
204
|
+
|---------|-----|
|
|
205
|
+
| `p-*`, `px-*`, `py-*`, `pt-*`, `pr-*`, `pb-*`, `pl-*` | Padding |
|
|
206
|
+
| `ps-*`, `pe-*` | Padding inline start/end (logical) |
|
|
207
|
+
| `m-*`, `mx-*`, `my-*`, `mt-*`, `mr-*`, `mb-*`, `ml-*` | Margin |
|
|
208
|
+
| `ms-*`, `me-*` | Margin inline start/end (logical) |
|
|
209
|
+
| `space-x-*`, `space-y-*` | Space between children |
|
|
210
|
+
|
|
211
|
+
Negative values supported: `-m-4`, `-translate-x-1`.
|
|
212
|
+
|
|
213
|
+
### Sizing
|
|
214
|
+
|
|
215
|
+
| Utility | CSS |
|
|
216
|
+
|---------|-----|
|
|
217
|
+
| `w-*`, `h-*` | Width & height |
|
|
218
|
+
| `size-*` | Width + height shorthand |
|
|
219
|
+
| `min-w-*`, `max-w-*`, `min-h-*`, `max-h-*` | Min/max sizing |
|
|
220
|
+
|
|
221
|
+
Values: spacing scale, `auto`, `full` (100%), `screen` (100vw/vh), `min`, `max`, `fit`, fractions (`w-1/2`).
|
|
222
|
+
|
|
223
|
+
### Colors
|
|
224
|
+
|
|
225
|
+
All color utilities support opacity modifiers:
|
|
226
|
+
|
|
227
|
+
```html
|
|
228
|
+
<!-- Integer opacity (0-100 scale) -->
|
|
229
|
+
<div class="bg-blue-500/50"> <!-- 50% opacity -->
|
|
230
|
+
<div class="text-white/75"> <!-- 75% opacity -->
|
|
231
|
+
<div class="border-black/10"> <!-- 10% opacity -->
|
|
232
|
+
|
|
233
|
+
<!-- Arbitrary opacity (0-1 scale) -->
|
|
234
|
+
<div class="bg-white/[0.04]"> <!-- 4% opacity -->
|
|
235
|
+
<div class="text-black/[0.87]"> <!-- 87% opacity -->
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
| Utility | CSS |
|
|
239
|
+
|---------|-----|
|
|
240
|
+
| `bg-*` | Background color |
|
|
241
|
+
| `text-*` | Text color |
|
|
242
|
+
| `border-*` | Border color |
|
|
243
|
+
| `ring-*` | Ring color |
|
|
244
|
+
| `divide-*` | Divide color (supports opacity: `divide-white/10`) |
|
|
245
|
+
| `placeholder-*` | Placeholder color |
|
|
246
|
+
| `accent-*` | Accent color |
|
|
247
|
+
| `caret-*` | Caret color |
|
|
248
|
+
| `fill-*`, `stroke-*` | SVG fill & stroke |
|
|
249
|
+
|
|
250
|
+
Special values: `current` (currentColor), `transparent`, `inherit`, `white`, `black`.
|
|
251
|
+
|
|
252
|
+
### Typography
|
|
253
|
+
|
|
254
|
+
| Utility | CSS |
|
|
255
|
+
|---------|-----|
|
|
256
|
+
| `text-xs` through `text-9xl` | Font size |
|
|
257
|
+
| `font-thin` through `font-black` | Font weight |
|
|
258
|
+
| `font-sans`, `font-serif`, `font-mono` | Font family |
|
|
259
|
+
| `italic`, `not-italic` | Font style |
|
|
260
|
+
| `tracking-*` | Letter spacing |
|
|
261
|
+
| `leading-*` | Line height |
|
|
262
|
+
| `text-left`, `text-center`, `text-right`, `text-justify` | Alignment |
|
|
263
|
+
| `uppercase`, `lowercase`, `capitalize`, `normal-case` | Text transform |
|
|
264
|
+
| `underline`, `overline`, `line-through`, `no-underline` | Decoration |
|
|
265
|
+
| `decoration-*` | Decoration style, color, thickness |
|
|
266
|
+
| `truncate`, `text-ellipsis`, `text-clip` | Text overflow |
|
|
267
|
+
| `text-wrap`, `text-nowrap`, `text-balance`, `text-pretty` | Text wrap |
|
|
268
|
+
| `line-clamp-*` | Line clamp |
|
|
269
|
+
| `indent-*` | Text indent |
|
|
270
|
+
| `antialiased`, `subpixel-antialiased` | Font smoothing |
|
|
271
|
+
| `tabular-nums`, `lining-nums`, `oldstyle-nums`, etc. | Font variant numeric |
|
|
272
|
+
| `hyphens-none`, `hyphens-manual`, `hyphens-auto` | Hyphens |
|
|
273
|
+
| `whitespace-*` | White space |
|
|
274
|
+
| `break-normal`, `break-words`, `break-all` | Word break |
|
|
275
|
+
|
|
276
|
+
### Borders
|
|
277
|
+
|
|
278
|
+
| Utility | CSS |
|
|
279
|
+
|---------|-----|
|
|
280
|
+
| `border`, `border-0`, `border-2`, `border-4`, `border-8` | Border width |
|
|
281
|
+
| `border-t`, `border-r`, `border-b`, `border-l` | Side borders |
|
|
282
|
+
| `border-s`, `border-e` | Logical side borders (inline-start/end) |
|
|
283
|
+
| `border-x`, `border-y` | Axis borders |
|
|
284
|
+
| `border-solid`, `border-dashed`, `border-dotted`, `border-double`, `border-none` | Style |
|
|
285
|
+
| `rounded-*` | Border radius |
|
|
286
|
+
| `rounded-s-*`, `rounded-e-*` | Logical border radius |
|
|
287
|
+
| `rounded-ss-*`, `rounded-se-*`, `rounded-es-*`, `rounded-ee-*` | Corner-specific logical radius |
|
|
288
|
+
| `outline-*` | Outline width, style, color |
|
|
289
|
+
| `outline-offset-*` | Outline offset |
|
|
290
|
+
| `ring-*`, `ring-offset-*` | Ring |
|
|
291
|
+
| `divide-x`, `divide-y` | Divide width |
|
|
292
|
+
| `divide-*` | Divide color, style, opacity |
|
|
293
|
+
|
|
294
|
+
### Effects & Filters
|
|
295
|
+
|
|
296
|
+
| Utility | CSS |
|
|
297
|
+
|---------|-----|
|
|
298
|
+
| `shadow-*` | Box shadow |
|
|
299
|
+
| `shadow-{color}` | Shadow color |
|
|
300
|
+
| `opacity-*` | Opacity (0-100) |
|
|
301
|
+
| `mix-blend-*` | Mix blend mode |
|
|
302
|
+
| `bg-blend-*` | Background blend mode |
|
|
303
|
+
| `blur-*`, `brightness-*`, `contrast-*`, `grayscale-*` | Filters |
|
|
304
|
+
| `invert-*`, `saturate-*`, `sepia-*`, `hue-rotate-*` | Filters |
|
|
305
|
+
| `drop-shadow-*` | Drop shadow filter |
|
|
306
|
+
| `backdrop-blur-*`, `backdrop-brightness-*`, etc. | Backdrop filters |
|
|
307
|
+
|
|
308
|
+
### Backgrounds & Gradients
|
|
309
|
+
|
|
310
|
+
| Utility | CSS |
|
|
311
|
+
|---------|-----|
|
|
312
|
+
| `bg-fixed`, `bg-local`, `bg-scroll` | Background attachment |
|
|
313
|
+
| `bg-clip-border`, `bg-clip-padding`, `bg-clip-content`, `bg-clip-text` | Background clip |
|
|
314
|
+
| `bg-top`, `bg-center`, `bg-bottom`, etc. | Background position |
|
|
315
|
+
| `bg-repeat`, `bg-no-repeat`, `bg-repeat-x`, `bg-repeat-y` | Background repeat |
|
|
316
|
+
| `bg-auto`, `bg-cover`, `bg-contain` | Background size |
|
|
317
|
+
|
|
318
|
+
**Linear gradients:**
|
|
319
|
+
|
|
320
|
+
```html
|
|
321
|
+
<div class="bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500">
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Directions: `bg-gradient-to-{t,tr,r,br,b,bl,l,tl}`.
|
|
325
|
+
|
|
326
|
+
**Radial gradients:**
|
|
327
|
+
|
|
328
|
+
```html
|
|
329
|
+
<div class="bg-radial from-white to-blue-500">
|
|
330
|
+
<div class="bg-radial-at-t from-yellow-200 to-orange-500">
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Positions: `bg-radial-at-{t,tr,r,br,b,bl,l,tl,c}`.
|
|
334
|
+
|
|
335
|
+
**Conic gradients:**
|
|
336
|
+
|
|
337
|
+
```html
|
|
338
|
+
<div class="bg-conic from-red-500 via-yellow-500 to-green-500">
|
|
339
|
+
<div class="bg-conic-from-r from-blue-500 to-purple-500">
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Starting angles: `bg-conic-from-{t,tr,r,br,b,bl,l,tl}`.
|
|
343
|
+
|
|
344
|
+
### Transforms & Transitions
|
|
345
|
+
|
|
346
|
+
| Utility | CSS |
|
|
347
|
+
|---------|-----|
|
|
348
|
+
| `scale-*`, `scale-x-*`, `scale-y-*` | Scale |
|
|
349
|
+
| `rotate-*` | Rotate |
|
|
350
|
+
| `translate-x-*`, `translate-y-*` | Translate |
|
|
351
|
+
| `skew-x-*`, `skew-y-*` | Skew |
|
|
352
|
+
| `origin-*` | Transform origin |
|
|
353
|
+
| `transition`, `transition-all`, `transition-colors`, `transition-opacity`, `transition-shadow`, `transition-transform` | Transition property |
|
|
354
|
+
| `duration-*` | Transition duration |
|
|
355
|
+
| `ease-linear`, `ease-in`, `ease-out`, `ease-in-out` | Timing function |
|
|
356
|
+
| `delay-*` | Transition delay |
|
|
357
|
+
| `animate-spin`, `animate-ping`, `animate-pulse`, `animate-bounce`, `animate-none` | Animation |
|
|
358
|
+
| `perspective-*` | 3D perspective |
|
|
359
|
+
| `backface-visible`, `backface-hidden` | Backface visibility |
|
|
360
|
+
|
|
361
|
+
### Interactivity
|
|
362
|
+
|
|
363
|
+
| Utility | CSS |
|
|
364
|
+
|---------|-----|
|
|
365
|
+
| `cursor-*` | Cursor style |
|
|
366
|
+
| `pointer-events-none`, `pointer-events-auto` | Pointer events |
|
|
367
|
+
| `resize`, `resize-x`, `resize-y`, `resize-none` | Resize |
|
|
368
|
+
| `select-none`, `select-text`, `select-all`, `select-auto` | User select |
|
|
369
|
+
| `scroll-auto`, `scroll-smooth` | Scroll behavior |
|
|
370
|
+
| `scroll-m-*`, `scroll-p-*` | Scroll margin/padding |
|
|
371
|
+
| `snap-x`, `snap-y`, `snap-both`, `snap-mandatory`, `snap-proximity` | Scroll snap |
|
|
372
|
+
| `snap-start`, `snap-end`, `snap-center`, `snap-align-none` | Snap align |
|
|
373
|
+
| `touch-auto`, `touch-none`, `touch-manipulation`, `touch-pan-*` | Touch action |
|
|
374
|
+
| `will-change-auto`, `will-change-scroll`, `will-change-contents`, `will-change-transform` | Will change |
|
|
375
|
+
| `appearance-none`, `appearance-auto` | Appearance |
|
|
376
|
+
| `scrollbar-auto`, `scrollbar-thin`, `scrollbar-none` | Scrollbar width |
|
|
377
|
+
|
|
378
|
+
### Content
|
|
379
|
+
|
|
380
|
+
```html
|
|
381
|
+
<div class="before:content-['*'] before:text-red-500">Required</div>
|
|
382
|
+
<div class="before:content-empty before:block before:h-4"></div>
|
|
383
|
+
<div class="after:content-[attr(data-count)]"></div>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
| Utility | CSS |
|
|
387
|
+
|---------|-----|
|
|
388
|
+
| `content-none` | `content: none` |
|
|
389
|
+
| `content-empty` | `content: ""` |
|
|
390
|
+
| `content-['text']` | `content: 'text'` |
|
|
391
|
+
| `content-[attr(data-*)]` | `content: attr(data-*)` |
|
|
392
|
+
|
|
393
|
+
### SVG
|
|
394
|
+
|
|
395
|
+
| Utility | CSS |
|
|
396
|
+
|---------|-----|
|
|
397
|
+
| `fill-none`, `fill-current`, `fill-{color}` | Fill |
|
|
398
|
+
| `stroke-none`, `stroke-current`, `stroke-{color}` | Stroke color |
|
|
399
|
+
| `stroke-0`, `stroke-1`, `stroke-2` | Stroke width |
|
|
400
|
+
|
|
401
|
+
### Tables
|
|
402
|
+
|
|
403
|
+
| Utility | CSS |
|
|
404
|
+
|---------|-----|
|
|
405
|
+
| `border-collapse`, `border-separate` | Border collapse |
|
|
406
|
+
| `border-spacing-*` | Border spacing |
|
|
407
|
+
| `table-auto`, `table-fixed` | Table layout |
|
|
408
|
+
| `caption-top`, `caption-bottom` | Caption side |
|
|
409
|
+
|
|
410
|
+
## Variants
|
|
411
|
+
|
|
412
|
+
### Pseudo-class Variants
|
|
413
|
+
|
|
414
|
+
```html
|
|
415
|
+
<div class="hover:bg-blue-600 focus:ring-2 active:scale-95">
|
|
416
|
+
<div class="first:mt-0 last:mb-0 odd:bg-gray-50 even:bg-white">
|
|
417
|
+
<div class="disabled:opacity-50 checked:bg-blue-500 required:border-red-500">
|
|
418
|
+
<div class="focus-within:ring-2 focus-visible:outline-2">
|
|
419
|
+
<div class="visited:text-purple-600 target:ring-2">
|
|
420
|
+
<div class="open:rotate-180 empty:hidden">
|
|
421
|
+
<div class="valid:border-green-500 invalid:border-red-500 read-only:opacity-75">
|
|
422
|
+
<div class="autofill:bg-yellow-50 indeterminate:bg-gray-300">
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Negation Variants
|
|
426
|
+
|
|
427
|
+
```html
|
|
428
|
+
<div class="not-first:mt-4 not-last:mb-4">
|
|
429
|
+
<div class="not-disabled:cursor-pointer not-empty:block">
|
|
430
|
+
<div class="not-checked:bg-gray-100 not-only:border-b">
|
|
431
|
+
<div class="not-first-of-type:pt-4 not-last-of-type:pb-4">
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Pseudo-element Variants
|
|
435
|
+
|
|
436
|
+
```html
|
|
437
|
+
<div class="before:content-[''] before:block before:h-4">
|
|
438
|
+
<div class="after:content-['*'] after:text-red-500">
|
|
439
|
+
<li class="marker:text-blue-500">Item</li>
|
|
440
|
+
<input class="placeholder:text-gray-400">
|
|
441
|
+
<p class="selection:bg-blue-200">Selectable text</p>
|
|
442
|
+
<input class="file:bg-blue-50 file:border-0" type="file">
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Responsive Variants
|
|
446
|
+
|
|
447
|
+
```html
|
|
448
|
+
<div class="p-4 sm:p-6 md:p-8 lg:p-12 xl:p-16 2xl:p-20">
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
| Variant | Breakpoint |
|
|
452
|
+
|---------|-----------|
|
|
453
|
+
| `sm:` | 640px |
|
|
454
|
+
| `md:` | 768px |
|
|
455
|
+
| `lg:` | 1024px |
|
|
456
|
+
| `xl:` | 1280px |
|
|
457
|
+
| `2xl:` | 1536px |
|
|
458
|
+
|
|
459
|
+
### Dark & Light Mode
|
|
460
|
+
|
|
461
|
+
```html
|
|
462
|
+
<div class="bg-white dark:bg-gray-900 light:bg-gray-50">
|
|
463
|
+
<div class="text-gray-900 dark:text-white">
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Uses class-based strategy (`.dark`/`.light` parent class).
|
|
467
|
+
|
|
468
|
+
### Group & Peer Variants
|
|
469
|
+
|
|
470
|
+
```html
|
|
471
|
+
<!-- Group: parent state affects children -->
|
|
472
|
+
<div class="group">
|
|
473
|
+
<p class="group-hover:text-blue-500">Hovering parent</p>
|
|
474
|
+
<p class="group-focus:ring-2">Parent focused</p>
|
|
475
|
+
<p class="group-has-[:checked]:bg-blue-50">Parent has checked input</p>
|
|
476
|
+
</div>
|
|
477
|
+
|
|
478
|
+
<!-- Named groups (for nesting) -->
|
|
479
|
+
<div class="group/card">
|
|
480
|
+
<div class="group/button">
|
|
481
|
+
<span class="group-hover/card:text-blue-500">Card hovered</span>
|
|
482
|
+
</div>
|
|
483
|
+
</div>
|
|
484
|
+
|
|
485
|
+
<!-- Peer: sibling state affects element -->
|
|
486
|
+
<input class="peer" />
|
|
487
|
+
<p class="peer-invalid:text-red-500">Error message</p>
|
|
488
|
+
<p class="peer-has-[:checked]:text-green-500">Checked sibling</p>
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### has: Variant
|
|
492
|
+
|
|
493
|
+
```html
|
|
494
|
+
<div class="has-[:focus]:ring-2"> <!-- :has(:focus) -->
|
|
495
|
+
<div class="has-[input:checked]:bg-blue-50"> <!-- :has(input:checked) -->
|
|
496
|
+
<div class="has-[>img]:p-4"> <!-- :has(>img) -->
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### aria-* Variants
|
|
500
|
+
|
|
501
|
+
```html
|
|
502
|
+
<div class="aria-disabled:opacity-50"> <!-- [aria-disabled="true"] -->
|
|
503
|
+
<div class="aria-expanded:rotate-180"> <!-- [aria-expanded="true"] -->
|
|
504
|
+
<div class="aria-[sort=ascending]:text-blue-500"> <!-- [aria-sort=ascending] -->
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### data-* Variants
|
|
508
|
+
|
|
509
|
+
```html
|
|
510
|
+
<div class="data-loading:opacity-50"> <!-- [data-loading] -->
|
|
511
|
+
<div class="data-[state=active]:bg-white"> <!-- [data-state=active] -->
|
|
512
|
+
<div class="data-[theme=dark]:bg-black"> <!-- [data-theme=dark] -->
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Media Query Variants
|
|
516
|
+
|
|
517
|
+
```html
|
|
518
|
+
<div class="landscape:flex-row portrait:flex-col">
|
|
519
|
+
<div class="motion-safe:animate-bounce motion-reduce:animate-none">
|
|
520
|
+
<div class="contrast-more:border-2 contrast-less:border-0">
|
|
521
|
+
<div class="forced-colors:border print:hidden">
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Media variants stack with responsive variants:
|
|
525
|
+
|
|
526
|
+
```html
|
|
527
|
+
<div class="lg:landscape:flex-row">
|
|
528
|
+
<!-- @media (min-width: 1024px) and (orientation: landscape) -->
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### @supports Variant
|
|
532
|
+
|
|
533
|
+
```html
|
|
534
|
+
<div class="supports-[display:grid]:grid">
|
|
535
|
+
<div class="supports-[backdrop-filter:blur(0)]:backdrop-blur-sm">
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Container Queries
|
|
539
|
+
|
|
540
|
+
```html
|
|
541
|
+
<div class="@container">
|
|
542
|
+
<div class="@sm:flex @md:grid @lg:grid-cols-3">
|
|
543
|
+
</div>
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Direction Variants
|
|
547
|
+
|
|
548
|
+
```html
|
|
549
|
+
<div class="rtl:text-right ltr:text-left">
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
## Arbitrary Values
|
|
553
|
+
|
|
554
|
+
Use brackets for any CSS value:
|
|
555
|
+
|
|
556
|
+
```html
|
|
557
|
+
<div class="w-[350px] h-[calc(100vh-4rem)] p-[clamp(1rem,3vw,2rem)]">
|
|
558
|
+
<div class="text-[#1a1a1a] bg-[rgb(255,0,0)] border-[oklch(50%_0.2_240)]">
|
|
559
|
+
<div class="grid-cols-[120px_1fr_200px]"> <!-- underscores become spaces -->
|
|
560
|
+
<div class="text-[clamp(1rem,3vw,2rem)]">
|
|
561
|
+
<div class="font-[600]">
|
|
562
|
+
<div class="tracking-[0.2em]">
|
|
563
|
+
<div class="leading-[1.7]">
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Arbitrary Properties
|
|
567
|
+
|
|
568
|
+
```html
|
|
569
|
+
<div class="[mask-type:luminance]">
|
|
570
|
+
<div class="[text-wrap:balance]">
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Type Hints
|
|
574
|
+
|
|
575
|
+
```html
|
|
576
|
+
<div class="text-[color:var(--brand)]">
|
|
577
|
+
<div class="text-[length:var(--size)]">
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
## Important Modifier
|
|
581
|
+
|
|
582
|
+
Prefix with `!` to apply `!important`:
|
|
583
|
+
|
|
584
|
+
```html
|
|
585
|
+
<div class="!p-4 !text-red-500">
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Presets
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
import type { Preset } from '@ts-css/core'
|
|
592
|
+
|
|
593
|
+
const myPreset: Preset = {
|
|
594
|
+
name: 'my-preset',
|
|
595
|
+
theme: {
|
|
596
|
+
extend: {
|
|
597
|
+
colors: {
|
|
598
|
+
brand: '#3b82f6',
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
shortcuts: {
|
|
603
|
+
btn: 'px-4 py-2 rounded font-semibold',
|
|
604
|
+
},
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
export default {
|
|
608
|
+
presets: [myPreset],
|
|
609
|
+
}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
## Shortcuts
|
|
613
|
+
|
|
614
|
+
Define reusable class combinations:
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
export default {
|
|
618
|
+
shortcuts: {
|
|
619
|
+
'btn': 'px-4 py-2 rounded-lg font-semibold transition-colors',
|
|
620
|
+
'btn-primary': 'btn bg-blue-500 text-white hover:bg-blue-600',
|
|
621
|
+
'card': 'rounded-xl border border-gray-200 p-6 shadow-sm',
|
|
622
|
+
},
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## Performance
|
|
627
|
+
|
|
628
|
+
Optimisations that carry the most weight:
|
|
629
|
+
|
|
630
|
+
- **O(1) static utility map** for ~80% of common utilities (display, flex, grid, transitions, etc.)
|
|
631
|
+
- **Pre-computed color map** with a flat cache for instant colour lookups
|
|
632
|
+
- **Class-level caching** prevents duplicate generation
|
|
633
|
+
- **Selector and media-query caching** avoids rebuilding either across rebuilds
|
|
634
|
+
- **Negative match cache** to skip known-unmatched utilities
|
|
635
|
+
- **Memoised `toCSS()`** — serialising is O(rules) and watch mode calls it every
|
|
636
|
+
pass, usually with nothing new to say; a revision counter turns an unchanged
|
|
637
|
+
rebuild into three integer comparisons
|
|
638
|
+
- **Theme-derived tables memoised per sub-object** — a build reusing the default
|
|
639
|
+
theme skips the palette scan entirely, which was two thirds of a cold build
|
|
640
|
+
- **Allocation-lean serialiser** — sorts an index array and groups in one pass,
|
|
641
|
+
rather than a wrapper object per rule plus two intermediate arrays
|
|
642
|
+
- **Content-hashed atomic rules** so two components declaring `padding: 16`
|
|
643
|
+
share one class
|
|
644
|
+
|
|
645
|
+
Cold build, Apple M3 Pro, Bun 1.3.14 — every engine constructed from scratch
|
|
646
|
+
per iteration, all given a utilities-only input so they are asked for the same
|
|
647
|
+
thing:
|
|
648
|
+
|
|
649
|
+
| Scenario | ts-css | UnoCSS | Tailwind v4 | Tailwind v3 |
|
|
650
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
651
|
+
| Simple utilities (10) | **11.52 µs** | 901.82 µs | 987.70 µs | 11.77 ms |
|
|
652
|
+
| Real-world components (~60) | **64.95 µs** | 1.77 ms | 1.21 ms | 11.95 ms |
|
|
653
|
+
| Full project (~800) | **208.23 µs** | 4.55 ms | 1.66 ms | 13.11 ms |
|
|
654
|
+
| 1000 arbitrary values | **992.54 µs** | 97.71 ms | 3.33 ms | 26.55 ms |
|
|
655
|
+
|
|
656
|
+
Warm rebuild — engines held open the way watch mode holds them, every one
|
|
657
|
+
answering from its own cache:
|
|
658
|
+
|
|
659
|
+
| Scenario | ts-css | Tailwind v4 | UnoCSS |
|
|
660
|
+
| --- | ---: | ---: | ---: |
|
|
661
|
+
| Simple utilities (10) | **34.47 ns** | 100.65 ns | 34.66 µs |
|
|
662
|
+
| Full project (~800) | **796.45 ns** | 1.89 µs | 358.63 µs |
|
|
663
|
+
|
|
664
|
+
Style objects vs StyleX, with a correctness gate asserting both emit the same
|
|
665
|
+
number of atomic rules:
|
|
666
|
+
|
|
667
|
+
| Workload | ts-css | StyleX |
|
|
668
|
+
| --- | ---: | ---: |
|
|
669
|
+
| component (8 declarations) | **28.81 µs** | 507.29 µs |
|
|
670
|
+
| design system (200) | **531.81 µs** | 11.05 ms |
|
|
671
|
+
|
|
672
|
+
Full methodology, including the two flaws an earlier revision of the benchmark
|
|
673
|
+
had, is in the [repository README](https://github.com/cwcss/crosswind#performance).
|
|
674
|
+
|
|
675
|
+
## License
|
|
676
|
+
|
|
677
|
+
MIT
|