Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 124 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,118 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

## [0.3.0] - 2026-05-24

Class taxonomy refactor. New `slashed.macros` cascade layer, 12 macro
recipes, ACSS-parity essentials, and a v0.3.0+ blueprint for 8 reserved
components. Plus the in-flight WebKit a11y/contrast fixes that were sitting
in `Unreleased`.

### ⚠️ Breaking Changes

#### New `slashed.macros` cascade layer

A new `slashed.macros` layer is introduced between `slashed.components`
and `slashed.utilities`. Macros may compose with primitives and
components, but a single-property utility still wins on the same
selector. Updated layer order:

```text
tokens β†’ reset β†’ base β†’ forms β†’ layout β†’ components β†’ macros β†’
utilities β†’ states β†’ themes β†’ motion β†’ accessibility β†’ print β†’
legacy β†’ overrides
```

Three classes change cascade layer (selectors and properties unchanged):

- `.sf-prose`, `.sf-not-prose` β€” `slashed.layout` β†’ `slashed.macros`.
Moved out of `core/layout.css` into the new `core/macros.css`.
- `.focus-parent` β€” `slashed.states` β†’ `slashed.accessibility`.
Now lives in `core/accessibility.css` alongside the rest of the focus /
a11y rules.

A site that worked in 0.2.x works in 0.3.0 without any markup changes.
Consumers who previously wrote `@layer slashed.layout { .sf-prose { … } }`
or `@layer slashed.states { .focus-parent { … } }` overrides should move
those rules to `slashed.overrides` (the documented escape hatch).
See `docs/migration.md` for details.

### Added

- **`core/macros.css`** β€” 12 recipes / patterns.
- `.sf-prose`, `.sf-not-prose` (relocated from `core/layout.css`).
- `.sf-flow` β€” Heydon Pickering's lobotomized owl
(`> * + * { margin-block-start: var(--sf-flow-space) }`).
- `.sf-truncate` β€” single-line ellipsis.
- `.sf-line-clamp-2`, `.sf-line-clamp-3`, `.sf-line-clamp-N`.
- `.sf-equal-height` β€” flex children stretch to tallest.
- `.sf-aspect` β€” generic aspect-ratio container.
- `.sf-scroll-shadow` β€” top + bottom mask gradient on a vertical
scroll container.
- `.sf-scroll-snap` β€” vertical scroll-snap container.
- `.sf-overflow-fade` β€” end-edge horizontal mask fade.
- `.sf-no-tap-highlight` β€” suppress mobile tap-highlight.
- **`core/tokens.macros.css`** β€” semantic tokens for the macros:
`--sf-flow-space`, `--sf-line-clamp`, `--sf-truncate-suffix`,
`--sf-aspect`, `--sf-scroll-shadow-size`.
- **A11y patterns:**
- `.sf-clickable-parent` β€” the card-with-link pattern (overlay-based
full-card click target with single AT announcement; text remains
selectable; secondary buttons / links keep working;
`[data-no-overlay]` is the consumer escape hatch).
- **Layout extras (ACSS parity):**
- `.sf-icon--boxed` β€” modifier on the existing `.sf-icon` that wraps
the glyph in a padded, bordered, optionally coloured frame
(content-box; consumes new `--sf-icon-box-*` tokens).
- Border-style scale: `--sf-border-style`, `--sf-border-style-strong`,
`--sf-border-style-soft`, `--sf-border-style-dotted` β€” pairs with
the existing `--sf-border-width-*` and `--sf-color-border*` so
consumers can switch decorative styles without rewriting rules.
- Icon-boxed tokens: `--sf-icon-box-pad`, `--sf-icon-box-radius`,
`--sf-icon-box-bg`, `--sf-icon-box-border`.
- **Components blueprint** β€” `optional/components.css` and
`optional/tokens.components.css` move from empty `/* TODO */` stubs
to structured BLUEPRINTs. Both files keep an active `@layer`
declaration to RESERVE cascade position; every class definition and
every component token is commented out. Activation is planned for
upcoming minor releases (additive only). The 8 reserved components
are: `.sf-button`, `.sf-card`, `.sf-badge`, `.sf-tag`, `.sf-alert`,
`.sf-avatar`, `.sf-modal`, `.sf-skeleton`. Card tokens (originally
proposed for the essential bundle) live here, commented, alongside
the rest β€” so the entire component-related surface (classes +
tokens) shares one activation cycle. See `docs/components.md`.

### Documentation

- **`docs/architecture.md`** β€” new "Class taxonomy" section with the
7 categories and a 5-step decision tree for adding new classes;
layer order updated; file structure updated.
- **`docs/macros.md`** *(new)* β€” full reference for every macro with
signature, usage example, and consumed tokens.
- **`docs/components.md`** *(new)* β€” components blueprint and the
ratified out-of-scope list (`tabs`, `accordion`, `tooltip`, …) with
rationale for each exclusion.
- **`docs/migration.md`** β€” adds an "0.2.x β†’ 0.3.0" section explaining
the three relocations and the `slashed.overrides` escape hatch.
- README updated for the new layer order, file list, and bundle
contents.

