CLI — Rails, Django, Laravel, PHP

Build a static nib.css with the CLI and use NibCSS from any server-rendered framework, with no bundler involved.

3 min readv1

Not every project has a JavaScript build. The CLI scans whatever files you point it at — ERB, Blade, Django templates, plain HTML, even .js strings — and writes a stylesheet you link like any other.

1. Install#

Terminal
npm i @nibcss/runtime
npm i -D @nibcss/cli
Terminal
npx nib init          # writes a fully commented nibcss.config.ts

2. Build the stylesheet#

Terminal
npx nib build -o assets/nib.css --content "**/*.html"
npx nib watch -o assets/nib.css --content "**/*.html"   # and rebuild on change

As scripts:

JSON
{
  "scripts": {
    "nib": "nib watch -o assets/nib.css --content \"**/*.{html,js}\"",
    "build": "nib build -o assets/nib.css --content \"**/*.{html,js}\" --minify"
  }
}

Command

What it does

nib init

create nibcss.config.ts

nib build

scan once, write the stylesheet and the config files

nib watch

the same, then rebuild on every change (about 50 ms)

Flag

Default

What it does

-o, --out <file>

nib.css

where the stylesheet goes; the config files are written beside it

--content <glob>

project sources

globs to scan, repeatable; node_modules, dist and .git are always skipped

--minify

off

strip comments and whitespace

-c, --config <file>

auto-detected

a config file somewhere else

Scanning .js as well as your templates is worth it: attributes inside strings your scripts inject end up in the stylesheet too. Anything containing ${…} is left to the runtime, as it must be.

Three files are written beside each other:

File

For

assets/nib.css

the stylesheet — link it

assets/nib.config.js

sets window.__NIB__, for a classic <script>

assets/nib.config.mjs

the same data as a module, plus node_modules/@nibcss/config

3. The page#

HTML
<head>
  <link rel="stylesheet" href="assets/nib.css" />
  <script src="assets/nib.config.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/@nibcss/runtime"></script>
</head>

From node_modules instead of a CDN: <script src="node_modules/@nibcss/runtime/dist/nib.global.js"></script>.

4. Rails, Django, Laravel#

Terminal
nib watch -o app/assets/stylesheets/nib.css --content "app/views/**/*.erb"
ERB
<%# app/views/layouts/application.html.erb %>
<%= stylesheet_link_tag "nib" %>
<script src="/assets/nib.config.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@nibcss/runtime"></script>

Run nib watch beside the framework's own dev server; it has nothing to coordinate with.

5. What you give up, and what you do not#

CLI

bundler plugin

Classes in the HTML the server sends

no — the runtime adds them

yes, compiled in

Styled before the first paint

yes, with the runtime tag synchronous in <head>

yes

JavaScript required

yes

only for dynamic values

Live rebuild

nib watch, then the browser reloads

applied without a reload

Diagnostics

terminal (file and line) + nib:warning events

the same, plus the overlay

6. The browser API#

window.Nib is the runtime: classesFor(attr), toggleDarkMode(), darkMode.isDark(), configure(partial), engine.config, engine.stats(), flush(), and NibRuntime for a second instance scoped to a subtree.

Common pitfalls#

  • Linking nib.css but not nib.config.js — with a custom prefix nothing matches.
  • A --content glob that misses a template directory: those attributes are simply not in the stylesheet, and only the runtime saves them.
  • Running nib build in CI but committing a stale nib.css — build it in the pipeline instead.
  • defer/async on the runtime tag.

Next steps#