Layered CSS architecture built on native @layer. Design tokens, modern reset, CSS Grid,
Container Queries, dark mode, optional PostCSS pipeline. No JS frameworks, no third-party CSS dependencies.
Extracted from the personal project Ravenstern — designed as a self-contained system from the start.
- @layer cascade — explicit priority, no
!important - Design tokens — colors, spacing, typography, shadows, animations — defined once
- CSS Grid — fixed columns, auto-fit, holy grail, sidebar, dashboard layouts
- Container Queries — components responding to their parent, not the viewport
- Dark mode — automatic (
prefers-color-scheme) + manual toggle ([data-theme]) - PostCSS pipeline — optional,
postcss-import+cssnanofor production - BEM naming — predictable, readable selectors
- Accessibility — focus-visible, sr-only, prefers-reduced-motion
<script src="static/js/theme-toggle.js"></script> <!-- before CSS, to prevent dark mode flash -->
<link rel="stylesheet" href="static/css/index.css">cd static
npm install
npm run dev # watch mode, no minification
npm run build # production → dist/style.css<link rel="stylesheet" href="static/dist/style.css">Create a file outside @layer — its rules will automatically override everything from the library:
/* _myproject.css — outside @layer, always wins */
:root {
--rg-color-primary: #7c3aed; /* override any token */
}
.my-component { /* override anything from _components.css */ }Link it after index.css:
<link rel="stylesheet" href="static/css/index.css">
<link rel="stylesheet" href="static/css/_myproject.css">Consumers vendor a physical copy of this repo — deploy-safe on plain FTP and git-checkout hosting, no build step or network needed at deploy time.
First time (in the consuming repo):
git subtree add --prefix=static/vendor/ravengrid \
https://github.com/R4V3NST3RN/ravengrid.git main --squashPulling updates later:
git subtree pull --prefix=static/vendor/ravengrid \
https://github.com/R4V3NST3RN/ravengrid.git main --squashRule: never edit the vendored copy directly. Every change belongs here,
in the ravengrid repo, and reaches consumers via git subtree pull —
direct edits are silently overwritten by the next pull and cause drift
between consumers. Any diff between static/vendor/ravengrid and this
repo is a bug.
ravengrid/
├── index.html ← Showcase landing page
├── CLAUDE.md ← Claude Code instructions
├── README.md ← This file
│
└── static/
├── package.json ← PostCSS CLI + dependencies
├── postcss.config.js ← PostCSS pipeline
├── js/
│ └── theme-toggle.js ← Dark/light toggle (vanilla JS)
├── css/
│ ├── index.css ← Entry point — @layer order, responsive utilities
│ ├── _tokens.css ← Design tokens (colors, spacing, typography, …)
│ ├── _reset.css ← Modern CSS reset + base elements
│ ├── _fonts.css ← Font loading template (@font-face)
│ ├── _theme-toggle.css ← [data-theme] overrides + toggle button
│ ├── _grid.css ← CSS Grid + flexbox utility classes
│ ├── _containers.css ← Container Queries + components (card, nav, form-grid)
│ ├── _components.css ← UI components (btn, input, badge, alert, avatar, …)
│ ├── _utilities.css ← Helper utility classes
│ └── _project.css ← Showcase layer — outside @layer, glassmorphism UI
└── images/
└── assets/
├── RA1.jpg ← Background texture (showcase)
└── ravenstern2.png ← Logo
Layer order is declared once in index.css:
reset → tokens → layout → components → utilities → prose
Lower layers have lower priority. Anything outside @layer (your project file) overrides everything without !important.
| Layer | File(s) | Contents |
|---|---|---|
reset |
_reset.css |
Browser normalization, base elements |
tokens |
_tokens.css |
CSS custom properties — single source of truth |
layout |
_grid.css, _containers.css |
Grid, flexbox, container queries |
components |
_components.css, _theme-toggle.css |
Reusable UI components |
utilities |
_utilities.css |
Single-purpose helper classes |
prose |
index.css (inline) |
Styles for formatted content |
| (outside @layer) | _project.css |
Project overrides — always win |
Future direction (not implemented): a 9-layer split (
reset → tokens → base → layout → grid → components → effects → utilities → project) is under consideration — it would separate base elements, grid, and visual effects into their own layers. The current 6-layer architecture stays until there is a real need; this note exists so the idea isn't lost.
| Type of change | Correct file |
|---|---|
| Token (spacing, color, typography, radius) | _tokens.css |
| Brand token (glow, glass, portal, brand fonts) | _tokens.css — RAVENSTERN BRAND section |
| Shared UI component (btn, badge, alert) | _components.css |
| Layout utility (.container, grid classes) | _grid.css or index.css |
| Container query component (card, nav) | _containers.css |
| Dark mode, theme-toggle button | _theme-toggle.css |
| New page / project layer | new _name.css outside @layer |
All visual values are CSS custom properties in _tokens.css. Override them in your project file outside @layer.
/* Semantic tokens — use these, not raw hex values */
--rg-color-primary /* #3b82f6 — blue accent */
--rg-color-primary-hover
--rg-color-primary-light /* focus ring background */
--rg-color-text-primary /* main text */
--rg-color-text-secondary /* labels, metadata */
--rg-color-bg-base /* page background */
--rg-color-bg-subtle /* slightly differentiated surfaces */
--rg-color-bg-muted /* cards, sections */
--rg-color-border /* standard border */
--rg-color-border-focus /* focus ring */
--rg-color-success / -bg / -fg
--rg-color-warning / -bg / -fg
--rg-color-error / -bg / -fg / -hover / -focusShared visual identity of ravenstern.com and ravenstern-laravel — navy
surfaces, cyan glow, glassmorphism, Cinzel/Jost. Defined once in
_tokens.css; consumers only reference them.
--rg-glow-rgb /* 0, 212, 255 — compose alphas via rgba(var(--rg-glow-rgb), X) */
--rg-color-glow /* solid cyan accent (#00d4ff) */
--rg-color-glow-dim / -border / -border-strong
--rg-text-bright / -soft / -muted / -faint /* text on navy */
--rg-glass-bg / -bg-hover / -blur / -border / -shadow
--rg-portal-bg / -bg-hover / -border / -border-hover / -text / -text-hover
--rg-font-display /* 'Cinzel' — headings (load via Google Fonts in the consumer) */
--rg-font-ui /* 'Jost' — UI text */4px base. Numeric scale + aliases for readability:
--rg-space-1: 0.25rem /* 4px */
--rg-space-2: 0.5rem /* 8px */ /* ← --rg-space-xs */
--rg-space-3: 0.75rem /* 12px */ /* ← --rg-space-sm */
--rg-space-4: 1rem /* 16px */ /* ← --rg-space-md (base) */
--rg-space-5: 1.25rem /* 20px */
--rg-space-6: 1.5rem /* 24px */ /* ← --rg-space-lg */
--rg-space-7: 1.75rem /* 28px */
--rg-space-8: 2rem /* 32px */ /* ← --rg-space-xl */
--rg-space-10: 2.5rem /* 40px */
--rg-space-12: 3rem /* 48px */ /* ← --rg-space-2xl */
--rg-space-16: 4rem /* 64px */ /* ← --rg-space-3xl */
--rg-space-20: 5rem /* 80px */
--rg-space-24: 6rem /* 96px */
--rg-space-32: 8rem /* 128px */--rg-font-sans / --rg-font-serif / --rg-font-mono
--rg-text-xs → --rg-text-6xl /* 0.75rem → 3.75rem, modular scale ×1.25 */
--rg-weight-light / normal / medium / semibold / bold / black
--rg-leading-tight / snug / normal / relaxed / loose
--rg-tracking-tight / normal / wide / wider / widest--rg-duration-fast / base / slow / slower /* 100ms → 500ms */
--rg-ease-out / ease-in / ease-in-out / ease-bounce
/* Compose as needed: */
.btn { transition: var(--rg-transition-color), var(--rg-transition-transform); }
.card { transition: var(--rg-transition-base); }/* -1 → 600 */
--rg-z-below / base / raised / dropdown / sticky / overlay / modal / toast / tooltipWorks in two layers:
1. _tokens.css — system preference (automatic)
@media (prefers-color-scheme: dark) {
:root { /* override semantic color tokens */ }
}2. _theme-toggle.css — manual toggle
[data-theme="dark"] { /* same values as the media query */ }
[data-theme="light"] { /* explicit light values */ }theme-toggle.js is loaded in <head> before CSS to prevent a flash of incorrect theme. Priority: localStorage → prefers-color-scheme → fallback light.
The button requires id="theme-toggle" and icons with classes .theme-toggle__icon--sun and .theme-toggle__icon--moon.
Optional — during development you can link files directly. For production:
cd static
npm install| Command | Output | Notes |
|---|---|---|
npm run dev |
dist/style.css (watch) |
No minification, comments preserved |
npm run build |
dist/style.css + source map |
Minified via cssnano |
npm run build:clean |
Deletes dist/, then builds |
For a clean production deployment |
Plugins:
postcss-import— bundles all@importstatements into a single filecssnano— minification in production (NODE_ENV=production)
cssnano is configured with mergeRules: false (preserves @layer order) and reduceIdents: false (preserves CSS custom property names).
| Feature | Minimum support |
|---|---|
CSS @layer |
Chrome 99, Firefox 97, Safari 15.4 |
| Container Queries | Chrome 105, Firefox 110, Safari 16 |
| CSS subgrid | Chrome 117, Firefox 71, Safari 16 |
backdrop-filter |
Chrome 76, Firefox 103, Safari 9 (prefixed) |
100svh |
Chrome 108, Firefox 101, Safari 15.4 |
Target baseline: browsers from 2022 onwards.
MIT — see LICENSE.