scaffold CSS architecture - #1
Conversation
Replaces the two empty placeholder files with a fully structured cascade-layer CSS scaffold ready for real token and rule content. Structure: - main.css — single @layer declaration + @import entry point - core/ — 7 files: tokens, reset, base, layout, states, accessibility, print (always loaded) - optional/ — 5 files: components.tokens, components, utilities, themes, motion (load what you need) - docs/architecture.md — full architectural spec - README.md — framework intro, module table, authoring guide Removes: css/slashed-core.css, css/slashed-tokens.css
slashed.tokens, slashed.reset, slashed.base, etc. — prevents name collisions when SLASHED is loaded alongside other layered frameworks. Updates all CSS files, README, and architecture docs.
Full token set from tokens.global.css: @Property typed colors, scale multipliers, fluid type and spacing scales, oklch color definitions, semantic aliases, dark mode (media + data-theme).
All scaffold CSS files (base, reset, layout, states, accessibility, print, optional/*) reduced to header comment + empty @layer block. They contained premature code using non-existent --color-* tokens (not --sf-* prefix) that would have caused silent failures at runtime. Also removes two CSS rules (background-color, color) that were incorrectly placed inside @layer slashed.tokens in core/tokens.css — that layer is custom properties only. https://claude.ai/code/session_01768pzp54ChSPy8jYxZ34ds
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughSLASHED introduces a cascade-layer CSS framework with zero build dependencies: rewritten README, architecture spec, a comprehensive tokens implementation with light/dark modes, core layer ordering via core/layers.css, and placeholder scaffold files for core and optional layers. ChangesSLASHED CSS Framework Foundation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/architecture.md`:
- Around line 37-54: The fenced code blocks in docs/architecture.md (the
file-tree block shown and the other block around lines 150-164) are unlabeled
which trips MD040; update each opening triple-backtick to include a language
marker (e.g., ```text for the ASCII file tree or ```css/```bash where
appropriate) so Markdown lint accepts them and the examples remain correctly
highlighted.
- Around line 197-203: The examples use non-namespaced tokens and layer names;
update them to the documented namespace by replacing instances like `:root {
--color-primary: ... }`, `style="--button-radius: var(--radius-full)"`, `@layer
overrides { … }`, unnamespaced token examples, and `[data-theme="your-theme"] {
--color-primary: … }` with the corresponding slashed.* and --sf-* names used by
the project (e.g., use the slashed.* layer name and --sf-... token names
consistently across the Token override, Component instance token, Layer
override, Unlayered CSS, and Theme examples so consumers target the correct
layer/token namespace).
In `@README.md`:
- Around line 108-113: The fenced code block containing the hierarchy lines
(slashed.tokens → slashed.reset → slashed.base → slashed.layout →
slashed.components → slashed.utilities → slashed.states → slashed.themes →
slashed.motion → slashed.accessibility → slashed.print → slashed.overrides) is
missing a fence language and triggers MD040; fix it by adding a language
identifier after the opening backticks (for example ```text or ```dot) so the
block starts with ```text (or another appropriate language) to satisfy the
linter.
- Around line 91-101: The README CSS example uses non-existent tokens like
--color-surface, --radius-lg and --shadow-sm; update the .product-card example
to use the shipped token naming convention (the --sf-* tokens) so copy-paste
works — e.g. replace --color-surface with --sf-color-surface (or the appropriate
--sf-color-* token), --radius-lg with --sf-radius-md/--sf-radius-lg as
appropriate, and --shadow-sm with --sf-shadow-sm (and similarly ensure spacing
uses --sf-spacing-4); edit the .product-card block in the README to use these
--sf-* token names consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 86d7e03a-eba4-47b5-af05-22bc0ef60d63
📒 Files selected for processing (17)
README.mdcore/accessibility.csscore/base.csscore/layout.csscore/print.csscore/reset.csscore/states.csscore/tokens.csscss/slashed-core.csscss/slashed-tokens.cssdocs/architecture.mdmain.cssoptional/components.cssoptional/components.tokens.cssoptional/motion.cssoptional/themes.cssoptional/utilities.css
💤 Files with no reviewable changes (2)
- css/slashed-core.css
- css/slashed-tokens.css
main.css mixed two concerns: layer order declaration and file loading. Replaced with core.css which owns both responsibilities cleanly — @layer order at top, then @import of all core/* files. Optional modules are loaded by the user via <link> or @import. components.css now imports components.tokens.css itself, so the user loads one file and gets component tokens automatically. https://claude.ai/code/session_01768pzp54ChSPy8jYxZ34ds
core.css is layer-order declaration only. Every module is loaded independently by the consumer via <link rel="stylesheet">. https://claude.ai/code/session_01768pzp54ChSPy8jYxZ34ds
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core.css`:
- Around line 1-3: The comment in core.css claims "Load this file first, always"
which conflicts with the documented single entry point main.css; either rename
core.css to main.css to make it the documented entry file (ensure `@layer`
declarations remain intact and update any imports that referenced core.css), or
keep the filename core.css and change the header comment to explicitly state
that core.css defines cascade layers but should be imported by the project's
entry stylesheet (main.css) — update the comment text accordingly and, if this
PR adds a new main.css, ensure it imports core.css and documents its role as the
single entry point.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
layers.css is a cleaner name — the file's sole job is declaring the cascade layer order. No imports, no styles. README fixes: - remove "full bundle" concept and main.css reference - quick start now shows layers.css + individual <link> per module - remove components.tokens.css from user-facing table (loaded by components.css internally) - fix all token examples to --sf-* prefix - fix layer hierarchy reference: main.css → layers.css - add ```text language to fenced hierarchy block (MD040) - remove PHILOSOPHY.md reference (file does not exist) https://claude.ai/code/session_01768pzp54ChSPy8jYxZ34ds
--sf-spacing-* → --sf-space-* (correct token namespace) add core/layers.css to module overview table https://claude.ai/code/session_01768pzp54ChSPy8jYxZ34ds
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Around line 33-52: The module table omits shipped entry modules like main.css
and optional/components.tokens.css; update the README tables to include an entry
for `main.css` in the primary table (describe it as the shipped bundle/entry
that imports the core layers and define its layer name, e.g. `slashed` or
`slashed.entry`) and add `optional/components.tokens.css` (or the correct tokens
filename) to the Optional table with its layer (e.g.
`slashed.components.tokens`) and a concise “what’s inside” description; ensure
the new rows use the same column format as existing entries and keep the
ordering/comments about load order consistent with `core/layers.css`.
- Around line 11-25: The README's quick-start HTML snippet lists many individual
CSS files (core/layers.css, core/tokens.css, core/reset.css, core/base.css,
core/layout.css, core/states.css, core/accessibility.css, core/print.css,
optional/components.css, optional/utilities.css, optional/themes.css,
optional/motion.css) which conflicts with the project's single-entry approach;
update the snippet and surrounding text to instruct users to include the unified
entry point (main.css) instead, replacing the multiple <link> references and
clarifying that main.css bundles core and optional layers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| | File | Layer | What's inside | | ||
| |------|-------|---------------| | ||
| | `core/layers.css` | — | Cascade layer order declaration. Load first, always | | ||
| | `core/tokens.css` | `slashed.tokens` | Global design tokens — colors, spacing, typography, radii, shadows, z-index, transitions | | ||
| | `core/reset.css` | `slashed.reset` | Browser normalization. No design decisions. No `var()` | | ||
| | `core/base.css` | `slashed.base` | Opinionated element defaults — headings, links, code, tables. All values via tokens | | ||
| | `core/layout.css` | `slashed.layout` | Composable primitives: `.stack`, `.cluster`, `.sidebar`, `.cover`, `.grid`, `.container` | | ||
| | `core/states.css` | `slashed.states` | Global state markers: `.is-hidden`, `.is-disabled`, `.is-loading`, `.is-active`, … | | ||
| | `core/accessibility.css` | `slashed.accessibility` | Focus styles, `.sr-only`, `.skip-link`, reduced-motion token reset | | ||
| | `core/print.css` | `slashed.print` | Print-only overrides inside `@media print` | | ||
|
|
||
| ### Optional | ||
|
|
||
| | File | Layer | What's inside | | ||
| |------|-------|---------------| | ||
| | `optional/components.css` | `slashed.components` | Pre-built UI: button, card, badge, alert, form elements, modal, nav | | ||
| | `optional/utilities.css` | `slashed.utilities` | Single-purpose helpers — spacing, typography, display, flexbox, color, cursor | | ||
| | `optional/themes.css` | `slashed.themes` | Dark mode, forced colors, and brand palette token overrides | | ||
| | `optional/motion.css` | `slashed.motion` | Keyframes, transition utilities, animation classes | | ||
|
|
There was a problem hiding this comment.
Module table is missing shipped entry modules.
The overview omits main.css (and likely optional/components.tokens.css), so the “what to load” story is incomplete. This is a docs correctness gap that can confuse adopters.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 33 - 52, The module table omits shipped entry modules
like main.css and optional/components.tokens.css; update the README tables to
include an entry for `main.css` in the primary table (describe it as the shipped
bundle/entry that imports the core layers and define its layer name, e.g.
`slashed` or `slashed.entry`) and add `optional/components.tokens.css` (or the
correct tokens filename) to the Optional table with its layer (e.g.
`slashed.components.tokens`) and a concise “what’s inside” description; ensure
the new rows use the same column format as existing entries and keep the
ordering/comments about load order consistent with `core/layers.css`.
Remove verbose descriptions, fix stale main.css references, fix extension point token names to --sf-* namespace. https://claude.ai/code/session_01768pzp54ChSPy8jYxZ34ds
Resolves the 10 concerns surfaced in the design-level review of PR #130 (stored at semantic-review/2026-05-27-233854-pr-130.md, gitignored). #1 Migrate-mode silent data loss on existing-class collision - Added validateMigrate() pre-validation pass that runs BEFORE any mutation. Detects three cases: a) Target class doesn't exist → safe (creates with seed). b) Target class exists, no key overlap → safe (additive merge). c) Target class exists, same key with different value → CONFLICT. Hard-errors before any mutation, with a message naming the conflicting keys and suggesting Add mode or a different name. - Per-op merge logic: when target exists with no conflicts, missing seed keys are added to its settings before upsert. upsertGlobalClass still never overwrites — we only ADD keys it didn't have. The 'never overwrite' policy is preserved (overwrite = replacing existing values; this is purely additive). - End result: migrate is now safe in all three cases. removeMigratedKeys is only called after every key is guaranteed to be provided by the (new or existing) class with the same value. #2 Modifier-mode auto-numbering rejected legitimate multi-row applies - Two siblings each producing card__image--lg is the canonical 'attach this modifier to all of them' case, NOT a collision. Replaced the blanket name.includes('--') reject with a per-mode check: in modifier mode, intra-plan duplicates are intentional, no error and no numbering. upsertGlobalClass dedupes by name so all rows share the single class. #3 + #10 'label' provenance not honored as authoritative - Added AUTHORITATIVE_PROVENANCE = new Set(['user', 'label']) and use it consistently in applyAutoNumbering. Both user-typed and structure-panel-label-derived names are treated as authoritative and never auto-renumbered. Two authoritative rows colliding is a hard error (mentions row count, not provenance details, since the user shouldn't need to know about reBEMer's internal vocabulary). - Updated apply.js JSDoc typedef to enumerate all five provenance values: 'user' | 'label' | 'element-type' | 'fallback' | 'auto-number'. - §0 status table no longer overstates this row. #4 Auto-numbering didn't re-check post-numbered names - Added a final post-numbering integrity pass: in non-modifier modes, every op must have a unique finalClass after numbering. Catches the case where a user-typed card__image-1 collides with an auto-numbered card__image-1 from a different group. Modifier mode is exempt (duplicates are by design). #5 Badge dataset flag never cleared on host disconnect - Removed the ATTACHED_FLAG dataset entirely. refreshBadges() now uses badgeInstances map state as the source of truth, with two explicit passes: reap stale (host disconnected), then mount any <li> without a live host inside it. The li.contains(existing.host) check handles the common Bricks pattern of rerendering inner .structure-item without removing the outer <li>. - Eliminates the permanent-skip bug where a row that was rendered once and then had its inner subtree rebuilt by Bricks would lose its badge forever. #6 'Use existing' hint not migrate-aware - Row.svelte recommendation copy now branches on mode. Migrate-mode copy reflects the new validateMigrate semantics: 'On Apply, missing style keys will be merged into it. Conflicting values block the migration — pick a different name or use Add.' - Non-migrate modes keep the original 'attach the existing class instead of creating a duplicate' copy. #7 Auto-numbered names didn't refresh the 'use existing' hint - Refactored apply.js to expose a pure buildPlan({ rootId, rows, mode }) → { ok, ops, error }. Returns the ops with their POST-numbering finalClass values. - BemPanel.svelte computes a previewClassNames Map that runs buildPlan and indexes the result by row id. Reactive — re-runs when rows or mode change. - Row.svelte now accepts finalClassName as a prop (from the panel's preview map) instead of computing candidateClassName locally. The 'use existing' hint matches what apply.js will actually produce, even after auto-numbering. - Same buildPlan is used by applyToSubtree, so preview and apply can never disagree. #8 Unused-classes scan was non-deterministic past the cap - Added ORDER BY meta_id ASC to the postmeta query in class-rebemer-rest.php. The cap+1 truncation detection now pairs with reproducible content: two truncated runs on the same site return the same first 5000 rows. #9 Apply has no mid-apply rollback (acknowledged spec-only) - Snapshot/rollback transactional apply (§10) remains spec-only, but the partial-failure path now (a) console.warn-logs the failure with the count of completed mutations, and (b) surfaces a partial-success toast: 'Applied to N elements before halting: <error>. State is partially applied — undo via Bricks (Cmd-Z) before retrying.' The user has actionable info instead of a bare error. Also addresses cleanup - Added semantic-review/ to .gitignore so future review artifacts from the sub-agent don't accidentally land in commits. Build artifacts regenerated. Bundle grew from 60.20 kB to 62.25 kB JS (+0.6 kB gzip) — the new buildPlan refactor + validateMigrate pre-pass + post-numbering check. Acceptable for the correctness gain. What's still spec-only after this PR Items #4 and #6 in §0 status table are now ✅ shipped where they were partial; the remaining ❌ rows are unchanged (cross-page preflight, snapshot/rollback, undo ring buffer, i18n, reserved-name guard wired from inventory).
- Fix #1: use normalizedQuery (trimmed) in template search branch so whitespace-only input doesn't switch to the full 275-token view - Fix #2: return applyToColorInput() result from onPickValue callback; show error toast and keep picker open if application fails - Fix #3: simplify toggleFamily() to always reset alphaOpen to a new Set(), preventing stale open-alpha state when switching families - Fix #4: reorder getBricksPanelRight() selectors — panel-specific IDs first, drop broad builder-root selectors (#brx-builder, .brx-builder) - Fix #5: add :focus-visible rules for all new interactive elements (.slashed-cp__qu-cell, .slashed-cp__scan-row, .slashed-cp__fam-banner, .slashed-cp__strip-sw, .slashed-cp__alpha-btn) - Fix #6: change .slashed-cp__strip-sw border from 0 to 1px solid transparent so --alpha dashed override actually renders https://claude.ai/code/session_01HesqASnoEx3Amm4H7f17jj
…ne release bug
Previously the displayed framework version was read from a static field
(`_sync.frameworkVersion`) in `api-index.generated.json`. That field had
to be updated in sync with `package.json` on every release, creating a
persistent class of drift:
- The sync-main CI job ran version-sync.js but initially omitted the
file from `git add` (fix #1: 720e339). Later the git add was added
(fix #2: 1a3e949). But post-release PR merges from branches developed
pre-release kept overwriting main's correct value with the old one,
causing CI failures and manual re-syncs every release cycle.
Root fix: remove frameworkVersion from api-index.generated.json entirely
and inject it at Vite build time via `define.__SLASHED_VERSION__` in
vite.config.js, reading directly from the root package.json.
Results:
- The version displayed in the header/output drawer is always exactly
the package.json version that was current when `vite build` ran — no
separate sync step, no committed JSON field to maintain.
- sync-api.mjs no longer writes frameworkVersion to the generated index.
- version-sync.js no longer patches api-index.generated.json.
- check-version-sync.js no longer checks api-index frameworkVersion.
- release.yml sync-main no longer stages api-index.generated.json.
- model.js falls back to sync.frameworkVersion for any dev builds that
bypass Vite (e.g. direct node imports in unit tests).
https://claude.ai/code/session_01MAgtQ7JY16X2TqZZyGfkuu
…ne release bug
Previously the displayed framework version was read from a static field
(`_sync.frameworkVersion`) in `api-index.generated.json`. That field had
to be updated in sync with `package.json` on every release, creating a
persistent class of drift:
- The sync-main CI job ran version-sync.js but initially omitted the
file from `git add` (fix #1: 720e339). Later the git add was added
(fix #2: 1a3e949). But post-release PR merges from branches developed
pre-release kept overwriting main's correct value with the old one,
causing CI failures and manual re-syncs every release cycle.
Root fix: remove frameworkVersion from api-index.generated.json entirely
and inject it at Vite build time via `define.__SLASHED_VERSION__` in
vite.config.js, reading directly from the root package.json.
Results:
- The version displayed in the header/output drawer is always exactly
the package.json version that was current when `vite build` ran — no
separate sync step, no committed JSON field to maintain.
- sync-api.mjs no longer writes frameworkVersion to the generated index.
- version-sync.js no longer patches api-index.generated.json.
- check-version-sync.js no longer checks api-index frameworkVersion.
- release.yml sync-main no longer stages api-index.generated.json.
- model.js falls back to sync.frameworkVersion for any dev builds that
bypass Vite (e.g. direct node imports in unit tests).
https://claude.ai/code/session_01MAgtQ7JY16X2TqZZyGfkuu
Summary
css/slashed-core.css,css/slashed-tokens.css) with a fully structured cascade-layer CSS scaffoldmain.cssas the single entry point (one@layerdeclaration + all@imports)core/(7 files, always loaded) andoptional/(5 files, load what you need) directoriesdocs/architecture.mdwith the full architectural specREADME.mdwith framework intro, module table, and authoring guideFile structure
Layer hierarchy
overridesships no framework rules — it is the consumer's escape hatch. Unlayered consumer CSS beats all layers automatically.Test plan
main.cssin a test HTML file — all imports resolve, no 404smain.cssmatches the speccore/reset.csshas zerovar()callscore/base.cssand component files usesvar()core/files only (nooptional/) — confirms selective import worksoptional/themes.csspropagate to component colorsGenerated by Claude Code
Summary by CodeRabbit
Documentation
New Features
Chores