@keenmate/pure-admin-core 2.5.0 → 2.6.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/README.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  Lightweight, data-focused CSS/SCSS admin framework with Corporate theme as default.
4
4
 
5
+ ## What's New in 2.6.0
6
+
7
+ - **Seven KPI showcase designs** under the new `KPI` sidebar group — Terminal grid (Bloomberg-style dense panel with view-mode toggle), Sparkline list (one-row-per-KPI with filled-area chart), Comparison gauges (progress bars with target tick), Hero + supporting (big focal metric + side rail), Bento (asymmetric grid with sparklines behind values), Numeric strip (tabular spreadsheet-style), and Editorial minimal (light-weight typography with hairline rules). Each page ships with a Usage Guide + CSS Classes Reference card so the markup is documented in-place.
8
+ - **Framework token consolidation** — single source of truth for role colours (`--pa-success / --pa-warning / --pa-danger / --pa-info`), a new 5-step sentiment scale (`--pa-very-positive` through `--pa-very-negative`), text-contrast tiers (`--pa-text-strong / -secondary / -tertiary`), surface tints (`--pa-surface-hover / -track`), popover chrome (`--pa-detail-*`), and chart trendline tokens (`--pa-chart-trendline-height / -stroke`). Button + contextual surfaces rewired so a single override of `--pa-success` cascades through everything.
9
+ - **Role colours migrated to Tailwind palette (Visual breaking)** — `$base-success-color` → `#22c55e`, `$base-warning-color` → `#f97316` (most visible: warning yellow → orange), `$base-danger-color` → `#ef4444`. Themes that override these via `!default` still win; consumers using stock defaults will see the shift on buttons, alerts, and contextual chips.
10
+ - **`pa-stat--square` redesigned (Visual breaking)** — `__symbol` is now an inline 50%-sized companion next to `__number` (was a giant 12%-opacity watermark), markup order drives prefix-vs-suffix layout (`<symbol><number>` for `$847K`, `<number><symbol>` for `87%`), and font sizes are container-relative (`cqi`) so values track tile width instead of viewport. Multi-character units like `°C`, `¥`, `M` now render correctly at any tile size.
11
+ - **Theme-aware dashboard chart + new `pa:theme-change` event** — the demo's D3 chart was reading CSS custom properties from `<html>` (always returned `:root` defaults) and snapshotting them once at draw time. Fixed both bugs and introduced a `pa:theme-change` window event that any consumer code with cached theme-dependent values can listen to and re-render.
12
+ - **Progress ring / gauge dark-theme fixes** — `__inner` mask switched from compile-time `$card-bg` to runtime `var(--pa-card-bg)` so the percentage value stays readable in dark themes; track colours moved from `rgba(0,0,0,0.08)` to a `color-mix(... var(--pa-text-color-1) 12%, transparent)` so the unfilled portion is visible on both light and dark surfaces.
13
+
5
14
  ## What's New in 2.5.0
6
15
 
7
16
  - **Form Demo showcase page** (`/showcases/form-demo`) — Vanilla-JS CRUD form mirroring the LiveView form demo from `keen-pure-admin`: inline validation slots, edit/update flow, optimistic delete with toast-undo, and a `simulateServerSave()` failure path so error states are exercisable without any tooling.
@@ -10,13 +19,6 @@ Lightweight, data-focused CSS/SCSS admin framework with Corporate theme as defau
10
19
  - **Popconfirm position classes renamed (Breaking)** — `pa-popconfirm--right` / `--left` → `pa-popconfirm--end` / `--start`. SCSS uses logical properties so the popconfirm and its arrow mirror in `dir="rtl"`. No backwards-compatibility aliases — update markup directly.
11
20
  - **`pa-alert__heading` size now opt-in (Breaking)** — Was hardcoded `font-size-lg`; defaults to the alert's body font-size now. Add `pa-alert__heading--lg` on existing alerts to keep the previous bigger treatment.
12
21
 
