# Contributing to SLASHED

Current version: **0.6.30.0** (pre-1.0).

SLASHED is a buildless, BEM-first CSS framework (BEM authoring methodology + tokens + cascade layers, with layout primitives wrapping blocks and utilities as subordinate helpers). Source files are hand-authored; consumers link individual CSS files directly.

See `CLAUDE.md` for the framework philosophy and mandatory actions on every change.
See [`PHILOSOPHY.md`](PHILOSOPHY.md) for the frozen, immutable project constitution.

## Version freeze (active)

**The project is frozen at `0.6.x.x`** until the token & variable system is
finished, tested, and set in stone. See [`docs/VERSION-FREEZE.md`](docs/VERSION-FREEZE.md)
for the full policy, unlock conditions, and rationale.

- `bin/bump-version.sh` rejects any target `≥ 0.7.0.0` unless `FREEZE_UNLOCK` exists.
- Within `0.6.x.x`, normal MINOR/PATCH releases continue unrestricted.
- Free renames, removals, and restructuring remain available during the freeze.

## Repo layout

```text
css/                   Framework CSS (hand-authored, no build)
  Core (always link these):
    tokens-default.css     Design tokens on :root (frozen source of truth)
    slashed-core.css       Reset + base elements + layout primitives + a11y + print

  Opt-in (link as needed):
    slashed-utilities.css            Layout, function, typography utilities (no decoration)
    slashed-utilities-visual.css     Decorative utilities (bg, border, radius, shadow, ...)
    slashed-utilities-viewport.css   Viewport breakpoint utilities (sm:/md:/lg:/xl:)
    component-tokens.css             Instance tokens scoped to component selectors
    slashed-components.css           Pre-baked .sf-* components
    slashed-animations.css           Animation and transition helpers

  Opt-in special (load BEFORE core):
    tokens-legacy.css                Static fallbacks for sub-baseline browsers

  Consumer template (never ship):
    tokens-user.example.css          Starter template for consumer overrides

  Opt-in experimental:
    slashed-experimental.css         Features without stable cross-browser support

js/
  slashed-ui.js          Optional a11y enhancements + CSS platform-gap polyfills
docs/
  BEM.md                 Canonical BEM naming convention (cross-repo source of truth)
  ICONS.md               Icon utility guidelines
  INSTALLATION.md        Setup for static sites, WordPress, Bricks, and CDN
  TOKENS.md              Design tokens reference
  UTILITIES.md           Core utility classes reference
  UTILITIES-VISUAL.md    Visual utility classes reference
  VERSION-FREEZE.md      Version ceiling policy and unlock conditions
bin/
  bump-version.sh        Update version strings everywhere (0.7+ blocked by freeze)
  check-cheatsheet-sync.sh   Pre-commit guard: CSS/JS edits require cheatsheet update
  check-philosophy-drift.sh  Detect code that violates the seven pillars (PHILOSOPHY.md)
  check-readme-sync.sh   Pre-commit guard: every CSS source listed in README
  check-token-parity.sh  Verify token coverage against DX expectations (Tailwind/Open Props parity)
  check-version-sync.sh  Verify every versioned anchor in the repo matches CHANGELOG
  lint-bem.py            Enforce BEM naming convention on css/slashed-*.css
  setup-hooks.sh         Install git hooks
  sync-docs.sh           Master verification: run ALL checks in one command
cheatsheet.html          Interactive quick-reference page
```

## Blueprints library

The blueprint library lives in a separate repository (`slashed-blueprints`). It has its own versioning, CI, and class-discipline rules.

## Authoring order and BEM naming

**BEM-first is the authoring methodology, not a component library.** Author repeating UI patterns as BEM blocks (use a shipped `.sf-*` block when one fits, or roll your own). Layout primitives wrap blocks. Utilities patch the gaps.

**Mobile-first, always.** All `@media` breakpoints use `min-width`. Reach for fluid `clamp()` tokens first (no `@media` needed), then container-aware layout primitives (respond to their own width, not the page), and only then `sm:`/`md:`/`lg:`/`xl:` breakpoint utilities for viewport-level switches.

**The canonical BEM naming convention** for the entire ecosystem (framework, blueprints, consumer code) lives in [`docs/BEM.md`](docs/BEM.md). It defines:

