@keenmate/pure-css 1.0.4 → 1.0.6

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,37 @@
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.6] — 2026-09-20 [PUBLISHED]
7
+
8
+ ### Added
9
+
10
+ - **`--base-icon-check-size` — a runtime mask-size knob for the selection glyph,
11
+ so the glyph and its scale re-theme in lockstep.** The check mark
12
+ (`--base-icon-check`) and the indeterminate dash (`--base-icon-indeterminate`)
13
+ share one mask box, but the size that box was painted at was hard-coded
14
+ downstream per consumer. This adds a single `$base-icon-check-size` (`68%`
15
+ default, emitted as `--base-icon-check-size`) that both pure-admin and the
16
+ Keenmate web components read, so overriding the check glyph **and** its scale
17
+ re-sizes the mark everywhere at once — e.g. swap in an edge-to-edge glyph like a
18
+ star and shrink it from this one knob. `68%` leaves breathing room inside the
19
+ box; it also sizes the indeterminate mark since that shares the mask box.
20
+ Authored in `variables/_base.scss` and emitted from `output-base-css-variables`,
21
+ so both `base.css` and the `pure-css.css` bundle carry it. Mirrors the canonical
22
+ `@keenmate/base-css-variables` contract (parity guard green).
23
+
24
+ ## [1.0.5] — 2026-09-18 [PUBLISHED]
25
+
26
+ ### Fixed
27
+
28
+ - **Ordered lists (`<ol>`) regressed to `disc` bullets instead of numbers.** In
29
+ 1.0.4 the `--pc-list-bullet-type` knob was applied to the shared `ul, ol`
30
+ reboot rule with a `disc` default, so every `<ol>` (bare, or `.pa-list-ordered`
31
+ and any consumer's ordered list) rendered filled bullets instead of `decimal`
32
+ numbers. The marker knob is an **unordered** concern, so it now lives on a
33
+ `ul`-only rule; `<ol>` keeps the browser-default `decimal` (ordered-marker
34
+ variants like roman/alpha stay a per-component override). The shared
35
+ margin/padding rule on `ul, ol` is unchanged.
36
+
6
37
  ## [1.0.4] — 2026-09-18 [PUBLISHED]
7
38
 
8
39
  ### Added
package/README.md CHANGED
@@ -1,218 +1,215 @@
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.4
16
-
17
- - **Reboot — `--pc-list-bullet-type` turns the list marker into a runtime knob, backed by the `--base-*` contract** — every `ul` / `ol` styled by `reboot.scss` now reads its `list-style-type` from `var(--pc-list-bullet-type, var(--base-list-bullet-type, disc))`, so you can switch a list's marker (`disc` / `circle` / `square` / `none` / `decimal` / …) without a recompile: set `--pc-list-bullet-type` at `:root`, on an ancestor, or per-instance via `style="--pc-list-bullet-type: square"`. The per-instance `--pc-` knob wins when set, else the themeable `--base-list-bullet-type` default (new in `@keenmate/base-css-variables`, emitted at `:root` by `base.css` and the bundle), else the inline browser-default `disc`. The `--pc-` knob itself is deliberately **not** emitted by the `output-*` variable mixins you opt in only where a per-instance marker is wanted. Lists that intentionally drop the marker (`list-style: none` on nav/sidebar menus, `.unstyled`, pure-admin's `--unstyled` / `--bordered` / list modifiers) still win on source order, so this only affects lists that already show a marker.
18
- - **Icons — `download`, `link` and `external-link` glyphs join the `--base-*` contract** — three more mask-friendly Lucide glyphs so every consumer paints the same actions from one theme knob. `--base-icon-download` (a tray + down arrow) is save-to-disk / export, `--base-icon-link` (a chain) is the hyperlink / attach-URL mark, and `--base-icon-external-link` (a diagonal arrow-out-of-box) is its companion for links that open in a new tab / leave the app. All are authored in `variables/_base.scss` and emitted from `output-base-css-variables`, so both the standalone `base.css` and the `pure-css.css` bundle carry them; they mirror the canonical `@keenmate/base-css-variables` contract (parity guard green) and back pure-admin's `--pa-icon-download` / `-link` / `-external-link`, which routed through these names with inline fallbacks until now. Consume via `mask: var(--base-icon-link); background: currentColor`.
19
-
20
- ## What's New in 1.0.3
21
-
22
- - **Icons — seven more glyphs join the `--base-*` contract: config/notification/profile plus the full status family** — three affordance marks and a shared severity family, so every consumer paints them from one theme knob. `--base-icon-settings` (a cog), `--base-icon-bell` (a notification bell) and `--base-icon-user` (a person) cover the preferences / notifications / profile trio, while `--base-icon-info` (circle-i), `--base-icon-success` (circle-check), `--base-icon-warning` (triangle-alert) and `--base-icon-danger` (circle-x) form one severity family so every alert, toast and badge shows the same mark. All are mask-friendly Lucide glyphs authored in `variables/_base.scss` and emitted from `output-base-css-variables`, so both the standalone `base.css` and the `pure-css.css` bundle carry them; they mirror the canonical `@keenmate/base-css-variables` contract (parity guard green) and back pure-admin's `--pa-icon-settings` / `-bell` / `-user` / `-info` / `-success` / `-warning` / `-danger`. Consume via `mask: var(--base-icon-info); background: currentColor`.
23
-
24
- - **Sidebar search — the magnifier is now a themeable masked glyph, not a 🔍 emoji** — `.pc-sidebar__search-icon` previously rendered a literal 🔍 text emoji, which varied by platform and font and couldn't be re-skinned. It's now painted in `currentColor` via `mask: var(--base-icon-search)` — the same technique as `.pc-sidebar__chevron` — so it renders identically everywhere and re-skins from the shared `--base-icon-search` knob, matching the navbar search and pure-admin's `.pa-icon--search`. `font-size: 0` collapses any legacy 🔍 still in markup so it never double-renders, letting consumers drop the emoji from `<button class="pc-sidebar__search-icon">`; the literal Lucide glyph stays the standalone fallback when `base.css` is absent.
25
-
26
- ## Why
27
-
28
- pure-css is a **standalone foundation** you drop onto any surface — a docs site, a marketing page, a
29
- widget host, or a full application. One small, dependency-free package gives you theming, layout and
30
- utilities without buying into a component framework.
31
-
32
- Its heart is a single **`--base-*` theming contract**: override one block of custom properties and
33
- everything re-themes at once — the grid, the utilities, the optional app shell, and any component
34
- that reads the same variables. Light and dark are built in via `light-dark()`, there's no build step
35
- to consume it (just link the prebuilt CSS), and it pulls in no runtime dependencies.
36
-
37
- ```
38
- @keenmate/pure-css (this package)
39
- ├─ --base-* theming contract
40
- ├─ .pc-row / .pc-col grid
41
- ├─ utility classes
42
- └─ optional app shell + JS runtime
43
- ▲ consumed directly, as built CSS, by…
44
- ├── docs sites · portals · marketing pages · widget & component hosts
45
- └── @keenmate/pure-admin-core — adds a full component library on top (just one consumer)
46
- ```
47
-
48
- ## Installation
49
-
50
- ```bash
51
- npm install @keenmate/pure-css
52
- ```
53
-
54
- ## Quick Start
55
-
56
- **Prebuilt CSS (simplest):**
57
-
58
- ```html
59
- <link rel="stylesheet" href="node_modules/@keenmate/pure-css/dist/css/pure-css.css">
60
- ```
61
-
62
- or cherry-pick:
63
-
64
- ```html
65
- <link rel="stylesheet" href="…/pure-css/dist/css/base.css"> <!-- variables only -->
66
- <link rel="stylesheet" href="…/pure-css/dist/css/grid.css"> <!-- + grid -->
67
- ```
68
-
69
- **SCSS (customize before compiling):**
70
-
71
- ```scss
72
- // Override the source of truth; everything re-derives.
73
- $base-accent-color: #4f46e5;
74
- $base-page-bg: #0b1020;
75
-
76
- @use '@keenmate/pure-css/scss/pure-css';
77
- ```
78
-
79
- ## What's in it
80
-
81
- | Artifact | Contents | When to link |
82
- | --- | --- | --- |
83
- | `dist/css/pure-css.css` | everything below, in one file | the common case |
84
- | `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 |
85
- | `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` |
86
- | `dist/css/grid.css` | `.pc-row` / `.pc-col-*` (percentage + fraction columns, container-query responsive) | layout only |
87
- | `dist/css/utilities.css` | spacing / flex / display / width-height utilities (`.m-4`, `.d-flex`, `.w-50`, …) | utilities only |
88
-
89
- The `pure-css.css` bundle also includes the **app shell** (navbar, sidebar,
90
- layout container) — `base.css` / `grid.css` / `utilities.css` do not.
91
-
92
- ### The app-shell runtime (`./js`)
93
-
94
- The shell's behaviour (nav fit/collapse, dropdowns, drag-to-resize, container
95
- breakpoints) ships as dependency-free source JS via the `./js` export — no
96
- bundler required, drop it in with a `<script>` and call `initAll`:
97
-
98
- ```html
99
- <link rel="stylesheet" href="node_modules/@keenmate/pure-css/dist/css/pure-css.css">
100
- <script src="node_modules/@keenmate/pure-css/src/js/pure-css.js"></script>
101
- <script src="node_modules/@keenmate/pure-css/src/js/fit.js"></script>
102
- <script src="node_modules/@keenmate/pure-css/src/js/navbar-dropdown.js"></script>
103
- <script src="node_modules/@keenmate/pure-css/src/js/sidebar-resize.js"></script>
104
- <script>window.pureCss.components.initAll(document);</script>
105
- ```
106
-
107
- `window.pureCss` also exposes an event bus and live `viewport` / `colorScheme` /
108
- `device` sources. The runtime is optional — shell CSS is authored no-JS-safe, so
109
- the styling stands on its own and the JS only adds the interactive behaviour.
110
-
111
- ### The `--base-*` contract
112
-
113
- `--base-*` is the **single source of truth for theming**. Framework colors, component variables
114
- (`--pc-*`) and web/svelte components all derive from it via fallback chains
115
- (`--ms-accent-color: var(--base-accent-color, #3b82f6)`). Categories: accent, text, background,
116
- border, input, dropdown, tooltip, contextual (success/danger/warning/info), interactive states,
117
- typography, border-radius, spacing/shadow/motion/z-index scales, and icons. The full list is
118
- `src/scss/variables/_base.scss`.
119
-
120
- #### Icons
121
-
122
- `--base-icon-*` are mask-friendly SVG glyphs (Lucide defaults) for the shared UI affordances, so the
123
- pure-css shell, pure-admin components, and the web/svelte components render the **same** marks and a
124
- theme re-skins them in one place. Each is consumed via `mask: var(--base-icon-x); background:
125
- currentColor`, so the glyph inherits text colour — override a token with any mask-friendly `url()` to
126
- swap the icon set.
127
-
128
- | Token | Glyph | Use |
129
- | --- | --- | --- |
130
- | `--base-icon-chevron` | stroked angle `›` | expanders / nav — **rotate-one-glyph** disclosure (points right, rotate 90° when open) |
131
- | `--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 |
132
- | `--base-icon-close` | `✕` | dismiss a transient **surface** (dialog, panel, popover, toast) |
133
- | `--base-icon-clear` | `✕` | clear a **field** — distinct purpose, same glyph; **follows** `--base-icon-close`, override alone to diverge |
134
- | `--base-icon-remove` | `✕` | take an **item** out of a collection (chip / tag / row) non-destructive; follows `--base-icon-close` |
135
- | `--base-icon-expand` / `--base-icon-collapse` | `+` / `−` | **swap-two-glyphs** disclosure (tree nodes, accordions): show `+` when collapsed, `−` when open |
136
- | `--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) |
137
- | `--base-icon-search` | magnifying glass | search inputs, command palette find **by text** |
138
- | `--base-icon-filter` | funnel | refine / **narrow a list** by criteria (filter toggles, faceted search) — distinct from `search` |
139
- | `--base-icon-refresh` | two curved arrows | reload / re-fetch a view or dataset |
140
- | `--base-icon-check` / `--base-icon-indeterminate` | `✓` / `−` | **selection** pair (checkboxes, multiselect, tree nodes): `check` = selected, `indeterminate` = a tri-state parent whose children are a mix |
141
- | `--base-icon-copy` | two overlapping sheets | copy-to-clipboard |
142
- | `--base-icon-ellipsis` | three dots `⋯` | "more / overflow" affordance — **rotate-one-glyph** for the vertical `⋮` variant (rotate 90°) |
143
- | `--base-icon-save` | floppy disk | persist / commit |
144
-
145
- Three intentional distinctions:
146
-
147
- - **Disclosure models:** **chevron rotates one glyph** (sidebar, multiselect), while **expand/collapse swaps
148
- two glyphs** (trees, accordions) a component never rotates a `+` into a `−`.
149
- - **✕ vs trash:** `close` / `clear` / `remove` are three *dismiss* purposes that share the ✕ glyph (and
150
- cascade off `--base-icon-close`), while `delete` is a separate *destructive* action drawn as a trash can.
151
- `add` shares the `+` shape with `expand` but is an independent knob (create ≠ disclosure).
152
- - **Selection ≠ disclosure:** `indeterminate` shares the `−` shape with `collapse` but is its own knob —
153
- a partially-selected checkbox is not a collapsed node.
154
-
155
- ## Theming
156
-
157
- A **theme** is nothing but a set of `--base-*` values. The lightest possible theme is a stylesheet
158
- that redeclares them, loaded *after* `base.css`:
159
-
160
- ```css
161
- :root {
162
- --base-accent-color: #4f46e5;
163
- --base-page-bg: #f6f8fb;
164
- --base-text-color-1: #1a2233;
165
- }
166
- ```
167
-
168
- Because pure-admin-core, the components and any consumer all read the same variables, that one block
169
- re-themes all of them at once. This is the same model as
170
- [`@keenmate/pure-admin-themes`](https://github.com/Keenmate/pure-admin-themes), so the same CLI and
171
- publishing infrastructure applies.
172
-
173
- ### Mode & variant class placement
174
-
175
- Light/dark and colour-variant switching is done by toggling a class `.pc-mode-light` /
176
- `.pc-mode-dark` and `.pa-color-*`. **Apply these to the `:root` element (`<html>`), not `<body>`.**
177
-
178
- The mode/variant blocks override input tokens (`--pc-*` / `--base-*`). Many themed tokens are
179
- *derived* from those inputs and emitted once at `:root` e.g. core emits
180
- `--pa-btn-info-bg: var(--pc-info)`. CSS resolves a custom property's `var()` **at the element that
181
- declares it**, so a derived token declared on `:root` bakes in `:root`'s input value. If the mode
182
- class sits on a *descendant* (`<body>`), the override comes too late and the derived token stays
183
- frozen at the default-mode value — the classic symptom is a role button or surface that doesn't
184
- change colour when you switch modes. Putting the class on `:root` (the same element that declares
185
- the tokens) makes the overrides win and the derived tokens re-resolve.
186
-
187
- pure-css re-emits its own base text-tier tokens at `:root, .pc-mode-light, .pc-mode-dark` to tolerate
188
- either placement, but that does not extend to the pure-admin component layer, hence the `:root` rule.
189
-
190
- To avoid a colour "flash" on switch, disable transitions for one frame during the swap (add a
191
- `transition: none !important` class to `:root`, change the mode/variant class, force a reflow, then
192
- remove it).
193
-
194
- ## Build
195
-
196
- ```bash
197
- make install # sass
198
- make build # src/scss -> dist/css (bundle + base + grid + utilities)
199
- make sizes # show artifact sizes
200
- ```
201
-
202
- `dist/` is committed so consumers can vendor the built CSS without a Sass toolchain.
203
-
204
- ## Provenance
205
-
206
- The SCSS is the foundation extracted from `pure-admin-core`'s `src/scss` the `variables/` modules,
207
- `_base-css-variables.scss`, `utilities.scss`, `_fonts.scss`, and the native grid (`_pa-grid.scss`,
208
- formerly core's `core-components/_grid.scss`). **pure-admin-core now consumes this package** as its
209
- single source for the foundation (thin `@import`/`@forward` shims), so the two no longer drift —
210
- core's compiled `--base-*` values and grid output match pure-css exactly.
211
-
212
- One intentional difference: `utilities.scss` here `@use`s `_fonts.scss` so the generic
213
- `.font-family-*` classes ship with the other utilities, whereas core keeps `_fonts.scss` standalone.
214
- `_rtl-helpers.scss` and the component layer stay in core.
215
-
216
- ## License
217
-
218
- 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.6
16
+
17
+ - **Icons — `--base-icon-check-size` turns the selection glyph's scale into a theme knob** — the check mark (`--base-icon-check`) and the indeterminate dash (`--base-icon-indeterminate`) share one mask box, but the size that box was painted at used to be hard-coded downstream. This adds a single `--base-icon-check-size` token (`68%` default, authored as `$base-icon-check-size` in `variables/_base.scss` and emitted from `output-base-css-variables`) that both pure-admin and every Keenmate web component read, so overriding the check glyph **and** its scale now re-sizes the mark in lockstep across the whole stackdrop in an edge-to-edge glyph such as a star and shrink it from this one knob. `68%` leaves breathing room inside the box, and because indeterminate shares the mask box it re-sizes too. Both `base.css` and the `pure-css.css` bundle emit it, and it mirrors the canonical `@keenmate/base-css-variables` contract (parity guard green).
18
+
19
+ ## What's New in 1.0.5
20
+
21
+ - **Reboot — ordered lists (`<ol>`) render numbers again** — a 1.0.4 regression applied the new `--pc-list-bullet-type` knob to the shared `ul, ol` rule with a `disc` default, so every ordered list showed filled bullets instead of `decimal` numbers. The marker knob is an unordered concern, so it now sits on a `ul`-only rule; `<ol>` keeps the browser-default `decimal` (roman/alpha ordered variants stay a per-component override). The shared list margin/padding is unchanged, and `--pc-list-bullet-type` / `--base-list-bullet-type` continue to drive `ul` markers exactly as before.
22
+
23
+ ## Why
24
+
25
+ pure-css is a **standalone foundation** you drop onto any surface — a docs site, a marketing page, a
26
+ widget host, or a full application. One small, dependency-free package gives you theming, layout and
27
+ utilities without buying into a component framework.
28
+
29
+ Its heart is a single **`--base-*` theming contract**: override one block of custom properties and
30
+ everything re-themes at once — the grid, the utilities, the optional app shell, and any component
31
+ that reads the same variables. Light and dark are built in via `light-dark()`, there's no build step
32
+ to consume it (just link the prebuilt CSS), and it pulls in no runtime dependencies.
33
+
34
+ ```
35
+ @keenmate/pure-css (this package)
36
+ ├─ --base-* theming contract
37
+ ├─ .pc-row / .pc-col grid
38
+ ├─ utility classes
39
+ └─ optional app shell + JS runtime
40
+ consumed directly, as built CSS, by…
41
+ ├── docs sites · portals · marketing pages · widget & component hosts
42
+ └── @keenmate/pure-admin-core adds a full component library on top (just one consumer)
43
+ ```
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ npm install @keenmate/pure-css
49
+ ```
50
+
51
+ ## Quick Start
52
+
53
+ **Prebuilt CSS (simplest):**
54
+
55
+ ```html
56
+ <link rel="stylesheet" href="node_modules/@keenmate/pure-css/dist/css/pure-css.css">
57
+ ```
58
+
59
+ or cherry-pick:
60
+
61
+ ```html
62
+ <link rel="stylesheet" href="…/pure-css/dist/css/base.css"> <!-- variables only -->
63
+ <link rel="stylesheet" href="…/pure-css/dist/css/grid.css"> <!-- + grid -->
64
+ ```
65
+
66
+ **SCSS (customize before compiling):**
67
+
68
+ ```scss
69
+ // Override the source of truth; everything re-derives.
70
+ $base-accent-color: #4f46e5;
71
+ $base-page-bg: #0b1020;
72
+
73
+ @use '@keenmate/pure-css/scss/pure-css';
74
+ ```
75
+
76
+ ## What's in it
77
+
78
+ | Artifact | Contents | When to link |
79
+ | --- | --- | --- |
80
+ | `dist/css/pure-css.css` | everything below, in one file | the common case |
81
+ | `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 |
82
+ | `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` |
83
+ | `dist/css/grid.css` | `.pc-row` / `.pc-col-*` (percentage + fraction columns, container-query responsive) | layout only |
84
+ | `dist/css/utilities.css` | spacing / flex / display / width-height utilities (`.m-4`, `.d-flex`, `.w-50`, ) | utilities only |
85
+
86
+ The `pure-css.css` bundle also includes the **app shell** (navbar, sidebar,
87
+ layout container) — `base.css` / `grid.css` / `utilities.css` do not.
88
+
89
+ ### The app-shell runtime (`./js`)
90
+
91
+ The shell's behaviour (nav fit/collapse, dropdowns, drag-to-resize, container
92
+ breakpoints) ships as dependency-free source JS via the `./js` export — no
93
+ bundler required, drop it in with a `<script>` and call `initAll`:
94
+
95
+ ```html
96
+ <link rel="stylesheet" href="node_modules/@keenmate/pure-css/dist/css/pure-css.css">
97
+ <script src="node_modules/@keenmate/pure-css/src/js/pure-css.js"></script>
98
+ <script src="node_modules/@keenmate/pure-css/src/js/fit.js"></script>
99
+ <script src="node_modules/@keenmate/pure-css/src/js/navbar-dropdown.js"></script>
100
+ <script src="node_modules/@keenmate/pure-css/src/js/sidebar-resize.js"></script>
101
+ <script>window.pureCss.components.initAll(document);</script>
102
+ ```
103
+
104
+ `window.pureCss` also exposes an event bus and live `viewport` / `colorScheme` /
105
+ `device` sources. The runtime is optional — shell CSS is authored no-JS-safe, so
106
+ the styling stands on its own and the JS only adds the interactive behaviour.
107
+
108
+ ### The `--base-*` contract
109
+
110
+ `--base-*` is the **single source of truth for theming**. Framework colors, component variables
111
+ (`--pc-*`) and web/svelte components all derive from it via fallback chains
112
+ (`--ms-accent-color: var(--base-accent-color, #3b82f6)`). Categories: accent, text, background,
113
+ border, input, dropdown, tooltip, contextual (success/danger/warning/info), interactive states,
114
+ typography, border-radius, spacing/shadow/motion/z-index scales, and icons. The full list is
115
+ `src/scss/variables/_base.scss`.
116
+
117
+ #### Icons
118
+
119
+ `--base-icon-*` are mask-friendly SVG glyphs (Lucide defaults) for the shared UI affordances, so the
120
+ pure-css shell, pure-admin components, and the web/svelte components render the **same** marks and a
121
+ theme re-skins them in one place. Each is consumed via `mask: var(--base-icon-x); background:
122
+ currentColor`, so the glyph inherits text colour override a token with any mask-friendly `url()` to
123
+ swap the icon set.
124
+
125
+ | Token | Glyph | Use |
126
+ | --- | --- | --- |
127
+ | `--base-icon-chevron` | stroked angle `›` | expanders / nav — **rotate-one-glyph** disclosure (points right, rotate 90° when open) |
128
+ | `--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 |
129
+ | `--base-icon-close` | `✕` | dismiss a transient **surface** (dialog, panel, popover, toast) |
130
+ | `--base-icon-clear` | `✕` | clear a **field**distinct purpose, same glyph; **follows** `--base-icon-close`, override alone to diverge |
131
+ | `--base-icon-remove` | `✕` | take an **item** out of a collection (chip / tag / row) non-destructive; follows `--base-icon-close` |
132
+ | `--base-icon-expand` / `--base-icon-collapse` | `+` / `−` | **swap-two-glyphs** disclosure (tree nodes, accordions): show `+` when collapsed, `−` when open |
133
+ | `--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) |
134
+ | `--base-icon-search` | magnifying glass | search inputs, command palettefind **by text** |
135
+ | `--base-icon-filter` | funnel | refine / **narrow a list** by criteria (filter toggles, faceted search) distinct from `search` |
136
+ | `--base-icon-refresh` | two curved arrows | reload / re-fetch a view or dataset |
137
+ | `--base-icon-check` / `--base-icon-indeterminate` | `✓` / `−` | **selection** pair (checkboxes, multiselect, tree nodes): `check` = selected, `indeterminate` = a tri-state parent whose children are a mix |
138
+ | `--base-icon-copy` | two overlapping sheets | copy-to-clipboard |
139
+ | `--base-icon-ellipsis` | three dots `⋯` | "more / overflow" affordance — **rotate-one-glyph** for the vertical `⋮` variant (rotate 90°) |
140
+ | `--base-icon-save` | floppy disk | persist / commit |
141
+
142
+ Three intentional distinctions:
143
+
144
+ - **Disclosure models:** **chevron rotates one glyph** (sidebar, multiselect), while **expand/collapse swaps
145
+ two glyphs** (trees, accordions) — a component never rotates a `+` into a `−`.
146
+ - **✕ vs trash:** `close` / `clear` / `remove` are three *dismiss* purposes that share the ✕ glyph (and
147
+ cascade off `--base-icon-close`), while `delete` is a separate *destructive* action drawn as a trash can.
148
+ `add` shares the `+` shape with `expand` but is an independent knob (create ≠ disclosure).
149
+ - **Selection disclosure:** `indeterminate` shares the `−` shape with `collapse` but is its own knob
150
+ a partially-selected checkbox is not a collapsed node.
151
+
152
+ ## Theming
153
+
154
+ A **theme** is nothing but a set of `--base-*` values. The lightest possible theme is a stylesheet
155
+ that redeclares them, loaded *after* `base.css`:
156
+
157
+ ```css
158
+ :root {
159
+ --base-accent-color: #4f46e5;
160
+ --base-page-bg: #f6f8fb;
161
+ --base-text-color-1: #1a2233;
162
+ }
163
+ ```
164
+
165
+ Because pure-admin-core, the components and any consumer all read the same variables, that one block
166
+ re-themes all of them at once. This is the same model as
167
+ [`@keenmate/pure-admin-themes`](https://github.com/Keenmate/pure-admin-themes), so the same CLI and
168
+ publishing infrastructure applies.
169
+
170
+ ### Mode & variant class placement
171
+
172
+ Light/dark and colour-variant switching is done by toggling a class — `.pc-mode-light` /
173
+ `.pc-mode-dark` and `.pa-color-*`. **Apply these to the `:root` element (`<html>`), not `<body>`.**
174
+
175
+ The mode/variant blocks override input tokens (`--pc-*` / `--base-*`). Many themed tokens are
176
+ *derived* from those inputs and emitted once at `:root` e.g. core emits
177
+ `--pa-btn-info-bg: var(--pc-info)`. CSS resolves a custom property's `var()` **at the element that
178
+ declares it**, so a derived token declared on `:root` bakes in `:root`'s input value. If the mode
179
+ class sits on a *descendant* (`<body>`), the override comes too late and the derived token stays
180
+ frozen at the default-mode value the classic symptom is a role button or surface that doesn't
181
+ change colour when you switch modes. Putting the class on `:root` (the same element that declares
182
+ the tokens) makes the overrides win and the derived tokens re-resolve.
183
+
184
+ pure-css re-emits its own base text-tier tokens at `:root, .pc-mode-light, .pc-mode-dark` to tolerate
185
+ either placement, but that does not extend to the pure-admin component layer, hence the `:root` rule.
186
+
187
+ To avoid a colour "flash" on switch, disable transitions for one frame during the swap (add a
188
+ `transition: none !important` class to `:root`, change the mode/variant class, force a reflow, then
189
+ remove it).
190
+
191
+ ## Build
192
+
193
+ ```bash
194
+ make install # sass
195
+ make build # src/scss -> dist/css (bundle + base + grid + utilities)
196
+ make sizes # show artifact sizes
197
+ ```
198
+
199
+ `dist/` is committed so consumers can vendor the built CSS without a Sass toolchain.
200
+
201
+ ## Provenance
202
+
203
+ The SCSS is the foundation extracted from `pure-admin-core`'s `src/scss` — the `variables/` modules,
204
+ `_base-css-variables.scss`, `utilities.scss`, `_fonts.scss`, and the native grid (`_pa-grid.scss`,
205
+ formerly core's `core-components/_grid.scss`). **pure-admin-core now consumes this package** as its
206
+ single source for the foundation (thin `@import`/`@forward` shims), so the two no longer drift —
207
+ core's compiled `--base-*` values and grid output match pure-css exactly.
208
+
209
+ One intentional difference: `utilities.scss` here `@use`s `_fonts.scss` so the generic
210
+ `.font-family-*` classes ship with the other utilities, whereas core keeps `_fonts.scss` standalone.
211
+ `_rtl-helpers.scss` and the component layer stay in core.
212
+
213
+ ## License
214
+
215
+ MIT © Keenmate. The grid is derived from [Pure](https://purecss.io/) (Yahoo!, BSD).
package/dist/css/base.css CHANGED
@@ -140,6 +140,7 @@
140
140
  --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");
141
141
  --base-icon-refresh: 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 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8%22/%3E%3Cpath d=%22M21 3v5h-5%22/%3E%3Cpath d=%22M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16%22/%3E%3Cpath d=%22M3 21v-5h5%22/%3E%3C/svg%3E");
142
142
  --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");
143
+ --base-icon-check-size: 68%;
143
144
  --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");
144
145
  --base-icon-copy: 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%3Crect width=%2214%22 height=%2214%22 x=%228%22 y=%228%22 rx=%222%22 ry=%222%22/%3E%3Cpath d=%22M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2%22/%3E%3C/svg%3E");
145
146
  --base-icon-ellipsis: 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=%2212%22 cy=%2212%22 r=%221%22/%3E%3Ccircle cx=%2219%22 cy=%2212%22 r=%221%22/%3E%3Ccircle cx=%225%22 cy=%2212%22 r=%221%22/%3E%3C/svg%3E");
@@ -44,12 +44,15 @@ ul, ol {
44
44
  margin: 0 0 1.6rem 0;
45
45
  padding: 0;
46
46
  padding-inline-start: 3.2rem;
47
- list-style-type: var(--pc-list-bullet-type, var(--base-list-bullet-type, disc));
48
47
  }
49
48
  ul li, ol li {
50
49
  margin-bottom: 0.4rem;
51
50
  }
52
51
 
52
+ ul {
53
+ list-style-type: var(--pc-list-bullet-type, var(--base-list-bullet-type, disc));
54
+ }
55
+
53
56
  ul.unstyled, ol.unstyled {
54
57
  list-style: none;
55
58
  padding-inline-start: 0;
@@ -6083,6 +6086,7 @@ body.pc-layout--sticky .pc-layout__inner {
6083
6086
  --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");
6084
6087
  --base-icon-refresh: 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 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8%22/%3E%3Cpath d=%22M21 3v5h-5%22/%3E%3Cpath d=%22M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16%22/%3E%3Cpath d=%22M3 21v-5h5%22/%3E%3C/svg%3E");
6085
6088
  --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");
6089
+ --base-icon-check-size: 68%;
6086
6090
  --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");
6087
6091
  --base-icon-copy: 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%3Crect width=%2214%22 height=%2214%22 x=%228%22 y=%228%22 rx=%222%22 ry=%222%22/%3E%3Cpath d=%22M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2%22/%3E%3C/svg%3E");
6088
6092
  --base-icon-ellipsis: 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=%2212%22 cy=%2212%22 r=%221%22/%3E%3Ccircle cx=%2219%22 cy=%2212%22 r=%221%22/%3E%3Ccircle cx=%225%22 cy=%2212%22 r=%221%22/%3E%3C/svg%3E");
@@ -33,12 +33,15 @@ ul, ol {
33
33
  margin: 0 0 1.6rem 0;
34
34
  padding: 0;
35
35
  padding-inline-start: 3.2rem;
36
- list-style-type: var(--pc-list-bullet-type, var(--base-list-bullet-type, disc));
37
36
  }
38
37
  ul li, ol li {
39
38
  margin-bottom: 0.4rem;
40
39
  }
41
40
 
41
+ ul {
42
+ list-style-type: var(--pc-list-bullet-type, var(--base-list-bullet-type, disc));
43
+ }
44
+
42
45
  ul.unstyled, ol.unstyled {
43
46
  list-style: none;
44
47
  padding-inline-start: 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keenmate/pure-css",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
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": {
@@ -228,6 +228,10 @@
228
228
  // Shared by web-multiselect / web-treeview / checkboxes. indeterminate shares the
229
229
  // minus shape with collapse but is its OWN knob (selection ≠ disclosure).
230
230
  --base-icon-check: #{$base-icon-check};
231
+ // mask-size for the selection glyph inside its box — one knob every consumer
232
+ // reads, so overriding the glyph AND its scale re-sizes the mark in lockstep.
233
+ // Also sizes indeterminate (it shares the mask box).
234
+ --base-icon-check-size: #{$base-icon-check-size};
231
235
  --base-icon-indeterminate: #{$base-icon-indeterminate};
232
236
 
233
237
  // === Action / affordance icons ===
@@ -64,24 +64,32 @@ ul, ol {
64
64
  margin: $list-margin-top 0 $list-margin-bottom 0;
65
65
  padding: 0;
66
66
  padding-inline-start: $list-padding-left; // RTL: flips to right
67
- // Runtime-overridable marker. Set --pc-list-bullet-type (disc | circle |
68
- // square | none | decimal | …) at :root, on an ancestor, or per-instance
69
- // (style="--pc-list-bullet-type: square") to switch the marker without a
70
- // recompile. It layers over the base contract: --pc-list-bullet-type wins when
71
- // set, else the themeable --base-list-bullet-type default (emitted by
72
- // output-base-css-variables), else the inline browser-default disc. The --pc-
73
- // knob itself is not emitted by the variable mixins — set it only where a
74
- // per-instance marker is wanted. Components that reset the marker
75
- // (list-style: none on nav/sidebar menus, .unstyled, pure-admin's
76
- // --unstyled/--bordered/… list modifiers) win on source order, so this only
77
- // affects lists that still show a marker.
78
- list-style-type: var(--pc-list-bullet-type, var(--base-list-bullet-type, disc));
79
67
 
80
68
  li {
81
69
  margin-bottom: $list-item-margin-bottom;
82
70
  }
83
71
  }
84
72
 
73
+ // Runtime-overridable UNORDERED marker — ul only, so <ol> keeps its numbers
74
+ // (a shared `ul, ol` rule with a `disc` default turned ordered lists into
75
+ // bullets). Set --pc-list-bullet-type (disc | circle | square | none | …) at
76
+ // :root, on an ancestor, or per-instance (style="--pc-list-bullet-type: square")
77
+ // to switch the marker without a recompile. It layers over the base contract:
78
+ // --pc-list-bullet-type wins when set, else the themeable --base-list-bullet-type
79
+ // default (emitted by output-base-css-variables), else the inline browser-default
80
+ // disc. The --pc- knob itself is not emitted by the variable mixins — set it only
81
+ // where a per-instance marker is wanted. Components that reset the marker
82
+ // (list-style: none on nav/sidebar menus, .unstyled, pure-admin's
83
+ // --unstyled/--bordered/… list modifiers) win on source order, so this only
84
+ // affects unordered lists that still show a marker. By design this applies at
85
+ // EVERY nesting depth (a single, controllable marker), so nested <ul>s all use
86
+ // the same marker rather than the browser's disc→circle→square depth cascade —
87
+ // intentional, not a bug: the knob trades depth-varying markers for one uniform,
88
+ // themeable bullet.
89
+ ul {
90
+ list-style-type: var(--pc-list-bullet-type, var(--base-list-bullet-type, disc));
91
+ }
92
+
85
93
  // Lists - unstyled (for navigation, etc.)
86
94
  ul.unstyled, ol.unstyled {
87
95
  list-style: none;
@@ -374,6 +374,11 @@ $base-icon-refresh: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/20
374
374
  // Selection: check = ✓ (Lucide check), indeterminate = − (Lucide minus). indeterminate
375
375
  // shares the minus shape with `collapse` but is its own knob (selection ≠ disclosure).
376
376
  $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;
377
+ // mask-size for the selection glyph inside its box. One knob both pure-admin and
378
+ // the web components read, so overriding the glyph AND its scale re-sizes the mark
379
+ // everywhere at once. 68% leaves breathing room; an edge-to-edge glyph (e.g. a
380
+ // star) can be shrunk from here. Also sizes indeterminate (shares the mask box).
381
+ $base-icon-check-size: 68% !default;
377
382
  $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;
378
383
  // copy = two overlapping sheets (Lucide copy); ellipsis = three dots (Lucide
379
384
  // ellipsis, the "more / overflow" affordance — the vertical variant is the same