Source
bevel.css is one plain, unminified stylesheet. What you read is what ships: a release is this file with its version in a comment on the first line.
Layers
Every rule sits in one of five cascade layers, declared once at the top of the file. A later layer beats an earlier one whatever the selectors, and CSS outside any layer beats them all, so an app overrides bevel without !important or specificity tricks.
@layer bevel.reset, bevel.base, bevel.layout, bevel.components, bevel.utilities;
| Layer | Holds |
|---|---|
bevel.reset | box-sizing: border-box everywhere; no margins on headings, paragraphs and lists; images and video shrink to fit. |
bevel.base | The tokens, and styles for text and form controls, all inside :where(), so a bare element is styled at zero specificity. |
bevel.layout | .container, .row, .section and .page-header. |
bevel.components | Navbar, buttons, forms and add-ons, tables, alerts, labels and badges, hero unit, pagination, progress bars, stats and wells. |
bevel.utilities | .muted, .text-right, .table-scroll and .visually-hidden, plus the two rules that must beat components: trimming the last margin in a row’s columns, and turning motion off for anyone who asks. |
| Your app’s CSS | Unlayered, so it wins. |
House rules
- Only Baseline Widely available CSS: features in every major browser for at least 30 months. Anything newer would have to sit inside
@supportswith a working fallback. - Every custom property starts with
--bv-. Global tokens are--bv-<group>-<name>; a component’s variables are--bv-<component>-<property>, and its variants only reassign them. - Class names follow Bootstrap 2.x, such as
.btn-small,.table-stripedand.alert-error, so 2.x markup works for what bevel has. - States take the class or the ARIA attribute:
.activeoraria-current,.disabledoraria-disabled,.errororaria-invalid. - Specificity stays flat: no IDs, no
!important, and at most one class plus one state. - Units:
remfor type and spacing,pxfor borders, radii and shadows. Mobile first, with breakpoints at 768px, 980px and 1200px.
Gates
A release ships only when every gate passes.
| Gate | Tool | Fails when |
|---|---|---|
| Baseline | Stylelint | A feature isn’t Widely available and isn’t inside @supports |
| House rules | Stylelint | !important, an ID selector, specificity above 0,2,0, a custom property without --bv-, or a rule outside a layer |
| Size | Go | bevel.css is over 20 KB gzipped |
| Tokens | Go | A token is used but never defined, or defined but never used |
| Coverage | Go | A class is missing from the reference, or a page uses a class bevel.css doesn’t define |
| Releases | Go | The changelog page is missing a release, or a page names an old one |
| Visual | Playwright | A screenshot of the reference differs from its approved baseline |
| Accessibility | axe-core | The reference has a WCAG 2.2 AA violation |
The gates need Node and Go.
make test # every gate
make update-snapshots # after reviewing a visual diff
make release # runs the gates, then writes dist/bevel-<version>.css
Adding a component
A component is styled inside an app first, and joins bevel when a second app needs it:
- Add it to the spec, with exact measurements and every state.
- Add it to the reference, with every state.
- Build it in
bevel.components, using--bv-variables only. - Pass every gate and approve the new baselines.
- Add an entry to
CHANGELOG.mdand the changelog page, and bump the minor version.