@keenmate/pure-css 1.0.0-rc08 → 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,56 @@
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
+
34
+ ## [1.0.0-rc09] — 2026-09-10 [PUBLISHED]
35
+
36
+ The **shell/namespace decoupling** release. Part of the ecosystem-wide
37
+ `--pc-*` → `--pa-*` component-token rename (custom-property prefix now matches
38
+ class prefix: `--pc-*`/`.pc-*` = pure-css foundation + shell, `--pa-*`/`.pa-*` =
39
+ pure-admin components). pure-css owns none of the renamed tokens; this release
40
+ only removes the shell's few incidental references to pure-admin component
41
+ tokens so the foundation never points into pure-admin's namespace.
42
+
43
+ ### Changed
44
+
45
+ - **The app shell no longer references pure-admin component tokens.** Three shell
46
+ surfaces read a `--pc-*` *component* token as the first tier of a fallback
47
+ chain — the fit-flyout, navbar dropdown and resize-handle used
48
+ `var(--pc-card-bg, var(--base-main-bg))`; the sidebar search used
49
+ `var(--pc-input-bg, var(--base-input-bg))`; and the sidebar/navbar chevrons
50
+ used `var(--pc-icon-chevron, var(--base-icon-chevron, …))`. Those component
51
+ tokens are being renamed to `--pa-*` (pure-admin's namespace), which the
52
+ foundation must not depend on, so each reference now reads its `--base-*`
53
+ foundation value directly. **Rendered output is unchanged** — every dropped
54
+ tier resolved to the same `--base-*` fallback already.
55
+
6
56
  ## [1.0.0-rc08] — 2026-09-10 [PUBLISHED]
7
57
 
8
58
  The **foundation-only + coherent-contract** release. rc08 finishes what rc07
package/README.md CHANGED
@@ -1,184 +1,213 @@
1
- # @keenmate/pure-css
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.
6
-
7
- ## What's New in 1.0.0-rc08
8
-
9
- - **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.
10
- - **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*.
11
- - **Contract — a 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.
12
- - **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.
13
- - **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-*`.
14
- - **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.
15
- - **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.
16
-
17
- ## What's New in 1.0.0-rc07
18
-
19
- - **Themingone runtime knob re-themes pure-admin components and the web components together** before rc07 the same visual token was produced twice and independently: pure-admin's `--pc-*` component tokens were baked as compile-time literals, while the KeenMate web components read `--base-*` live, so they agreed only by coincidence and diverged the instant a theme retuned `--base-*` at runtime (every dark-mode toggle does exactly this at `.pc-mode-dark`). ~40 themed `--pc-*` tokens in `_base-css-variables.scss` (buttons, cards, inputs, checkbox, input groups, tables, modal, tooltip/popover, command palette, multiselect) were rewritten from `#{$…}` literals to the guiding-rule form `var(--base-x, #{$fallback})`. The `#{$fallback}` preserves today's compiled value so light-mode output is byte-for-byte unchanged, but each token now follows any runtime `--base-*` override the same knob the web components already read.
20
- - **App shell — navbar/sidebar/footer surfaces stay a distinct brand colour, deliberately not `--base-*`-derived** — the shell's surface + text tokens (`--pc-navbar-*`, `--pc-sidebar-*`, `--pc-footer-*`) are emitted as plain `#{$…}` literals, *not* `var(--base-main-bg, …)`. A theme sets the shell's brand (e.g. a yellow navbar) independently of the `--base-*` card/page palette, and since `--base-main-bg` is always emitted, deriving the navbar from it would let the base palette win and erase the brand. Standalone (`base.css`-only) rendering still works because the shell CSS resolves `var(--pc-navbar-bg, var(--base-main-bg))` — the `--base-*` floor applies only when no `--pc-*` is emitted (the rc06 contract), never as a runtime override of it.
21
- - **Theming — gap `--base-*` tokens the web components read but pure-css never emitted** — several `--base-*` vars the components already consumed were falling through to hardcoded component defaults because pure-css didn't emit them, so they couldn't be themed. rc07 emits them: `--base-text-inverted`, `--base-checkbox-border-color`, `--base-input-border-color` (+ the `--base-input-border` shorthand), `--base-input-clear-color` / `--base-input-clear-bg-hover`, the solid role fills `--base-success-bg` / `--base-danger-bg` / `--base-warning-bg`, and `--base-rem`.
22
- - **Surfaces — `--pc-main-bg` / `--pc-subtle-bg` naming realigned with the `--base-*` model** — `--pc-main-bg` is now the **white surface** and `--pc-subtle-bg` the **muted grey**, matching `--base-main-bg` / `--base-subtle-bg`; previously the two were inverted (`--pc-main-bg` *was* the grey canvas). pure-css's own canvas usages (reboot `body`, `.pc-layout`, scrollbars) were repointed `--pc-main-bg` → `--pc-page-bg` so the page canvas stays grey. **Downstream code reading `--pc-main-bg` / `--pc-subtle-bg` must re-check intent** — see pure-admin-core rc19.
23
- - **Fixedinput borders finally track the theme** — `$input-border` was a hardcoded `#ced4da` (its own comment wrongly claimed it came from base); it's reconnected to `$base-input-border-color`. Also a semantic surface reconciliation so pure-admin and the web components resolve the *same* base token per surface (card/table headers + striped rows `--base-elevated-bg`, dropdown/popover `--base-dropdown-bg`, hover `--base-hover-bg`).
24
- - **Fixed — sidebar search box alignment + collapsed-rail gating** — the search input's inline padding was rebalanced (`0 $spacing-base 0 $spacing-sm`) so its magnifier lines up with the nav-row icons below it, and the collapsed-rail rules that strip the search down to the submit icon were re-scoped from `.pc-layout__sidebar--icon-collapse` to `.sidebar-hidden .pc-layout__sidebar--icon-collapse` — the mode class stays on the element while the rail is expanded, so the unscoped rules were stripping the search frame in the expanded state too.
25
-
26
- ## Why
27
-
28
- Any surface that isn't a full admin app a docs site, a marketing page, a standalone widget host —
29
- wants the *foundation* (variables + grid + utilities) without pure-admin-core's 40+ components. And
30
- every KeenMate web component (`<web-multiselect>`, …) and Svelte component already reads its colors
31
- from the `--base-*` custom properties. Shipping those from one small package means one theming layer
32
- that the components, the admin framework, and everything else all agree on.
33
-
34
- ```
35
- @keenmate/pure-css @keenmate/pure-admin-core
36
- ├─ --base-* variables ◀──── imports pure-css, adds
37
- ├─ .pc-row / .pc-col grid the component library
38
- └─ utility classes
39
-
40
- └── docs sites, portals, component hosts consume the built CSS directly
41
- ```
42
-
43
- ## Installation
44
-
45
- ```bash
46
- npm install @keenmate/pure-css
47
- ```
48
-
49
- ## Quick Start
50
-
51
- **Prebuilt CSS (simplest):**
52
-
53
- ```html
54
- <link rel="stylesheet" href="node_modules/@keenmate/pure-css/dist/css/pure-css.css">
55
- ```
56
-
57
- or cherry-pick:
58
-
59
- ```html
60
- <link rel="stylesheet" href="…/pure-css/dist/css/base.css"> <!-- variables only -->
61
- <link rel="stylesheet" href="…/pure-css/dist/css/grid.css"> <!-- + grid -->
62
- ```
63
-
64
- **SCSS (customize before compiling):**
65
-
66
- ```scss
67
- // Override the source of truth; everything re-derives.
68
- $base-accent-color: #4f46e5;
69
- $base-page-bg: #0b1020;
70
-
71
- @use '@keenmate/pure-css/scss/pure-css';
72
- ```
73
-
74
- ## What's in it
75
-
76
- | Artifact | Contents | When to link |
77
- | --- | --- | --- |
78
- | `dist/css/pure-css.css` | everything below, in one file | the common case |
79
- | `dist/css/base.css` | only `:root { --base-*; --pc-*; }` | you just need the theming contract (e.g. to theme embedded web components) or a base for a theme override |
80
- | `dist/css/component-reset.css` | a `:host` reset (box-sizing + inherited typography pinned to `--base-*`) — the Shadow-DOM counterpart to reboot | building a web component: adopt it into the shadow root (e.g. `import '@keenmate/pure-css/component-reset?inline'`) so the host page can't bleed styles in; pair with `base` |
81
- | `dist/css/grid.css` | `.pc-row` / `.pc-col-*` (percentage + fraction columns, container-query responsive) | layout only |
82
- | `dist/css/utilities.css` | spacing / flex / display / width-height utilities (`.m-4`, `.d-flex`, `.w-50`, …) | utilities only |
83
-
84
- The `pure-css.css` bundle also includes the **app shell** (navbar, sidebar,
85
- layout container) — `base.css` / `grid.css` / `utilities.css` do not.
86
-
87
- ### The app-shell runtime (`./js`)
88
-
89
- The shell's behaviour (nav fit/collapse, dropdowns, drag-to-resize, container
90
- breakpoints) ships as dependency-free source JS via the `./js` export — no
91
- bundler required, drop it in with a `<script>` and call `initAll`:
92
-
93
- ```html
94
- <link rel="stylesheet" href="node_modules/@keenmate/pure-css/dist/css/pure-css.css">
95
- <script src="node_modules/@keenmate/pure-css/src/js/pure-css.js"></script>
96
- <script src="node_modules/@keenmate/pure-css/src/js/fit.js"></script>
97
- <script src="node_modules/@keenmate/pure-css/src/js/navbar-dropdown.js"></script>
98
- <script src="node_modules/@keenmate/pure-css/src/js/sidebar-resize.js"></script>
99
- <script>window.pureCss.components.initAll(document);</script>
100
- ```
101
-
102
- `window.pureCss` also exposes an event bus and live `viewport` / `colorScheme` /
103
- `device` sources. The runtime is optional — shell CSS is authored no-JS-safe, so
104
- the styling stands on its own and the JS only adds the interactive behaviour.
105
-
106
- ### The `--base-*` contract
107
-
108
- `--base-*` is the **single source of truth for theming**. Framework colors, component variables
109
- (`--pc-*`) and web/svelte components all derive from it via fallback chains
110
- (`--ms-accent-color: var(--base-accent-color, #3b82f6)`). Categories: accent, text, background,
111
- border, input, dropdown, tooltip, contextual (success/danger/warning/info), interactive states,
112
- typography, border-radius, spacing/shadow/motion/z-index scales, and icons. The full list is
113
- `src/scss/variables/_base.scss`.
114
-
115
- #### Icons
116
-
117
- `--base-icon-*` are mask-friendly SVG glyphs (Lucide defaults) for the shared UI affordances, so the
118
- pure-css shell, pure-admin components, and the web/svelte components render the **same** marks and a
119
- theme re-skins them in one place. Each is consumed via `mask: var(--base-icon-x); background:
120
- currentColor`, so the glyph inherits text colour — override a token with any mask-friendly `url()` to
121
- swap the icon set.
122
-
123
- | Token | Glyph | Use |
124
- | --- | --- | --- |
125
- | `--base-icon-chevron` | stroked angle `›` | expanders / nav — **rotate-one-glyph** disclosure (points right, rotate 90° when open) |
126
- | `--base-icon-caret-down` / `--base-icon-caret-up` | solid triangles `▾` / `▴` | static dropdown / `<select>` affordance (down) and sort-direction / upward-dropdown counterpart (up) — a caret never rotates |
127
- | `--base-icon-close` | `✕` | dismiss a transient **surface** (dialog, panel, popover, toast) |
128
- | `--base-icon-clear` | `✕` | clear a **field** — distinct purpose, same glyph; **follows** `--base-icon-close`, override alone to diverge |
129
- | `--base-icon-remove` | `✕` | take an **item** out of a collection (chip / tag / row) — non-destructive; follows `--base-icon-close` |
130
- | `--base-icon-expand` / `--base-icon-collapse` | `+` / `−` | **swap-two-glyphs** disclosure (tree nodes, accordions): show `+` when collapsed, `−` when open |
131
- | `--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) |
132
- | `--base-icon-search` | magnifying glass | search inputs, filter fields, command palette |
133
-
134
- Two intentional distinctions:
135
-
136
- - **Disclosure models:** **chevron rotates one glyph** (sidebar, multiselect), while **expand/collapse swaps
137
- two glyphs** (trees, accordions) a component never rotates a `+` into a `−`.
138
- - **✕ vs trash:** `close` / `clear` / `remove` are three *dismiss* purposes that share the glyph (and
139
- cascade off `--base-icon-close`), while `delete` is a separate *destructive* action drawn as a trash can.
140
- `add` shares the `+` shape with `expand` but is an independent knob (create ≠ disclosure).
141
-
142
- ## Theming
143
-
144
- A **theme** is nothing but a set of `--base-*` values. The lightest possible theme is a stylesheet
145
- that redeclares them, loaded *after* `base.css`:
146
-
147
- ```css
148
- :root {
149
- --base-accent-color: #4f46e5;
150
- --base-page-bg: #f6f8fb;
151
- --base-text-color-1: #1a2233;
152
- }
153
- ```
154
-
155
- Because pure-admin-core, the components and any consumer all read the same variables, that one block
156
- re-themes all of them at once. This is the same model as
157
- [`@keenmate/pure-admin-themes`](https://github.com/KeenMate/pure-admin-themes), so the same CLI and
158
- publishing infrastructure applies.
159
-
160
- ## Build
161
-
162
- ```bash
163
- make install # sass
164
- make build # src/scss -> dist/css (bundle + base + grid + utilities)
165
- make sizes # show artifact sizes
166
- ```
167
-
168
- `dist/` is committed so consumers can vendor the built CSS without a Sass toolchain.
169
-
170
- ## Provenance
171
-
172
- The SCSS is the foundation extracted from `pure-admin-core`'s `src/scss` — the `variables/` modules,
173
- `_base-css-variables.scss`, `utilities.scss`, `_fonts.scss`, and the native grid (`_pa-grid.scss`,
174
- formerly core's `core-components/_grid.scss`). **pure-admin-core now consumes this package** as its
175
- single source for the foundation (thin `@import`/`@forward` shims), so the two no longer drift —
176
- core's compiled `--base-*` values and grid output match pure-css exactly.
177
-
178
- One intentional difference: `utilities.scss` here `@use`s `_fonts.scss` so the generic
179
- `.font-family-*` classes ship with the other utilities, whereas core keeps `_fonts.scss` standalone.
180
- `_rtl-helpers.scss` and the component layer stay in core.
181
-
182
- ## License
183
-
184
- MIT © KeenMate. The grid is derived from [Pure](https://purecss.io/) (Yahoo!, BSD).
1
+ # @keenmate/pure-css
2
+
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).
9
+
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.
14
+
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`.
18
+
19
+ - **Docsmode & 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.
20
+
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.
24
+
25
+ ## Why
26
+
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.
35
+
36
+ ```
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)
45
+ ```
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ npm install @keenmate/pure-css
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ **Prebuilt CSS (simplest):**
56
+
57
+ ```html
58
+ <link rel="stylesheet" href="node_modules/@keenmate/pure-css/dist/css/pure-css.css">
59
+ ```
60
+
61
+ or cherry-pick:
62
+
63
+ ```html
64
+ <link rel="stylesheet" href="…/pure-css/dist/css/base.css"> <!-- variables only -->
65
+ <link rel="stylesheet" href="…/pure-css/dist/css/grid.css"> <!-- + grid -->
66
+ ```
67
+
68
+ **SCSS (customize before compiling):**
69
+
70
+ ```scss
71
+ // Override the source of truth; everything re-derives.
72
+ $base-accent-color: #4f46e5;
73
+ $base-page-bg: #0b1020;
74
+
75
+ @use '@keenmate/pure-css/scss/pure-css';
76
+ ```
77
+
78
+ ## What's in it
79
+
80
+ | Artifact | Contents | When to link |
81
+ | --- | --- | --- |
82
+ | `dist/css/pure-css.css` | everything below, in one file | the common case |
83
+ | `dist/css/base.css` | only `:root { --base-*; --pc-*; }` | you just need the theming contract (e.g. to theme embedded web components) or a base for a theme override |
84
+ | `dist/css/component-reset.css` | a `:host` reset (box-sizing + inherited typography pinned to `--base-*`) — the Shadow-DOM counterpart to reboot | building a web component: adopt it into the shadow root (e.g. `import '@keenmate/pure-css/component-reset?inline'`) so the host page can't bleed styles in; pair with `base` |
85
+ | `dist/css/grid.css` | `.pc-row` / `.pc-col-*` (percentage + fraction columns, container-query responsive) | layout only |
86
+ | `dist/css/utilities.css` | spacing / flex / display / width-height utilities (`.m-4`, `.d-flex`, `.w-50`, …) | utilities only |
87
+
88
+ The `pure-css.css` bundle also includes the **app shell** (navbar, sidebar,
89
+ layout container) `base.css` / `grid.css` / `utilities.css` do not.
90
+
91
+ ### The app-shell runtime (`./js`)
92
+
93
+ The shell's behaviour (nav fit/collapse, dropdowns, drag-to-resize, container
94
+ breakpoints) ships as dependency-free source JS via the `./js` export — no
95
+ bundler required, drop it in with a `<script>` and call `initAll`:
96
+
97
+ ```html
98
+ <link rel="stylesheet" href="node_modules/@keenmate/pure-css/dist/css/pure-css.css">
99
+ <script src="node_modules/@keenmate/pure-css/src/js/pure-css.js"></script>
100
+ <script src="node_modules/@keenmate/pure-css/src/js/fit.js"></script>
101
+ <script src="node_modules/@keenmate/pure-css/src/js/navbar-dropdown.js"></script>
102
+ <script src="node_modules/@keenmate/pure-css/src/js/sidebar-resize.js"></script>
103
+ <script>window.pureCss.components.initAll(document);</script>
104
+ ```
105
+
106
+ `window.pureCss` also exposes an event bus and live `viewport` / `colorScheme` /
107
+ `device` sources. The runtime is optional — shell CSS is authored no-JS-safe, so
108
+ the styling stands on its own and the JS only adds the interactive behaviour.
109
+
110
+ ### The `--base-*` contract
111
+
112
+ `--base-*` is the **single source of truth for theming**. Framework colors, component variables
113
+ (`--pc-*`) and web/svelte components all derive from it via fallback chains
114
+ (`--ms-accent-color: var(--base-accent-color, #3b82f6)`). Categories: accent, text, background,
115
+ border, input, dropdown, tooltip, contextual (success/danger/warning/info), interactive states,
116
+ typography, border-radius, spacing/shadow/motion/z-index scales, and icons. The full list is
117
+ `src/scss/variables/_base.scss`.
118
+
119
+ #### Icons
120
+
121
+ `--base-icon-*` are mask-friendly SVG glyphs (Lucide defaults) for the shared UI affordances, so the
122
+ pure-css shell, pure-admin components, and the web/svelte components render the **same** marks and a
123
+ theme re-skins them in one place. Each is consumed via `mask: var(--base-icon-x); background:
124
+ currentColor`, so the glyph inherits text colour — override a token with any mask-friendly `url()` to
125
+ swap the icon set.
126
+
127
+ | Token | Glyph | Use |
128
+ | --- | --- | --- |
129
+ | `--base-icon-chevron` | stroked angle `›` | expanders / nav — **rotate-one-glyph** disclosure (points right, rotate 90° when open) |
130
+ | `--base-icon-caret-down` / `--base-icon-caret-up` | solid triangles `▾` / `▴` | static dropdown / `<select>` affordance (down) and sort-direction / upward-dropdown counterpart (up) a caret never rotates |
131
+ | `--base-icon-close` | `✕` | dismiss a transient **surface** (dialog, panel, popover, toast) |
132
+ | `--base-icon-clear` | `✕` | clear a **field** — distinct purpose, same glyph; **follows** `--base-icon-close`, override alone to diverge |
133
+ | `--base-icon-remove` | `✕` | take an **item** out of a collection (chip / tag / row) — non-destructive; follows `--base-icon-close` |
134
+ | `--base-icon-expand` / `--base-icon-collapse` | `+` / `−` | **swap-two-glyphs** disclosure (tree nodes, accordions): show `+` when collapsed, `−` when open |
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) |
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 |
139
+
140
+ Three intentional distinctions:
141
+
142
+ - **Disclosure models:** **chevron rotates one glyph** (sidebar, multiselect), while **expand/collapse swaps
143
+ two glyphs** (trees, accordions) — a component never rotates a `+` into a `−`.
144
+ - **✕ vs trash:** `close` / `clear` / `remove` are three *dismiss* purposes that share the ✕ glyph (and
145
+ cascade off `--base-icon-close`), while `delete` is a separate *destructive* action drawn as a trash can.
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.
149
+
150
+ ## Theming
151
+
152
+ A **theme** is nothing but a set of `--base-*` values. The lightest possible theme is a stylesheet
153
+ that redeclares them, loaded *after* `base.css`:
154
+
155
+ ```css
156
+ :root {
157
+ --base-accent-color: #4f46e5;
158
+ --base-page-bg: #f6f8fb;
159
+ --base-text-color-1: #1a2233;
160
+ }
161
+ ```
162
+
163
+ Because pure-admin-core, the components and any consumer all read the same variables, that one block
164
+ re-themes all of them at once. This is the same model as
165
+ [`@keenmate/pure-admin-themes`](https://github.com/KeenMate/pure-admin-themes), so the same CLI and
166
+ publishing infrastructure applies.
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
+
189
+ ## Build
190
+
191
+ ```bash
192
+ make install # sass
193
+ make build # src/scss -> dist/css (bundle + base + grid + utilities)
194
+ make sizes # show artifact sizes
195
+ ```
196
+
197
+ `dist/` is committed so consumers can vendor the built CSS without a Sass toolchain.
198
+
199
+ ## Provenance
200
+
201
+ The SCSS is the foundation extracted from `pure-admin-core`'s `src/scss` — the `variables/` modules,
202
+ `_base-css-variables.scss`, `utilities.scss`, `_fonts.scss`, and the native grid (`_pa-grid.scss`,
203
+ formerly core's `core-components/_grid.scss`). **pure-admin-core now consumes this package** as its
204
+ single source for the foundation (thin `@import`/`@forward` shims), so the two no longer drift —
205
+ core's compiled `--base-*` values and grid output match pure-css exactly.
206
+
207
+ One intentional difference: `utilities.scss` here `@use`s `_fonts.scss` so the generic
208
+ `.font-family-*` classes ship with the other utilities, whereas core keeps `_fonts.scss` standalone.
209
+ `_rtl-helpers.scss` and the component layer stay in core.
210
+
211
+ ## License
212
+
213
+ MIT © KeenMate. The grid is derived from [Pure](https://purecss.io/) (Yahoo!, BSD).
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;
@@ -2070,7 +2070,7 @@ body.pc-container-2xl .pc-navbar {
2070
2070
  top: 100%;
2071
2071
  inset-inline-start: 0;
2072
2072
  min-width: 19.2rem;
2073
- background-color: var(--pc-card-bg, var(--base-main-bg));
2073
+ background-color: var(--base-main-bg);
2074
2074
  border: 1px solid var(--pc-border-color);
2075
2075
  border-radius: var(--pc-border-radius);
2076
2076
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
@@ -2154,8 +2154,8 @@ body.pc-container-2xl .pc-navbar {
2154
2154
  flex-shrink: 0;
2155
2155
  font-size: 0;
2156
2156
  background-color: currentColor;
2157
- -webkit-mask: var(--pc-icon-chevron, var(--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"))) center/1.1rem no-repeat;
2158
- mask: var(--pc-icon-chevron, var(--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"))) center/1.1rem no-repeat;
2157
+ -webkit-mask: var(--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")) center/1.1rem no-repeat;
2158
+ mask: var(--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")) center/1.1rem no-repeat;
2159
2159
  transition: transform 0.1s ease-out;
2160
2160
  }
2161
2161
  .pc-navmenu__item--more.is-open .pc-navmenu__more-chevron {
@@ -2320,7 +2320,7 @@ body.pc-container-2xl .pc-navbar {
2320
2320
  width: 14px;
2321
2321
  height: 44px;
2322
2322
  border-radius: min(var(--pc-border-radius-lg), 4px);
2323
- background-color: var(--pc-card-bg, var(--base-main-bg));
2323
+ background-color: var(--base-main-bg);
2324
2324
  border: 2px solid var(--pc-border-color);
2325
2325
  color: var(--pc-text-color-2);
2326
2326
  font-size: 1.8rem;
@@ -2431,7 +2431,7 @@ body.loaded .pc-sidebar__link {
2431
2431
  height: 3.5rem;
2432
2432
  padding: 0 1.6rem 0 0.8rem;
2433
2433
  color: var(--pc-text-color-2);
2434
- background-color: var(--pc-input-bg, var(--base-input-bg));
2434
+ background-color: var(--base-input-bg);
2435
2435
  border: 1px solid var(--pc-border-color);
2436
2436
  border-radius: var(--pc-border-radius);
2437
2437
  text-align: start;
@@ -2585,8 +2585,8 @@ body.loaded .pc-sidebar__label {
2585
2585
  flex-shrink: 0;
2586
2586
  font-size: 0;
2587
2587
  background-color: currentColor;
2588
- -webkit-mask: var(--pc-icon-chevron, var(--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"))) center/1.2rem no-repeat;
2589
- mask: var(--pc-icon-chevron, var(--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"))) center/1.2rem no-repeat;
2588
+ -webkit-mask: var(--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")) center/1.2rem no-repeat;
2589
+ mask: var(--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")) center/1.2rem no-repeat;
2590
2590
  }
2591
2591
  body.loaded .pc-sidebar__chevron {
2592
2592
  transition: transform 0.1s ease-out;
@@ -3115,7 +3115,7 @@ body.pc-layout--sticky .pc-layout__inner {
3115
3115
  min-width: 20rem;
3116
3116
  max-width: 32rem;
3117
3117
  padding: 0.8rem;
3118
- background: var(--pc-card-bg, var(--base-main-bg));
3118
+ background: var(--base-main-bg);
3119
3119
  border: 1px solid var(--pc-border-color);
3120
3120
  border-radius: var(--pc-border-radius);
3121
3121
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
@@ -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-rc08",
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": {