### Tests

- `tests/macros.spec.js` *(new)* β€” behavioural tests for each macro,
covering both default behaviour and token-override paths.
- `tests/layers.spec.js` β€” three new ordering invariants:
`macros > components`, `utilities > macros`,
`accessibility > motion`.
- `tests/coverage.spec.js` β€” three new exclusions for selectors
exercised by dedicated specs (`.sf-overflow-fade`,
`.sf-no-tap-highlight`, `.sf-clickable-parent`).
- `tests/tokens.spec.js` β€” adds `core/tokens.macros.css` to
the token-coverage source list.
- `docs/demo.html` β€” new `<section id="macros">` exercising every
macro inline. Existing `tests/demo-visual.spec.js` screenshots are
locator-scoped and unaffected.

### Changed

- **CI: skip `[webkit] axe: no WCAG A/AA violations (dark)`** with a
Expand All @@ -26,13 +138,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
bump from `0.68` to `0.72` was made under the (incorrect) belief that
the dark-mode link floor was the cause of the WebKit axe contrast
failure. The actual cause was selector specificity (now fixed via
`a:link`, see above) and the WebKit value was painting `-webkit-link`
`a:link`, see below) and the WebKit value was painting `-webkit-link`
regardless of how the formula computed. Restoring PR #70's `0.68`
floor β€” that floor is still a defensive contrast cushion above the
pre-PR-#70 `0.62`, just without the speculative extra bump that
served no purpose. Tiny visible delta in dark mode across all
browsers; the formula's value never reached WebKit anyway and now
Chromium/Firefox revert to PR #70's tested floor.
floor β€” still a defensive contrast cushion above the pre-PR-#70
`0.62`, without the speculative extra bump that served no purpose.

### Fixed

Expand All @@ -56,6 +165,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
pickers is unaffected (the `:where()` block only matches text-like
inputs).

### Bundle size

- essential: 9.9 kB β†’ 10.4 kB gzip (+5%).
- optimal: 12.2 kB β†’ 12.7 kB gzip (+4%).
- optimal-components / full: minifier strips blueprint comments, so
min sizes are unchanged from optimal.
- All bundles remain well under their `tests/bundle-size.spec.js`
budgets (15 / 18 / 20 kB gzip).

## [0.2.12] - 2026-05-23