13
- ## What's New in 2.4.0
14
-
15
- - **`download-themes` bin removed** — Theme management now lives in the [`pureadmin` CLI](https://www.npmjs.com/package/@keenmate/pureadmin) (`npx pureadmin themes add/update/list`). README's Theme Setup section rewritten to match.
16
- - **Theme-aware charts** — Demo dashboard chart rewritten to source colors and fonts from Pure Admin CSS custom properties (`--pa-accent`, `--pa-text-color-*`, `--base-font-family`). Establishes the canonical pattern for SVG charts tracking the active theme.
17
- - **KPI square stats theme correctly** — `pa-stat--square` color variants now route through `--pa-accent` / `--pa-success-bg` / etc. instead of raw SCSS vars that resolved to Bootstrap defaults in every theme.
18
- - **Profile panel readable on colored headers** — Name, email, role badge, and tab icons now use `--pa-header-profile-name-color` (guaranteed to contrast with header bg) instead of body text vars, with opacity/color-mix for hierarchy.
19
-
20
22
  ## Installation
21
23
 
22
24
  ```bash
package/dist/css/main.css CHANGED
@@ -5664,11 +5664,11 @@ body:not(.sidebar-hidden) .pa-layout__sidebar--icon-collapse .pa-sidebar__icon {
5664
5664
  border-top: none !important;
5665
5665
  }
5666
5666
  .pa-card--live-up {
5667
- background-color: rgba(40, 167, 69, 0.1);
5667
+ background-color: rgba(34, 197, 94, 0.1);
5668
5668
  transition: background-color 0.3s ease;
5669
5669
  }
5670
5670
  .pa-card--live-down {
5671
- background-color: rgba(220, 53, 69, 0.1);
5671
+ background-color: rgba(239, 68, 68, 0.1);
5672
5672
  transition: background-color 0.3s ease;
5673
5673
  }
5674
5674
  .pa-card--live-neutral {
@@ -6299,12 +6299,12 @@ a.pa-card p {
6299
6299
  color: #007bff;
6300
6300
  }
6301
6301
  .pa-stat__icon--success {
6302
- background-color: rgba(40, 167, 69, 0.1);
6303
- color: #28a745;
6302
+ background-color: rgba(34, 197, 94, 0.1);
6303
+ color: #22c55e;
6304
6304
  }
6305
6305
  .pa-stat__icon--warning {
6306
- background-color: rgba(255, 193, 7, 0.1);
6307
- color: #ffc107;
6306
+ background-color: rgba(249, 115, 22, 0.1);
6307
+ color: #f97316;
6308
6308
  }
6309
6309
  .pa-stat__icon--info {
6310
6310
  background-color: rgba(23, 162, 184, 0.1);
@@ -6348,18 +6348,21 @@ a.pa-card p {
6348
6348
  font-weight: 600;
6349
6349
  }
6350
6350
  .pa-stat--hero .pa-stat__change--positive, .pa-stat--hero-compact .pa-stat__change--positive {
6351
- color: #28a745;
6351
+ color: #22c55e;
6352
6352
  }
6353
6353
  .pa-stat--hero .pa-stat__change--negative, .pa-stat--hero-compact .pa-stat__change--negative {
6354
- color: #dc3545;
6354
+ color: #ef4444;
6355
6355
  }
6356
6356
  .pa-stat--hero .pa-stat__change--neutral, .pa-stat--hero-compact .pa-stat__change--neutral {
6357
6357
  color: var(--pa-text-color-2);
6358
6358
  }
6359
6359
  .pa-stat--square {
6360
+ container-type: inline-size;
6360
6361
  display: flex;
6361
- align-items: center;
6362
- justify-content: space-between;
6362
+ flex-wrap: wrap;
6363
+ align-content: space-between;
6364
+ align-items: baseline;
6365
+ column-gap: 0.15em;
6363
6366
  padding: 2.4rem;
6364
6367
  min-height: 12.8rem;
6365
6368
  min-width: 12.8rem;
@@ -6374,41 +6377,32 @@ a.pa-card p {
6374
6377
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
6375
6378
  }
6376
6379
  .pa-stat--square .pa-stat__number {
6377
- font-size: clamp(4.8rem, 8vw, 7.2rem);
6380
+ font-size: clamp(3.2rem, 25cqi, 9.6rem);
6378
6381
  font-weight: 700;
6379
- line-height: 1.1;
6382
+ line-height: 1;
6380
6383
  color: inherit;
6381
- z-index: 2px;
6382
- position: relative;
6383
6384
  text-shadow: 0 4px 8px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.15);
6384
6385
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
6385
6386
  word-break: keep-all;
6386
6387
  white-space: nowrap;
6387
6388
  }
6388
6389
  .pa-stat--square .pa-stat__symbol {
6389
- font-size: clamp(6.4rem, 10vw, 9.6rem);
6390
+ font-size: clamp(1.6rem, 12cqi, 4.8rem);
6390
6391
  font-weight: 700;
6391
- line-height: 1.1;
6392
- opacity: 0.12;
6392
+ line-height: 1;
6393
6393
  color: inherit;
6394
- position: absolute;
6395
- right: 2.4rem;
6396
- top: 50%;
6397
- transform: translateY(-50%);
6398
- z-index: 0;
6394
+ opacity: 0.85;
6395
+ word-break: keep-all;
6396
+ white-space: nowrap;
6399
6397
  }
6400
6398
  .pa-stat--square .pa-stat__label {
6401
- position: absolute;
6402
- bottom: 1.6rem;
6403
- left: 2.4rem;
6399
+ flex-basis: 100%;
6404
6400
  font-size: 1.2rem;
6405
6401
  text-transform: uppercase;
6406
6402
  letter-spacing: 0.05em;
6407
6403
  font-weight: 500;
6408
6404
  color: inherit;
6409
6405
  opacity: 0.8;
6410
- z-index: 2;
6411
- max-width: calc(100% - 4.8rem);
6412
6406
  overflow: hidden;
6413
6407
  text-overflow: ellipsis;
6414
6408
  white-space: nowrap;
@@ -6503,13 +6497,13 @@ a.pa-card p {
6503
6497
  }
6504
6498
  .pa-list-basic--icon li::before {
6505
6499
  content: "✓";
6506
- color: #28a745;
6500
+ color: #22c55e;
6507
6501
  font-weight: 600;
6508
6502
  flex-shrink: 0;
6509
6503
  }
6510
6504
  .pa-list-basic--icon.pa-list-basic--danger li::before {
6511
6505
  content: "✗";
6512
- color: #dc3545;
6506
+ color: #ef4444;
6513
6507
  }
6514
6508
  .pa-list-basic--icon.pa-list-basic--info li::before {
6515
6509
  content: "→";
@@ -6517,7 +6511,7 @@ a.pa-card p {
6517
6511
  }
6518
6512
  .pa-list-basic--icon.pa-list-basic--warning li::before {
6519
6513
  content: "!";
6520
- color: #ffc107;
6514
+ color: #f97316;
6521
6515
  }
6522
6516
 
6523
6517
  .pa-list-ordered {
@@ -6773,7 +6767,7 @@ a.pa-card p {
6773
6767
  }
6774
6768
  .pa-composite-badge__button:focus {
6775
6769
  outline: none;
6776
- box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.25);
6770
+ box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.25);
6777
6771
  }
6778
6772
 
6779
6773
  /* ========================================
@@ -6823,7 +6817,7 @@ a.pa-card p {
6823
6817
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.12);
6824
6818
  }
6825
6819
  .pa-composite-badge--success .pa-composite-badge__icon {
6826
- background-color: #28a745;
6820
+ background-color: #22c55e;
6827
6821
  color: #ffffff;
6828
6822
  }
6829
6823
  .pa-composite-badge--success .pa-composite-badge__label {
@@ -6834,17 +6828,17 @@ a.pa-card p {
6834
6828
  background-color: rgb(194.0245901639, 229.4754098361, 202.5327868852);
6835
6829
  }
6836
6830
  .pa-composite-badge--success .pa-composite-badge__button {
6837
- background-color: #28a745;
6831
+ background-color: #22c55e;
6838
6832
  color: #ffffff;
6839
6833
  }
6840
6834
  .pa-composite-badge--success .pa-composite-badge__button:hover {
6841
- background-color: rgb(30.1449275362, 125.8550724638, 52);
6835
+ background-color: rgb(26.4935064935, 153.5064935065, 73.2467532468);
6842
6836
  }
6843
6837
  .pa-composite-badge--success .pa-composite-badge__button:focus {
6844
6838
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.12);
6845
6839
  }
6846
6840
  .pa-composite-badge--danger .pa-composite-badge__icon {
6847
- background-color: #dc3545;
6841
+ background-color: #ef4444;
6848
6842
  color: #ffffff;
6849
6843
  }
6850
6844
  .pa-composite-badge--danger .pa-composite-badge__label {
@@ -6855,17 +6849,17 @@ a.pa-card p {
6855
6849
  background-color: rgb(244.2021276596, 193.2978723404, 197.9255319149);
6856
6850
  }
6857
6851
  .pa-composite-badge--danger .pa-composite-badge__button {
6858
- background-color: #dc3545;
6852
+ background-color: #ef4444;
6859
6853
  color: #ffffff;
6860
6854
  }
6861
6855
  .pa-composite-badge--danger .pa-composite-badge__button:hover {
6862
- background-color: rgb(189.2151898734, 32.7848101266, 47.7721518987);
6856
+ background-color: rgb(234.9802955665, 21.0197044335, 21.0197044335);
6863
6857
  }
6864
6858
  .pa-composite-badge--danger .pa-composite-badge__button:focus {
6865
6859
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.12);
6866
6860
  }
6867
6861
  .pa-composite-badge--warning .pa-composite-badge__icon {
6868
- background-color: #ffc107;
6862
+ background-color: #f97316;
6869
6863
  color: #212529;
6870
6864
  }
6871
6865
  .pa-composite-badge--warning .pa-composite-badge__label {
@@ -6876,11 +6870,11 @@ a.pa-card p {
6876
6870
  background-color: rgb(255, 236.88, 179.5);
6877
6871
  }
6878
6872
  .pa-composite-badge--warning .pa-composite-badge__button {
6879
- background-color: #ffc107;
6873
+ background-color: #f97316;
6880
6874
  color: #212529;
6881
6875
  }
6882
6876
  .pa-composite-badge--warning .pa-composite-badge__button:hover {
6883
- background-color: rgb(211, 158.25, 0);
6877
+ background-color: rgb(214.4769874477, 91.129707113, 5.5230125523);
6884
6878
  }
6885
6879
  .pa-composite-badge--warning .pa-composite-badge__button:focus {
6886
6880
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.12);
@@ -6955,13 +6949,13 @@ a.pa-card p {
6955
6949
  background-color: #6c757d;
6956
6950
  }
6957
6951
  .pa-composite-badge--success .pa-composite-badge__icon {
6958
- background-color: #28a745;
6952
+ background-color: #22c55e;
6959
6953
  }
6960
6954
  .pa-composite-badge--danger .pa-composite-badge__icon {
6961
- background-color: #dc3545;
6955
+ background-color: #ef4444;
6962
6956
  }
6963
6957
  .pa-composite-badge--warning .pa-composite-badge__icon {
6964
- background-color: #ffc107;
6958
+ background-color: #f97316;
6965
6959
  color: #212529;
6966
6960
  }
6967
6961
  .pa-composite-badge--info .pa-composite-badge__icon {
@@ -7049,23 +7043,23 @@ a.pa-card p {
7049
7043
  box-shadow: 0 0 0 2px rgba(108, 117, 125, 0.25);
7050
7044
  }
7051
7045
  .pa-composite-badge--btn-success .pa-composite-badge__button {
7052
- background-color: #28a745;
7046
+ background-color: #22c55e;
7053
7047
  }
7054
7048
  .pa-composite-badge--btn-success .pa-composite-badge__button:hover {
7055
- background-color: rgb(30.1449275362, 125.8550724638, 52);
7049
+ background-color: rgb(26.4935064935, 153.5064935065, 73.2467532468);
7056
7050
  }
7057
7051
  .pa-composite-badge--btn-success .pa-composite-badge__button:focus {
7058
- box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.25);
7052
+ box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.25);
7059
7053
  }
7060
7054
  .pa-composite-badge--btn-warning .pa-composite-badge__button {
7061
- background-color: #ffc107;
7055
+ background-color: #f97316;
7062
7056
  color: #212529;
7063
7057
  }
7064
7058
  .pa-composite-badge--btn-warning .pa-composite-badge__button:hover {
7065
- background-color: rgb(211, 158.25, 0);
7059
+ background-color: rgb(214.4769874477, 91.129707113, 5.5230125523);
7066
7060
  }
7067
7061
  .pa-composite-badge--btn-warning .pa-composite-badge__button:focus {
7068
- box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.25);
7062
+ box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.25);
7069
7063
  }
7070
7064
  .pa-composite-badge--btn-info .pa-composite-badge__button {
7071
7065
  background-color: #17a2b8;
@@ -7908,13 +7902,13 @@ a.pa-card p {
7908
7902
  border-top-color: #6c757d;
7909
7903
  }
7910
7904
  .pa-spinner--success {
7911
- border-top-color: #28a745;
7905
+ border-top-color: #22c55e;
7912
7906
  }
7913
7907
  .pa-spinner--danger {
7914
- border-top-color: #dc3545;
7908
+ border-top-color: #ef4444;
7915
7909
  }
7916
7910
  .pa-spinner--warning {
7917
- border-top-color: #ffc107;
7911
+ border-top-color: #f97316;
7918
7912
  }
7919
7913
  .pa-spinner--info {
7920
7914
  border-top-color: #17a2b8;
@@ -9728,7 +9722,7 @@ a.pa-card p {
9728
9722
  }
9729
9723
  .pa-input-wrapper__clear:hover {
9730
9724
  opacity: 1;
9731
- color: #dc3545;
9725
+ color: #ef4444;
9732
9726
  }
9733
9727
  .pa-input-wrapper__clear:focus {
9734
9728
  outline: none;
@@ -9811,8 +9805,8 @@ a.pa-card p {
9811
9805
  }
9812
9806
  .pa-search-highlight__operator {
9813
9807
  display: inline;
9814
- background-color: rgba(40, 167, 69, 0.15);
9815
- color: #28a745;
9808
+ background-color: rgba(34, 197, 94, 0.15);
9809
+ color: #22c55e;
9816
9810
  padding: 0.2rem 0.4rem;
9817
9811
  border-radius: var(--pa-border-radius-sm);
9818
9812
  font-weight: 500;
@@ -9967,7 +9961,7 @@ a.pa-card p {
9967
9961
  color: var(--pa-text-color-1);
9968
9962
  }
9969
9963
  .pa-inline-query-token--field.pa-inline-query-token--invalid {
9970
- background-color: rgba(220, 53, 69, 0.15);
9964
+ background-color: rgba(239, 68, 68, 0.15);
9971
9965
  color: var(--pa-text-color-1);
9972
9966
  text-decoration: wavy underline;
9973
9967
  }
@@ -9976,11 +9970,11 @@ a.pa-card p {
9976
9970
  color: var(--pa-text-color-1);
9977
9971
  }
9978
9972
  .pa-inline-query-token--value {
9979
- background-color: rgba(40, 167, 69, 0.15);
9973
+ background-color: rgba(34, 197, 94, 0.15);
9980
9974
  color: var(--pa-text-color-1);
9981
9975
  }
9982
9976
  .pa-inline-query-token--keyword {
9983
- background-color: rgba(255, 193, 7, 0.15);
9977
+ background-color: rgba(249, 115, 22, 0.15);
9984
9978
  color: var(--pa-text-color-1);
9985
9979
  font-style: italic;
9986
9980
  }
@@ -10361,7 +10355,7 @@ web-multiselect {
10361
10355
  width: 2rem;
10362
10356
  height: 2rem;
10363
10357
  border: none;
10364
- background: #dc3545;
10358
+ background: #ef4444;
10365
10359
  color: #ffffff;
10366
10360
  cursor: pointer;
10367
10361
  border-radius: 50%;
@@ -10381,7 +10375,7 @@ web-multiselect {
10381
10375
  }
10382
10376
  .pa-file-dropzone__file-card-remove:hover {
10383
10377
  transform: scale(1.1);
10384
- background: rgba(220, 53, 69, 0.9);
10378
+ background: rgba(239, 68, 68, 0.9);
10385
10379
  }
10386
10380
  .pa-file-dropzone__image-card {
10387
10381
  position: relative;
@@ -10422,7 +10416,7 @@ web-multiselect {
10422
10416
  opacity: 1;
10423
10417
  }
10424
10418
  .pa-file-dropzone__image-card-remove:hover {
10425
- background: #dc3545;
10419
+ background: #ef4444;
10426
10420
  transform: scale(1.1);
10427
10421
  }
10428
10422
  .pa-file-dropzone__summary {
@@ -10606,10 +10600,10 @@ web-multiselect {
10606
10600
  color: #007bff;
10607
10601
  }
10608
10602
  .pa-file-popover__status--complete {
10609
- color: #28a745;
10603
+ color: #22c55e;
10610
10604
  }
10611
10605
  .pa-file-popover__status--error {
10612
- color: #dc3545;
10606
+ color: #ef4444;
10613
10607
  }
10614
10608
  .pa-file-popover__remove-cell {
10615
10609
  text-align: center;
@@ -10621,7 +10615,7 @@ web-multiselect {
10621
10615
  height: 3.2rem;
10622
10616
  border: 1px solid var(--pa-border-color);
10623
10617
  background: var(--pa-card-bg);
10624
- color: #dc3545;
10618
+ color: #ef4444;
10625
10619
  cursor: pointer;
10626
10620
  border-radius: var(--pa-border-radius-sm);
10627
10621
  font-size: 2.4rem;
@@ -10633,9 +10627,9 @@ web-multiselect {
10633
10627
  font-weight: 700;
10634
10628
  }
10635
10629
  .pa-file-popover__remove-btn:hover {
10636
- background: #dc3545;
10630
+ background: #ef4444;
10637
10631
  color: #ffffff;
10638
- border-color: #dc3545;
10632
+ border-color: #ef4444;
10639
10633
  transform: scale(1.1);
10640
10634
  }
10641
10635
 
@@ -10710,8 +10704,8 @@ web-multiselect {
10710
10704
  justify-content: center;
10711
10705
  }
10712
10706
  .pa-file-item__remove:hover {
10713
- background: rgba(220, 53, 69, 0.1);
10714
- color: #dc3545;
10707
+ background: rgba(239, 68, 68, 0.1);
10708
+ color: #ef4444;
10715
10709
  }
10716
10710
  .pa-file-item__remove::before {
10717
10711
  content: "×";
@@ -10758,7 +10752,7 @@ web-multiselect {
10758
10752
  justify-content: center;
10759
10753
  }
10760
10754
  .pa-file-preview__remove:hover {
10761
- background: #dc3545;
10755
+ background: #ef4444;
10762
10756
  transform: scale(1.1);
10763
10757
  }
10764
10758
  .pa-file-preview__remove::before {
@@ -10806,10 +10800,10 @@ web-multiselect {
10806
10800
  font-weight: 600;
10807
10801
  }
10808
10802
  .pa-file-progress__status--complete {
10809
- color: #28a745;
10803
+ color: #22c55e;
10810
10804
  }
10811
10805
  .pa-file-progress__status--error {
10812
- color: #dc3545;
10806
+ color: #ef4444;
10813
10807
  }
10814
10808
 
10815
10809
  .pa-file-icon--pdf::before {
@@ -10980,7 +10974,7 @@ web-multiselect {
10980
10974
  cursor: not-allowed;
10981
10975
  }
10982
10976
  .pa-checkbox-list__item--locked:hover {
10983
- background-color: rgba(255, 193, 7, 0.05);
10977
+ background-color: rgba(249, 115, 22, 0.05);
10984
10978
  }
10985
10979
  .pa-checkbox-list__item--locked .pa-checkbox-list__checkbox {
10986
10980
  cursor: not-allowed;
@@ -10991,7 +10985,7 @@ web-multiselect {
10991
10985
  position: relative;
10992
10986
  }
10993
10987
  .pa-checkbox-list__item--locked .pa-checkbox-list__label .pa-checkbox-list__text {
10994
- color: #ffc107;
10988
+ color: #f97316;
10995
10989
  }
10996
10990
  .pa-checkbox-list__item--locked .pa-checkbox-list__label .pa-checkbox-list__text::before {
10997
10991
  content: "🔒 ";
@@ -13273,16 +13267,16 @@ code {
13273
13267
  box-shadow: 3px 3px 0 rgba(0, 123, 255, 0.3);
13274
13268
  }
13275
13269
  .pa-timeline--simple .pa-timeline__item--success::before {
13276
- border-color: #28a745;
13277
- box-shadow: 3px 3px 0 rgba(40, 167, 69, 0.3);
13270
+ border-color: #22c55e;
13271
+ box-shadow: 3px 3px 0 rgba(34, 197, 94, 0.3);
13278
13272
  }
13279
13273
  .pa-timeline--simple .pa-timeline__item--warning::before {
13280
- border-color: #ffc107;
13281
- box-shadow: 3px 3px 0 rgba(255, 193, 7, 0.3);
13274
+ border-color: #f97316;
13275
+ box-shadow: 3px 3px 0 rgba(249, 115, 22, 0.3);
13282
13276
  }
13283
13277
  .pa-timeline--simple .pa-timeline__item--danger::before {
13284
- border-color: #dc3545;
13285
- box-shadow: 3px 3px 0 rgba(220, 53, 69, 0.3);
13278
+ border-color: #ef4444;
13279
+ box-shadow: 3px 3px 0 rgba(239, 68, 68, 0.3);
13286
13280
  }
13287
13281
  .pa-timeline--simple .pa-timeline__item--info::before {
13288
13282
  border-color: #17a2b8;
@@ -13299,13 +13293,13 @@ code {
13299
13293
  background: #007bff;
13300
13294
  }
13301
13295
  .pa-timeline--simple .pa-timeline__item--filled.pa-timeline__item--success::before {
13302
- background: #28a745;
13296
+ background: #22c55e;
13303
13297
  }
13304
13298
  .pa-timeline--simple .pa-timeline__item--filled.pa-timeline__item--warning::before {
13305
- background: #ffc107;
13299
+ background: #f97316;
13306
13300
  }
13307
13301
  .pa-timeline--simple .pa-timeline__item--filled.pa-timeline__item--danger::before {
13308
- background: #dc3545;
13302
+ background: #ef4444;
13309
13303
  }
13310
13304
  .pa-timeline--simple .pa-timeline__item--filled.pa-timeline__item--info::before {
13311
13305
  background: #17a2b8;
@@ -13440,7 +13434,7 @@ code {
13440
13434
  width: 40px;
13441
13435
  height: 40px;
13442
13436
  top: calc(50% - 20px);
13443
- background: #ffc107;
13437
+ background: #f97316;
13444
13438
  border: 2px solid #007bff;
13445
13439
  border-radius: 50%;
13446
13440
  font-size: 1.8rem;
@@ -13448,7 +13442,7 @@ code {
13448
13442
  z-index: 1;
13449
13443
  }
13450
13444
  .pa-timeline--alternating .pa-timeline__content {
13451
- background: #ffc107;
13445
+ background: #f97316;
13452
13446
  position: relative;
13453
13447
  }
13454
13448
  .pa-timeline--alternating .pa-timeline__content h3 {
@@ -14202,7 +14196,7 @@ code {
14202
14196
  background: #fff;
14203
14197
  }
14204
14198
  .pa-logic-tree__block--and {
14205
- border-color: #ffc107;
14199
+ border-color: #f97316;
14206
14200
  background: linear-gradient(135deg, #fff8e1 0%, #ffffff 100%);
14207
14201
  }
14208
14202
  .pa-logic-tree__block--or {
@@ -14407,7 +14401,7 @@ code {
14407
14401
  min-width: 1.6rem;
14408
14402
  height: 1.6rem;
14409
14403
  padding: 0 0.4rem;
14410
- background-color: #dc3545;
14404
+ background-color: #ef4444;
14411
14405
  color: #ffffff;
14412
14406
  font-size: 1rem;
14413
14407
  font-weight: 600;
@@ -14512,16 +14506,16 @@ code {
14512
14506
  color: #007bff;
14513
14507
  }
14514
14508
  .pa-notifications__icon-wrapper--success {
14515
- background-color: rgba(40, 167, 69, 0.1);
14516
- color: #28a745;
14509
+ background-color: rgba(34, 197, 94, 0.1);
14510
+ color: #22c55e;
14517
14511
  }
14518
14512
  .pa-notifications__icon-wrapper--warning {
14519
- background-color: rgba(255, 193, 7, 0.1);
14520
- color: #ffc107;
14513
+ background-color: rgba(249, 115, 22, 0.1);
14514
+ color: #f97316;
14521
14515
  }
14522
14516
  .pa-notifications__icon-wrapper--danger {
14523
- background-color: rgba(220, 53, 69, 0.1);
14524
- color: #dc3545;
14517
+ background-color: rgba(239, 68, 68, 0.1);
14518
+ color: #ef4444;
14525
14519
  }
14526
14520
  .pa-notifications__icon-wrapper--secondary {
14527
14521
  background-color: rgba(var(--pa-text-color-2), 0.1);
@@ -16066,7 +16060,7 @@ code {
16066
16060
  .pa-progress {
16067
16061
  width: 100%;
16068
16062
  height: 0.8rem;
16069
- background: rgba(0, 0, 0, 0.08);
16063
+ background: color-mix(in srgb, var(--pa-text-color-1) 12%, transparent);
16070
16064
  border-radius: var(--pa-border-radius);
16071
16065
  overflow: hidden;
16072
16066
  position: relative;
@@ -16099,13 +16093,13 @@ code {
16099
16093
  height: 1.2rem;
16100
16094
  }
16101
16095
  .pa-progress--success > .pa-progress__fill {
16102
- background: #28a745;
16096
+ background: #22c55e;
16103
16097
  }
16104
16098
  .pa-progress--warning > .pa-progress__fill {
16105
- background: #ffc107;
16099
+ background: #f97316;
16106
16100
  }
16107
16101
  .pa-progress--danger > .pa-progress__fill {
16108
- background: #dc3545;
16102
+ background: #ef4444;
16109
16103
  }
16110
16104
  .pa-progress--info > .pa-progress__fill {
16111
16105
  background: #17a2b8;
@@ -16141,7 +16135,7 @@ code {
16141
16135
  height: 1.2rem;
16142
16136
  border-radius: var(--pa-border-radius);
16143
16137
  overflow: hidden;
16144
- background: rgba(0, 0, 0, 0.08);
16138
+ background: color-mix(in srgb, var(--pa-text-color-1) 12%, transparent);
16145
16139
  }
16146
16140
  .pa-stacked-bar__segment {
16147
16141
  height: 100%;
@@ -16150,13 +16144,13 @@ code {
16150
16144
  background: #007bff;
16151
16145
  }
16152
16146
  .pa-stacked-bar__segment--success {
16153
- background: #28a745;
16147
+ background: #22c55e;
16154
16148
  }
16155
16149
  .pa-stacked-bar__segment--warning {
16156
- background: #ffc107;
16150
+ background: #f97316;
16157
16151
  }
16158
16152
  .pa-stacked-bar__segment--danger {
16159
- background: #dc3545;
16153
+ background: #ef4444;
16160
16154
  }
16161
16155
  .pa-stacked-bar__segment--info {
16162
16156
  background: #17a2b8;
@@ -16187,13 +16181,13 @@ code {
16187
16181
  background: #007bff;
16188
16182
  }
16189
16183
  .pa-stacked-bar__legend-swatch--success {
16190
- background: #28a745;
16184
+ background: #22c55e;
16191
16185
  }
16192
16186
  .pa-stacked-bar__legend-swatch--warning {
16193
- background: #ffc107;
16187
+ background: #f97316;
16194
16188
  }
16195
16189
  .pa-stacked-bar__legend-swatch--danger {
16196
- background: #dc3545;
16190
+ background: #ef4444;
16197
16191
  }
16198
16192
  .pa-stacked-bar__legend-swatch--info {
16199
16193
  background: #17a2b8;
@@ -16215,7 +16209,7 @@ code {
16215
16209
  width: 8rem;
16216
16210
  height: 8rem;
16217
16211
  border-radius: 50%;
16218
- background: conic-gradient(#007bff calc(var(--value, 0) * 3.6deg), rgba(0, 0, 0, 0.08) 0deg);
16212
+ background: conic-gradient(#007bff calc(var(--value, 0) * 3.6deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16219
16213
  display: flex;
16220
16214
  align-items: center;
16221
16215
  justify-content: center;
@@ -16225,7 +16219,7 @@ code {
16225
16219
  width: 70%;
16226
16220
  height: 70%;
16227
16221
  border-radius: 50%;
16228
- background: #ffffff;
16222
+ background: var(--pa-card-bg);
16229
16223
  display: flex;
16230
16224
  flex-direction: column;
16231
16225
  align-items: center;
@@ -16263,16 +16257,16 @@ code {
16263
16257
  font-size: 1.2rem;
16264
16258
  }
16265
16259
  .pa-progress-ring--success {
16266
- background: conic-gradient(#28a745 calc(var(--value, 0) * 3.6deg), rgba(0, 0, 0, 0.08) 0deg);
16260
+ background: conic-gradient(#22c55e calc(var(--value, 0) * 3.6deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16267
16261
  }
16268
16262
  .pa-progress-ring--warning {
16269
- background: conic-gradient(#ffc107 calc(var(--value, 0) * 3.6deg), rgba(0, 0, 0, 0.08) 0deg);
16263
+ background: conic-gradient(#f97316 calc(var(--value, 0) * 3.6deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16270
16264
  }
16271
16265
  .pa-progress-ring--danger {
16272
- background: conic-gradient(#dc3545 calc(var(--value, 0) * 3.6deg), rgba(0, 0, 0, 0.08) 0deg);
16266
+ background: conic-gradient(#ef4444 calc(var(--value, 0) * 3.6deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16273
16267
  }
16274
16268
  .pa-progress-ring--info {
16275
- background: conic-gradient(#17a2b8 calc(var(--value, 0) * 3.6deg), rgba(0, 0, 0, 0.08) 0deg);
16269
+ background: conic-gradient(#17a2b8 calc(var(--value, 0) * 3.6deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16276
16270
  }
16277
16271
 
16278
16272
  .pa-gauge {
@@ -16280,7 +16274,7 @@ code {
16280
16274
  height: calc(12rem / 2);
16281
16275
  border-radius: 12rem 12rem 0 0;
16282
16276
  overflow: hidden;
16283
- background: conic-gradient(from 0.75turn, #007bff calc(var(--value, 0) * 1.8deg), rgba(0, 0, 0, 0.08) 0deg);
16277
+ background: conic-gradient(from 0.75turn, #007bff calc(var(--value, 0) * 1.8deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16284
16278
  position: relative;
16285
16279
  flex-shrink: 0;
16286
16280
  }
@@ -16291,7 +16285,7 @@ code {
16291
16285
  right: 15%;
16292
16286
  height: 70%;
16293
16287
  border-radius: 12rem 12rem 0 0;
16294
- background: #ffffff;
16288
+ background: var(--pa-card-bg);
16295
16289
  display: flex;
16296
16290
  flex-direction: column;
16297
16291
  align-items: center;
@@ -16322,19 +16316,19 @@ code {
16322
16316
  right: 0;
16323
16317
  }
16324
16318
  .pa-gauge--success {
16325
- background: conic-gradient(from 0.75turn, #28a745 calc(var(--value, 0) * 1.8deg), rgba(0, 0, 0, 0.08) 0deg);
16319
+ background: conic-gradient(from 0.75turn, #22c55e calc(var(--value, 0) * 1.8deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16326
16320
  }
16327
16321
  .pa-gauge--warning {
16328
- background: conic-gradient(from 0.75turn, #ffc107 calc(var(--value, 0) * 1.8deg), rgba(0, 0, 0, 0.08) 0deg);
16322
+ background: conic-gradient(from 0.75turn, #f97316 calc(var(--value, 0) * 1.8deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16329
16323
  }
16330
16324
  .pa-gauge--danger {
16331
- background: conic-gradient(from 0.75turn, #dc3545 calc(var(--value, 0) * 1.8deg), rgba(0, 0, 0, 0.08) 0deg);
16325
+ background: conic-gradient(from 0.75turn, #ef4444 calc(var(--value, 0) * 1.8deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16332
16326
  }
16333
16327
  .pa-gauge--info {
16334
- background: conic-gradient(from 0.75turn, #17a2b8 calc(var(--value, 0) * 1.8deg), rgba(0, 0, 0, 0.08) 0deg);
16328
+ background: conic-gradient(from 0.75turn, #17a2b8 calc(var(--value, 0) * 1.8deg), color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 0deg);
16335
16329
  }
16336
16330
  .pa-gauge--zones {
16337
- background: conic-gradient(from 0.75turn, #28a745 0deg, #28a745 90deg, #ffc107 90deg, #ffc107 135deg, #dc3545 135deg, #dc3545 180deg, rgba(0, 0, 0, 0.08) 180deg);
16331
+ background: conic-gradient(from 0.75turn, #22c55e 0deg, #22c55e 90deg, #f97316 90deg, #f97316 135deg, #ef4444 135deg, #ef4444 180deg, color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) 180deg);
16338
16332
  }
16339
16333
 
16340
16334
  .pa-data-bar {
@@ -16364,19 +16358,19 @@ code {
16364
16358
  transition: width 0.25s ease;
16365
16359
  }
16366
16360
  .pa-data-bar--success .pa-data-bar__fill {
16367
- background: #28a745;
16361
+ background: #22c55e;
16368
16362
  }
16369
16363
  .pa-data-bar--warning .pa-data-bar__fill {
16370
- background: #ffc107;
16364
+ background: #f97316;
16371
16365
  }
16372
16366
  .pa-data-bar--danger .pa-data-bar__fill {
16373
- background: #dc3545;
16367
+ background: #ef4444;
16374
16368
  }
16375
16369
  .pa-data-bar--info .pa-data-bar__fill {
16376
16370
  background: #17a2b8;
16377
16371
  }
16378
16372
  .pa-data-bar--negative .pa-data-bar__fill {
16379
- background: #dc3545;
16373
+ background: #ef4444;
16380
16374
  margin-inline-start: auto;
16381
16375
  }
16382
16376
 
@@ -16388,7 +16382,7 @@ code {
16388
16382
  }
16389
16383
  .pa-heatmap__cell {
16390
16384
  border-radius: var(--pa-border-radius-sm);
16391
- background: rgba(0, 0, 0, 0.08);
16385
+ background: color-mix(in srgb, var(--pa-text-color-1) 12%, transparent);
16392
16386
  }
16393
16387
  .pa-heatmap__cell[data-level="1"] {
16394
16388
  background: rgba(0, 123, 255, 0.2);
@@ -16403,28 +16397,28 @@ code {
16403
16397
  background: #007bff;
16404
16398
  }
16405
16399
  .pa-heatmap--success .pa-heatmap__cell[data-level="1"] {
16406
- background: rgba(40, 167, 69, 0.2);
16400
+ background: rgba(34, 197, 94, 0.2);
16407
16401
  }
16408
16402
  .pa-heatmap--success .pa-heatmap__cell[data-level="2"] {
16409
- background: rgba(40, 167, 69, 0.4);
16403
+ background: rgba(34, 197, 94, 0.4);
16410
16404
  }
16411
16405
  .pa-heatmap--success .pa-heatmap__cell[data-level="3"] {
16412
- background: rgba(40, 167, 69, 0.65);
16406
+ background: rgba(34, 197, 94, 0.65);
16413
16407
  }
16414
16408
  .pa-heatmap--success .pa-heatmap__cell[data-level="4"] {
16415
- background: #28a745;
16409
+ background: #22c55e;
16416
16410
  }
16417
16411
  .pa-heatmap--danger .pa-heatmap__cell[data-level="1"] {
16418
- background: rgba(220, 53, 69, 0.2);
16412
+ background: rgba(239, 68, 68, 0.2);
16419
16413
  }
16420
16414
  .pa-heatmap--danger .pa-heatmap__cell[data-level="2"] {
16421
- background: rgba(220, 53, 69, 0.4);
16415
+ background: rgba(239, 68, 68, 0.4);
16422
16416
  }
16423
16417
  .pa-heatmap--danger .pa-heatmap__cell[data-level="3"] {
16424
- background: rgba(220, 53, 69, 0.65);
16418
+ background: rgba(239, 68, 68, 0.65);
16425
16419
  }
16426
16420
  .pa-heatmap--danger .pa-heatmap__cell[data-level="4"] {
16427
- background: #dc3545;
16421
+ background: #ef4444;
16428
16422
  }
16429
16423
  .pa-heatmap__legend {
16430
16424
  display: flex;
@@ -16438,7 +16432,7 @@ code {
16438
16432
  width: 1.2rem;
16439
16433
  height: 1.2rem;
16440
16434
  border-radius: var(--pa-border-radius-sm);
16441
- background: rgba(0, 0, 0, 0.08);
16435
+ background: color-mix(in srgb, var(--pa-text-color-1) 12%, transparent);
16442
16436
  }
16443
16437
  .pa-heatmap__legend-cell[data-level="1"] {
16444
16438
  background: rgba(0, 123, 255, 0.2);
@@ -16476,13 +16470,13 @@ code {
16476
16470
  min-height: 1px;
16477
16471
  }
16478
16472
  .pa-sparkline--success .pa-sparkline__bar {
16479
- background: #28a745;
16473
+ background: #22c55e;
16480
16474
  }
16481
16475
  .pa-sparkline--warning .pa-sparkline__bar {
16482
- background: #ffc107;
16476
+ background: #f97316;
16483
16477
  }
16484
16478
  .pa-sparkline--danger .pa-sparkline__bar {
16485
- background: #dc3545;
16479
+ background: #ef4444;
16486
16480
  }
16487
16481
  .pa-sparkline--info .pa-sparkline__bar {
16488
16482
  background: #17a2b8;
@@ -16544,13 +16538,13 @@ code {
16544
16538
  transition: width 0.25s ease;
16545
16539
  }
16546
16540
  .pa-bar-list--success .pa-bar-list__bar::after {
16547
- background: #28a745;
16541
+ background: #22c55e;
16548
16542
  }
16549
16543
  .pa-bar-list--warning .pa-bar-list__bar::after {
16550
- background: #ffc107;
16544
+ background: #f97316;
16551
16545
  }
16552
16546
  .pa-bar-list--danger .pa-bar-list__bar::after {
16553
- background: #dc3545;
16547
+ background: #ef4444;
16554
16548
  }
16555
16549
  .pa-bar-list--info .pa-bar-list__bar::after {
16556
16550
  background: #17a2b8;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keenmate/pure-admin-core",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
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": {
@@ -207,6 +207,42 @@
207
207
  --pa-alert-info-border: color-mix(in srgb, var(--pa-info-bg) #{$alert-border-opacity-dark}%, transparent);
208
208
  }
209
209
 
210
+ // ============================================================================
211
+ // DERIVED TOKENS — TEXT CONTRAST TIERS + SURFACE TINTS
212
+ // ============================================================================
213
+ // These tokens use `color-mix(... var(--pa-text-color-1) ...)` to derive
214
+ // theme-aware contrast levels. They MUST be emitted at every selector that
215
+ // could carry an override of --pa-text-color-1, because CSS custom property
216
+ // substitution bakes nested var() references at the *defining element*, not
217
+ // the using element. If we only defined these at `:root, .pa-mode-light`,
218
+ // the light-mode text colour would be frozen into the value, and elements
219
+ // inside `.pa-mode-dark` (which overrides --pa-text-color-1 at body) would
220
+ // inherit the light-mode-baked tier values → low contrast on dark surfaces.
221
+ //
222
+ // Emitting at all three selectors causes each scope to recompute the tiers
223
+ // against its own --pa-text-color-1. The selector list `:root, .pa-mode-light,
224
+ // .pa-mode-dark` covers html, body in light mode, and body in dark mode —
225
+ // regardless of which class layout each theme uses for mode switching.
226
+ //
227
+ // This is a TOP-LEVEL rule (not inside the mixin) because themes only call
228
+ // the mixin from their light-mode block; they don't call it again in the
229
+ // dark-mode block. Emitting at framework level means themes get correct
230
+ // dark-mode tiers without changing any theme code.
231
+ //
232
+ // Override these per-theme by redefining them in the same scope you
233
+ // override --pa-text-color-1.
234
+ // ============================================================================
235
+
236
+ :root,
237
+ .pa-mode-light,
238
+ .pa-mode-dark {
239
+ --pa-text-strong: color-mix(in srgb, var(--pa-text-color-1) 85%, transparent);
240
+ --pa-text-secondary: color-mix(in srgb, var(--pa-text-color-1) 70%, transparent);
241
+ --pa-text-tertiary: color-mix(in srgb, var(--pa-text-color-1) 55%, transparent);
242
+ --pa-surface-hover: color-mix(in srgb, var(--pa-text-color-1) 4%, transparent);
243
+ --pa-surface-track: color-mix(in srgb, var(--pa-text-color-1) 12%, transparent);
244
+ }
245
+
210
246
  // ============================================================================
211
247
  // PURE ADMIN THEME CSS VARIABLES OUTPUT MIXIN
212
248
  // ============================================================================
@@ -244,6 +280,77 @@
244
280
  // Border
245
281
  --pa-border-color: #{$border-color};
246
282
 
283
+ // =========================================================================
284
+ // ROLE COLORS — single source of truth for the four semantic roles.
285
+ // Button surfaces (--pa-btn-{success,warning,danger,info}-bg) and
286
+ // contextual surfaces (--pa-{success,warning,danger,info}-bg) below
287
+ // both reference these via var() so a theme can override one place
288
+ // and the change cascades everywhere.
289
+ //
290
+ // --pa-warning here is also the "off-target / approaching-limit" axis
291
+ // colour used by KPI components — orange-500, deliberately distinct
292
+ // from the previous Bootstrap yellow.
293
+ // =========================================================================
294
+
295
+ --pa-success: #{$base-success-color};
296
+ --pa-warning: #{$base-warning-color};
297
+ --pa-danger: #{$base-danger-color};
298
+ --pa-info: #{$base-info-color};
299
+
300
+ // =========================================================================
301
+ // SENTIMENT SCALE — 5-step direction-of-change.
302
+ // Used by KPI / data-display components for indicating directional
303
+ // movement (delta colours, sparkline trend colours, etc.).
304
+ // The middle three values alias the role colours so a theme that
305
+ // retunes --pa-success / --pa-danger automatically retunes the
306
+ // matching sentiment positions; the outliers (very-positive /
307
+ // very-negative) are explicit darker shades.
308
+ //
309
+ // Sentiment is on a *different axis* from role colours — direction
310
+ // (ordinal: -2 to +2) vs. urgency (categorical: success / warning /
311
+ // danger / info). They coexist intentionally.
312
+ // =========================================================================
313
+
314
+ --pa-very-positive: #{$base-very-positive};
315
+ --pa-positive: var(--pa-success);
316
+ --pa-neutral: #{$base-neutral};
317
+ --pa-negative: var(--pa-danger);
318
+ --pa-very-negative: #{$base-very-negative};
319
+
320
+ // (Text contrast tiers and surface tints are emitted as a top-level rule
321
+ // OUTSIDE this mixin — see below — because their values use
322
+ // `var(--pa-text-color-1)` and CSS custom property substitution bakes
323
+ // nested var() at the *defining element*, not the using element. That
324
+ // means tier vars defined here at `:root, .pa-mode-light` would freeze
325
+ // the light-mode text colour into them; under `.pa-mode-dark` (which
326
+ // overrides --pa-text-color-1 at body) the tiers would still resolve
327
+ // against the light value. Emitting them at all three selectors
328
+ // simultaneously fixes the substitution scope.)
329
+
330
+ // =========================================================================
331
+ // CHART / SPARKLINE
332
+ // =========================================================================
333
+ // Single height token used by every KPI showcase that renders a sparkline
334
+ // / trendline (terminal grid, sparkline list, hero+supporting, bento).
335
+ // Centralised so the line proportions stay consistent across designs;
336
+ // override globally for taller or shorter trendlines, or per-component
337
+ // if a particular layout (e.g. a "hero" pattern) wants a different size.
338
+
339
+ --pa-chart-trendline-height: 3rem;
340
+ --pa-chart-trendline-stroke: 2.1; // SVG user-space units (viewBox-relative)
341
+
342
+ // =========================================================================
343
+ // DETAIL POPOVER — Bloomberg-dark by default, regardless of host
344
+ // theme, for the terminal/data-dashboard aesthetic. Override the
345
+ // five tokens to adopt a theme-aware popover instead.
346
+ // =========================================================================
347
+
348
+ --pa-detail-bg: rgba(15, 17, 21, 0.97);
349
+ --pa-detail-text: #ffffff;
350
+ --pa-detail-row-label: rgba(255, 255, 255, 0.75);
351
+ --pa-detail-title: rgba(255, 255, 255, 0.55);
352
+ --pa-detail-shadow: 0 1.4rem 3.6rem rgba(0, 0, 0, 0.55);
353
+
247
354
  // =========================================================================
248
355
  // LAYOUT COLORS
249
356
  // =========================================================================
@@ -283,23 +390,23 @@
283
390
  --pa-btn-secondary-text: #{$btn-secondary-text};
284
391
  --pa-btn-secondary-outline-color: #{$btn-secondary-text};
285
392
 
286
- // Success
287
- --pa-btn-success-bg: #{$btn-success-bg};
393
+ // Success — bg references role colour for runtime cascade
394
+ --pa-btn-success-bg: var(--pa-success);
288
395
  --pa-btn-success-bg-hover: #{$btn-success-bg-hover};
289
396
  --pa-btn-success-text: #{$btn-success-text};
290
397
 
291
- // Danger
292
- --pa-btn-danger-bg: #{$btn-danger-bg};
398
+ // Danger — bg references role colour for runtime cascade
399
+ --pa-btn-danger-bg: var(--pa-danger);
293
400
  --pa-btn-danger-bg-hover: #{$btn-danger-bg-hover};
294
401
  --pa-btn-danger-text: #{$btn-danger-text};
295
402
 
296
- // Warning
297
- --pa-btn-warning-bg: #{$btn-warning-bg};
403
+ // Warning — bg references role colour for runtime cascade
404
+ --pa-btn-warning-bg: var(--pa-warning);
298
405
  --pa-btn-warning-bg-hover: #{$btn-warning-bg-hover};
299
406
  --pa-btn-warning-text: #{$btn-warning-text};
300
407
 
301
- // Info
302
- --pa-btn-info-bg: #{$btn-info-bg};
408
+ // Info — bg references role colour for runtime cascade
409
+ --pa-btn-info-bg: var(--pa-info);
303
410
  --pa-btn-info-bg-hover: #{$btn-info-bg-hover};
304
411
  --pa-btn-info-text: #{$btn-info-text};
305
412
 
@@ -317,8 +424,8 @@
317
424
  // CONTEXTUAL/SEMANTIC COLORS
318
425
  // =========================================================================
319
426
 
320
- // Success
321
- --pa-success-bg: #{$success-bg};
427
+ // Success — bg references role colour for runtime cascade
428
+ --pa-success-bg: var(--pa-success);
322
429
  --pa-success-bg-hover: #{$success-bg-hover};
323
430
  --pa-success-bg-light: #{$success-bg-light};
324
431
  --pa-success-bg-subtle: #{$success-bg-subtle};
@@ -326,8 +433,8 @@
326
433
  --pa-success-text: #{$success-text};
327
434
  --pa-success-text-light: #{$success-text-light};
328
435
 
329
- // Danger
330
- --pa-danger-bg: #{$danger-bg};
436
+ // Danger — bg references role colour for runtime cascade
437
+ --pa-danger-bg: var(--pa-danger);
331
438
  --pa-danger-bg-hover: #{$danger-bg-hover};
332
439
  --pa-danger-bg-light: #{$danger-bg-light};
333
440
  --pa-danger-bg-subtle: #{$danger-bg-subtle};
@@ -335,8 +442,8 @@
335
442
  --pa-danger-text: #{$danger-text};
336
443
  --pa-danger-text-light: #{$danger-text-light};
337
444
 
338
- // Warning
339
- --pa-warning-bg: #{$warning-bg};
445
+ // Warning — bg references role colour for runtime cascade
446
+ --pa-warning-bg: var(--pa-warning);
340
447
  --pa-warning-bg-hover: #{$warning-bg-hover};
341
448
  --pa-warning-bg-light: #{$warning-bg-light};
342
449
  --pa-warning-bg-subtle: #{$warning-bg-subtle};
@@ -344,8 +451,8 @@
344
451
  --pa-warning-text: #{$warning-text};
345
452
  --pa-warning-text-light: #{$warning-text-light};
346
453
 
347
- // Info
348
- --pa-info-bg: #{$info-bg};
454
+ // Info — bg references role colour for runtime cascade
455
+ --pa-info-bg: var(--pa-info);
349
456
  --pa-info-bg-hover: #{$info-bg-hover};
350
457
  --pa-info-bg-light: #{$info-bg-light};
351
458
  --pa-info-bg-subtle: #{$info-bg-subtle};
@@ -185,7 +185,7 @@
185
185
  width: 70%;
186
186
  height: 70%;
187
187
  border-radius: 50%;
188
- background: $card-bg;
188
+ background: var(--pa-card-bg);
189
189
  display: flex;
190
190
  flex-direction: column;
191
191
  align-items: center;
@@ -288,7 +288,7 @@
288
288
  right: 15%;
289
289
  height: 70%;
290
290
  border-radius: #{$gauge-size} #{$gauge-size} 0 0;
291
- background: $card-bg;
291
+ background: var(--pa-card-bg);
292
292
  display: flex;
293
293
  flex-direction: column;
294
294
  align-items: center;
@@ -98,11 +98,17 @@
98
98
  }
