Getting Started
Porchlight is a native CSS framework with zero JavaScript dependencies. It ships tokens, themes, base styles, layout primitives, component contracts, utilities, and progressive enhancements as prebuilt CSS files.
For server templates, HTMX swaps, Alpine state, and optional CSS Modules, see
HTMX, Alpine, and CSS Modules
.
Install
npm install @cawalch/porchlight
# or
pnpm add @cawalch/porchlight
# or
bun add @cawalch/porchlight
Install into your application; you do not need to clone this repository. The package has no runtime dependencies and ships prebuilt CSS. Interactive contracts such as tabs and comboboxes still need application-owned behavior, keyboard handling, and state updates; CSS does not provide a controller. For working on Porchlight itself, use the repository’s contributor setup.
Full bundle
The package main export is the complete prebuilt stylesheet:
@layer porchlight, app;
@import "@cawalch/porchlight";
@layer app {
/* your product styles here */
}
Use this in a bundler-resolved CSS entry when you want the whole framework. Load that entry once. For plain HTML, use the static-assets instructions below.
Import only what you need
For smaller surfaces, start with core.css and add selected layers or
components.
@layer porchlight, app;
@import "@cawalch/porchlight/core.css";
@import "@cawalch/porchlight/layout.css";
@import "@cawalch/porchlight/components/button.css";
@import "@cawalch/porchlight/components/field.css";
@import "@cawalch/porchlight/components/card.css";
@import "@cawalch/porchlight/utilities.css";
core.css includes layer order, reset, tokens, themes, and base styles.
Component files consume those tokens, so keep core.css first.
If you want the whole practical framework without experimental enhancement
syntax, use compat.css. It includes core, layout, all components, and
utilities, but leaves out enhancements.css.
@layer porchlight, app;
@import "@cawalch/porchlight/compat.css";
Static assets
Server-rendered apps can copy prebuilt files from
node_modules/@cawalch/porchlight/dist/ without asking a bundler to parse
modern CSS. Use the included copy helper for the common paths:
Install the package first, then run its local executable:
npx --no-install porchlight copy --out public/porchlight --compat
<link rel="stylesheet" href="/porchlight/compat.css" />
These URLs assume your server exposes public/ at /; adjust them for your
asset mount or deployment base path.
For a smaller static slice, name the components you use:
npx --no-install porchlight copy --out public/porchlight --components button,field --layout --utilities
<link rel="stylesheet" href="/porchlight/core.css" />
<link rel="stylesheet" href="/porchlight/layout.css" />
<link rel="stylesheet" href="/porchlight/components/button.css" />
<link rel="stylesheet" href="/porchlight/components/field.css" />
<link rel="stylesheet" href="/porchlight/utilities.css" />
<link rel="stylesheet" href="/app.css" />
Then declare the layer order in app CSS:
@layer porchlight, app;
The package also ships dist/porchlight.manifest.json, which lists the
available files, component names, and recommended load order for copy scripts.
Vite
Vite resolves package CSS imports. Import one CSS entry from your app or link it from Vite’s HTML entry; bare package imports are not browser URLs.
@layer porchlight, app;
@import "@cawalch/porchlight/core.css";
@import "@cawalch/porchlight/components/button.css";
@import "@cawalch/porchlight/components/data-table.css";
@import "@cawalch/porchlight/utilities.css";
You can also import CSS from a JS or TS entry:
import "@cawalch/porchlight/core.css";
import "@cawalch/porchlight/components/button.css";
import "./app.css";
For production builds, set a CSS target that matches your supported browsers.
The docs build uses build.cssTarget: "chrome149" to preserve native color
functions. This is a Chromium target, not a cross-browser support guarantee.
The full bundle was also checked with Vite 8.2.1’s default target in a small
light/dark consumer fixture. Recheck your rendered output after changing targets
or minifiers; see Vite’s CSS target option.
Bun and parser fallbacks
Bun can install Porchlight and run its copy helper. Using Bun as a package
manager is separate from sending CSS through bun build.
Verified with Bun 1.4.2 and Porchlight 0.11.1:
| Path | Result |
|---|---|
bun add @cawalch/porchlight |
Installs the package and local CLI |
Bundle compat.css or core.css + selected components |
Builds; emits non-fatal @property warnings |
Bundle the default export or enhancements.css |
Fails at @container scroll-state(...) |
| Copy prebuilt CSS and serve it directly | Preserves the full CSS without parser transformation |
For Bun’s CSS bundler, put this in app.css, then run
bun build ./app.css --outdir ./dist:
@layer porchlight, app;
@import "@cawalch/porchlight/compat.css";
Load the emitted stylesheet in your page; a CSS build alone does not attach it to the DOM. Basic button/card colors were checked in light and dark themes; this is not a guarantee for every component or future Bun release.
For the full bundle, run the installed CLI with Bun’s runtime explicitly:
bunx --bun --no-install porchlight copy --out public/porchlight --full
<link rel="stylesheet" href="/porchlight/porchlight.css" />
Serve that directory as static files. Feeding the copied CSS back into Bun’s
HTML/CSS bundler reintroduces the parser limitation. --bun avoids the CLI’s
Node shebang; --no-install uses the locally installed package.
See Bun’s CSS bundler and
bunx runtime selection.
The layer model
Porchlight wraps everything in a single top-level cascade layer:
@layer porchlight. Inside it, sub-layers declare a deterministic order:
@layer porchlight {
@layer reset, tokens, themes, base, layout,
components, utilities, enhancements, print;
}
For normal stylesheet declarations, later layers win: components beat base,
utilities beat components, and app beats Porchlight. !important reverses
layer precedence; do not override accessibility safeguards casually.
Do not pass layer(...) to Porchlight imports. Modules already
self-layer.
Using components
Each component uses the .pl-c-* class prefix. Drop them into your HTML:
<button class="pl-c-button" data-variant="primary">Save</button>
<div class="pl-c-card">
<div class="pl-c-card__header">
<h2 class="pl-c-card__title">Title</h2>
</div>
<div class="pl-c-card__body">Content</div>
</div>
Browse the component reference or the preview gallery to see every component with live examples.
Token metadata
Use the generated token module for editor autocomplete, validation, or build tooling:
import tokenDoc, { tokenGroups } from "@cawalch/porchlight/tokens";
console.log(tokenDoc.tokens["--pl-color-accent"].value);
console.log(tokenGroups.map((group) => group.name));
JSON is also available:
import tokens from "@cawalch/porchlight/tokens.json" with { type: "json" };
Exports reference
| Import path | What you get |
|---|---|
@cawalch/porchlight |
Full prebuilt CSS bundle |
@cawalch/porchlight/min.css |
Minified full bundle |
@cawalch/porchlight/compat.css |
Bundle without the enhancement layer |
@cawalch/porchlight/core.css |
Layer order, reset, tokens, themes, base |
@cawalch/porchlight/layout.css |
Layout primitives |
@cawalch/porchlight/components.css |
All component CSS |
@cawalch/porchlight/components/button.css |
One component CSS file |
@cawalch/porchlight/utilities.css |
Utility classes |
@cawalch/porchlight/enhancements.css |
Progressive enhancement layer |
@cawalch/porchlight/tokens |
Typed token metadata module |
@cawalch/porchlight/tokens.json |
Token metadata JSON |
@cawalch/porchlight/manifest.json |
Static-copy manifest |
@cawalch/porchlight/src/* |
Raw source escape hatch |
Browser support
Porchlight targets modern browsers and does not polyfill old CSS engines.
The core uses cascade layers, custom properties, :has(), @scope,
color-mix(), and OKLCH. Advanced features are gated where possible.
See the Browser Support guide for the full feature matrix.
Next steps
- Theming guide - override tokens and create a brand theme
-
Architecture guide
- deep dive into the layer model
-
Composition Recipes
- compose dashboards, tables, settings, and dense admin screens
- Preview gallery - see every component live