Release infrastructure and Bricks Builder integration.
Expand Down
44 changes: 28 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ A cascade-layer CSS framework. No build step. No Node. No runtime dependencies.
<link rel="stylesheet" href="core/layers.css">
<link rel="stylesheet" href="core/tokens.css">
<link rel="stylesheet" href="core/tokens.layout.css">
<link rel="stylesheet" href="core/tokens.macros.css">
<link rel="stylesheet" href="core/reset.css">
<link rel="stylesheet" href="core/base.css">
<link rel="stylesheet" href="core/themes.css">
<link rel="stylesheet" href="core/layout.css">
<link rel="stylesheet" href="core/macros.css">
<link rel="stylesheet" href="core/states.css">
<link rel="stylesheet" href="core/motion.css">
<link rel="stylesheet" href="core/accessibility.css">
Expand All @@ -35,11 +37,14 @@ A cascade-layer CSS framework. No build step. No Node. No runtime dependencies.
<link rel="stylesheet" href="optional/legacy.css">
```

> **Note:** `optional/components.css`, `optional/utilities.css` and
> `optional/tokens.components.css` are empty placeholders reserved for a
> future component/utility layer. They ship (as no-ops) only in the
> `*-components` / `*-utilities` / `full` bundles and do nothing until
> populated β€” there's no need to link them individually yet.
> **Note:** `optional/components.css` and `optional/tokens.components.css`
> ship as **blueprints** in v0.3.0 β€” their `@layer` declarations reserve
> cascade position, but every class definition and component token is
> commented out (no CSS is emitted). They appear (as no-ops) in the
> `*-components` and `full` bundles only. `optional/utilities.css`
> remains an empty stub β€” SLASHED is BEM-first by design and ships no
> utility classes in 0.x. There's no need to link any of the three
> individually.

**Recommended:** use a pre-built bundle instead of wiring up every file
(see [Releases](https://github.com/codeslash-dev/SLASHED/releases/latest)):
Expand All @@ -58,8 +63,9 @@ A cascade-layer CSS framework. No build step. No Node. No runtime dependencies.
## Cascade layer order

```text
slashed.tokens β†’ reset β†’ base β†’ forms β†’ layout β†’ components β†’ utilities β†’
states β†’ themes β†’ motion β†’ accessibility β†’ print β†’ legacy β†’ overrides
slashed.tokens β†’ reset β†’ base β†’ forms β†’ layout β†’ components β†’ macros β†’
utilities β†’ states β†’ themes β†’ motion β†’ accessibility β†’ print β†’ legacy β†’
overrides
```

Declared in `core/layers.css`. Later layers win. `slashed.overrides`
Expand All @@ -68,7 +74,10 @@ is reserved for your own overrides and sits last so it always wins.
`@supports not (...)` and are inert on modern engines.

`slashed.forms` (between `base` and `layout`) holds the opt-in
classless form styling from `optional/forms.css`.
classless form styling from `optional/forms.css`. `slashed.macros`
(between `components` and `utilities`, added in v0.3.0) holds recipes
like `.sf-prose`, `.sf-flow`, `.sf-truncate`, `.sf-aspect`,
`.sf-scroll-shadow`. See [`docs/macros.md`](docs/macros.md).

## Scope of the base layer

Expand Down Expand Up @@ -96,17 +105,18 @@ the `slashed.tokens` layer.

| Bundle | Contents |
| --- | --- |
| `slashed.essential.css` | all `core/` (`layers` + `tokens` + `tokens.layout` + `reset` + `base` + `themes` + `layout` + `states` + `motion` + `accessibility` + `print`) |
| `slashed.essential.css` | all `core/` (`layers` + `tokens` + `tokens.layout` + `tokens.macros` + `reset` + `base` + `themes` + `layout` + `macros` + `states` + `motion` + `accessibility` + `print`) |
| `slashed.optimal.css` | essential + `tokens.palette` + `forms` + `legacy` |
| `slashed.optimal-components.css` | optimal + `tokens.components` + `components` |
| `slashed.optimal-components.css` | optimal + `tokens.components` *(blueprint)* + `components` *(blueprint)* |
| `slashed.optimal-utilities.css` | optimal + `utilities` |
| `slashed.full.css` | optimal + `tokens.components` + `components` + `utilities` |
| `slashed.full.css` | optimal + `tokens.components` *(blueprint)* + `components` *(blueprint)* + `utilities` |

`optional/legacy.css` is always concatenated last. Every rule lives in an
`@layer`, so concatenation order never affects the cascade β€” `core/layers.css`
fixes it. The `components` / `utilities` / `tokens.components` files are still
empty stubs: they ship (no-op) only in the `*-components` / `*-utilities` /
`full` bundles and do nothing until populated.
fixes it. `components.css` and `tokens.components.css` ship as **blueprints**
in 0.3.0 β€” their `@layer` declarations reserve cascade position, but every
selector and token is commented out (no CSS is emitted). `utilities.css`
ships as an empty stub.

Γ€ la carte is also supported β€” start from `essential` (or raw `core/`) and add
hand-picked optional files in any order. When building a custom bundle by hand,
Expand Down Expand Up @@ -179,15 +189,17 @@ versions above β€” colors will collapse to `initial` on older engines.

| Guide | What's inside |
| --- | --- |
| [Architecture](docs/architecture.md) | layers, file structure, bundles, token contract, naming |
| [Architecture](docs/architecture.md) | layers, file structure, class taxonomy, bundles, token contract |
| [Theming](docs/theming.md) | rebrand in 6 tokens, multi-brand, contrast |
| [Dark mode](docs/dark-mode.md) | toggle script, scoped themes, per-value overrides |
| [Layout primitives](docs/layout.md) | every `.sf-*` layout class + tokens |
| [Macros / recipes](docs/macros.md) | every `.sf-*` macro-class + tokens (v0.3.0+) |
| [Components blueprint](docs/components.md) | reserved component names + roadmap |
| [State classes](docs/states.md) | every `.is-*` + ARIA mapping + overlap semantics |
| [Token reference](docs/tokens.md) | all `--sf-*` tokens + defaults (generated) |
| [Browser support](docs/browser-support.md) | the support floor and why |
| [Performance](docs/performance.md) | modern-CSS footguns to avoid |
| [Migration](docs/migration.md) | Pico / Bulma / Tailwind β†’ SLASHED |
| [Migration](docs/migration.md) | 0.2.x β†’ 0.3.0 + Pico / Bulma / Tailwind β†’ SLASHED |
| [Contributing](CONTRIBUTING.md) | setup, conventions, tests |

## Development
Expand Down
20 changes: 20 additions & 0 deletions bundle.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -23,10 +25,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -39,10 +43,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -59,10 +65,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -78,10 +86,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -100,10 +110,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -121,10 +133,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -142,10 +156,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -162,10 +178,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand All @@ -185,10 +203,12 @@
"core/layers.css",
"core/tokens.css",
"core/tokens.layout.css",
"core/tokens.macros.css",
"core/reset.css",
"core/base.css",
"core/themes.css",
"core/layout.css",
"core/macros.css",
"core/states.css",
"core/motion.css",
"core/accessibility.css",
Expand Down
Loading
Loading