@pre.style/core 3.1.0-alpha.2 → 3.1.0-alpha.4
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 +409 -3
- package/dist/cn.d.ts +2 -1
- package/dist/cn.js +4 -2
- package/dist/compiler/block.d.ts +3 -0
- package/dist/compiler/block.js +17 -4
- package/dist/compiler/compiler.d.ts +17 -0
- package/dist/compiler/compiler.js +90 -22
- package/dist/compiler/config.d.ts +1 -0
- package/dist/compiler/config.js +4 -0
- package/dist/compiler/custom-properties.d.ts +5 -0
- package/dist/compiler/custom-properties.js +150 -0
- package/dist/compiler/index.d.ts +3 -2
- package/dist/compiler/index.js +1 -1
- package/dist/compiler/interpolate.d.ts +11 -0
- package/dist/compiler/interpolate.js +318 -0
- package/dist/compiler/libraries.d.ts +2 -0
- package/dist/compiler/libraries.js +9 -2
- package/dist/compiler/mapper.js +3 -1
- package/dist/compiler/stylesheet.js +9 -0
- package/dist/compiler/variables.d.ts +13 -0
- package/dist/compiler/variables.js +272 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/runtime-dev.d.ts +2 -0
- package/dist/runtime-dev.js +9 -0
- package/dist/runtime.d.ts +16 -0
- package/dist/runtime.js +105 -0
- package/package.json +11 -1
- package/package.schema.json +8 -0
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ After the build, `button` is a plain string, `"B C E D A G F"`. The block is rep
|
|
|
50
50
|
}
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
- **No runtime.** Blocks are replaced with class strings during the build
|
|
53
|
+
- **No runtime by default.** Blocks are replaced with class strings during the build, and nothing is shipped to parse or inject CSS. The exception is [runtime values](#runtime-values), which you opt in to by writing `${…}`.
|
|
54
54
|
- **Small CSS.** Each declaration is written once, however many components use it, so the CSS file grows slowly as your project grows.
|
|
55
55
|
- **Just CSS.** Blocks are standard CSS (with nesting) or SCSS. There's no new syntax to learn.
|
|
56
56
|
- **Fast.** Built on [Lightning CSS](https://lightningcss.dev/). Every block is parsed once, in one process.
|
|
@@ -161,7 +161,328 @@ const title = scss`
|
|
|
161
161
|
`;
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
### Runtime values
|
|
165
|
+
|
|
166
|
+
Use `${…}` for values that are only known while your app runs, such as props:
|
|
167
|
+
|
|
168
|
+
```jsx
|
|
169
|
+
function Bar({ width }) {
|
|
170
|
+
const bar = css`
|
|
171
|
+
width: ${width}px;
|
|
172
|
+
`;
|
|
173
|
+
return <div className={bar} />;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Each `${…}` becomes a custom property, and the block's CSS is still written to the CSS file during the build:
|
|
178
|
+
|
|
179
|
+
```css
|
|
180
|
+
@property --A {
|
|
181
|
+
syntax: "*";
|
|
182
|
+
inherits: false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.A {
|
|
186
|
+
width: var(--A);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
In the browser, a small runtime generates one class for each distinct value and inserts its rule once. `bar` is still a plain string, `"A _A3fK9"`, so it works anywhere a class string does, including `cn`.
|
|
191
|
+
|
|
192
|
+
```css
|
|
193
|
+
._A3fK9 {
|
|
194
|
+
--A: 240px;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
> [!IMPORTANT]
|
|
199
|
+
> **`${…}` adds JavaScript to your bundle.** It's the one part of pre.style with a runtime, and only apps that use `${…}` load it. Everything else stays static, so use `${…}` only for values that change at runtime. For constants, such as design tokens, use [Sass or prepended files](#sharing-values-with-prepended-files), which cost nothing in the browser. `${tokens.red}` uses the runtime, even though its value never changes.
|
|
200
|
+
|
|
201
|
+
The generated `@property` keeps the variable from inheriting. Variables are shared by every block with the same declaration, so without it, an element with no value of its own would take its parent's.
|
|
202
|
+
|
|
203
|
+
#### Default values
|
|
204
|
+
|
|
205
|
+
Write `var()` around a value to give it a default:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
css`
|
|
209
|
+
width: var(${width}px, 120px);
|
|
210
|
+
color: var(${color}, currentColor);
|
|
211
|
+
background-color: var(${color}, var(--surface));
|
|
212
|
+
`;
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
```css
|
|
216
|
+
.A {
|
|
217
|
+
width: var(--A, 120px);
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The default can be any CSS. It's used when the value is `null` or `undefined`, in which case no class is generated, and until the runtime sets the value in the browser (see [Server rendering](#server-rendering)). A value of the wrong type doesn't use it: `width: var(${"red"}, 120px)` resets `width`. To catch those, use [`type()`](#typed-values-with-type).
|
|
222
|
+
|
|
223
|
+
#### Several values
|
|
224
|
+
|
|
225
|
+
Each `${…}` and the text touching it is one value, with its own variable:
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
css`
|
|
229
|
+
color: #${hex};
|
|
230
|
+
margin-top: calc(${gap}px * 2);
|
|
231
|
+
background-image: linear-gradient(${from}, ${to});
|
|
232
|
+
`;
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Values inside one `var()` share a variable, and so share a default:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
css`
|
|
239
|
+
margin: var(${y}px ${x}px, auto);
|
|
240
|
+
`;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
```css
|
|
244
|
+
.A {
|
|
245
|
+
margin: var(--A, auto);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Values work in nested selectors too, including pseudo-elements and other elements. The generated class's rule uses the same selector, so the value reaches them:
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
css`
|
|
253
|
+
& > * {
|
|
254
|
+
margin-top: ${gap}px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
& + li {
|
|
258
|
+
color: ${color};
|
|
259
|
+
}
|
|
260
|
+
`;
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
```css
|
|
264
|
+
/* Inserted by the runtime */
|
|
265
|
+
._A3fK9 > * {
|
|
266
|
+
--A: 8px;
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### Strings and URLs
|
|
271
|
+
|
|
272
|
+
Write quotes around `${…}` for a string, with any text you like. The runtime escapes the value, and numbers work too. `url()` needs its quotes:
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
css`
|
|
276
|
+
background-image: url("/img/${name}.png");
|
|
277
|
+
|
|
278
|
+
&::before {
|
|
279
|
+
content: var("Count: ${count}", "Loading");
|
|
280
|
+
}
|
|
281
|
+
`;
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
```css
|
|
285
|
+
/* Inserted by the runtime */
|
|
286
|
+
._A3fK9 {
|
|
287
|
+
--A: url("/img/photo.png");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
._B7xQ2::before {
|
|
291
|
+
--B: "Count: 5";
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The whole string is the value, so a default goes around it, as above. `content: ${x}` without quotes doesn't add any: use it for values that are already CSS, such as `none` or `counter(n)`. In `scss` blocks, `#{…}` can't be in the same value as `${…}`.
|
|
296
|
+
|
|
297
|
+
#### Custom properties
|
|
298
|
+
|
|
299
|
+
Name a custom property to share a value: with other elements, which inherit it, with something else that reads that name, or across several declarations in a block. For a value that's only used on one element, write `${…}` directly instead. A name is global, so two blocks using the same one can affect each other. To avoid that, use distinctive names, or [`customPropertyLocalization`](#local-custom-properties).
|
|
300
|
+
|
|
301
|
+
A custom property set directly on the element is set by name. It needs no generated variable or `@property`, only the generated class:
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
const badge = css`
|
|
305
|
+
--brand: ${color};
|
|
306
|
+
color: var(--brand);
|
|
307
|
+
`;
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
```css
|
|
311
|
+
.A {
|
|
312
|
+
color: var(--brand);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* Inserted by the runtime */
|
|
316
|
+
._B7xQ2 {
|
|
317
|
+
--brand: #00f;
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
To give it a default, write `var()` around the value, or register it with `@property`, whose `initial-value` is the default. Write the rule yourself for full control, such as `inherits: true`:
|
|
322
|
+
|
|
323
|
+
```js
|
|
324
|
+
css`
|
|
325
|
+
@property --brand {
|
|
326
|
+
syntax: "<color>";
|
|
327
|
+
inherits: false;
|
|
328
|
+
initial-value: #f00;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
--brand: ${color};
|
|
332
|
+
color: var(--brand);
|
|
333
|
+
`;
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
With `inherits: true`, an element with no value of its own takes its parent's rather than the `initial-value`.
|
|
337
|
+
|
|
338
|
+
#### Typed values with `type()`
|
|
339
|
+
|
|
340
|
+
Add `type()` to say what kind of value a variable holds, the same way as in [`attr()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/attr). It takes any [`@property` syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@property/syntax):
|
|
341
|
+
|
|
342
|
+
```js
|
|
343
|
+
css`
|
|
344
|
+
width: var(${width}px type(<length>), 120px);
|
|
345
|
+
height: var(${height} type(<length> | auto), auto);
|
|
346
|
+
border-color: var(${color} type(<color>), red);
|
|
347
|
+
translate: var(${x}px ${y}px type(<length>+), 0px);
|
|
348
|
+
`;
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The default moves to the variable's `initial-value`, and the variable gets the type:
|
|
352
|
+
|
|
353
|
+
```css
|
|
354
|
+
@property --A {
|
|
355
|
+
syntax: "<length>";
|
|
356
|
+
inherits: false;
|
|
357
|
+
initial-value: 120px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.A {
|
|
361
|
+
width: var(--A);
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
On a custom property set directly on the element, it registers that property, so you don't have to write the `@property` yourself:
|
|
366
|
+
|
|
367
|
+
```js
|
|
368
|
+
css`
|
|
369
|
+
--brand: var(${color} type(<color>), #f00);
|
|
370
|
+
color: var(--brand);
|
|
371
|
+
transition: --brand 200ms;
|
|
372
|
+
`;
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
```css
|
|
376
|
+
@property --brand {
|
|
377
|
+
syntax: "<color>";
|
|
378
|
+
inherits: false;
|
|
379
|
+
initial-value: red;
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Typed values are larger, and each type and default gets its own variable, so they're opt-in. In return:
|
|
384
|
+
|
|
385
|
+
- A value of the wrong type uses the default, instead of resetting the property.
|
|
386
|
+
- The variable can be transitioned and animated, as in the example above.
|
|
387
|
+
- The default is checked against the type during the build.
|
|
388
|
+
|
|
389
|
+
A typed value must have a default, which can't use `${…}`, `var()`, or anything that depends on where it's used, such as `em`, `rem`, container units, `env()`, or `attr()`: browsers ignore the whole `@property` rule otherwise, so the build fails. Browsers without `@property` (before July 2024) have no default for a typed value until the runtime sets it.
|
|
390
|
+
|
|
391
|
+
#### Animations
|
|
392
|
+
|
|
393
|
+
`@keyframes`, `@position-try`, and `@function` read variables from the element using them, so they can hold values too:
|
|
394
|
+
|
|
395
|
+
```js
|
|
396
|
+
const bar = css`
|
|
397
|
+
@keyframes grow {
|
|
398
|
+
to {
|
|
399
|
+
width: var(${width}px, 200px);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
animation: grow 1s forwards;
|
|
404
|
+
`;
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
The generated class has to be on the element that runs the animation, since variables don't inherit.
|
|
408
|
+
|
|
409
|
+
#### Using `cn`
|
|
410
|
+
|
|
411
|
+
Blocks with values work with `cn`, and the later value wins. A `cn` call with one runs in the browser, so it includes the lookup table:
|
|
412
|
+
|
|
413
|
+
```js
|
|
414
|
+
cn(
|
|
415
|
+
css`width: ${small}px;`,
|
|
416
|
+
css`width: ${large}px;`,
|
|
417
|
+
);
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
A block whose value is `null` or `undefined` adds no class, so it doesn't clear an earlier block's value of the same variable.
|
|
421
|
+
|
|
422
|
+
Use `cn` to combine blocks that set the same declaration, rather than joining their class strings yourself. Blocks with the same declaration share a variable, so with `` `${a} ${b}` `` the value inserted last wins, whichever order the classes are in.
|
|
423
|
+
|
|
424
|
+
#### Where values can go
|
|
425
|
+
|
|
426
|
+
`${…}` becomes a `var()`, so it can only go where `var()` works: in a declaration's value. These fail the build:
|
|
427
|
+
|
|
428
|
+
```js
|
|
429
|
+
css`
|
|
430
|
+
.${name} { color: red; } /* selectors */
|
|
431
|
+
margin-${side}: 0; /* property names */
|
|
432
|
+
@media (min-width: ${bp}px) { top: 0; } /* at-rule conditions, see Sharing media queries */
|
|
433
|
+
@font-face { font-weight: ${weight}; } /* @font-face, @property, @page, and similar */
|
|
434
|
+
background-image: url(${path}); /* url() without quotes */
|
|
435
|
+
`;
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Values must be strings or numbers, or `null` or `undefined` to use the default. Anything else, including `false`, `NaN`, and objects, [throws an error](#runtime-errors) when the block runs, so write `${open ? width : null}` rather than `${open && width}`. To combine blocks, use `cn` rather than interpolating one into another.
|
|
439
|
+
|
|
440
|
+
#### Server rendering
|
|
441
|
+
|
|
442
|
+
The server renders the same class strings as the browser, so hydration matches, but it doesn't write the generated rules. The page uses the defaults until the runtime sets the values in the browser, so pick a default close to the usual value to avoid a visible jump. Without a default, the declaration has no effect until then.
|
|
443
|
+
|
|
444
|
+
Values need code that runs in the browser. `${…}` fails the build in HTML, and in the frontmatter and templates of Astro components, where it could never be set (Astro client scripts are fine). React Server Components have the same problem, but the build can't detect it there: use client components for blocks with values.
|
|
445
|
+
|
|
446
|
+
#### Browser support
|
|
447
|
+
|
|
448
|
+
Runtime values work in every [Baseline](https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility) browser. They need [constructable stylesheets](https://caniuse.com/mdn-api_document_adoptedstylesheets) and [`@property`](https://caniuse.com/mdn-css_at-rules_property), so they fully work from:
|
|
449
|
+
|
|
450
|
+
| Chrome and Edge | Firefox | Safari and iOS Safari | Samsung Internet |
|
|
451
|
+
| --- | --- | --- | --- |
|
|
452
|
+
| 99 | 128 | 16.4 | 18 |
|
|
453
|
+
|
|
454
|
+
In older browsers:
|
|
455
|
+
|
|
456
|
+
- **Without `@property`** (Firefox 101 to 127), values work, but an element with no value of its own takes its parent's instead of its default, and a typed value has no default.
|
|
457
|
+
- **Without constructable stylesheets, or where they can't be added to** (Chrome before 99, Firefox before 101, Safari before 16.4), values aren't set, so the page keeps its defaults.
|
|
458
|
+
|
|
459
|
+
`@keyframes` works wherever these do. `@position-try` is newer: it needs Chrome 125, Firefox 147, or Safari 26. `@function` isn't Baseline: it needs Chrome 139, and isn't yet in Firefox or Safari, so a block using it there keeps the rest of its styles but not the function's.
|
|
460
|
+
|
|
461
|
+
#### When to use `style` instead
|
|
462
|
+
|
|
463
|
+
A block compiles to a class string, so the runtime can't tell which element it's for. That's why each distinct value gets its own class, rather than one class whose value changes: two elements from the same block can have different values, as in a list. So the class changes with the value, and every new value adds a rule that's never removed.
|
|
464
|
+
|
|
465
|
+
That suits values from a small set, such as a theme color or a chosen size, where going back to a value reuses its class. For values that change constantly, such as a counter, a drag position, or anything set per frame, use the `style` attribute and read it from a static block. The class never changes, no rules are added, the server renders the real value, and no runtime is needed:
|
|
466
|
+
|
|
467
|
+
```jsx
|
|
468
|
+
const box = css`
|
|
469
|
+
transform: translateX(var(--x, 0));
|
|
470
|
+
`;
|
|
471
|
+
|
|
472
|
+
<div className={box} style={{ "--x": `${x}px` }} />;
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
| | `${…}` | `style` |
|
|
476
|
+
| --- | --- | --- |
|
|
477
|
+
| The class | Changes with the value | Stays the same |
|
|
478
|
+
| CSS rules | One per distinct value, kept | None added |
|
|
479
|
+
| On the server | The default, until the runtime runs | The real value |
|
|
480
|
+
| In `cn` | Later values win | Not involved |
|
|
481
|
+
| Runtime | Needed | Not needed |
|
|
482
|
+
| In pseudo-elements and other elements | Reaches them | Only if the property inherits |
|
|
483
|
+
| Best for | Values from a small set | Values that change constantly |
|
|
484
|
+
|
|
485
|
+
The demos' Interpolation pages show both side by side.
|
|
165
486
|
|
|
166
487
|
### Selectors
|
|
167
488
|
|
|
@@ -208,6 +529,41 @@ By default, keyframes names are global, so two blocks that each define a differe
|
|
|
208
529
|
- Names a block or file doesn't define are left as they are, so `animation-name: spin` still refers to a global `@keyframes spin`, for example one in a CSS file that pre.style doesn't process. That also means a block can't use keyframes another block defines.
|
|
209
530
|
- Names in custom properties (`--anim: spin`) aren't renamed, because pre.style can't tell they name keyframes.
|
|
210
531
|
|
|
532
|
+
#### Local custom properties
|
|
533
|
+
|
|
534
|
+
By default, custom properties are global, so two blocks that each set `--gap` on nested elements affect each other. Set `"customPropertyLocalization": true` in your `package.json`'s `pre.style` field to give each custom property a generated name:
|
|
535
|
+
|
|
536
|
+
```js
|
|
537
|
+
const card = css`
|
|
538
|
+
--gap: 8px;
|
|
539
|
+
padding: var(--gap);
|
|
540
|
+
`;
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
```css
|
|
544
|
+
.A {
|
|
545
|
+
--B: 8px;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.C {
|
|
549
|
+
padding: var(--B);
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
- A name a block or stylesheet file declares (with `--x: …` or `@property --x`) gets a generated name there, including in `var()`, `transition-property`, `will-change`, `@keyframes`, and `@container style()`. In a stylesheet file, every class in the file shares it.
|
|
554
|
+
- Names it only reads are left as they are, so `color: var(--token)` still refers to a global `--token`.
|
|
555
|
+
- Names from [prepended files](#sharing-values-with-prepended-files) are left as they are, even when a block sets them, so they're the way to share custom properties.
|
|
556
|
+
- Other names that start with `--`, such as `anchor-name` and `@custom-media`, aren't custom properties, and aren't renamed.
|
|
557
|
+
- [Runtime values](#custom-properties) follow: `--brand: ${color}` sets the generated name.
|
|
558
|
+
|
|
559
|
+
> [!WARNING]
|
|
560
|
+
> This changes what a block's CSS means, and failures are silent:
|
|
561
|
+
>
|
|
562
|
+
> - `style={{ "--gap": "16px" }}` no longer overrides a block's `--gap`, since the block's name has changed. Neither does `getPropertyValue("--gap")` find it.
|
|
563
|
+
> - A child block can't read a `--gap` its parent block sets. Put a value that's shared between blocks in a prepended file.
|
|
564
|
+
|
|
565
|
+
**It's for avoiding collisions, not for smaller output.** Blocks stop sharing classes for the same declaration, and in our tests that usually made the CSS larger: 50 blocks setting `--gap: 8px` grew by about 170%, gzipped. Long names used once are the exception: they shrink, by about 10% gzipped. Most projects don't need this.
|
|
566
|
+
|
|
211
567
|
### `composes` in blocks
|
|
212
568
|
|
|
213
569
|
A block can build on classes from a [stylesheet file](#stylesheet-files), or on global class names, with `composes`, as in CSS Modules:
|
|
@@ -420,6 +776,45 @@ An array applies the files to every block. An object applies `css` files only to
|
|
|
420
776
|
|
|
421
777
|
Rules the files output, such as `:root { --space: 4px }`, appear in the CSS file once. Keep prepended files to things that don't output CSS, or output it once (variables, mixins, custom properties). Large libraries slow the build down.
|
|
422
778
|
|
|
779
|
+
### Sharing media queries
|
|
780
|
+
|
|
781
|
+
Use Sass variables to share media queries. They need `scss` blocks, and they compile to ordinary `@media` rules:
|
|
782
|
+
|
|
783
|
+
```scss
|
|
784
|
+
// src/styles/_variables.scss, in prependFiles.scss
|
|
785
|
+
$md: 768px;
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
```js
|
|
789
|
+
scss`
|
|
790
|
+
flex-direction: column;
|
|
791
|
+
|
|
792
|
+
@media (min-width: $md) {
|
|
793
|
+
flex-direction: row;
|
|
794
|
+
}
|
|
795
|
+
`;
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
`@custom-media` isn't resolved by pre.style. It's copied to the output as written, and no browser supports it by default yet, so `@media (--md)` rules don't apply. To use it anyway, pass the output CSS through another tool that resolves it, such as another Lightning CSS run:
|
|
799
|
+
|
|
800
|
+
```js
|
|
801
|
+
import { Features, transform } from 'lightningcss';
|
|
802
|
+
|
|
803
|
+
const { code } = transform({
|
|
804
|
+
filename: 'pre.style.css',
|
|
805
|
+
code: Buffer.from(css),
|
|
806
|
+
drafts: { customMedia: true },
|
|
807
|
+
include: Features.CustomMediaQueries,
|
|
808
|
+
});
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
or PostCSS with [`postcss-custom-media`](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media).
|
|
812
|
+
|
|
813
|
+
Either way, pre.style has already written the file by then, and it treats an alias as its own query:
|
|
814
|
+
|
|
815
|
+
- Aliases aren't sorted with `min-width` queries, so an alias for a wider breakpoint can come before a narrower query, which then wins. Check the order of the output.
|
|
816
|
+
- `cn` doesn't treat `(--md)` and the query it stands for as conflicting, so it keeps classes from both.
|
|
817
|
+
|
|
423
818
|
## Component libraries
|
|
424
819
|
|
|
425
820
|
A component library styled with pre.style ships its blocks uncompiled, and each app's pre.style plugin compiles them along with the app's own. The library's classes share names and the CSS file with the app's, so a `color: red` in the library and one in the app are the same class, and `cn` resolves conflicts between them.
|
|
@@ -471,6 +866,7 @@ A library's class names depend on the whole app, so they differ between apps. Do
|
|
|
471
866
|
| `exclude` | | Globs removed from `include`. |
|
|
472
867
|
| `prependFiles` | | Files added to the start of the library's blocks and stylesheet files, relative to the package. They apply only to the library's, not the app's, so the library's tokens and mixins are always available to it and never leak into the app. |
|
|
473
868
|
| `keyframeLocalization` | `false` | Give each `@keyframes` in the library's blocks and stylesheet files a generated name. See [Local keyframes](#local-keyframes). The library's setting applies to its blocks, whatever the app sets, since the library's styles are written for it. |
|
|
869
|
+
| `customPropertyLocalization` | `false` | Give each custom property declared in the library's blocks and stylesheet files a generated name, used only there. See [Local custom properties](#local-custom-properties). |
|
|
474
870
|
|
|
475
871
|
`"pre.style": true` uses the defaults. A library's field uses the same name and shape as an app's (see [Configuration](#configuration)). `libraries` and `quirksMode` only apply to the app, so in a library's field they're ignored, with a warning. A package that is both, such as a library with its own demo app, is read as the app when it's the Vite root, and as a library when it's a dependency.
|
|
476
872
|
|
|
@@ -568,6 +964,7 @@ Options every tool that reads your styles has to agree on (the bundler plugin, a
|
|
|
568
964
|
| `libraries` | | Component libraries to compile, by package name, in addition to dependencies with a `pre.style` field. A config replaces the package's field, `true` uses the defaults, and `false` leaves the package out. See [Component libraries](#component-libraries). |
|
|
569
965
|
| `quirksMode` | `false` | **You probably don't want this.** Use lowercase class names only, for pages in quirks mode. Names get longer. See [Quirks mode](#quirks-mode). |
|
|
570
966
|
| `keyframeLocalization` | `false` | Give each `@keyframes` in the app's blocks and stylesheet files a generated name, used only in the block or file that defines it. See [Local keyframes](#local-keyframes). Libraries use their own setting. |
|
|
967
|
+
| `customPropertyLocalization` | `false` | Give each custom property declared in the app's blocks and stylesheet files a generated name, used only there. See [Local custom properties](#local-custom-properties). |
|
|
571
968
|
|
|
572
969
|
The plugin warns about keys it doesn't know, and fails the build on values of the wrong type. For completion and validation in your editor, `@pre.style/core` includes a JSON Schema for the field, at `node_modules/@pre.style/core/package.schema.json`. In VS Code, add it to your settings:
|
|
573
970
|
|
|
@@ -650,12 +1047,21 @@ The build fails, with the file, line, and column, when:
|
|
|
650
1047
|
|
|
651
1048
|
- a block has invalid CSS syntax;
|
|
652
1049
|
- Sass fails to compile an `scss` block;
|
|
653
|
-
- a block
|
|
1050
|
+
- a block has `${…}` anywhere but a declaration's value, or in HTML or an Astro component's frontmatter or template (see [Where values can go](#where-values-can-go) and [Server rendering](#server-rendering));
|
|
1051
|
+
- a `type()` has no default, or its default is invalid for the type or depends on where it's used;
|
|
654
1052
|
- `css` or `scss` is used other than as a tagged template;
|
|
655
1053
|
- a project has an `scss` block or file but Sass isn't installed;
|
|
656
1054
|
- a stylesheet file has invalid CSS, or fails to compile with Sass;
|
|
657
1055
|
- `composes` comes after other declarations, is used in a nested selector or at-rule, names a class or file that doesn't exist, or forms a cycle.
|
|
658
1056
|
|
|
1057
|
+
### Runtime errors
|
|
1058
|
+
|
|
1059
|
+
[Runtime values](#runtime-values) are checked in the browser and on the server. Outside production builds, errors have full messages. To keep the runtime small, production builds only have a number, the value's type, and a link to [a page that explains it](https://pre.style/errors):
|
|
1060
|
+
|
|
1061
|
+
| Error | Meaning |
|
|
1062
|
+
| --- | --- |
|
|
1063
|
+
| [`pre.style error 1 (<type>)`](https://pre.style/errors/1) | A `${…}` value wasn't a string or a finite number, or `null` or `undefined` to use the default. `number` means it was `NaN` or `Infinity`. For a condition, write `${open ? width : null}` rather than `${open && width}`. |
|
|
1064
|
+
|
|
659
1065
|
## Known limitations
|
|
660
1066
|
|
|
661
1067
|
- `@charset` is dropped. Lightning CSS always writes UTF-8.
|
package/dist/cn.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type CnTable = Record<string, Record<string, string>>;
|
|
2
|
+
export type CnLookup = Map<string, readonly [prefix: string, property: string]>;
|
|
2
3
|
export type ClassValue = string | number | bigint | boolean | null | undefined | ClassDictionary | ClassValue[];
|
|
3
4
|
export type ClassDictionary = Record<string, unknown>;
|
|
4
|
-
export declare function createCn(table: CnTable): (...inputs: ClassValue[]) => string;
|
|
5
|
+
export declare function createCn(table: CnTable, lookup?: CnLookup): (...inputs: ClassValue[]) => string;
|
package/dist/cn.js
CHANGED
|
@@ -45,8 +45,10 @@ function resolve(classes, lookup) {
|
|
|
45
45
|
}
|
|
46
46
|
return kept.toReversed();
|
|
47
47
|
}
|
|
48
|
-
export function createCn(table
|
|
49
|
-
|
|
48
|
+
export function createCn(table,
|
|
49
|
+
// Filled from the table. `generatedClasses` passes one that also knows
|
|
50
|
+
// classes generated at runtime.
|
|
51
|
+
lookup = new Map()) {
|
|
50
52
|
for (const [prefix, properties] of Object.entries(table))
|
|
51
53
|
for (const [property, classes] of Object.entries(properties))
|
|
52
54
|
for (const c of classes.split(' '))
|
package/dist/compiler/block.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Declaration, Rule, SelectorList, TransformOptions, CustomAtRules } from 'lightningcss';
|
|
2
|
+
import type { Variable } from './variables.ts';
|
|
2
3
|
export type Lang = 'css' | 'scss';
|
|
3
4
|
export type LightningOptions = Omit<TransformOptions<CustomAtRules>, 'filename' | 'code' | 'sourceMap' | 'inputSourceMap' | 'projectRoot' | 'cssModules'>;
|
|
4
5
|
export interface Position {
|
|
@@ -47,6 +48,7 @@ export interface ParsedBlock {
|
|
|
47
48
|
atoms: Atom[];
|
|
48
49
|
globals: GlobalRule[];
|
|
49
50
|
composes: Compose[];
|
|
51
|
+
variables?: Variable[];
|
|
50
52
|
}
|
|
51
53
|
export declare class PreStyleError extends Error {
|
|
52
54
|
readonly position: Position | null;
|
|
@@ -70,6 +72,7 @@ export interface ParseOptions {
|
|
|
70
72
|
sass: SassCompiler | null;
|
|
71
73
|
warn: (message: string) => void;
|
|
72
74
|
keyframeLocalization: boolean;
|
|
75
|
+
customPropertyLocalization: boolean;
|
|
73
76
|
}
|
|
74
77
|
export declare const key: (value: unknown) => string;
|
|
75
78
|
export declare function parseBlock(code: string, lang: Lang, options: ParseOptions): ParsedBlock;
|
package/dist/compiler/block.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { composeVisitors, transform } from 'lightningcss';
|
|
2
2
|
import { atRuleInfo } from './at-rules.js';
|
|
3
3
|
import { expand } from './expand.js';
|
|
4
|
+
import { localizeCustomProperties } from './custom-properties.js';
|
|
5
|
+
import { SLOT_PREFIX } from './interpolate.js';
|
|
4
6
|
import { localizeKeyframes } from './keyframes.js';
|
|
5
7
|
import { compose, fromPlaceholder, isElement, PLACEHOLDER, repair } from './selectors.js';
|
|
6
8
|
import { splitBlock } from './split.js';
|
|
9
|
+
import { extractVariables } from './variables.js';
|
|
7
10
|
import { SHORTHANDS } from '../shorthands.js';
|
|
8
11
|
export class PreStyleError extends Error {
|
|
9
12
|
position;
|
|
@@ -83,11 +86,21 @@ export function parseBlock(code, lang, options) {
|
|
|
83
86
|
// Sass output positions don't match the block, so scss atoms have none.
|
|
84
87
|
const position = (loc) => lang === 'css' && loc ? toBlock(loc.line, loc.column) : null;
|
|
85
88
|
parseRules(css, options.lightningcss, position, (rules) => walk(rules, [], null, parsed, position));
|
|
86
|
-
|
|
89
|
+
let atoms = dropOverridden(parsed.atoms);
|
|
90
|
+
let { globals } = parsed;
|
|
91
|
+
if (options.customPropertyLocalization) {
|
|
92
|
+
const local = localizeCustomProperties(atoms, globals, `block\n${code}`, lang, options);
|
|
93
|
+
atoms = local.atoms(atoms);
|
|
94
|
+
({ globals } = local);
|
|
95
|
+
}
|
|
96
|
+
let variables;
|
|
97
|
+
if (code.includes(SLOT_PREFIX))
|
|
98
|
+
({ atoms, globals, variables } = extractVariables(atoms, globals));
|
|
99
|
+
const composes = parsed.composes;
|
|
87
100
|
if (!options.keyframeLocalization)
|
|
88
|
-
return { atoms, globals
|
|
89
|
-
const local = localizeKeyframes(
|
|
90
|
-
return { atoms: local.atoms(atoms), globals: local.globals, composes
|
|
101
|
+
return { atoms, globals, composes, variables };
|
|
102
|
+
const local = localizeKeyframes(globals);
|
|
103
|
+
return { atoms: local.atoms(atoms), globals: local.globals, composes, variables };
|
|
91
104
|
}
|
|
92
105
|
// Parses CSS with Lightning CSS, and passes its rules to `collect` once the
|
|
93
106
|
// user's visitor has changed them. `position` converts error locations.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Lang, LightningOptions, SassCompiler } from './block.ts';
|
|
2
|
+
import type { Slot } from './interpolate.ts';
|
|
2
3
|
import type { Printed } from './print.ts';
|
|
3
4
|
import type { CnTable } from '../cn.ts';
|
|
5
|
+
import type { CnVariables } from '../runtime.ts';
|
|
4
6
|
export type PrependFiles = string[] | {
|
|
5
7
|
css?: string[];
|
|
6
8
|
scss?: string[];
|
|
@@ -9,6 +11,7 @@ export interface Library {
|
|
|
9
11
|
dir: string;
|
|
10
12
|
prependFiles?: PrependFiles;
|
|
11
13
|
keyframeLocalization?: boolean;
|
|
14
|
+
customPropertyLocalization?: boolean;
|
|
12
15
|
}
|
|
13
16
|
export interface CompilerOptions {
|
|
14
17
|
prependFiles?: PrependFiles;
|
|
@@ -19,6 +22,7 @@ export interface CompilerOptions {
|
|
|
19
22
|
warn?: (message: string) => void;
|
|
20
23
|
quirksMode?: boolean;
|
|
21
24
|
keyframeLocalization?: boolean;
|
|
25
|
+
customPropertyLocalization?: boolean;
|
|
22
26
|
layer?: false | string;
|
|
23
27
|
}
|
|
24
28
|
export interface Block {
|
|
@@ -27,6 +31,13 @@ export interface Block {
|
|
|
27
31
|
file?: string;
|
|
28
32
|
line?: number;
|
|
29
33
|
column?: number;
|
|
34
|
+
slots?: Slot[];
|
|
35
|
+
}
|
|
36
|
+
export interface BlockVariable {
|
|
37
|
+
name: string;
|
|
38
|
+
property: string;
|
|
39
|
+
slot: Slot;
|
|
40
|
+
selector: string[] | null;
|
|
30
41
|
}
|
|
31
42
|
export interface Stylesheet {
|
|
32
43
|
code: string;
|
|
@@ -54,6 +65,10 @@ export interface ClassUsage {
|
|
|
54
65
|
order: number;
|
|
55
66
|
sources: Source[];
|
|
56
67
|
}
|
|
68
|
+
export declare function interpolatedBlock(block: Omit<Block, 'code' | 'slots'> & {
|
|
69
|
+
strings: string[];
|
|
70
|
+
expressions: string[];
|
|
71
|
+
}): Block;
|
|
57
72
|
export declare function createCompiler(options?: CompilerOptions): Promise<Compiler>;
|
|
58
73
|
export declare class Compiler {
|
|
59
74
|
#private;
|
|
@@ -63,6 +78,7 @@ export declare class Compiler {
|
|
|
63
78
|
scanStylesheet(sheet: Stylesheet): void;
|
|
64
79
|
assignNames(): void;
|
|
65
80
|
classString(block: Block): string;
|
|
81
|
+
variables(block: Block): BlockVariable[];
|
|
66
82
|
dependencies(block: Block): string[];
|
|
67
83
|
classNames(sheet: Stylesheet): string[];
|
|
68
84
|
usages(classNames: Iterable<string>): ClassUsage[];
|
|
@@ -70,4 +86,5 @@ export declare class Compiler {
|
|
|
70
86
|
stylesheet(sheet: Stylesheet): CompiledStylesheet;
|
|
71
87
|
css(): Printed;
|
|
72
88
|
cnTable(): CnTable;
|
|
89
|
+
cnVariables(): CnVariables;
|
|
73
90
|
}
|