Nuxt
Add @nibcss/nuxt to modules and everything is wired: the Vite plugin, the Vue plugin, SSR, hot config reload and dark mode before the first paint.
1. Create a project#
npx nuxi@latest init my-app
cd my-app
npm install
npm run dev # http://localhost:30002. Install#
npm i @nibcss/vue @nibcss/runtime
npm i -D @nibcss/nuxt3. Add the module#
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["@nibcss/nuxt"],
});That is the whole setup. There is nothing to import and no provider to write — the stylesheet is added
to nuxt.options.css for you. The module wires four pieces, all reading the same
nibcss.config.ts:
Piece | What it does |
|---|---|
the Vite plugin | scans your components, writes the stylesheet, publishes |
a Vue plugin | one runtime for the page, |
a server plugin (dev) | re-reads the config per render, so the server-rendered half never lags behind an edit, and sets the dark-mode class before the first paint |
two client plugins (dev) | apply a config change to the open page, and mount the diagnostics overlay |
Start the app once and nibcss.config.ts is created beside nuxt.config.ts — not inside
app/.
4. Write components#
<!-- app/app.vue -->
<script setup lang="ts">
import { ref } from "vue";
import { useNib, useNibRuntime } from "@nibcss/vue";
const rt = useNibRuntime();
const pad = ref(16);
const box = useNib(() => `p=${pad.value}px; bd=1px solid $color.border; bdrd=$radius.md`);
</script>
<template>
<main nib="mxw=720px; mi=auto; p=$space.xl $space.md; d=flex; flxd=column; g=$space.lg">
<header nib="d=flex; ali=center; jsc=space-between; g=$space.md">
<h1 nib="m=0; fts=28px; ftw=800; lts=-.02em">Dashboard</h1>
<button nib="btn" @click="rt.toggleDarkMode()">
{{ rt.darkMode.isDark() ? "Light" : "Dark" }}
</button>
</header>
<section nib="d=flex; flxd=column; g=$space.md; md:flxd=row">
<article nib="card-hover; flg=1"><h2 nib="m=0; fts=18px">Revenue</h2></article>
<article nib="card-hover; flg=1"><h2 nib="m=0; fts=18px">Users</h2></article>
</section>
<input type="range" min="4" max="48" v-model.number="pad" nib="w=100%; acc=$color.brand" />
<div :class="box">padding: {{ pad }}px</div>
</main>
</template>5. Options#
They go under a nib key, and only when you need them:
Option | Default | What it does |
|---|---|---|
|
| install the Vue plugin automatically |
|
| mount the overlay in dev (needs |
| beside | path to the config file |
| project sources | extra globs to scan, or skip |
| — | config fields that win over the file |
|
|
|
export default defineNuxtConfig({
modules: ["@nibcss/nuxt"],
nib: {
include: ["content/**/*.md"],
override: { debug: process.env.CI !== "true" },
},
});6. Editing the config while the server runs#
Save nibcss.config.ts and the open page updates by itself — no manual refresh, no reload, no
restart. prefix, darkMode, debug, pseudos, preflight, strict, tokens, recipes and
shorthands are all picked up in about a second, and rules the change removes go away too.
It works by being told rather than by reloading: the build pushes the new config and the new stylesheet
over the dev channel, and the page swaps the stylesheet and re-resolves its elements. The one exception
is language — the overlay's labels are baked in when its module loads, so those follow on the next
reload.
7. Dark mode arrives dark#
With darkMode: "class" the module inlines a few bytes at the top of <head> that set
html.dark from the stored choice, or the OS preference when there is none, before the first
paint. Without it the runtime adds the class after hydration, which in a dark app is a white flash on
every load. Nothing to configure.
8. What the module generates#
Path | What it is |
|---|---|
| the stylesheet, added to |
| the resolved config, as a module and as data |
| a real package re-exporting it, so the import type-checks |
| the generated plugins (virtual in dev) |
Common pitfalls#
- Putting
nibcss.config.tsinsideapp/— the module looks besidenuxt.config.ts. - Adding the Vite plugin by hand as well as the module: two scans, two stylesheets.
- Setting
runtime: falseand then callinguseNibRuntime()— nothing provides it.