@keenmate/pure-css 1.0.0-rc09 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,34 @@
3
3
  All notable changes to `@keenmate/pure-css` are documented here. Format based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
+ ## [1.0.0] — 2026-09-12 [PUBLISHED]
7
+
8
+ The **first stable release** — the 1.0.0-rc series culminates here. Two additive
9
+ changes since rc09: the shared `--base-*` icon contract gains a `filter` glyph and
10
+ a `check` / `indeterminate` selection pair, and the mode/variant class-placement
11
+ rule is now documented.
12
+
13
+ ### Added
14
+
15
+ - **Three `--base-icon-*` tokens: `filter`, `check`, `indeterminate`.** All
16
+ mask-friendly Lucide glyphs, authored in the canonical
17
+ `@keenmate/base-css-variables` contract first and mirrored into
18
+ `variables/_base.scss` + `output-base-css-variables` (parity drift-guard stays green).
19
+ - `filter` (funnel) — "refine / narrow a list", deliberately distinct from
20
+ `search` (find-by-text).
21
+ - `check` (✓) + `indeterminate` (−) — the checkbox / tree-node selection pair
22
+ shared by web-multiselect, web-treeview and plain checkboxes: `check` =
23
+ selected, `indeterminate` = a tri-state parent whose children are a mix.
24
+ `indeterminate` shares the minus shape with `collapse` but is its own
25
+ independently-overridable knob (selection ≠ disclosure, mirroring add vs expand).
26
+ - **"Mode & variant class placement" documentation.** A new README section, plus a
27
+ matching `NOTE` in `_base-css-variables.scss`, explaining that `.pc-mode-*` /
28
+ `.pa-color-*` classes must sit on `<html>` (`:root`), not `<body>`: derived
29
+ component tokens emitted once at `:root` (e.g. `--pa-btn-info-bg: var(--pc-info)`)
30
+ freeze at the default-mode value if the mode class lands on a descendant, so role
31
+ buttons/surfaces fail to recolour on switch. Includes the one-frame
32
+ `transition: none` trick to avoid a colour flash.
33
+
6
34
  ## [1.0.0-rc09] — 2026-09-10 [PUBLISHED]
7
35
 
8
36
  The **shell/namespace decoupling** release. Part of the ecosystem-wide
package/README.md CHANGED
@@ -1,38 +1,47 @@
1
1
  # @keenmate/pure-css
2
2
 
