@navecss/core 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONSUMER-ATOMS.md CHANGED
@@ -122,20 +122,22 @@ Compiles to:
122
122
  user-select: none;
123
123
  -webkit-tap-highlight-color: transparent;
124
124
  outline: none;
125
- transition-property: color, background-color, border-color, opacity, box-shadow;
126
- transition-duration: var(--nave-motion-duration-base);
127
- transition-timing-function: var(--nave-motion-easing-standard);
128
- background: var(--nave-color-action-primary);
129
- color: var(--nave-color-on-action-primary);
130
- padding: var(--nave-spacing-control-md) var(--nave-spacing-control-lg);
131
- border-radius: var(--nave-radius-control);
132
- font-weight: var(--nave-font-weight-medium);
133
125
  &:focus-visible {
134
126
  outline-style: solid;
135
127
  outline-width: var(--nave-border-width-focus, 2px);
136
128
  outline-color: var(--nave-color-border-focus, currentColor);
137
129
  outline-offset: 2px;
138
130
  }
131
+ & {
132
+ transition-property: color, background-color, border-color, opacity, box-shadow;
133
+ transition-duration: var(--nave-motion-duration-base);
134
+ transition-timing-function: var(--nave-motion-easing-standard);
135
+ background: var(--nave-color-action-primary);
136
+ color: var(--nave-color-on-action-primary);
137
+ padding: var(--nave-spacing-control-md) var(--nave-spacing-control-lg);
138
+ border-radius: var(--nave-radius-control);
139
+ font-weight: var(--nave-font-weight-medium);
140
+ }
139
141
  &:hover {
140
142
  background: var(--nave-color-action-primary-hover);
141
143
  }
@@ -293,15 +295,17 @@ Compiles to:
293
295
  display: flex;
294
296
  flex-direction: column;
295
297
  gap: var(--nave-spacing-content-md);
296
- background: var(--nave-color-surface-raised);
297
- border-radius: var(--nave-radius-card);
298
- padding: var(--nave-spacing-content-md);
299
298
  @container (width >= 28rem) {
300
299
  & {
301
300
  flex-direction: row;
302
301
  align-items: center;
303
302
  }
304
303
  }
304
+ & {
305
+ background: var(--nave-color-surface-raised);
306
+ border-radius: var(--nave-radius-card);
307
+ padding: var(--nave-spacing-content-md);
308
+ }
305
309
  }
306
310
  }
307
311
  ```
package/LICENSE CHANGED
@@ -55,3 +55,25 @@ JavaScript under dist/: the member names of a version 3 source map, the
55
55
  layout of its mappings string, the base64 VLQ encoding of each field in it,
56
56
  and the base64 alphabet. The code implementing them is this package's own, and
57
57
  everything in this package is offered under the MIT licence above.
58
+
59
+ This package also includes material copied from or derived from mdn-data
60
+ (css/properties.json in the mdn/data repository, version 2.37.2,
61
+ https://github.com/mdn/data), which its authors dedicated to the public domain
62
+ under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/),
63
+ and from CSS Logical Properties and Values Module Level 1, CSS Overflow Module
64
+ Level 3, CSS Overscroll Behavior Module Level 1, CSS Scroll Snap Module Level 1,
65
+ CSS Box Sizing Module Level 4, CSS Fonts Module Level 4, CSS Text Module Level
66
+ 4, CSS Gaps Module Level 1, CSS Fragmentation Module Level 4, CSS Backgrounds
67
+ and Borders Module Level 3, and CSS Cascading and Inheritance Module Level 5 (in
68
+ the w3c/csswg-drafts repository, at commit
69
+ 40527902c4d0840cdb7a808c8cd42302be1c3bc8). Copyright (c) 2026 World Wide Web
70
+ Consortium. Those specifications, and the repository carrying them, are
71
+ published under the W3C Software and Document License
72
+ (https://www.w3.org/copyright/software-license-2023/).
73
+
74
+ What is copied or derived is implemented in
75
+ src/directive/property-table.generated.ts, built into the JavaScript under
76
+ dist/: the names of CSS properties, the properties each shorthand sets, which
77
+ names are legacy name aliases of another property, and which logical and
78
+ physical properties belong to one group. The code using them is this package's
79
+ own, and everything in this package is offered under the MIT licence above.
package/README.md CHANGED
@@ -107,10 +107,140 @@ Requires the Vite plugin or the PostCSS plugin. Everything stays in CSS files.
107
107
  ```
108
108
 
109
109
  Declarations are inlined at build time, and nothing of `@nave` is left at run time.