99
99
  }
100
100
 
101
- // Square variant - compact square with big number and shadowed symbol
101
+ // Square variant - compact square with a big number and a smaller, inline unit symbol.
102
+ // Layout: number + symbol on one row (baseline-aligned) at the top, label pinned to the
103
+ // bottom. Markup order drives visual order — `<symbol><number>` works for prefix
104
+ // currencies ($1,234), `<number><symbol>` for suffix units (87% / 23°C).
102
105
  &--square {
106
+ container-type: inline-size; // enables cqi units in __number / __symbol clamps
103
107
  display: flex;
104
- align-items: center;
105
- justify-content: space-between;
108
+ flex-wrap: wrap;
109
+ align-content: space-between; // number/symbol row sticks to the top, label to the bottom
110
+ align-items: baseline; // baseline-align number and symbol (clean even when symbol is multi-char like "°C")
111
+ column-gap: $stat-square-symbol-gap;
106
112
  padding: $spacing-lg;
107
113
  min-height: $stat-square-min-size;
108
114
  min-width: $stat-square-min-size;
@@ -120,10 +126,8 @@
120
126
  .pa-stat__number {
121
127
  font-size: clamp($stat-square-number-min, $stat-square-number-scale, $stat-square-number-max);
122
128
  font-weight: $font-weight-bold;
123
- line-height: 1.1;
129
+ line-height: 1;
124
130
  color: inherit;
125
- z-index: $focus-outline-width;
126
- position: relative;
127
131
  text-shadow: 0 $stat-text-shadow-1-y $stat-text-shadow-1-blur rgba(0, 0, 0, $btn-focus-ring-opacity),
128
132
  0 $stat-text-shadow-2-y $stat-text-shadow-2-blur rgba(0, 0, 0, $opacity-shadow-md);
129
133
  filter: drop-shadow(0 $stat-drop-shadow-y $stat-drop-shadow-blur rgba(0, 0, 0, $opacity-light));
@@ -134,28 +138,21 @@
134
138
  .pa-stat__symbol {
135
139
  font-size: clamp($stat-square-symbol-min, $stat-square-symbol-scale, $stat-square-symbol-max);
136
140
  font-weight: $font-weight-bold;
137
- line-height: 1.1;
138
- opacity: $stat-symbol-opacity;
141
+ line-height: 1;
139
142
  color: inherit;
140
- position: absolute;
141
- right: $spacing-lg;
142
- top: 50%;
143
- transform: translateY(-50%);
144
- z-index: $z-index-base;
143
+ opacity: $stat-symbol-opacity;
144
+ word-break: keep-all;
145
+ white-space: nowrap;
145
146
  }
146
147
 
147
148
  .pa-stat__label {
148
- position: absolute;
149
- bottom: $spacing-base;
150
- left: $spacing-lg;
149
+ flex-basis: 100%; // forces label onto its own row
151
150
  font-size: $font-size-xs;
152
151
  text-transform: uppercase;
153
152
  letter-spacing: $stat-label-letter-spacing;
154
153
  font-weight: $font-weight-medium;
155
154
  color: inherit;
156
155
  opacity: 0.8;
157
- z-index: 2;
158
- max-width: calc(100% - #{$spacing-lg * 2});
159
156
  overflow: hidden;
160
157
  text-overflow: ellipsis;
161
158
  white-space: nowrap;
@@ -108,7 +108,7 @@ $base-tooltip-text-color: #ffffff !default;
108
108
  // Success/positive semantic colors
109
109
  // =============================================================================
110
110
 
111
- $base-success-color: #28a745 !default;
111
+ $base-success-color: #22c55e !default; // Tailwind green-500 — aligns with --pa-positive in the sentiment scale
112
112
  $base-success-color-hover: color.adjust($base-success-color, $lightness: -10%) !default;
113
113
  $base-success-bg-light: rgba($base-success-color, 0.1) !default;
114
114
  $base-success-bg-subtle: rgba($base-success-color, 0.08) !default;
@@ -122,7 +122,7 @@ $base-text-on-success: #ffffff !default;
122
122
  // Danger/error semantic colors
123
123
  // =============================================================================
124
124
 
125
- $base-danger-color: #dc3545 !default;
125
+ $base-danger-color: #ef4444 !default; // Tailwind red-500 — aligns with --pa-negative in the sentiment scale
126
126
  $base-danger-color-hover: color.adjust($base-danger-color, $lightness: -10%) !default;
127
127
  $base-danger-bg-light: rgba($base-danger-color, 0.1) !default;
128
128
  $base-danger-bg-subtle: rgba($base-danger-color, 0.08) !default;
@@ -136,7 +136,7 @@ $base-text-on-danger: #ffffff !default;
136
136
  // Warning semantic colors
137
137
  // =============================================================================
138
138
 
139
- $base-warning-color: #ffc107 !default;
139
+ $base-warning-color: #f97316 !default; // Tailwind orange-500 — also serves as the off-target axis in KPI components
140
140
  $base-warning-color-hover: color.adjust($base-warning-color, $lightness: -10%) !default;
141
141
  $base-warning-bg-light: rgba($base-warning-color, 0.1) !default;
142
142
  $base-warning-bg-subtle: rgba($base-warning-color, 0.08) !default;
@@ -159,6 +159,18 @@ $base-info-text: #0c5460 !default;
159
159
  $base-info-text-light: #d1ecf1 !default;
160
160
  $base-text-on-info: #ffffff !default;
161
161
 
162
+ // =============================================================================
163
+ // SENTIMENT SCALE — 5-step direction-of-change
164
+ // Used by KPI / data-display components to indicate movement direction.
165
+ // `--pa-positive` and `--pa-negative` alias the role colours above; the
166
+ // outlier shades (`very-positive` / `very-negative`) are explicit darker
167
+ // stops since they're not derivable cleanly via color.adjust.
168
+ // =============================================================================
169
+
170
+ $base-very-positive: #16a34a !default; // Tailwind green-600 — outlier positive
171
+ $base-very-negative: #dc2626 !default; // Tailwind red-600 — outlier negative
172
+ $base-neutral: #9ca3af !default; // Tailwind gray-400 — "no meaningful change"
173
+
162
174
  // =============================================================================
163
175
  // INTERACTIVE STATES
164
176
  // Generic interaction feedback colors
@@ -336,15 +336,26 @@ $stat-square-min-size: 12.8rem !default; // 128px (was 8rem)
336
336
  $stat-label-letter-spacing: 0.05em !default;
337
337
  $stat-change-margin-bottom: 0.4rem !default; // 4px (was 0.25rem)
338
338
  $stat-number-margin-bottom: $spacing-xs !default;
339
- $stat-symbol-opacity: $opacity-shadow !default;
340
-
341
- // Stat square clamp values (10px base)
342
- $stat-square-number-min: 4.8rem !default; // 48px (was 3rem)
343
- $stat-square-number-scale: 8vw !default;
344
- $stat-square-number-max: 7.2rem !default; // 72px (was 4.5rem)
345
- $stat-square-symbol-min: 6.4rem !default; // 64px (was 4rem)
346
- $stat-square-symbol-scale: 10vw !default;
347
- $stat-square-symbol-max: 9.6rem !default; // 96px (was 6rem)
339
+ $stat-symbol-opacity: 0.85 !default;
340
+ $stat-square-symbol-gap: 0.15em !default; // tight gap between number and symbol on the same row
341
+
342
+ // Stat square clamp values (10px base).
343
+ // Scale uses `cqi` (container query inline-size, requires `container-type: inline-size`
344
+ // on .pa-stat--square) so font tracks the tile width, not the viewport. Without this,
345
+ // a wide-viewport / narrow-tile combination (e.g. 6 KPIs in a 33% column on 1920px)
346
+ // hits the max font size and overflows multi-character values like "$847K" or "¥12.4M".
347
+ //
348
+ // Sizing budget: a 5-char number ("12.4M") at $stat-square-number-scale plus a 1-char
349
+ // symbol at $stat-square-symbol-scale plus the inter-element gap should fit inside the
350
+ // tile's content area (tile width minus 2× $spacing-lg padding) at typical tile widths
351
+ // (~250–400px). Numbers are sized aggressively so the KPI reads as the dominant element.
352
+ $stat-square-number-min: 3.2rem !default; // 32px floor
353
+ $stat-square-number-scale: 25cqi !default; // 25% of tile width
354
+ $stat-square-number-max: 9.6rem !default; // 96px ceiling (was 7.2/6.4 — bumped so wide tiles render large)
355
+ // Symbol is intentionally smaller than the number (visual hierarchy: number primary, unit secondary).
356
+ $stat-square-symbol-min: 1.6rem !default; // 16px floor (~50% of number-min)
357
+ $stat-square-symbol-scale: 12cqi !default; // 12% of tile width (~48% of number)
358
+ $stat-square-symbol-max: 4.8rem !default; // 48px ceiling (~50% of number-max)
348
359
 
349
360
  // Stat shadow values
350
361
  $stat-text-shadow-1-y: 4px !default;
@@ -704,7 +715,7 @@ $progress-height: 0.8rem !default; // 8px
704
715
  $progress-height-xs: 0.3rem !default; // 3px
705
716
  $progress-height-sm: 0.5rem !default; // 5px
706
717
  $progress-height-lg: 1.2rem !default; // 12px
707
- $progress-bg: rgba(0, 0, 0, 0.08) !default;
718
+ $progress-bg: color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) !default;
708
719
  $progress-border-radius: $border-radius !default;
709
720
  $progress-stripe-angle: 45deg !default;
710
721
  $progress-stripe-size: 1.6rem !default; // 16px
@@ -723,14 +734,14 @@ $stacked-bar-legend-font-size: $font-size-xs !default;
723
734
  $progress-ring-size: 8rem !default; // 80px
724
735
  $progress-ring-size-sm: 5rem !default; // 50px
725
736
  $progress-ring-size-lg: 11rem !default; // 110px
726
- $progress-ring-track-color: rgba(0, 0, 0, 0.08) !default;
737
+ $progress-ring-track-color: color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) !default;
727
738
  $progress-ring-value-font-size: $font-size-lg !default;
728
739
  $progress-ring-value-font-weight: $font-weight-bold !default;
729
740
  $progress-ring-label-font-size: $font-size-2xs !default;
730
741
 
731
742
  // Gauge (pa-gauge)
732
743
  $gauge-size: 12rem !default; // 120px
733
- $gauge-track-color: rgba(0, 0, 0, 0.08) !default;
744
+ $gauge-track-color: color-mix(in srgb, var(--pa-text-color-1) 12%, transparent) !default;
734
745
  $gauge-label-font-size: $font-size-xs !default;
735
746
  $gauge-value-font-size: $font-size-2xl !default;
736
747
  $gauge-value-font-weight: $font-weight-bold !default;