@animus-ui/vite-plugin
v0.1.11
Published
Animus static CSS extraction Vite plugin
Maintainers
Readme
@animus-ui/vite-plugin
Static CSS extraction plugin for Vite. Transforms @animus-ui/system builder chains into static CSS at build time — zero runtime style injection.
Install
npm install @animus-ui/vite-plugin @animus-ui/systemSetup
// vite.config.ts
import react from '@vitejs/plugin-react';
import { animusExtract } from '@animus-ui/vite-plugin';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [react(), animusExtract({ system: './src/ds.ts' })],
});The system option points to the file that exports your built system instance. The plugin loads this file in a subprocess to serialize the prop config and theme data for the Rust extraction pipeline.
What It Does
- Build time: Analyzes all files importing from
@animus-ui/system, extracts static styles into CSS with@layerordering - Dev server: Runs extraction on startup, holds results in memory, serves CSS via virtual module
- Transforms: Resolves
__TRANSFORM__placeholders using your system's named transform functions - Global styles: Emits global styles (reset, base) from your exported
createGlobalStyles()configuration
Appearance bootstrap
Optional. Restores a persisted color mode before first paint, so a dark-persisted
page never flashes a light frame — with zero runtime and a real CSP story.
The restoration is a generated, dependency-free inline snippet (not a component,
not a listener), and its script-src hash is computed at build time from the
exact bytes generated. Runtime theme switchers have to choose between
unsafe-inline and a hand-maintained hash that goes stale; here the artifact IS
the hash source, so a theme edit can never strand a stale hash without also
changing the code it authorizes.
Generate the artifact in your Vite config — it is build tooling, and nothing
under src/ may import it:
// vite.config.ts
import { createAppearanceBootstrap } from '@animus-ui/system/bootstrap';
import { animusExtract } from '@animus-ui/vite-plugin';
import { defineConfig } from 'vite';
import { theme } from './src/ds';
const appearanceBootstrap = createAppearanceBootstrap(theme);
export default defineConfig({
plugins: [animusExtract({ system: './src/ds.ts', appearanceBootstrap })],
});The plugin is delivery-only: it injects artifact.code verbatim as an inline
<script data-animus-bootstrap> at the start of <head>, ahead of every
stylesheet reference, and interprets no appearance semantics. Omit the option and
the built HTML is byte-for-byte what it was before.
Content-Security-Policy
The artifact's second field, cspHash, authorizes that inline script. Two rules:
Derive it from the artifact at build time.
createAppearanceBootstrapreturns the hash of the exact bytes it just generated. Renaming a color mode or changingstorageKeychanges those bytes and therefore the hash — a hand-copied literal in a config file silently becomes stale, and a stale hash means a blocked script and a flash of the wrong mode.Single-quote it in
script-src. The value is returned ready to use:Content-Security-Policy: script-src 'sha256-…'Unquoted,
sha256-…parses as a host source, matches nothing, and silently fails to authorize the script.
Never fall back to unsafe-inline, and never use a build-time constant nonce.
// Build the header from the artifact — never from a copied string.
// The hash is single-quoted in the source list, per the rule above.
const csp = `script-src 'self' '${appearanceBootstrap.cspHash}'`;Next.js is deliberately not automatic: CSP nonces need request-time control the
bundler does not have, so the application places artifact.code itself and
supplies either cspHash or a per-request nonce. Worked App Router and Pages
Router examples live in the
@animus-ui/next-plugin README.
Important
- Do not add React resolve aliases to
vite.config.ts— they break the extraction transform pipeline - After system config changes, restart the dev server (the subprocess runs at
buildStart) - Run
bun run clean:lightif styles seem stale (clears.vitecache)
License
MIT