3
- The KeenMate CSS **foundation** — the `--base-*` theming contract, the flexbox grid (`.pc-row` / `.pc-col`), and the
4
- utility classes extracted from [`@keenmate/pure-admin-core`](https://github.com/KeenMate/pure-admin)
5
- so it can be consumed on its own.
3
+ KeenMate's CSS **foundation** — descended from [Yahoo's Pure CSS](https://purecss.io/) and extended into a
4
+ more robust, themeable layer for real apps. One small, dependency-free package gives you the
5
+ **`--base-*` theming contract** (one block of custom properties re-themes everything at once), a
6
+ modern **flexbox grid** (`.pc-row` / `.pc-col`, container-query responsive — replacing Pure's float
7
+ grid), a set of **utility classes**, and an optional **app shell + JS runtime** (in the
8
+ `pure-css.css` bundle).
6
9
 
7
- ## What's New in 1.0.0-rc09
10
+ It's the shared layer the whole KeenMate stack agrees on:
11
+ [`@keenmate/pure-admin-core`](https://github.com/KeenMate/pure-admin) builds its component library on
12
+ top of it, and every KeenMate web/Svelte component reads its colours from the same `--base-*`
13
+ variables.
8
14
 
9
- - **Namespace hygiene — the app shell no longer reaches into pure-admin's token namespace** — the KeenMate ecosystem is aligning its custom-property prefixes with its class prefixes: `--pc-*` / `.pc-*` belong to the pure-css foundation + app shell, `--pa-*` / `.pa-*` belong to pure-admin components. As part of that rename, pure-css's shell had a handful of incidental references to *component* tokens — `var(--pc-card-bg, …)` in the fit-flyout / navbar dropdown / resize-handle, `var(--pc-input-bg, …)` in the sidebar search, and `var(--pc-icon-chevron, …)` on the sidebar/navbar chevrons. Since those tokens are moving to `--pa-*` (pure-admin's namespace) and the foundation must not depend on pure-admin, each now reads its `--base-*` foundation value directly. Rendered output is byte-for-byte unchanged — every dropped tier already resolved to the same `--base-*` fallback.
15
+ ## What's New in 1.0.0
16
+
17
+ - **Icons — a `filter` glyph and a `check` / `indeterminate` selection pair join the shared `--base-*` contract** — three more mask-friendly Lucide glyphs so every consumer paints the same affordances from one theme knob. `--base-icon-filter` (a funnel) is the "refine / narrow a list" mark, deliberately separate from `--base-icon-search` (find-by-text) so a component can show both at once. `--base-icon-check` (✓) and `--base-icon-indeterminate` (−) are the checkbox / tree-node selection pair used by web-multiselect, web-treeview and plain checkboxes — `check` means selected, `indeterminate` means a tri-state parent whose children are a mix. `indeterminate` reuses the minus shape of `collapse` by default but is its own independently-overridable token, so selection never gets entangled with disclosure (the same discipline as `add` vs `expand`). All three are authored in the canonical `@keenmate/base-css-variables` package first and mirrored into `variables/_base.scss` + the emit mixin, keeping the parity drift-guard green; consume them via `mask: var(--base-icon-check); background: currentColor`.
10
18
 
11
- ## What's New in 1.0.0-rc08
19
+ - **Docs — mode & variant classes belong on `<html>`, not `<body>`** — a new "Mode & variant class placement" section in the README (mirrored by a `NOTE` in `_base-css-variables.scss`) documents a subtle theming pitfall. CSS resolves a custom property's `var()` at the element that *declares* it, so derived component tokens that pure-admin-core emits once at `:root` — e.g. `--pa-btn-info-bg: var(--pc-info)` — bake in `:root`'s input value. Put a `.pc-mode-dark` / `.pa-color-*` class on a descendant like `<body>` and the override arrives too late: the derived token stays frozen at its default-mode value and role buttons or surfaces don't recolour when you switch. Applying the class to `:root` (the element that declares the tokens) makes the overrides win and everything re-resolves. The section also documents the one-frame `transition: none !important` trick to suppress a colour flash during the swap.
12
20
 
13
- - **Foundation-only the `--pc-*` component layer moved to `@keenmate/pure-admin-core` (breaking)** — pure-css no longer ships `variables/_components.scss` or the component emit mixins (`output-pc-component-variables`, `output-pc-component-mode-variables`, `output-pc-alert-variables-{light,dark}`) — the whole buttons / cards / tables / modals / alerts / badges / command-palette / multiselect vocabulary. pure-css is now a true foundation: the `--base-*` bridge plus the base `--pc-*` tokens (surfaces, text, accent, links, border, role identities, palette slots, radius). SCSS consumers that `@include`d the component mixins must take them from pure-admin-core ≥ 2.9.0-rc20; consumers of the compiled `dist/css/*` are unaffected.
14
- - **Icons — a shared, themeable `--base-icon-*` contract (13 tokens)** — `chevron`, `caret-down`/`-up`, `close`, `clear`, `remove`, `expand`, `collapse`, `add`, `edit`, `delete`, `search` (Lucide defaults), emitted as percent-encoded SVG data-URIs painted via `mask` + `background: currentColor`. One override re-skins the shell, pure-admin components, and the web components together; the shell's sidebar/navbar chevrons are now SVG masks instead of a `›` text glyph. Two disclosure models are documented — chevron *rotates one glyph*, expand/collapse *swaps two*.
15
- - **Contracta coherent, fully-named `--base-*` token API (WS7)** — added `--base-border-width`, the `--base-primary-*` accent aliases, `--base-secondary-*`, the `--base-text-on-<role>` set, `--base-info-bg` for role symmetry, the `--base-color-1..9` (+`-text`) brand palette aliased by `--pc-color-N`, and the non-colour scales `--base-space-*`, `--base-shadow-{sm,md,lg}`, `--base-duration-*` + `--base-ease-*`, and `--base-z-*`. `--base-disabled-bg` got its own value (`#f1f3f5`) instead of colliding with the hover surface.
16
- - **Surfaces — hover/active split off the recessed surface** — new `--pc-hover-bg` / `--pc-active-bg` base tokens let component hover/active states read a dedicated interaction-state axis rather than borrowing the recessed `--base-subtle-bg`, which read as *raised* in several dark themes.
17
- - **Theme — default palette rebased onto pure-admin Corporate** — `$base-*` defaults now track Corporate (accent `#0ea5e9`, slate text/surfaces, emerald/red/amber/cyan roles), with the palette sourced from `$base-color-1..9`. This changes pure-css's *un-themed* default look; themed apps are unaffected since every theme sets its own `--base-*`.
18
- - **Shadow DOM — a new `component-reset` entry** — `./component-reset` (`dist/css/component-reset.css`) is the counterpart to `reboot` for web components: a `:host` box-sizing + inherited-typography reset pinned to `--base-*` so a host page can't bleed into a component's shadow root. No `rem` base — pair it with `base`. Brings the build to 7 artifacts.
19
- - **Internal — `@keenmate/base-css-variables` is now the canonical `--base-*` parent** — pure-css mirrors its token list into `$base-*` SCSS, and `scripts/check-base-parity.mjs` fails the build if the emitted names drift from the contract. New tokens are authored in base-css-variables first, then mirrored here.
21
+ ## What's New in 1.0.0-rc09
22
+
23
+ - **Namespace hygiene the app shell no longer reaches into pure-admin's token namespace** — the KeenMate ecosystem is aligning its custom-property prefixes with its class prefixes: `--pc-*` / `.pc-*` belong to the pure-css foundation + app shell, `--pa-*` / `.pa-*` belong to pure-admin components. As part of that rename, pure-css's shell had a handful of incidental references to *component* tokens — `var(--pc-card-bg, …)` in the fit-flyout / navbar dropdown / resize-handle, `var(--pc-input-bg, …)` in the sidebar search, and `var(--pc-icon-chevron, …)` on the sidebar/navbar chevrons. Since those tokens are moving to `--pa-*` (pure-admin's namespace) and the foundation must not depend on pure-admin, each now reads its `--base-*` foundation value directly. Rendered output is byte-for-byte unchanged every dropped tier already resolved to the same `--base-*` fallback.
20
24
 
21
25
  ## Why
22
26
 
23
- Any surface that isn't a full admin app — a docs site, a marketing page, a standalone widget host —
24
- wants the *foundation* (variables + grid + utilities) without pure-admin-core's 40+ components. And
25
- every KeenMate web component (`<web-multiselect>`, …) and Svelte component already reads its colors
26
- from the `--base-*` custom properties. Shipping those from one small package means one theming layer
27
- that the components, the admin framework, and everything else all agree on.
27
+ pure-css is a **standalone foundation** you drop onto any surface — a docs site, a marketing page, a
28
+ widget host, or a full application. One small, dependency-free package gives you theming, layout and
29
+ utilities without buying into a component framework.
30
+
31
+ Its heart is a single **`--base-*` theming contract**: override one block of custom properties and
32
+ everything re-themes at once — the grid, the utilities, the optional app shell, and any component
33
+ that reads the same variables. Light and dark are built in via `light-dark()`, there's no build step
34
+ to consume it (just link the prebuilt CSS), and it pulls in no runtime dependencies.
28
35
 
29
36
  ```
30
- @keenmate/pure-css @keenmate/pure-admin-core
31
- ├─ --base-* variables ◀──── imports pure-css, adds
32
- ├─ .pc-row / .pc-col grid the component library
33
- └─ utility classes
34
-
35
- └── docs sites, portals, component hosts consume the built CSS directly
37
+ @keenmate/pure-css (this package)
38
+ ├─ --base-* theming contract
39
+ ├─ .pc-row / .pc-col grid
40
+ ├─ utility classes
41
+ └─ optional app shell + JS runtime
42
+ ▲ consumed directly, as built CSS, by…
43
+ ├── docs sites · portals · marketing pages · widget & component hosts
44
+ └── @keenmate/pure-admin-core — adds a full component library on top (just one consumer)
36
45
  ```
37
46
 
38
47
  ## Installation
@@ -124,15 +133,19 @@ swap the icon set.
124
133
  | `--base-icon-remove` | `✕` | take an **item** out of a collection (chip / tag / row) — non-destructive; follows `--base-icon-close` |
125
134
  | `--base-icon-expand` / `--base-icon-collapse` | `+` / `−` | **swap-two-glyphs** disclosure (tree nodes, accordions): show `+` when collapsed, `−` when open |
126
135
  | `--base-icon-add` / `--base-icon-edit` / `--base-icon-delete` | `+` / pencil / trash | **CRUD action** verbs — create / modify / **destroy** (delete is a trash can, *not* an ✕, so it reads as destructive) |
127
- | `--base-icon-search` | magnifying glass | search inputs, filter fields, command palette |
136
+ | `--base-icon-search` | magnifying glass | search inputs, command palette — find **by text** |
137
+ | `--base-icon-filter` | funnel | refine / **narrow a list** by criteria (filter toggles, faceted search) — distinct from `search` |
138
+ | `--base-icon-check` / `--base-icon-indeterminate` | `✓` / `−` | **selection** pair (checkboxes, multiselect, tree nodes): `check` = selected, `indeterminate` = a tri-state parent whose children are a mix |
128
139
 
129
- Two intentional distinctions:
140
+ Three intentional distinctions:
130
141
 
131
142
  - **Disclosure models:** **chevron rotates one glyph** (sidebar, multiselect), while **expand/collapse swaps
132
143
  two glyphs** (trees, accordions) — a component never rotates a `+` into a `−`.
133
144
  - **✕ vs trash:** `close` / `clear` / `remove` are three *dismiss* purposes that share the ✕ glyph (and
134
145
  cascade off `--base-icon-close`), while `delete` is a separate *destructive* action drawn as a trash can.
135
146
  `add` shares the `+` shape with `expand` but is an independent knob (create ≠ disclosure).
147
+ - **Selection ≠ disclosure:** `indeterminate` shares the `−` shape with `collapse` but is its own knob —
148
+ a partially-selected checkbox is not a collapsed node.
136
149
 
137
150
  ## Theming
138
151
 
@@ -152,6 +165,27 @@ re-themes all of them at once. This is the same model as
152
165
  [`@keenmate/pure-admin-themes`](https://github.com/KeenMate/pure-admin-themes), so the same CLI and
153
166
  publishing infrastructure applies.
154
167
 
168
+ ### Mode & variant class placement
169
+
170
+ Light/dark and colour-variant switching is done by toggling a class — `.pc-mode-light` /
171
+ `.pc-mode-dark` and `.pa-color-*`. **Apply these to the `:root` element (`<html>`), not `<body>`.**
172
+
173
+ The mode/variant blocks override input tokens (`--pc-*` / `--base-*`). Many themed tokens are
174
+ *derived* from those inputs and emitted once at `:root` — e.g. core emits
175
+ `--pa-btn-info-bg: var(--pc-info)`. CSS resolves a custom property's `var()` **at the element that
176
+ declares it**, so a derived token declared on `:root` bakes in `:root`'s input value. If the mode
177
+ class sits on a *descendant* (`<body>`), the override comes too late and the derived token stays
178
+ frozen at the default-mode value — the classic symptom is a role button or surface that doesn't
179
+ change colour when you switch modes. Putting the class on `:root` (the same element that declares
180
+ the tokens) makes the overrides win and the derived tokens re-resolve.
181
+
182
+ pure-css re-emits its own base text-tier tokens at `:root, .pc-mode-light, .pc-mode-dark` to tolerate
183
+ either placement, but that does not extend to the pure-admin component layer, hence the `:root` rule.
184
+
185
+ To avoid a colour "flash" on switch, disable transitions for one frame during the swap (add a
186
+ `transition: none !important` class to `:root`, change the mode/variant class, force a reflow, then
187
+ remove it).
188
+
155
189
  ## Build
156
190
 
157
191
  ```bash
package/dist/css/base.css CHANGED
@@ -136,6 +136,9 @@
136
136
  --base-icon-edit: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z%22/%3E%3C/svg%3E");
137
137
  --base-icon-delete: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M3 6h18%22/%3E%3Cpath d=%22M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2%22/%3E%3Cpath d=%22M10 11v6%22/%3E%3Cpath d=%22M14 11v6%22/%3E%3C/svg%3E");
138
138
  --base-icon-search: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Ccircle cx=%2211%22 cy=%2211%22 r=%228%22/%3E%3Cpath d=%22m21 21-4.3-4.3%22/%3E%3C/svg%3E");
139
+ --base-icon-filter: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolygon points=%2222 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3%22/%3E%3C/svg%3E");
140
+ --base-icon-check: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M20 6 9 17l-5-5%22/%3E%3C/svg%3E");
141
+ --base-icon-indeterminate: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M5 12h14%22/%3E%3C/svg%3E");
139
142
  --base-color-1: #f59e0b;
140
143
  --base-color-2: #ec4899;
141
144
  --base-color-3: #10b981;
@@ -6081,6 +6081,9 @@ body.pc-layout--sticky .pc-layout__inner {
6081
6081
  --base-icon-edit: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z%22/%3E%3C/svg%3E");
6082
6082
  --base-icon-delete: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M3 6h18%22/%3E%3Cpath d=%22M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2%22/%3E%3Cpath d=%22M10 11v6%22/%3E%3Cpath d=%22M14 11v6%22/%3E%3C/svg%3E");
6083
6083
  --base-icon-search: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Ccircle cx=%2211%22 cy=%2211%22 r=%228%22/%3E%3Cpath d=%22m21 21-4.3-4.3%22/%3E%3C/svg%3E");
6084
+ --base-icon-filter: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolygon points=%2222 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3%22/%3E%3C/svg%3E");
6085
+ --base-icon-check: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M20 6 9 17l-5-5%22/%3E%3C/svg%3E");
6086
+ --base-icon-indeterminate: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M5 12h14%22/%3E%3C/svg%3E");
6084
6087
  --base-color-1: #f59e0b;
6085
6088
  --base-color-2: #ec4899;
6086
6089
  --base-color-3: #10b981;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keenmate/pure-css",
3
- "version": "1.0.0-rc09",
3
+ "version": "1.0.0",
4
4
  "description": "The KeenMate CSS foundation — --base-* theming contract, PureCSS grid and utility classes. Extracted from pure-admin-core so docs sites, standalone pages and every web/svelte component share one theming layer.",
5
5
  "style": "dist/css/pure-css.css",
6
6
  "exports": {
@@ -215,7 +215,17 @@
215
215
  --base-icon-delete: #{$base-icon-delete};
216
216
 
217
217
  // === Utility icons ===
218
+ // search = magnifying glass (find by text); filter = funnel (refine/narrow a
219
+ // list — distinct affordance from search, its own knob).
218
220
  --base-icon-search: #{$base-icon-search};
221
+ --base-icon-filter: #{$base-icon-filter};
222
+
223
+ // === Selection icons — checkbox / tree-node state pair ===
224
+ // check = ✓ (selected), indeterminate = − (tri-state parent, partially selected).
225
+ // Shared by web-multiselect / web-treeview / checkboxes. indeterminate shares the
226
+ // minus shape with collapse but is its OWN knob (selection ≠ disclosure).
227
+ --base-icon-check: #{$base-icon-check};
228
+ --base-icon-indeterminate: #{$base-icon-indeterminate};
219
229
 
220
230
  // === Theme palette slots (1–9) + paired contrast text (WS7.6) ===
221
231
  // Promoted into the --base-* contract so the branded/data-viz palette is
@@ -295,6 +305,13 @@
295
305
  // .pc-mode-dark` covers html, body in light mode, and body in dark mode —
296
306
  // regardless of which class layout each theme uses for mode switching.
297
307
  //
308
+ // NOTE: this placement-agnosticism only covers the base tokens pure-css emits
309
+ // here. The pure-admin COMPONENT tokens (e.g. `--pa-btn-info-bg: var(--pc-info)`)
310
+ // are emitted once at `:root` by core and are NOT re-emitted per mode, so they
311
+ // only re-resolve when the mode/variant class sits on the :root element itself.
312
+ // Consumers MUST therefore apply `.pc-mode-*` / `.pa-color-*` to `<html>`, not
313
+ // `<body>` — see README "Mode & variant class placement".
314
+ //
298
315
  // This is a TOP-LEVEL rule (not inside the mixin) because themes only call
299
316
  // the mixin from their light-mode block; they don't call it again in the
300
317
  // dark-mode block. Emitting at framework level means themes get correct
@@ -338,6 +338,15 @@ $base-rem: 1rem !default;
338
338
  //
339
339
  // Utility:
340
340
  // - search: magnifying glass — search inputs, filter fields, command palette.
341
+ // - filter: funnel — narrow a list/table by criteria (filter toggles, faceted
342
+ // search). Distinct from `search` (find by text) — read as "refine".
343
+ //
344
+ // Selection — the checkbox / tree-node state pair (shared by web-multiselect,
345
+ // web-treeview, checkboxes — one tick everywhere):
346
+ // - check: the ✓ — SELECTED. Checkbox checked, selected option/row/node.
347
+ // - indeterminate: the − — PARTIALLY selected (tri-state: a parent whose children
348
+ // are a mix). Shares the minus shape with `collapse` but is an
349
+ // INDEPENDENT knob — selection ≠ disclosure (cf. add vs expand).
341
350
  // =============================================================================
342
351
 
343
352
  $base-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22m9 18 6-6-6-6%22/%3E%3C/svg%3E") !default;
@@ -351,8 +360,13 @@ $base-icon-collapse: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2
351
360
  $base-icon-add: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M5 12h14%22/%3E%3Cpath d=%22M12 5v14%22/%3E%3C/svg%3E") !default;
352
361
  $base-icon-edit: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z%22/%3E%3C/svg%3E") !default;
353
362
  $base-icon-delete: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M3 6h18%22/%3E%3Cpath d=%22M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2%22/%3E%3Cpath d=%22M10 11v6%22/%3E%3Cpath d=%22M14 11v6%22/%3E%3C/svg%3E") !default;
354
- // Utility: magnifying glass (Lucide search).
363
+ // Utility: magnifying glass (Lucide search) + funnel (Lucide filter).
355
364
  $base-icon-search: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Ccircle cx=%2211%22 cy=%2211%22 r=%228%22/%3E%3Cpath d=%22m21 21-4.3-4.3%22/%3E%3C/svg%3E") !default;
365
+ $base-icon-filter: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolygon points=%2222 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3%22/%3E%3C/svg%3E") !default;
366
+ // Selection: check = ✓ (Lucide check), indeterminate = − (Lucide minus). indeterminate
367
+ // shares the minus shape with `collapse` but is its own knob (selection ≠ disclosure).
368
+ $base-icon-check: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M20 6 9 17l-5-5%22/%3E%3C/svg%3E") !default;
369
+ $base-icon-indeterminate: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M5 12h14%22/%3E%3C/svg%3E") !default;
356
370
 
357
371
  // =============================================================================
358
372
  // ALERT COLOR DERIVATION PARAMETERS