110
- Pseudo-class rules (`:focus-visible` from `focusRing`), `@media` and
111
- `@container` blocks are emitted as native CSS nesting inside your rule, so they
112
- apply to every selector in a selector list. Native nesting is inside the browser
113
- floor under [Requirements](#requirements).
110
+ Pseudo-class rules (`:focus-visible` from `focusRing`), `@media` and `@container` blocks are
111
+ emitted as native CSS nesting inside your rule, each right after its own atom's declarations, so
112
+ they apply to every selector in a selector list. Native nesting is inside the browser floor under
113
+ [Requirements](#requirements), and [Order inside a rule](#order-inside-a-rule) says what wins when
114
+ your rule sets the same property.
115
+
116
+ #### Order inside a rule
117
+
118
+ `@nave a b;` expands where you write it, in the order CSS mixins will apply
119
+ `@apply --a; @apply --b;`: the declarations of `a`, then the pseudo-class,
120
+ `@media` and `@container` blocks of `a`, then the same for `b`. Whatever your rule
121
+ says after the directive comes after all of it. So of two declarations of one
122
+ property that both apply, the later one wins, unless the earlier one has the more
123
+ specific selector (an atom's pseudo-class block, such as the `:focus-visible` of
124
+ `focusRing`, does), as anywhere else in CSS.
125
+
126
+ ```css
127
+ .panel {
128
+ @nave flex hideDesktopUp;
129
+ gap: var(--nave-spacing-content-md);
130
+ }
131
+ ```
132
+
133
+ expands to:
134
+
135
+ ```css
136
+ .panel {
137
+ display: flex;
138
+ @media (width >= 75em) {
139
+ & {
140
+ display: none;
141
+ }
142
+ }
143
+ & {
144
+ gap: var(--nave-spacing-content-md);
145
+ }
146
+ }
147
+ ```
148
+
149
+ A declaration that follows a nested block is written inside `& { }`, the wrapper
150
+ Nave already uses for a directive written after a nested rule. Under a selector
151
+ list, `&` matches with the specificity of the list's most specific selector, so a
152
+ wrapped declaration can win over a rule elsewhere that a bare one would lose to;
153
+ write it above the directive if that matters.
154
+
155
+ The order you write is the order that wins:
156
+
157
+ | You write | From 75em up |
158
+ | ------------------------------------- | ---------------- |
159
+ | `@nave flex hideDesktopUp;` | hidden |
160
+ | `@nave hideDesktopUp flex;` | shown, as flex |
161
+ | `display: grid; @nave hideDesktopUp;` | hidden |
162
+ | `@nave hideDesktopUp; display: grid;` | shown, as a grid |
163
+
164
+ Name a responsive or state atom after the atoms it adjusts, and write a declaration
165
+ that should give way to an atom's variants above the directive. A declaration or a
166
+ nested block below the directive is how you customize an atom inside your own rule.
167
+
168
+ The same order applies to the focus atoms. After `@nave focusRing;`, a later
169
+ `&:focus-visible { outline-color: var(--brand) }` recolours the ring, and a later
170
+ `&:focus { outline: none }` or `&:focus-visible { outline: none }` removes it,
171
+ which leaves the element with no focus indicator from `focusRing` under keyboard
172
+ focus. After `@nave srOnlyFocusable;`, a later `&:focus-visible { position: fixed }`
173
+ places the revealed element, and a later `clip-path: inset(50%)` in the same state
174
+ hides it again, so a skip link stays hidden when it has focus. To keep the atom's
175
+ result, write the block above the directive. The order warning below reports each
176
+ of these, and says so when the ring or the reveal is lost.
177
+
178
+ **The order warning.** Where something later in the rule overrides one of an atom's
179
+ pseudo-class, `@media` or `@container` blocks with a different value, the build
180
+ prints a warning at the later content. Where moving something lets the atom win
181
+ without a new warning, it says what to move, and it always shows how to mark the
182
+ directive if the later value is meant to win:
183
+
184
+ ```text
185
+ src/panel.css:3:3: @nave: display: grid comes after "hideDesktopUp", so under @media (width >= 75em) display is grid, not none. To let "hideDesktopUp" win, move display: grid above the directive. If it is meant to win, mark the directive that names "hideDesktopUp": `@nave /* nave-allow-override: display */ hideDesktopUp;`
186
+ ```
187
+
188
+ Where no move clears the warning, as when the later value comes from another atom's
189
+ block and moving either only turns the pair round, it prints only the marker. Write
190
+ the atom that should win last, and mark the directive the warning then names.
191
+
192
+ It never fails the build, and `onUnknown` does not change it. The Vite plugin prints
193
+ it through Vite's logger, the PostCSS plugin as a PostCSS warning (and with `console.warn` on
194
+ Next.js under Turbopack, which does not print PostCSS warnings), the Lightning CSS
195
+ adapter with `console.warn`, and `navecss-core expand` on standard error, without
196
+ changing its exit code. No option turns it off for the whole project.
197
+
198
+ A directive written inside a nested rule that applies to the same element, such as
199
+ `.card { & { @nave hideDesktopUp } display: flex; }`, is checked the same way: its atoms' blocks
200
+ are compared with what follows the directive inside that nested rule, and with what each
201
+ enclosing rule holds after the nested rule.
202
+
203
+ **Marking an override you mean.** Name the property in a comment inside the
204
+ directive:
205
+
206
+ ```css
207
+ .panel {
208
+ @nave /* nave-allow-override: display */ hideDesktopUp;
209
+ display: grid;
210
+ }
211
+ ```
212
+
213
+ The comment says that what comes after this directive's atoms (a later atom in it,
214
+ or your rule's own content) is meant to win for the properties it names. It
215
+ silences the warning for those properties on this directive and nothing else: an
216
+ override of another property, or of another directive's atoms, still warns. A
217
+ shorthand covers its longhands (`outline` covers `outline-color`). You can add a
218
+ reason after `--`: `/* nave-allow-override: display -- a grid on wide screens */`.
219
+ The head `nave-allow-override:` is matched in lower case only, as `eslint-disable`
220
+ is, so a comment that writes it in another case is not a marker: it allows nothing,
221
+ and the warning it was meant to silence still prints.
222
+ The comment never reaches your CSS. Only a comment inside the directive is read,
223
+ because some build steps remove the others before Nave sees the stylesheet (Vite
224
+ under `css.transformer: 'lightningcss'`, for one). A marker naming a property that
225
+ nothing overrides any more is reported, so remove it when you reorder. The Vite plugin under
226
+ `css.transformer: 'lightningcss'` does not report one, at any targets, because it cannot tell
227
+ whether Lightning CSS has already moved what follows the directive.
228
+
229
+ #### Output size
230
+
231
+ `@nave` copies an atom's declarations into every rule that uses it. That is the design: the directive is a build-time stand-in for native CSS mixins, whose `@apply` puts a mixin's declarations into the rule that applies it ([why](https://github.com/navecss/navecss/blob/main/docs/04-adr/0006-nave-directive-stands-in-for-css-mixins.md)). A browser that supports native mixins does that expansion itself, so such a stylesheet carries each mixin's body once, plus a short `@apply` where it is used; a build step has to write the copies out.
232
+
233
+ What the copies cost, in a model of a large app (10,000 component rules using a 25-atom design system and class names of about 20 characters, minified): about 70 percent of the raw CSS, 40 percent after gzip and 30 percent after brotli at level 11. Your app will differ, so measure your own bundle. What reduces it:
234
+
235
+ - **Compress CSS ahead of time with brotli at its highest level (11)**, instead of compressing on the fly at a lower one. In the model, level 11 sent 260 KB where level 5 sent 330 KB, which saves more than any way of removing the copies that was tried (below).
236
+ - **Split CSS by route**, so a page loads a shared part and its own rules rather than the whole app's. Split into one shared part and 40 routes, a page in the model loaded 65 KB at level 11, about a quarter, though all the parts together came to 348 KB, a third more than the single bundle.
237
+
238
+ What Nave does not do, and why:
239
+
240
+ - **It does not merge the copies into shared rules with combined selector lists.** In the model that saved 0.3 percent under brotli at level 11 and made gzip output 19 percent larger, because each selector then repeats once per atom, far apart. With 7-character class names the same merge saved 2 percent under gzip, 4 under brotli at level 11 and 12 under brotli at level 5, still less than level 11 saves over level 5. It also changed the computed style of 4 to 6 percent of the elements sampled: two atoms that set the same property stop resolving in the order you wrote them, and an atom's `@media` blocks move relative to your own declarations.
241
+ - **It does not turn your `@nave` atoms into shared classes for your markup to carry** (`cx()` does that for Nave's built-in atoms, where you choose it). That would move them into the `atomic` layer, below your component CSS, so your other rules and Nave's own component styles start beating them, and the class names it would add to your JavaScript and HTML cost about as much as the CSS it would remove under brotli, and about half as much under gzip.
242
+
243
+ The lasting fix is native CSS mixins. Once they ship in a browser and the specification settles, Nave adds a flag that writes `@mixin` and `@apply`, and once all three engines support them that becomes the default.
114
244
 
115
245
  ### Escape hatch — cx() utility
116
246
 
@@ -193,8 +323,11 @@ Every custom property Nave declares is in the stylesheet you already installed,
193
323
 
194
324
  ## Setting up `@nave`
195
325
 
196
- On Vite, use the Vite plugin: it is the one route this package teaches for Vite. On
197
- Next.js, webpack, or any other pipeline that runs PostCSS plugins, use the PostCSS plugin. A host
326
+ On Vite, use the Vite plugin: it is the one route this package teaches for Vite, and for Astro,
327
+ which builds with Vite ([Astro](#astro) has its config). On
328
+ Next.js, use [`@navecss/prune`](https://github.com/navecss/navecss/tree/main/packages/prune#readme)
329
+ to ship only the atoms the build can read a use for, or the PostCSS plugin to keep every atom. On
330
+ webpack, or any other pipeline that runs PostCSS plugins, use the PostCSS plugin. A host
198
331
  that runs Lightning CSS directly uses [the Lightning CSS adapter](#lightning-css-adapter-setup),
199
332
  and a project with no bundler uses [`navecss-core expand`](#navecss-core-expand).
200
333
 
@@ -218,8 +351,8 @@ It has no dependency and no peer. It expands `@nave` after Vite's own CSS step h
218
351
  through `@import`, a Sass file (including a `@mixin` that holds a directive), a CSS Module, an
219
352
  `?inline` or `?url` import, a Vue or Svelte style block and a `.css` inside `node_modules` are all
220
353
  read as the CSS they compile to, in `vite build` and in the dev server, under either
221
- `css.transformer`. A `?raw` import returns the file's text as it is. Astro is not covered: it was
222
- not measured at this release, and no fixture of it runs.
354
+ `css.transformer`. A `?raw` import returns the file's text as it is. Astro has its own setup and
355
+ its own measured list: [Astro](#astro).
223
356
 
224
357
  **It also chooses which atoms ship.** A `cx()` call in your code whose atoms the build cannot read,
225
358
  such as `cx(variant)` with a prop, is an error in the dev server and fails `vite build`.
@@ -242,10 +375,15 @@ Nave class it cannot see has no rule unless its atom is listed in `keep`.
242
375
 
243
376
  **Under `css.transformer: 'lightningcss'`** the plugin expands exactly as it does under the default
244
377
  transformer once both floor keys below are set. At Vite's default targets, which sit below the
245
- floor, Lightning CSS lowers CSS nesting before any plugin runs, and two things change.
378
+ floor, Lightning CSS lowers CSS nesting before any plugin runs, and three things change.
246
379
  A directive written after a declaration that follows a nested rule can be moved ahead of that
247
380
  declaration: in `.a { &:hover { color: red; } display: grid; @nave block; }`, `grid` wins with no
248
- message, where the default transformer makes `block` win. A directive in a group rule nested in a
381
+ message, where the default transformer makes `block` win.
382
+ What a rule says after a directive becomes a rule of its own, after the directive's, so it
383
+ already resolves in the order [Order inside a rule](#order-inside-a-rule) describes, but no order
384
+ warning is printed for it: a later `&:focus { outline: none }` after `focusRing`, for one, removes
385
+ the ring there with no message.
386
+ A directive in a group rule nested in a
249
387
  style rule (`.a { @media (...) { @nave flex; } }`) still fails the build, but the message no longer
250
388
  names the `& { @nave ...; }` workaround, and its position is in the CSS as Lightning CSS rewrote
251
389
  it, not in your file. The plugin also drops one message and only that one: Lightning CSS's
@@ -310,8 +448,9 @@ newest 8.x at the time of each release.
310
448
  The Vite plugin also chooses which of Nave's atoms reach your stylesheet. It reads the `cx()` calls
311
449
  in the code the build compiles, dependencies included, and ships the rules for the atoms those calls
312
450
  name, for the atom of each Nave class written as text (`nave-grid` in `index.html` or in a string in
313
- your code, say), and for the atoms you list in `keep` and `keepFor`; `srOnlyFocusable` also ships
314
- whenever `srOnly` does. The dev server serves the same atoms, from the code it has read. This is
451
+ your code, say) and each Nave class a CSS Module composes from global (`composes: nave-flex from global`),
452
+ and for the atoms you list in `keep` and `keepFor`; `srOnlyFocusable` also ships whenever `srOnly`
453
+ does. The dev server serves the same atoms, from the code it has read. This is
315
454
  what `navePlugin()` does with no options. On the CSS-first path, where `@nave` copies an atom's
316
455
  declarations into your own rule, a project that never calls `cx()` and writes no Nave class ships no
317
456
  rule from the atom layer.
@@ -456,16 +595,27 @@ list: with an empty list, or none, it fails the build. Each client build records
456
595
  `nave-used-atoms.json` file described below, the atoms it read from each listed package's calls,
457
596
  where the calls it could not read are, and any `keepFor` key that names no package the build read.
458
597
 
459
- `keepFor` stands in for the package's own calls, never for a call your code makes, so three problems
598
+ `keepFor` stands in for the package's own calls, never for a call your code makes, so four problems
460
599
  in a listed package still fail the build. A module of the package that re-exports `cx` under the
461
600
  name `cx` still fails when a file of yours imports `cx` from that module itself, in an `import` or
462
601
  `export ... from` statement, or a dependency that does more than re-export it imports it from there,
463
602
  and the report gives the `cxModules` entry that lets the build read those calls. Through a module
464
603
  that only re-exports it, such as the package's own entry file or another package, a listed module
465
604
  would be two steps away, so there the report names each file of yours that takes it: import `cx`
466
- from `@navecss/core/cx` there instead. A module that gives `cx` out under another name, as a default
467
- or inside a namespace, still fails when your code takes that export, and the report names each file
468
- of yours that does: import `cx` from `@navecss/core/cx` there instead. And a module of the package
605
+ from `@navecss/core/cx` there instead. A CommonJS module that forwards it with
606
+ `module.exports = require(...)` or `exports.cn = require(...).cn`, or with what TypeScript emits for
607
+ `export { cn } from` and `export * from` when it compiles to CommonJS, counts as one that only
608
+ re-exports it. A module that gives `cx` out under another name, as a default
609
+ or inside a namespace, or by assigning it first (`export const cn = cx`), still fails when your code
610
+ takes that export, and the report names each file of yours that does: import `cx` from
611
+ `@navecss/core/cx` there instead. A module of the package that uses `cx` where the build cannot
612
+ follow it to an export, such as `obj.cn = cx`, `memo(cx)` or `{ cn: cx }`, or that takes a module
613
+ holding a `cx` export in a way the build reads neither as calls only nor as a forward,
614
+ such as the CommonJS that Babel, esbuild or Rollup write for `export * from`, fails whenever it is
615
+ part of the build, whatever your code takes from the package, because the build cannot tell whether
616
+ a call of yours goes through it. The report names the file and the form. The fix is the package's,
617
+ in a release of it or a patched copy: export `cx` by name from an ES module. Until then, ship every
618
+ atom (below). And a module of the package
469
619
  that you list in `cxModules` but that does not re-export Nave's `cx` under that name still fails:
470
620
  remove it from `cxModules`.
471
621
 
@@ -481,6 +631,11 @@ A class your code takes at run time from `@navecss/core/atoms` (`atomClassMap[va
481
631
  `toClassName('grid')`) is one the build does not read: it neither reads nor refuses it, so list its
482
632
  atom in `keep`.
483
633
 
634
+ A package listed in `keepFor` can give `cx` to your code without the build failing if it redefines
635
+ or adds a method on `Function.prototype` or `Object.prototype`, or declares its own function under
636
+ one of the names compilers reserve for their interop helpers, such as `__importStar`: the build
637
+ cannot see either.
638
+
484
639
  If your pages are rendered outside the build, by a backend that serves its own templates and uses
485
640
  Vite for its assets only, the build reads none of their classes. While `keep` is empty, the plugin
486
641
  warns when it has read no HTML page, no server render and no use of Nave at all: no `cx()` call, no
@@ -490,7 +645,9 @@ templates use in `keep`, or, if they are many, ship every atom (below).
490
645
 
491
646
  The PostCSS plugin and the Lightning CSS adapter read no `cx()` call and leave every atom in the
492
647
  layer, on Next.js and webpack alike; so does a page with no bundler, which links the standalone
493
- stylesheet.
648
+ stylesheet. On Next.js,
649
+ [`@navecss/prune`](https://github.com/navecss/navecss/tree/main/packages/prune#readme) ships only
650
+ the atoms its build can read a use for.
494
651
 
495
652
  A server build leaves packages in `node_modules` external by default, so it does not read their
496
653
  code. For such a package that depends on `@navecss/core`, the build warns, naming it, unless it is
@@ -562,6 +719,82 @@ their own code, and `keepFor` does not reach it.
562
719
 
563
720
  List every atom your components can apply at run time, `focusRing`, `srOnly`, `srOnlyFocusable`, `hidden` and the `hide*` atoms included where you use them: a consumer passes them to `keepFor` under your package's name, and one left out renders as if the class were absent in their production build.
564
721
 
722
+ #### Astro
723
+
724
+ Astro builds with Vite, so the Vite plugin is the route there too. Add it under `vite` in your
725
+ Astro config, which is where Astro takes Vite plugins, and put the floor key under `vite` as well:
726
+ Astro's own `build` has no `cssTarget`, and drops one written there without an error.
727
+
728
+ ```js
729
+ // astro.config.mjs
730
+ import { defineConfig } from 'astro/config'
731
+ import { navePlugin } from '@navecss/core/vite'
732
+
733
+ export default defineConfig({
734
+ vite: {
735
+ plugins: [navePlugin()],
736
+ build: { cssTarget: ['chrome125', 'edge125', 'firefox128', 'safari18', 'ios18'] },
737
+ },
738
+ })
739
+ ```
740
+
741
+ The build chooses which atoms ship, so run your checks, keyboard, screen-reader and automated
742
+ accessibility checks included, against a production build as well as in dev: `astro build`, then
743
+ `astro preview` or however you serve production.
744
+
745
+ Put Nave's order statement first in the `<head>` of every layout, as an inline style, ahead of
746
+ anything that can bring a style, `<ClientRouter />` included:
747
+
748
+ ```astro
749
+ ---
750
+ // src/layouts/Layout.astro
751
+ import '../styles/app.css'
752
+ ---
753
+ <html lang="en">
754
+ <head>
755
+ <style is:inline>@layer tokens.defaults, tokens.presets, reset, atomic, components.nave, components.consumer, overrides;</style>
756
+ <meta charset="utf-8" />
757
+ <!-- the rest of your head, <ClientRouter /> included -->
758
+ </head>
759
+ <body><slot /></body>
760
+ </html>
761
+ ```
762
+
763
+ Astro can place a page's or a component's own style ahead of your layout's stylesheet, and with
764
+ `<ClientRouter />` it does so whatever the page imports first. A `@layer` in that style then fixes
765
+ its layer's place before Nave's order statement does, and the order inverts with no error. Astro
766
+ leaves an `is:inline` style where you write it, so this one is the first `@layer` the page sees.
767
+ `astro dev` does not show the inversion a build makes, so check the order in a build. The statement
768
+ is the one `@navecss/core/layers` holds, and the two must stay identical. Write the rules of a
769
+ `<style>` block inside `@layer components.consumer`, as for any CSS of yours: Astro leaves them
770
+ outside every layer otherwise, where they beat all of them.
771
+
772
+ The build chooses atoms as [Which atoms the build ships](#which-atoms-the-build-ships) says, with
773
+ three things particular to Astro. Astro writes its CSS in its server build, before the browser's
774
+ build runs, so an atom named only by code the browser runs, such as a `client:only` component or a
775
+ `<script>` in an `.astro` file, fails the build: list it in `keep`. A dependency the server build
776
+ leaves external is read just as late: add it to `resolve.noExternal` under `vite`, which reaches
777
+ every environment Astro builds, where `ssr.noExternal` does not. And in `output: 'server'` with
778
+ prerendered pages, the stylesheet the on-demand pages share ships every atom.
779
+
780
+ Both end-of-build checks, the `@nave` scan and the check that the atom layer holds exactly the atoms
781
+ the build chose, also read the stylesheets Astro writes into the page instead of as files (those
782
+ under 4 kB, by default). The file a message names for one of those is the name the stylesheet had
783
+ before Astro inlined it, so no file of that name is in `dist`. In an `.astro` file, the line of a
784
+ `@nave` error counts from the start of the `<style>` block that holds it, not from the top of the
785
+ file, and its column can differ from your file's, because Astro rewrites the block's whitespace.
786
+
787
+ In `astro dev`, the first paint holds the stylesheet as Vite's own CSS step left it, every atom and
788
+ every `@nave` unexpanded, until the page's scripts load; after that the dev server serves what the
789
+ build would, as far as it has read. The build is unaffected. As on any Vite project, run your
790
+ checks, keyboard, screen-reader and automated accessibility checks included, against a production
791
+ build as well as in dev: `astro build`, then `astro preview` or however you serve production.
792
+
793
+ A fixture measures this on Astro 7.4.0, with Vite 8.3.5 underneath: `@nave` expanded in `.css`
794
+ files and in `<style>` and `<style is:global>` blocks, and `cx()` read in frontmatter, in templates
795
+ and in React islands, in `astro build` for a static site and for `output: 'server'`, and in
796
+ `astro dev`. Islands in Vue, Svelte or Solid were not measured.
797
+
565
798
  ### PostCSS plugin setup
566
799
 
567
800
  For Next.js, webpack under `postcss-loader`, and any other pipeline that runs PostCSS plugins.
@@ -592,6 +825,10 @@ If you are using only `cx()` or tokens, you do not need to install it.
592
825
 
593
826
  #### Next.js
594
827
 
828
+ To ship only the atoms the build can read a use for, use
829
+ [`@navecss/prune`](https://github.com/navecss/navecss/tree/main/packages/prune#readme) in place of
830
+ this plugin. The setup below keeps every atom.
831
+
595
832
  Next.js reads a PostCSS plugin by its package name, so give it the name as a key and do not
596
833
  call `navePlugin()`:
597
834
 
@@ -618,6 +855,11 @@ Without it, Turbopack compiles for older browsers and rewrites every `light-dark
618
855
  Nave's colours included, into `--lightningcss-light` and `--lightningcss-dark` variables. Spell
619
856
  the floor out as above: Next 15 rejects the shorter `baseline 2024` with `Unknown browser baseline`.
620
857
 
858
+ **Where Nave's warnings show.** With webpack (`next build --webpack`), `next build` prints them under `Compiled with warnings`, once for each compilation that reads the stylesheet, so a CSS Module imported from a Server Component can show the same warning twice; `next dev --webpack` may not print a global stylesheet's, so read them from `next build`. Turbopack does not print PostCSS warnings, so under Turbopack the plugin prints them itself on standard error, with the file, line and column, in `next build` and `next dev` alike. Neither bundler shows them in the browser console or Next's error overlay.
859
+ A problem that [`onUnknown`](#options) covers is a warning only under `'warn'`; under the
860
+ default, `'error'`, it fails the build on either bundler. Where the warnings show was measured
861
+ on Next 16.4.1, not on Next 15.
862
+
621
863
  **If your Vite config sets `css.transformer: 'lightningcss'`, this plugin never
622
864
  runs.** That option replaces Vite's CSS pipeline with Lightning CSS, which does
623
865
  not run PostCSS plugins at all: the build stays green, `@nave` reaches the
@@ -667,7 +909,8 @@ Nave's own messages, with the file's path and the line and column of each proble
667
909
  and the map as a string, ready for `transform()`'s `code` and `inputSourceMap`. `resolver.read` reads each file
668
910
  `bundleAsync()` asks for and returns its expanded text, so a file reached through `@import` is
669
911
  covered too. It takes `extend` (an object of your own atoms) and `onUnknown` as the other plugins
670
- do ([Options](#options)); under `'warn'` it prints each problem with `console.warn`.
912
+ do ([Options](#options)); under `'warn'` it prints each problem with `console.warn`, and prints
913
+ the [order warning](#order-inside-a-rule) the same way under every `onUnknown`.
671
914
 
672
915
  The adapter imports nothing from `lightningcss`, types included, and declares no peer: you bring
673
916
  your own copy. The supported range is documented, not declared: `lightningcss` 1.24.1 and later.
@@ -696,6 +939,21 @@ in one error, positioned at the first: its message, then
696
939
  others. The report covers one stylesheet, so a bundler that stops at the first
697
940
  failing file reports one file per build. Under `'warn'`, each problem is a
698
941
  warning of its own.
942
+ The [order warning](#order-inside-a-rule) is not one of these problems: it is printed as a
943
+ warning whatever `onUnknown` says. On Turbopack, Next 16's default bundler, which shows no
944
+ PostCSS warning, the PostCSS plugin prints each problem under `'warn'` and each order warning
945
+ to the terminal instead, as `file:line:column:` and the message
946
+ ([where Next.js shows them](#nextjs)). It knows Turbopack by the `TURBOPACK` environment
947
+ variable, which Next.js sets for it, so wherever that variable is set and not empty, under Vite
948
+ or the PostCSS CLI as well, it does the same and raises no PostCSS warning. The Vite plugin and
949
+ the Lightning CSS adapter do not read the variable. The PostCSS plugin also takes
950
+ `printWarnings` (default `true`): with `printWarnings: false` it raises each problem under
951
+ `'warn'` and each order warning as a PostCSS warning and prints none of them, whatever
952
+ `TURBOPACK` holds, for a tool that runs the plugin itself and reports the warnings it raises.
953
+ Such a tool can import `isOrderWarning(warning)` from `@navecss/core/postcss` to tell them
954
+ apart: it is `true` for an order warning and `false` for each of these problems, so the tool
955
+ can report an order warning as the plugin does, whatever `onUnknown` says, and never fail a
956
+ build on one.
699
957
 
700
958
  To add atoms of your own to `@nave`, pass them as `extend`: see
701
959
  [CONSUMER-ATOMS.md](./CONSUMER-ATOMS.md). An `extend` object written inline in
@@ -795,6 +1053,9 @@ across every file is printed in one run, one report per stylesheet. Exit codes:
795
1053
  `--watch`, a directory that could not be watched. Nothing is written, except that files written
796
1054
  before an `--out` that failed stay written.
797
1055
 
1056
+ An order warning is printed and is not a problem here: it changes neither the exit code nor what is
1057
+ written.
1058
+
798
1059
  It does not resolve or inline `@import`; that would make it a CSS bundler. It
799
1060
  expands only the files you name with `--source`, so a stylesheet you import that
800
1061
  holds `@nave` needs a pair of its own. An `@import` of a relative or absolute URL
@@ -881,18 +1142,24 @@ Consumers of `@navecss/tokens` alone get no guide: it ships only in this package
881
1142
 
882
1143
  ## Exports
883
1144
 
884
- | Export | Description |
885
- | ---------------------------- | ----------------------------------------------------------------------------------------------------- |
886
- | `@navecss/core` | CSS entry point — `@import url('@navecss/core')` |
887
- | `@navecss/core/layers` | The `@layer` order statement alone. Import it first, before any other stylesheet |
888
- | `@navecss/core/no-tokens` | Entry point for projects that generate their own Nave token layer; see the header comment in the file |
889
- | `@navecss/core/reset` | Reset stylesheet only. It reads Nave's custom properties, so it needs a token layer beside it |
890
- | `@navecss/core/atomic` | Generated atomic CSS (global `nave-` classes) |
891
- | `@navecss/core/cx` | `cx()` / `cx.raw()` utilities + `AtomName` type |
892
- | `@navecss/core/atoms` | Atom definitions + `atomClassMap` |
893
- | `@navecss/core/vite` | Vite plugin — `navePlugin()` |
894
- | `@navecss/core/lightningcss` | Lightning CSS adapter — `navePlugin()`, returning `expand` and `resolver` |
895
- | `@navecss/core/standalone` | The whole stylesheet in one file, with no `@import`, for projects with no bundler |
896
- | `@navecss/core/postcss` | PostCSS plugin — `navePlugin()` |
897
- | `@navecss/core/check` | The survival check as a function — `check({ source })` |
898
- | `@navecss/core/collect` | Reads which atoms a module uses, for build tools; not meant for application code |
1145
+ | Export | Description |
1146
+ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------- |
1147
+ | `@navecss/core` | CSS entry point — `@import url('@navecss/core')` |
1148
+ | `@navecss/core/layers` | The `@layer` order statement alone. Import it first, before any other stylesheet |
1149
+ | `@navecss/core/no-tokens` | Entry point for projects that generate their own Nave token layer; see the header comment in the file |
1150
+ | `@navecss/core/reset` | Reset stylesheet only. It reads Nave's custom properties, so it needs a token layer beside it |
1151
+ | `@navecss/core/atomic` | Generated atomic CSS (global `nave-` classes) |
1152
+ | `@navecss/core/cx` | `cx()` / `cx.raw()` utilities + `AtomName` type |
1153
+ | `@navecss/core/atoms` | Atom definitions + `atomClassMap` |
1154
+ | `@navecss/core/vite` | Vite plugin — `navePlugin()` |
1155
+ | `@navecss/core/lightningcss` | Lightning CSS adapter — `navePlugin()`, returning `expand` and `resolver` |
1156
+ | `@navecss/core/standalone` | The whole stylesheet in one file, with no `@import`, for projects with no bundler |
1157
+ | `@navecss/core/postcss` | PostCSS plugin — `navePlugin()` |
1158
+ | `@navecss/core/check` | The survival check as a function — `check({ source })` |
1159
+ | `@navecss/core/collect` | Reads which atoms a module uses and trims the atom layer to them, for build tools; not meant for application code |
1160
+
1161
+ ## Trademark
1162
+
1163
+ This package is not affiliated with, endorsed by or sponsored by Vercel or the Next.js project.
1164
+
1165
+ Vercel, the Vercel design, Next.js and related marks, designs and logos are trademarks or registered trademarks of Vercel, Inc. or its affiliates in the US and other countries.
@@ -24,4 +24,5 @@ declare const toClassName: (name: AtomName) => string;
24
24
  */
25
25
  declare const atomClassMap: Record<AtomName, string>;
26
26
 
27
- export { type AtomName as A, atomClassMap as a, toClassName as t };
27
+ export { atomClassMap as a, toClassName as t };
28
+ export type { AtomName as A };
package/dist/atoms.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AtomName, a as atomClassMap, t as toClassName } from './atom-names-2OreuBx9.js';
1
+ export { A as AtomName, a as atomClassMap, t as toClassName } from './atom-names-B8Ud-7zD.js';
2
2
 
3
3
  type CSSDeclarations = Record<string, string>;
4
4
  type PseudoMap = Record<string, CSSDeclarations>;
@@ -406,4 +406,5 @@ declare const atoms: {
406
406
  };
407
407
  };
408
408
 
409
- export { type AtomDefinition, atoms };
409
+ export { atoms };
410
+ export type { AtomDefinition };