- block / element / modifier syntax (`.block__element--modifier`),
- the four namespaces (`sf-` framework, `sb-<file>NN` and `sb-pg-<stem>` blueprints, `<project>-` consumer),
- state classes (native attributes first, BEM modifier second, `is-*` last),
- the modifier-vs-utility decision rule,
- and the lint rules enforced by `bin/lint-bem.py` (this repo) and `bin/lint-wireframes.py` (blueprints).

Read `docs/BEM.md` before authoring framework CSS or contributing your first block.

**Style consumer blocks with framework tokens.** Your blocks inherit the design system by referencing `var(--sf-…)` tokens, not hardcoded values.

**Consumer BEM sits unlayered** and wins over every framework layer by default — consumer code never needs `!important` to beat framework rules. (The framework's own print utilities use `!important` deliberately to override print-time styles; that exception applies to framework source only.)

## Framework gotchas

- **`:root:root { font-size: max(16px, 1em) }` is defensive.** In `tokens-default.css`, this rule defeats WordPress/Bricks themes that set `font-size: 62.5%`. The doubled selector lifts specificity to (0,2,0). Keep it separate from the main tokens block and do not simplify it.

- **The `slashed.overrides` layer is intentionally empty** — reserved for consumer styles that must beat every framework layer. Framework source must never author into it.

- **Viewport breakpoints are hardcoded in `slashed-utilities-viewport.css` only.** CSS forbids custom properties inside `@media`. `sm/md/lg/xl` (30em / 48em / 64em / 80em) live in that file. Layout primitives in `slashed-core.css` use `@container` queries and are unaffected. Consumers who need custom breakpoints link `slashed-core.css` + their own modified copy of `slashed-utilities-viewport.css`.

- **`--warning` on white is ~2.8:1 — fails WCAG AA for text.** For warning text, use `var(--sf-warning-600)`. For warning messages, pair `--warning-100` background with `--warning-600` text.

- **`hanging-punctuation: first last` is set globally on `html`**, not scoped to `.prose`.

- **`.sf-grid-cols` reads `--sf-cols`, not `--sf-grid-cols`.** Setting `--sf-grid-cols: N` on `.sf-grid-cols` does nothing — that token is the knob for the `.sf-grid` primitive. For one-line column grids use `<div class="sf-grid-cols" style="--sf-cols: 3">`.

- **`.sf-grid-1-2` is 1fr 2fr** (narrow left, wide right). For content + sidebar where sidebar is narrow, use `.sf-grid-2-1`.

- **a11y layer beats visual.** `prefers-reduced-motion` overrides live in `slashed.a11y`, which ranks above decorative layers.

- **`css/slashed-experimental.css` exists** as a separate opt-in file for features without stable cross-browser support.

## Versioning & commits

- Version scheme: `0.MAJOR.MINOR.PATCH` — the leading `0.` is fixed. Only CSS/JS framework changes bump the version.
- **Before `0.8.0.0` — no migration overhead.** Anything on the public surface can be renamed, removed, or restructured in a single commit with no deprecation alias, no migration guide, and no advance notice.
- `CHANGELOG.md` is append-only (Keep-a-Changelog format). Add an entry for every framework-surface change. Breaking changes go under `### Breaking` with a one-line rename map.
- When implementing a roadmap item, move it out of "Pending work" and add a one-liner to "Explicitly NOT on the roadmap" in the same commit.
- Run `bin/bump-version.sh NEW_VERSION` to update all version strings and regenerate the bundle in one step.
- Run `bin/check-version-sync.sh` to verify every versioned anchor in the repo matches the latest `CHANGELOG.md` entry.

### Versioning policy — full repo lockstep

Every commit, PR, and merge ships with **all** versioned files in lockstep. Drift is treated as a bug, not as a maintenance backlog. The policy is enforced by three checkpoints:

1. **`bin/check-version-sync.sh` is the single source of truth.** It compares the latest released `## [X.Y.Z.W]` heading in `CHANGELOG.md` against every versioned anchor in the repo. **Discovery is automatic** — files are not enumerated by name. The script scans `css/`, `js/`, the repo root, and `docs/` for any file carrying one of the conventional anchors, and verifies every match. New CSS sources, new JS modules, new prose docs, and new HTML pages join the version-sync rotation automatically as soon as they adopt one of these patterns:
   - CSS source / bundle header: `... — vX.Y.Z.W` on line 2 of the banner.
   - JS source header: `// SLASHED <Name> — vX.Y.Z.W`.
   - Prose "current version": `Current version: **X.Y.Z.W**`.
   - Prose version line: `**Version:** X.Y.Z.W`.
   - Prose "last reviewed": `Last reviewed: vX.Y.Z.W`.
   - jsDelivr CDN URLs: `@X.Y.Z.W/`.
   - HTML version badge: `<span class="version-badge">vX.Y.Z.W</span>`.

   Files explicitly skipped: `CHANGELOG.md` (the source of truth), `css/tokens-user.example.css` (consumer template), and anything under `docs/archive/`. The companion `bin/bump-version.sh` uses the same discovery rules to rewrite anchors, plus skips the two regenerated bundles (`slashed-essential.css`, `slashed-full.css`).

2. **The pre-commit hook runs the check.** `bin/setup-hooks.sh` installs a hook that blocks any commit which would land version drift. Run `bin/setup-hooks.sh` once after cloning. The hook also enforces the cheatsheet-sync and README-sync guards, all in one place.

3. **CI re-runs the check on every push and PR.** `.github/workflows/version-sync.yml` is the merge-time guarantee — even if a contributor bypasses the local hook with `--no-verify`, drift cannot reach `main`.

Practical workflow:

- **Never edit a version string by hand.** Use `bin/bump-version.sh NEW_VERSION`. It re-stamps every anchor atomically and regenerates both bundles.
- **Add the `CHANGELOG.md` entry first**, then run `bin/bump-version.sh`. The bump script reads the current version from `tokens-default.css` and validates the format; the CHANGELOG entry provides the new target the verifier checks against on the next commit.
- **`--no-verify` is for pure refactors only**, narrowly defined as commits that change neither documented surface nor versioned anchors. Drift introduced via `--no-verify` will still be caught by CI.

## First-time setup

Install the git hooks so the cheatsheet-sync, README-sync, and version-sync guards run on every commit:

```sh
bin/setup-hooks.sh
```

This installs a `pre-commit` hook that blocks commits which:
- stage CSS/JS source files without also staging `cheatsheet.html`,
- add new `css/*.css` or `docs/*.md` files without also staging `README.md`,
- leave any versioned anchor out of lockstep with `CHANGELOG.md`.

## Philosophy enforcement & drift prevention

The project's philosophy is documented in [`PHILOSOPHY.md`](PHILOSOPHY.md) — a frozen
constitution that defines the seven non-negotiable pillars (Standalone, Lean,
Agnostic, Structured, Hybrid, Edgeless, Deterministic). Every change is
evaluated against these pillars.

### Automated guards

| Script | What it checks | CI workflow |
|---|---|---|
| `bin/check-philosophy-drift.sh` | No external deps, no framework-specific selectors, @layer discipline, no !important in consumer paths, magic number warnings | `philosophy-guard.yml` |
| `bin/check-token-parity.sh` | Token categories meet DX coverage expectations (24 categories) | `philosophy-guard.yml` |
| `bin/sync-docs.sh` | All 8 checks in one command (bundle, version, README, cheatsheet, philosophy, tokens, BEM, CSS lint) | — (local use) |

### How drift is prevented

1. **Pre-commit hooks** catch drift locally before push.
2. **CI workflows** block merge on any violation:
   - `bundle-check.yml` — bundles, lint, README sync, cheatsheet sync
   - `version-sync.yml` — versioned anchors
   - `philosophy-guard.yml` — philosophy drift, token parity, PHILOSOPHY.md frozen
3. **`bin/sync-docs.sh`** provides a single command for full verification before releases.
4. **Code review** — every PR is evaluated against the seven pillars.

### Running the full suite locally

```sh
bin/sync-docs.sh          # check-only mode (no file modifications)
bin/sync-docs.sh --fix    # auto-repair where possible (rebuilds bundles)
```

## Testing & verification

Visual verification is manual. Open `cheatsheet.html` in a browser and skim for regressions after CSS changes.

Automated checks (run locally and in CI on every PR):

```sh
bin/build-bundle.sh && git diff --exit-code -- css/slashed-essential.css css/slashed-full.css
npx --yes -p stylelint@16 -p stylelint-config-standard@36 stylelint "css/*.css"
python3 bin/lint-bem.py
bin/check-version-sync.sh
bin/check-cheatsheet-sync.sh
bin/check-philosophy-drift.sh
bin/check-token-parity.sh
```

Or run everything at once:

```sh
bin/sync-docs.sh
```

Or via npm (subset — does not include philosophy/token checks):

```sh
npm run verify
```
