@keenmate/pure-admin-core 2.7.1 → 2.9.0-rc01

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keenmate/pure-admin-core",
3
- "version": "2.7.1",
3
+ "version": "2.9.0-rc01",
4
4
  "description": "Lightweight, data-focused HTML/CSS admin framework built with PureCSS foundation and comprehensive component system",
5
5
  "style": "dist/css/main.css",
6
6
  "exports": {
@@ -58,7 +58,7 @@ to save horizontal space in the navbar.
58
58
  <!-- title= attrs give a tooltip when the text truncates with ellipsis -->
59
59
  <h3 class="pa-profile-panel__name" title="John Doe">John Doe</h3>
60
60
  <p class="pa-profile-panel__email" title="john.doe@company.com">john.doe@company.com</p>
61
- <span class="pa-profile-panel__role">Administrator</span>
61
+ <span class="pa-badge">Administrator</span>
62
62
  </div>
63
63
  <button class="pa-profile-panel__close" onclick="closeProfilePanel()" aria-label="Close Profile">
64
64
 
@@ -124,7 +124,7 @@ to save horizontal space in the navbar.
124
124
  <div class="pa-profile-panel__info">
125
125
  <h3 class="pa-profile-panel__name">John Doe</h3>
126
126
  <p class="pa-profile-panel__email">john.doe@company.com</p>
127
- <span class="pa-profile-panel__role">Administrator</span>
127
+ <span class="pa-badge">Administrator</span>
128
128
  </div>
129
129
  <button class="pa-profile-panel__close" onclick="closeProfilePanel()" aria-label="Close Profile">✕</button>
130
130
  </div>
@@ -170,7 +170,7 @@ to save horizontal space in the navbar.
170
170
  <div class="pa-profile-panel__info">
171
171
  <h3 class="pa-profile-panel__name">Jane Smith</h3>
172
172
  <p class="pa-profile-panel__email">jane.smith@corp.example</p>
173
- <span class="pa-profile-panel__role">Analyst</span>
173
+ <span class="pa-badge">Analyst</span>
174
174
  </div>
175
175
  <button class="pa-profile-panel__close" onclick="closeProfilePanel()" aria-label="Close Profile">✕</button>
176
176
  </div>
@@ -207,7 +207,7 @@ to save horizontal space in the navbar.
207
207
  <div class="pa-profile-panel__info">
208
208
  <h3 class="pa-profile-panel__name">John Doe</h3>
209
209
  <p class="pa-profile-panel__email">john.doe@company.com</p>
210
- <span class="pa-profile-panel__role">Administrator</span>
210
+ <span class="pa-badge">Administrator</span>
211
211
  </div>
212
212
  <button class="pa-profile-panel__close" onclick="closeProfilePanel()" aria-label="Close Profile">✕</button>
213
213
  </div>
@@ -425,7 +425,10 @@ HEADER:
425
425
  - .pa-profile-panel__info Text column (name + email + role).
426
426
  - .pa-profile-panel__name User name (ellipsis on overflow).
427
427
  - .pa-profile-panel__email Email (ellipsis on overflow).
428
- - .pa-profile-panel__role Role badge (uppercase, tinted bg).
428
+ - .pa-badge Role chip (standard badge component;
429
+ add `.pa-badge--primary` / `--info` /
430
+ `--success` / `--warning` / `--danger`
431
+ / `--light` for variant styling).
429
432
  - .pa-profile-panel__close Close button (top-end corner).
430
433
 
431
434
  Theme-colour design choices (why they're like this):
@@ -433,11 +436,13 @@ Theme-colour design choices (why they're like this):
433
436
  colour the header uses for the user's name. This guarantees contrast
434
437
  against dark / coloured header backgrounds across all themes (body
435
438
  text colours would read wrong on a dark header).
436
- - __role paints its background with color-mix(in srgb,
437
- var(--pa-header-profile-name-color) 15%, transparent) so the tint is
438
- derived from the header colour and works on any theme. An older
439
- --pa-accent-light fallback is declared first for browsers without
440
- color-mix.
439
+ - Role uses the standard `.pa-badge` component (var(--pa-btn-secondary-bg)
440
+ / -text by default), which themes already tune for both light and dark
441
+ modes. Previously this was a custom `.pa-profile-panel__role` element
442
+ coupled to --pa-header-profile-name-color that token is sized for the
443
+ header (e.g. black on yellow in Express) and went invisible on dark
444
+ panel bodies. Migrated to `.pa-badge` in 2.9.0; older snippet markup
445
+ using `pa-profile-panel__role` needs the class swap.
441
446
 
442
447
  TABS (optional — sits between __header and __body):
443
448
  - .pa-profile-panel__tabs Tabs container (border-bottom + header-bg).
@@ -14,6 +14,12 @@
14
14
  // --ms-accent-color: var(--base-accent-color, #3b82f6);
15
15
  // ============================================================================
16
16
 
17
+ // Make variables resolvable when this file is loaded as a @use module
18
+ // (e.g. from main.scss). Legacy @import callers (themes) are unaffected —
19
+ // Sass places @use'd members into the importing file's global scope, where
20
+ // theme-set overrides already live.
21
+ @use 'variables/index' as *;
22
+
17
23
  @mixin output-base-css-variables {
18
24
  // === Accent Colors ===
19
25
  --base-accent-color: #{$base-accent-color};
@@ -41,14 +47,6 @@
41
47
  --base-disabled-bg: #{$base-disabled-bg};
42
48
  --base-elevated-bg: #{$base-elevated-bg};
43
49
 
44
- // === Background Colors (legacy aliases) ===
45
- --base-surface-1: #{$base-surface-1};
46
- --base-surface-2: #{$base-surface-2};
47
- --base-surface-3: #{$base-surface-3};
48
- --base-surface-inverse: #{$base-surface-inverse};
49
- --base-primary-bg: #{$base-primary-bg};
50
- --base-primary-bg-hover: #{$base-primary-bg-hover};
51
-
52
50
  // === Border Colors ===
53
51
  --base-border-color: #{$base-border-color};
54
52
  --base-border: #{$base-border};
@@ -259,6 +257,18 @@
259
257
  // ============================================================================
260
258
 
261
259
  @mixin output-pa-css-variables {
260
+ // =========================================================================
261
+ // COLOR SCHEME SIGNAL
262
+ // Tells the browser whether this scope is light or dark so native UA
263
+ // elements (scrollbars, form controls) and `light-dark()` resolve
264
+ // correctly. Drives web components (e.g. <web-multiselect>) that ship
265
+ // adaptive palettes via `light-dark()`. Override $theme-color-scheme
266
+ // before importing variables for always-dark themes; for dual-mode
267
+ // themes leave default `light` here and add `color-scheme: dark;` to
268
+ // the `.pa-mode-dark` block.
269
+ // =========================================================================
270
+ color-scheme: #{$theme-color-scheme};
271
+
262
272
  // =========================================================================
263
273
  // CORE COLORS
264
274
  // =========================================================================
@@ -23,8 +23,13 @@
23
23
  &__header {
24
24
  padding: $card-header-padding-v $card-header-padding-h;
25
25
  min-height: $card-header-min-height;
26
- border-top-left-radius: $card-border-radius;
27
- border-top-right-radius: $card-border-radius;
26
+ // No own border-top-radius — the card's `overflow: hidden` + outer
27
+ // border-radius clips the header's square top corners to match. Setting
28
+ // a radius here makes the header curve at 8px while the card's INNER
29
+ // corner is ~7px (outer 8px minus the 1px border), leaving a thin
30
+ // wedge of card background visible at each top corner — most obvious
31
+ // on coloured variants (--primary/--success/--warning/--danger/--color-*)
32
+ // where the wedge shows as a white sliver against the variant colour.
28
33
  border-bottom: $border-width-base solid var(--pa-border-color);
29
34
  background: var(--pa-card-header-bg);
30
35
  display: flex;
@@ -240,11 +245,15 @@
240
245
  }
241
246
 
242
247
  // Card variants
248
+ // Coloured variants override the header's border-bottom-color to match the
249
+ // variant — otherwise the default light-gray hairline shows as a visible
250
+ // strip against the coloured surroundings (header bg + card border above).
243
251
  &--primary {
244
252
  border-color: var(--pa-accent);
245
253
 
246
254
  .pa-card__header {
247
255
  background-color: var(--pa-accent);
256
+ border-bottom-color: var(--pa-accent);
248
257
  color: var(--pa-btn-primary-text);
249
258
 
250
259
  h1,
@@ -263,6 +272,7 @@
263
272
 
264
273
  .pa-card__header {
265
274
  background-color: var(--pa-success-bg);
275
+ border-bottom-color: var(--pa-success-bg);
266
276
  color: var(--pa-btn-success-text);
267
277
 
268
278
  h1,
@@ -281,6 +291,7 @@
281
291
 
282
292
  .pa-card__header {
283
293
  background-color: var(--pa-warning-bg);
294
+ border-bottom-color: var(--pa-warning-bg);
284
295
  color: var(--pa-btn-warning-text);
285
296
 
286
297
  h1,
@@ -299,6 +310,7 @@
299
310
 
300
311
  .pa-card__header {
301
312
  background-color: var(--pa-danger-bg);
313
+ border-bottom-color: var(--pa-danger-bg);
302
314
  color: var(--pa-btn-danger-text);
303
315
 
304
316
  h1,
@@ -366,6 +378,7 @@
366
378
 
367
379
  .pa-card__header {
368
380
  background-color: var(--pa-color-#{$i});
381
+ border-bottom-color: var(--pa-color-#{$i});
369
382
  color: var(--pa-color-#{$i}-text);
370
383
 
371
384
  h1,
@@ -1,22 +1,32 @@
1
1
  /* ========================================
2
2
  KPI · Bento layout
3
3
  Magazine-style asymmetric tile sizing with sparklines as soft background
4
- fills behind the values. 6 tiles arranged on a 6-col × 3-row grid
4
+ fills behind the values. Default 6-tile layout on a 6-col × 3-row grid
5
5
  (hero left-half × 2 rows, two stacked right-half × 2 rows, three equal
6
- tiles bottom row). Tile placement by source order via :nth-child.
6
+ tiles bottom row). Tile placement is by source order via :nth-child, so
7
+ markup stays identical across layout modifiers.
8
+
9
+ Layout modifiers (below) swap `grid-template-areas` to provide
10
+ alternative compositions:
11
+ · default (no modifier) — 6 tiles, hero on the left
12
+ · `--hero-right` — 6 tiles, mirror of default (hero on right)
13
+ · `--5-tile` — 5 tiles, hero + 4 supporting
14
+ Row height is a CSS variable (`--pa-kpi-bento-row-height`, default
15
+ `12rem`) so authors can dial tile height per instance.
7
16
  ======================================== */
8
17
  @use '../variables' as *;
9
18
 
10
19
  .pa-kpi-bento {
11
20
  container-type: inline-size;
21
+ --pa-kpi-bento-row-height: 12rem;
12
22
  }
13
23
  .pa-kpi-bento__body { padding: 1.6rem; }
14
24
 
15
- /* ----- Bento grid ------------------------------------------------------- */
25
+ /* ----- Bento grid (default: 6 tiles, hero on left) ---------------------- */
16
26
  .pa-kpi-bento__grid {
17
27
  display: grid;
18
28
  grid-template-columns: repeat(6, 1fr);
19
- grid-template-rows: 12rem 12rem 12rem;
29
+ grid-template-rows: repeat(3, var(--pa-kpi-bento-row-height));
20
30
  grid-template-areas:
21
31
  "hero hero hero a a a"
22
32
  "hero hero hero b b b"
@@ -31,8 +41,33 @@
31
41
  > :nth-child(6) { grid-area: e; }
32
42
  }
33
43
 
34
- /* Narrow card stack everything single column. Hero modifier still
35
- bumps the value font-size so it reads as the headline. */
44
+ /* Modifier: --hero-right mirror of default. Hero on the right half,
45
+ two stacked tiles on the left of rows 1-2, three equal tiles below.
46
+ Same 6-tile contract as default; only the template flips. Source
47
+ order stays unchanged (1st = hero, 2nd = a, …). */
48
+ .pa-kpi-bento__grid--hero-right {
49
+ grid-template-areas:
50
+ "a a a hero hero hero"
51
+ "b b b hero hero hero"
52
+ "c c d d e e";
53
+ }
54
+
55
+ /* Modifier: --5-tile — hero + 4 supporting. Hero spans the left half ×
56
+ 2 rows, two stacked tiles on the right (rows 1-2), two equal halves on
57
+ the bottom row. Requires exactly 5 tiles — a 6th tile (if present)
58
+ would be auto-placed in a new row and break the layout. */
59
+ .pa-kpi-bento__grid--5-tile {
60
+ grid-template-areas:
61
+ "hero hero hero a a a"
62
+ "hero hero hero b b b"
63
+ "c c c d d d";
64
+ }
65
+
66
+ /* Narrow card → stack everything single column. Resets `grid-area: auto`
67
+ on every tile so the same markup works regardless of which layout
68
+ modifier the grid carries (the @container override sets
69
+ `grid-template-areas: none` and the `grid-area: auto` on nth-child
70
+ together neutralise any modifier's template). */
36
71
  @container (max-width: 700px) {
37
72
  .pa-kpi-bento__grid {
38
73
  grid-template-columns: 1fr;
@@ -3,55 +3,92 @@
3
3
  Goal-oriented progress bars. Each KPI shows label · value, a bar with a
4
4
  target tick, and a 0 · tgt scale below. Bar fill = value/target * 100%,
5
5
  capped visually so overshoots are signalled by colour, not overflow.
6
+
7
+ Layout is a cell-min-driven `auto-fit` grid — cells stay at least
8
+ `--pa-kpi-gauge-cell-min` wide, the grid fits as many columns as the
9
+ container allows, and the responsive cascade is intrinsic to the grid
10
+ template (no `@container` queries). `--max-N` cap modifiers cap the
11
+ column count at N while still collapsing below the cell-min × N
12
+ threshold. Same pattern as `_kpi-editorial-minimal.scss`.
6
13
  ======================================== */
7
14
  @use '../variables' as *;
8
15
 
9
- .pa-kpi-gauge-list {
10
- container-type: inline-size;
11
- }
12
16
  .pa-kpi-gauge-list__body { padding: 0; }
13
17
 
14
- /* 2-column internal grid by default; collapses to 1-col when card narrows. */
18
+ /* ----- Grid + hairline rules -------------------------------------------
19
+ Cell-min-driven responsive layout. Override `--pa-kpi-gauge-cell-min`
20
+ per instance to change density (smaller min → more columns at the same
21
+ container width).
22
+
23
+ `gap: 1px` over `background: var(--pa-border-color)` with each tile
24
+ painting `background: var(--pa-card-bg)` on top — only the gap shows
25
+ through, giving single-pixel hairlines on every interior boundary
26
+ regardless of column count. The card's outer border supplies the
27
+ perimeter. Replaces the previous per-tile `border-right` +
28
+ `border-bottom` + nth-child suppression machinery, which only worked
29
+ for the hardcoded 2-col layout. */
15
30
  .pa-kpi-gauge-list__grid {
16
31
  display: grid;
17
- grid-template-columns: repeat(2, 1fr);
32
+ grid-template-columns: repeat(auto-fit, minmax(var(--pa-kpi-gauge-cell-min, 20rem), 1fr));
33
+ gap: 1px;
34
+ background: var(--pa-border-color);
18
35
  }
19
- @container (max-width: 600px) {
20
- .pa-kpi-gauge-list__grid {
21
- grid-template-columns: 1fr;
22
- }
36
+
37
+ /* Modifier: force exactly 2 columns regardless of cell-min or container
38
+ width. For placements wanting a deterministic 2×N layout. */
39
+ .pa-kpi-gauge-list__grid--2col { grid-template-columns: repeat(2, 1fr); }
40
+
41
+ /* Cap-at-N modifiers: combine auto-fit with a ceiling on the column
42
+ count. Cells stay at least --pa-kpi-gauge-cell-min wide, but the grid
43
+ never exceeds N columns even when the container is wide enough to fit
44
+ more. Below the cell-min × N threshold the grid still collapses
45
+ responsively — these modifiers cap the maximum, not the minimum.
46
+
47
+ How the calc reads: each track's effective min is
48
+ max(cell-min, (container − total-gap) / N)
49
+ On a wide container the calc wins (so tracks grow, you stay at N).
50
+ On a narrow container cell-min wins (so the grid collapses).
51
+
52
+ Pick the cap so your item count divides into clean rows. */
53
+ .pa-kpi-gauge-list__grid--max-2 {
54
+ grid-template-columns:
55
+ repeat(auto-fit, minmax(max(var(--pa-kpi-gauge-cell-min, 20rem), calc((100% - 1px) / 2)), 1fr));
56
+ }
57
+ .pa-kpi-gauge-list__grid--max-3 {
58
+ grid-template-columns:
59
+ repeat(auto-fit, minmax(max(var(--pa-kpi-gauge-cell-min, 20rem), calc((100% - 1px * 2) / 3)), 1fr));
60
+ }
61
+ .pa-kpi-gauge-list__grid--max-4 {
62
+ grid-template-columns:
63
+ repeat(auto-fit, minmax(max(var(--pa-kpi-gauge-cell-min, 20rem), calc((100% - 1px * 3) / 4)), 1fr));
64
+ }
65
+ .pa-kpi-gauge-list__grid--max-5 {
66
+ grid-template-columns:
67
+ repeat(auto-fit, minmax(max(var(--pa-kpi-gauge-cell-min, 20rem), calc((100% - 1px * 4) / 5)), 1fr));
68
+ }
69
+ .pa-kpi-gauge-list__grid--max-6 {
70
+ grid-template-columns:
71
+ repeat(auto-fit, minmax(max(var(--pa-kpi-gauge-cell-min, 20rem), calc((100% - 1px * 5) / 6)), 1fr));
23
72
  }
24
73
 
25
74
  /* ----- Gauge tile (label/value head · bar · 0/tgt scale) ---------------- */
26
75
  .pa-kpi-gauge {
27
76
  position: relative;
77
+ background: var(--pa-card-bg);
28
78
  padding: 1.6rem 2rem;
29
- border-bottom: 1px solid var(--pa-border-color);
30
- border-right: 1px solid var(--pa-border-color);
79
+ min-width: 0;
31
80
 
32
81
  /* Per-tile bar colour cascade — modifiers below set the var, the fill
33
82
  reads it. Cleaner than per-modifier-per-element rules; host apps can
34
83
  override at the tile level via inline style="--pa-kpi-bar-color: …". */
35
84
  --pa-kpi-bar-color: var(--pa-positive);
36
85
 
37
- &:nth-child(2n) { border-right: 0; }
38
- &:nth-last-child(-n+2) { border-bottom: 0; }
39
-
40
86
  &--positive { --pa-kpi-bar-color: var(--pa-positive); }
41
87
  &--warning { --pa-kpi-bar-color: var(--pa-warning); }
42
88
  &--negative { --pa-kpi-bar-color: var(--pa-negative); }
43
89
  &--neutral { --pa-kpi-bar-color: color-mix(in srgb, var(--pa-text-color-1) 70%, transparent); }
44
90
  }
45
91
 
46
- @container (max-width: 600px) {
47
- .pa-kpi-gauge {
48
- border-right: 0;
49
-
50
- &:nth-last-child(-n+2) { border-bottom: 1px solid var(--pa-border-color); }
51
- &:last-child { border-bottom: 0; }
52
- }
53
- }
54
-
55
92
  /* ----- Head: label left, value right (baseline-aligned) ----------------- */
56
93
  .pa-kpi-gauge__head {
57
94
  display: flex;
@@ -1,6 +1,6 @@
1
1
  /* ========================================
2
2
  KPI · Editorial minimal
3
- Six KPIs in a 2×3 / 3×2 grid with hairline rules between cells,
3
+ KPI tiles in a responsive grid with hairline rules between cells,
4
4
  generous space, and an extra-light-weight number as the focal point per
5
5
  tile. No charts, no pills — the design's whole identity is the thin
6
6
  numeral. Renders as a table card (zero card-body padding) so hairlines
@@ -11,32 +11,73 @@
11
11
  .pa-kpi-edit__body { padding: 0; }
12
12
 
13
13
  /* ----- Grid + hairline rules -------------------------------------------
14
+ Cell-min-driven responsive layout: cells stay at least
15
+ --pa-kpi-edit-cell-min wide; the grid fits as many columns as the
16
+ container allows. No @container queries — `auto-fit + minmax` handles
17
+ the cascade intrinsically. Override the min per instance to change
18
+ density (smaller min → more columns at the same container width).
19
+
14
20
  `gap: 1px` over `background: var(--pa-border-color)` with each tile
15
- painting `background: var(--pa-card-bg)` on top. Only the gap shows
16
- through, giving perfect single-pixel hairlines on every interior
17
- boundary (vertical and horizontal) for free, regardless of column
18
- count. The card's outer border supplies the perimeter. */
21
+ painting `background: var(--pa-card-bg)` on top only the gap shows
22
+ through, giving single-pixel hairlines on every interior boundary
23
+ regardless of column count. The card's outer border supplies the
24
+ perimeter. */
19
25
  .pa-kpi-edit__grid {
20
26
  display: grid;
21
- grid-template-columns: repeat(3, 1fr);
27
+ grid-template-columns: repeat(auto-fit, minmax(var(--pa-kpi-edit-cell-min, 14rem), 1fr));
22
28
  gap: 1px;
23
29
  background: var(--pa-border-color);
24
- container-type: inline-size;
25
- }
26
- @container (max-width: 640px) {
27
- .pa-kpi-edit__grid { grid-template-columns: repeat(2, 1fr); }
28
- }
29
- @container (max-width: 360px) {
30
- .pa-kpi-edit__grid { grid-template-columns: 1fr; }
31
30
  }
32
31
 
33
- /* Modifier: force 2-col regardless of card width. For placements wanting
34
- a deterministic 2×N layout (e.g. 4 tiles as clean 2×2) instead of
35
- relying on the container query to land in the right bucket. */
32
+ /* Modifier: force exactly 2 columns regardless of cell-min or container
33
+ width. For placements wanting a deterministic 2×N layout (e.g. 4 tiles
34
+ as a clean 2×2 even when the container is wide enough for more). */
36
35
  .pa-kpi-edit__grid--2col { grid-template-columns: repeat(2, 1fr); }
37
36
 
38
- /* ----- Tile (editorial spacing leans on the 2.4rem padding) ------------- */
37
+ /* Cap-at-N modifiers: combine auto-fit with a ceiling on the column
38
+ count. Cells stay at least --pa-kpi-edit-cell-min wide, but the grid
39
+ never exceeds N columns even when the container is wide enough to fit
40
+ more. Below the cell-min × N threshold the grid still collapses
41
+ responsively — these modifiers cap the maximum, not the minimum.
42
+
43
+ How the calc reads: each track's effective min is
44
+ max(cell-min, (container − total-gap) / N)
45
+ On a wide container the calc wins (so tracks grow, you stay at N).
46
+ On a narrow container cell-min wins (so the grid collapses).
47
+
48
+ Why this matters for the gray-void edge case: `auto-fit` keeps as many
49
+ tracks as fit the container, then only collapses tracks empty across
50
+ the *whole* grid. With 6 items at 4-col auto-fit, tracks 1–4 all have
51
+ row-1 items, so row 2's tracks 3–4 stay (and show the gap background).
52
+ Capping at 3 makes 6 items pack 3×2 cleanly. Pick the cap so your item
53
+ count divides into clean rows. */
54
+ .pa-kpi-edit__grid--max-2 {
55
+ grid-template-columns:
56
+ repeat(auto-fit, minmax(max(var(--pa-kpi-edit-cell-min, 14rem), calc((100% - 1px) / 2)), 1fr));
57
+ }
58
+ .pa-kpi-edit__grid--max-3 {
59
+ grid-template-columns:
60
+ repeat(auto-fit, minmax(max(var(--pa-kpi-edit-cell-min, 14rem), calc((100% - 1px * 2) / 3)), 1fr));
61
+ }
62
+ .pa-kpi-edit__grid--max-4 {
63
+ grid-template-columns:
64
+ repeat(auto-fit, minmax(max(var(--pa-kpi-edit-cell-min, 14rem), calc((100% - 1px * 3) / 4)), 1fr));
65
+ }
66
+ .pa-kpi-edit__grid--max-5 {
67
+ grid-template-columns:
68
+ repeat(auto-fit, minmax(max(var(--pa-kpi-edit-cell-min, 14rem), calc((100% - 1px * 4) / 5)), 1fr));
69
+ }
70
+ .pa-kpi-edit__grid--max-6 {
71
+ grid-template-columns:
72
+ repeat(auto-fit, minmax(max(var(--pa-kpi-edit-cell-min, 14rem), calc((100% - 1px * 5) / 6)), 1fr));
73
+ }
74
+
75
+ /* ----- Tile (editorial spacing leans on the 2.4rem padding) -------------
76
+ Per-cell container: the value's `cqi`-based font-size scales with
77
+ *this* tile's width, not the whole grid's. Keeps typography legible as
78
+ the grid packs more cells into the same row. */
39
79
  .pa-kpi-edit__tile {
80
+ container-type: inline-size;
40
81
  background: var(--pa-card-bg);
41
82
  padding: 2.4rem 2rem;
42
83
  display: flex;
@@ -69,11 +110,13 @@
69
110
  "monospace at low contrast".
70
111
  - font-weight: 200 (extra-light). 300 was tested first but didn't read
71
112
  distinctly enough as "light" against the body's 400 default.
72
- - clamp() lets the number shrink in narrow 25% page-grid cells without
73
- manual breakpoints. */
113
+ - clamp() lets the number shrink in narrow cells without manual
114
+ breakpoints. `cqi` measures per-cell (tile is a container) so the
115
+ value tracks each cell's actual width as the grid packs more
116
+ columns into a wider container. */
74
117
  .pa-kpi-edit__value {
75
118
  font-family: var(--base-font-family);
76
- font-size: clamp(3.2rem, 18cqi, 5.6rem);
119
+ font-size: clamp(3.2rem, 22cqi, 5.6rem);
77
120
  font-weight: 200;
78
121
  letter-spacing: -0.02em;
79
122
  line-height: 1;
@@ -12,12 +12,26 @@
12
12
  }
13
13
  .pa-kpi-hero-list__body { padding: 1.6rem; }
14
14
 
15
- /* ----- Layout: hero left, rail right ------------------------------------ */
15
+ /* ----- Layout: hero left, rail right ------------------------------------
16
+ Default split is 1:1 (50% hero / 50% rail). Modifiers below shift the
17
+ weight to the hero, which is the more common "executive dashboard"
18
+ shape — one big headline, supporting tiles compressed into a narrower
19
+ rail. Pick a modifier per instance; the markup stays unchanged. */
16
20
  .pa-kpi-hero-list__layout {
17
21
  display: grid;
18
22
  grid-template-columns: 1fr 1fr;
19
23
  gap: 1.4rem;
20
24
  }
25
+ /* Hero 2/3, rail 1/3. */
26
+ .pa-kpi-hero-list__layout--hero-2-3 {
27
+ grid-template-columns: 2fr 1fr;
28
+ }
29
+ /* Hero 3/4, rail 1/4 — hero-dominant; rail is a thin sidebar. */
30
+ .pa-kpi-hero-list__layout--hero-3-4 {
31
+ grid-template-columns: 3fr 1fr;
32
+ }
33
+ /* Narrow card → stack to single column. Overrides any --hero-N-M modifier
34
+ that lives on the same element. */
21
35
  @container (max-width: 700px) {
22
36
  .pa-kpi-hero-list__layout {
23
37
  grid-template-columns: 1fr;
@@ -1,13 +1,27 @@
1
1
  /* ========================================
2
2
  KPI · Numeric strip (densest)
3
- Tabular "spreadsheet-style" table card with metric/now/prev/Δ%/vs target
4
- columns — most data per pixel, no chart chrome. Each row is its own
5
- grid sharing the same column template so cells align across rows
3
+ Tabular "spreadsheet-style" table card with metric / now / prev / Δ% /
4
+ target columns — most data per pixel, no chart chrome. Each row is its
5
+ own grid sharing the same column template so cells align across rows
6
6
  without needing subgrid. Wide-only by design — no responsive collapse;
7
7
  narrow placements should use Comparison gauges instead.
8
+
9
+ Column contract: `metric` + `now` are always present; `prev`, `delta`,
10
+ and `target` are independently optional via toggle modifiers on the
11
+ strip (`--no-prev`, `--no-delta`, `--no-target`). Modifiers compose, so
12
+ the visible column count ranges from 2 (all three optional columns
13
+ dropped) to 5 (none dropped). Each combination has its own template
14
+ selector below so the grid tracks always match the visible cell count.
8
15
  ======================================== */
9
16
  @use '../variables' as *;
10
17
 
18
+ /* Per-column track widths — referenced from every template selector. */
19
+ $strip-col-metric: minmax(0, 2fr);
20
+ $strip-col-now: minmax(0, 1.1fr);
21
+ $strip-col-prev: minmax(0, 1fr);
22
+ $strip-col-delta: minmax(0, 1fr);
23
+ $strip-col-target: minmax(0, 1.6fr);
24
+
11
25
  .pa-kpi-strip__body { padding: 0; }
12
26
 
13
27
  /* ----- Table layout ----------------------------------------------------- */
@@ -15,11 +29,11 @@
15
29
  .pa-kpi-strip__head-row {
16
30
  display: grid;
17
31
  grid-template-columns:
18
- minmax(0, 2fr) /* metric */
19
- minmax(0, 1.1fr) /* now */
20
- minmax(0, 1fr) /* prev */
21
- minmax(0, 1fr) /* delta */
22
- minmax(0, 1.6fr); /* target (bar + pct stacked) */
32
+ $strip-col-metric
33
+ $strip-col-now
34
+ $strip-col-prev
35
+ $strip-col-delta
36
+ $strip-col-target;
23
37
  column-gap: 1.6rem;
24
38
  align-items: center;
25
39
  position: relative;
@@ -142,13 +156,58 @@
142
156
  line-height: 1;
143
157
  }
144
158
 
145
- /* ----- --no-prev: 4-col layout (metric / now / Δ% / target) -------------
146
- Useful for narrow placements; markup omits the prev cells. */
159
+ /* ----- Toggle modifiers ------------------------------------------------
160
+ `--no-prev`, `--no-delta`, `--no-target` are independently composable.
161
+ Each hides its column's cells (data + header) and the matching template
162
+ selector below adjusts `grid-template-columns` to the visible cell
163
+ count, so source order (metric, now, prev, delta, target) is preserved
164
+ and the remaining cells slot into the right tracks.
165
+
166
+ `metric` and `now` are mandatory — no toggle drops them. If a strip
167
+ doesn't need a focal value, it's a different design (use comparison
168
+ gauges or editorial-minimal). */
169
+ .pa-kpi-strip--no-prev .pa-kpi-strip__prev,
170
+ .pa-kpi-strip--no-prev .pa-kpi-strip__head--prev { display: none; }
171
+ .pa-kpi-strip--no-delta .pa-kpi-strip__delta,
172
+ .pa-kpi-strip--no-delta .pa-kpi-strip__head--delta { display: none; }
173
+ .pa-kpi-strip--no-target .pa-kpi-strip__target,
174
+ .pa-kpi-strip--no-target .pa-kpi-strip__head--target { display: none; }
175
+
176
+ /* ----- Grid templates per visible-column combination -------------------
177
+ 8 combos (1 default + 3 single-drops + 3 double-drops + 1 triple-drop).
178
+ Each lists the visible columns in source order with their declared
179
+ `$strip-col-*` width. */
180
+
181
+ /* 4-col: one optional column dropped */
147
182
  .pa-kpi-strip--no-prev .pa-kpi-strip__row,
148
183
  .pa-kpi-strip--no-prev .pa-kpi-strip__head-row {
149
- grid-template-columns:
150
- minmax(0, 2fr)
151
- minmax(0, 1.1fr)
152
- minmax(0, 1fr)
153
- minmax(0, 1.6fr);
184
+ grid-template-columns: $strip-col-metric $strip-col-now $strip-col-delta $strip-col-target;
185
+ }
186
+ .pa-kpi-strip--no-delta .pa-kpi-strip__row,
187
+ .pa-kpi-strip--no-delta .pa-kpi-strip__head-row {
188
+ grid-template-columns: $strip-col-metric $strip-col-now $strip-col-prev $strip-col-target;
189
+ }
190
+ .pa-kpi-strip--no-target .pa-kpi-strip__row,
191
+ .pa-kpi-strip--no-target .pa-kpi-strip__head-row {
192
+ grid-template-columns: $strip-col-metric $strip-col-now $strip-col-prev $strip-col-delta;
193
+ }
194
+
195
+ /* 3-col: two optional columns dropped */
196
+ .pa-kpi-strip--no-prev.pa-kpi-strip--no-delta .pa-kpi-strip__row,
197
+ .pa-kpi-strip--no-prev.pa-kpi-strip--no-delta .pa-kpi-strip__head-row {
198
+ grid-template-columns: $strip-col-metric $strip-col-now $strip-col-target;
199
+ }
200
+ .pa-kpi-strip--no-prev.pa-kpi-strip--no-target .pa-kpi-strip__row,
201
+ .pa-kpi-strip--no-prev.pa-kpi-strip--no-target .pa-kpi-strip__head-row {
202
+ grid-template-columns: $strip-col-metric $strip-col-now $strip-col-delta;
203
+ }
204
+ .pa-kpi-strip--no-delta.pa-kpi-strip--no-target .pa-kpi-strip__row,
205
+ .pa-kpi-strip--no-delta.pa-kpi-strip--no-target .pa-kpi-strip__head-row {
206
+ grid-template-columns: $strip-col-metric $strip-col-now $strip-col-prev;
207
+ }
208
+
209
+ /* 2-col: all three optional columns dropped (metric + now only) */
210
+ .pa-kpi-strip--no-prev.pa-kpi-strip--no-delta.pa-kpi-strip--no-target .pa-kpi-strip__row,
211
+ .pa-kpi-strip--no-prev.pa-kpi-strip--no-delta.pa-kpi-strip--no-target .pa-kpi-strip__head-row {
212
+ grid-template-columns: $strip-col-metric $strip-col-now;
154
213
  }