@keenmate/pure-admin-core 2.6.0 → 2.7.1

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.
Files changed (40) hide show
  1. package/README.md +24 -16
  2. package/dist/css/main.css +1865 -257
  3. package/package.json +1 -1
  4. package/src/scss/_base-css-variables.scss +37 -19
  5. package/src/scss/_core.scss +11 -0
  6. package/src/scss/core-components/_alerts.scss +2 -2
  7. package/src/scss/core-components/_base.scss +19 -2
  8. package/src/scss/core-components/_buttons.scss +12 -8
  9. package/src/scss/core-components/_callouts.scss +1 -1
  10. package/src/scss/core-components/_cards.scss +4 -4
  11. package/src/scss/core-components/_checkbox-lists.scss +2 -2
  12. package/src/scss/core-components/_comparison.scss +7 -4
  13. package/src/scss/core-components/_data-display.scss +24 -15
  14. package/src/scss/core-components/_data-viz.scss +139 -131
  15. package/src/scss/core-components/_file-selector.scss +34 -34
  16. package/src/scss/core-components/_kpi-base.scss +169 -0
  17. package/src/scss/core-components/_kpi-bento.scss +182 -0
  18. package/src/scss/core-components/_kpi-comparison-gauges.scss +132 -0
  19. package/src/scss/core-components/_kpi-editorial-minimal.scss +128 -0
  20. package/src/scss/core-components/_kpi-hero-supporting.scss +210 -0
  21. package/src/scss/core-components/_kpi-numeric-strip.scss +154 -0
  22. package/src/scss/core-components/_kpi-sparkline-list.scss +171 -0
  23. package/src/scss/core-components/_kpi-terminal.scss +229 -0
  24. package/src/scss/core-components/_lists.scss +4 -4
  25. package/src/scss/core-components/_logic-tree.scss +2 -2
  26. package/src/scss/core-components/_modals.scss +69 -0
  27. package/src/scss/core-components/_notifications.scss +17 -17
  28. package/src/scss/core-components/_popconfirm.scss +1 -1
  29. package/src/scss/core-components/_statistics.scss +25 -19
  30. package/src/scss/core-components/_tabs.scss +12 -12
  31. package/src/scss/core-components/_timeline.scss +30 -30
  32. package/src/scss/core-components/badges/_composite-badge-variants.scss +7 -7
  33. package/src/scss/core-components/badges/_composite-badge.scss +1 -1
  34. package/src/scss/core-components/badges/_labels.scss +6 -6
  35. package/src/scss/core-components/forms/_input-wrapper.scss +1 -1
  36. package/src/scss/core-components/forms/_query-editor.scss +10 -10
  37. package/src/scss/core-components/layout/_sidebar-states.scss +1 -0
  38. package/src/scss/core-components/layout/_sidebar.scss +1 -0
  39. package/src/scss/variables/_colors.scss +1 -0
  40. package/src/scss/variables/_components.scss +3 -2
@@ -0,0 +1,171 @@
1
+ /* ========================================
2
+ KPI · Sparkline list
3
+ Each KPI is one row: label · sparkline · value · Δ%. Built for vertical
4
+ scanning rather than per-tile depth — no view-mode toggle, no status
5
+ pills. Container queries collapse 4-col → 2-row → 3-row as the card
6
+ narrows.
7
+ ======================================== */
8
+ @use '../variables' as *;
9
+
10
+ /* Card is a query container so rows react to *card* width, not viewport. */
11
+ .pa-kpi-spark-list {
12
+ container-type: inline-size;
13
+ }
14
+ .pa-kpi-spark-list__body { padding: 0; }
15
+
16
+ /* ----- Row: wide 4-column grid (label · chart · value · delta) ---------- */
17
+ .pa-kpi-spark-row {
18
+ display: grid;
19
+ grid-template-columns:
20
+ minmax(14rem, 28%)
21
+ minmax(10rem, 1fr)
22
+ minmax(8rem, 18%)
23
+ minmax(7rem, 12%);
24
+ align-items: center;
25
+ gap: 1.6rem;
26
+ padding: 1.4rem 2rem;
27
+ border-bottom: 1px solid var(--pa-border-color);
28
+
29
+ &:last-child { border-bottom: 0; }
30
+ }
31
+
32
+ /* Mid-narrow card (1×3 page-grid + 45% column): stack to 2 rows.
33
+ Label/value/delta on top, full-width chart below. Default order is
34
+ value-above-chart; use .pa-kpi-spark-list--chart-first to flip. */
35
+ @container (max-width: 640px) {
36
+ .pa-kpi-spark-row {
37
+ grid-template-columns: minmax(0, 1fr) auto auto;
38
+ grid-template-rows: auto auto;
39
+ grid-template-areas:
40
+ "label value delta"
41
+ "chart chart chart";
42
+ column-gap: 1.2rem;
43
+ row-gap: 0.4rem;
44
+ align-items: baseline;
45
+ padding: 1.2rem 1.6rem;
46
+ }
47
+ .pa-kpi-spark-row__label { grid-area: label; align-self: center; font-size: 1.25rem; }
48
+ .pa-kpi-spark-row__value { grid-area: value; }
49
+ .pa-kpi-spark-row__delta { grid-area: delta; font-size: 1.25rem; }
50
+ .pa-kpi-spark-row__chart { grid-area: chart; }
51
+ .pa-kpi-spark-row__num { font-size: 2rem; }
52
+
53
+ /* --chart-first: rotates the canonical L→R order 90°. Label on top,
54
+ chart in the middle, value+delta side-by-side at the bottom. */
55
+ .pa-kpi-spark-list--chart-first .pa-kpi-spark-row {
56
+ grid-template-columns: 1fr auto;
57
+ grid-template-rows: auto auto auto;
58
+ grid-template-areas:
59
+ "label label"
60
+ "chart chart"
61
+ "value delta";
62
+ row-gap: 0.5rem;
63
+ column-gap: 1rem;
64
+ padding: 1.2rem 1.6rem;
65
+ }
66
+ .pa-kpi-spark-list--chart-first .pa-kpi-spark-row__label { align-self: start; }
67
+ .pa-kpi-spark-list--chart-first .pa-kpi-spark-row__value { text-align: start; align-self: baseline; }
68
+ .pa-kpi-spark-list--chart-first .pa-kpi-spark-row__delta { align-self: baseline; }
69
+ }
70
+
71
+ /* Very narrow card (~280–360px, 25% page-grid stress test): force the
72
+ chart-first 3-row layout regardless of modifier. */
73
+ @container (max-width: 360px) {
74
+ .pa-kpi-spark-row {
75
+ grid-template-columns: 1fr auto;
76
+ grid-template-rows: auto auto auto;
77
+ grid-template-areas:
78
+ "label label"
79
+ "chart chart"
80
+ "value delta";
81
+ row-gap: 0.5rem;
82
+ column-gap: 1rem;
83
+ padding: 1.2rem 1.4rem;
84
+ }
85
+ .pa-kpi-spark-row__label { grid-area: label; align-self: start; }
86
+ .pa-kpi-spark-row__chart { grid-area: chart; }
87
+ .pa-kpi-spark-row__value { grid-area: value; text-align: start; align-self: baseline; }
88
+ .pa-kpi-spark-row__delta { grid-area: delta; align-self: baseline; }
89
+ }
90
+
91
+ /* ----- Cell typography -------------------------------------------------- */
92
+ .pa-kpi-spark-row__label {
93
+ font-family: var(--base-font-family-mono);
94
+ font-size: 1.4rem;
95
+ font-weight: 700;
96
+ letter-spacing: 0.1em;
97
+ text-transform: uppercase;
98
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
99
+ }
100
+
101
+ /* Sparkline cell — line + filled area + trailing dot (the dot is rendered
102
+ as a CSS span by the JS init pass; see kpi-showcases.js). */
103
+ .pa-kpi-spark-row__chart {
104
+ position: relative; /* anchor for .pa-kpi-spark-dot */
105
+
106
+ svg {
107
+ display: block;
108
+ width: 100%;
109
+ height: var(--pa-chart-trendline-height);
110
+ overflow: visible;
111
+ }
112
+
113
+ polyline {
114
+ fill: none;
115
+ stroke: currentColor;
116
+ stroke-width: var(--pa-chart-trendline-stroke);
117
+ stroke-linecap: round;
118
+ stroke-linejoin: round;
119
+ }
120
+
121
+ polygon {
122
+ fill: currentColor;
123
+ fill-opacity: 0.18; /* same hue as line, soft area shading */
124
+ stroke: none;
125
+ }
126
+ }
127
+
128
+ /* Sentiment-coloured sparkline. Color is set on the chart wrapper so
129
+ currentColor resolves for both the SVG content (line/area) and the
130
+ dot inside the wrapper. */
131
+ .pa-kpi-spark-row {
132
+ &--up-strong .pa-kpi-spark-row__chart { color: var(--pa-very-positive); }
133
+ &--up .pa-kpi-spark-row__chart { color: var(--pa-positive); }
134
+ &--flat .pa-kpi-spark-row__chart { color: var(--pa-neutral); }
135
+ &--down .pa-kpi-spark-row__chart { color: var(--pa-negative); }
136
+ &--down-strong .pa-kpi-spark-row__chart { color: var(--pa-very-negative); }
137
+ }
138
+
139
+ /* ----- Value (focal white number + muted unit) ------------------------- */
140
+ .pa-kpi-spark-row__value {
141
+ font-family: var(--base-font-family-mono);
142
+ text-align: end;
143
+ line-height: 1;
144
+ }
145
+ .pa-kpi-spark-row__num {
146
+ font-size: 2.6rem;
147
+ font-weight: 700;
148
+ letter-spacing: -0.02em;
149
+ color: var(--pa-text-color-1);
150
+ }
151
+ .pa-kpi-spark-row__unit {
152
+ font-size: 1.3rem;
153
+ font-weight: 500;
154
+ color: var(--pa-text-secondary);
155
+ margin-left: 0.2rem;
156
+ }
157
+
158
+ /* ----- Delta (sentiment-coloured) -------------------------------------- */
159
+ .pa-kpi-spark-row__delta {
160
+ font-family: var(--base-font-family-mono);
161
+ font-size: 1.4rem;
162
+ font-weight: 600;
163
+ text-align: end;
164
+ font-variant-numeric: tabular-nums;
165
+
166
+ &--very-positive { color: var(--pa-very-positive); }
167
+ &--positive { color: var(--pa-positive); }
168
+ &--neutral { color: var(--pa-neutral); }
169
+ &--negative { color: var(--pa-negative); }
170
+ &--very-negative { color: var(--pa-very-negative); }
171
+ }
@@ -0,0 +1,229 @@
1
+ /* ========================================
2
+ KPI · Terminal grid
3
+ Bloomberg-style dense panel: mono numbers, status pills, inline SVG
4
+ sparklines, ▲▼ deltas, segmented view-mode toggle (VALUE/Δ%/TREND).
5
+ Shared chrome (header, live, footer, detail popover, spark-dot) is in
6
+ _kpi-base.scss.
7
+ ======================================== */
8
+ @use '../variables' as *;
9
+
10
+ /* ----- View-mode toggle (segmented button group) ------------------------ */
11
+ .pa-kpi-terminal__controls {
12
+ display: inline-flex;
13
+ align-items: center;
14
+ gap: 1.6rem;
15
+ }
16
+ .pa-kpi-terminal__viewtoggle {
17
+ display: inline-flex;
18
+ border: 1px solid var(--pa-border-color);
19
+ border-radius: 0.4rem;
20
+ overflow: hidden;
21
+ }
22
+ .pa-kpi-terminal__viewbtn {
23
+ background: transparent;
24
+ border: 0;
25
+ color: var(--pa-text-color-2);
26
+ padding: 0.4rem 1.1rem;
27
+ font-family: var(--base-font-family-mono);
28
+ font-size: 1.1rem;
29
+ font-weight: 600;
30
+ letter-spacing: 0.06em;
31
+ cursor: pointer;
32
+ transition: background-color 0.1s ease-out, color 0.1s ease-out;
33
+
34
+ &:hover {
35
+ color: var(--pa-text-color-1);
36
+ }
37
+
38
+ &.is-active {
39
+ background: var(--pa-text-color-1);
40
+ color: var(--pa-card-bg);
41
+ }
42
+ }
43
+
44
+ /* ----- Grid + tile borders ----------------------------------------------
45
+ Hairline 1px borders between tiles, no gap. Last-row/last-column
46
+ borders suppressed via :nth-last-child / :nth-child selectors. */
47
+ .pa-kpi-terminal__body {
48
+ padding: 0;
49
+ }
50
+ .pa-kpi-terminal__grid {
51
+ display: grid;
52
+ grid-template-columns: repeat(2, 1fr);
53
+
54
+ .pa-kpi-tile {
55
+ border-bottom: 1px solid var(--pa-border-color);
56
+ border-right: 1px solid var(--pa-border-color);
57
+ }
58
+ }
59
+ .pa-kpi-terminal__grid--2col {
60
+ .pa-kpi-tile:nth-child(2n) { border-right: 0; }
61
+ .pa-kpi-tile:nth-last-child(-n+2) { border-bottom: 0; }
62
+ }
63
+
64
+ /* ----- Tile (per-KPI panel) --------------------------------------------- */
65
+ .pa-kpi-tile {
66
+ position: relative;
67
+ padding: 1.4rem 1.8rem 1.6rem;
68
+ min-height: 16rem;
69
+ display: flex;
70
+ flex-direction: column;
71
+
72
+ /* Standalone modifier: tile lives directly inside a .pa-col-* (no
73
+ neighbour cells in a parent grid) — gets a full border + card bg so
74
+ it doesn't look orphaned. */
75
+ &--standalone {
76
+ background: var(--pa-card-bg);
77
+ border: 1px solid var(--pa-border-color);
78
+ margin-bottom: 1.2rem;
79
+
80
+ &:last-child { margin-bottom: 0; }
81
+ }
82
+ }
83
+
84
+ /* ----- Tile head: ID · period + status pill ----------------------------- */
85
+ .pa-kpi-tile__head {
86
+ display: flex;
87
+ justify-content: space-between;
88
+ align-items: center;
89
+ font-family: var(--base-font-family-mono);
90
+ font-size: 1.3rem;
91
+ letter-spacing: 0.04em;
92
+ margin-bottom: 0.3rem;
93
+ }
94
+ .pa-kpi-tile__id {
95
+ color: var(--pa-text-tertiary);
96
+ font-weight: 600;
97
+ }
98
+ .pa-kpi-tile__status {
99
+ font-family: var(--base-font-family-mono);
100
+ font-size: 1.2rem;
101
+ font-weight: 700;
102
+ letter-spacing: 0.08em;
103
+ padding: 0.3rem 0.9rem;
104
+ line-height: 1.3;
105
+
106
+ &--warn {
107
+ background: var(--pa-warning-bg);
108
+ color: var(--pa-btn-warning-text);
109
+ }
110
+
111
+ /* GOOD is the "default" state — text-only, no chrome */
112
+ &--good {
113
+ background: transparent;
114
+ color: var(--pa-text-color-1);
115
+ padding: 0.2rem 0;
116
+ }
117
+
118
+ &--neutral {
119
+ background: color-mix(in srgb, var(--pa-text-color-2) 25%, transparent);
120
+ color: var(--pa-text-color-1);
121
+ }
122
+ }
123
+
124
+ /* ----- Label ------------------------------------------------------------ */
125
+ .pa-kpi-tile__label {
126
+ font-family: var(--base-font-family-mono);
127
+ font-size: 1.4rem;
128
+ font-weight: 700;
129
+ letter-spacing: 0.1em;
130
+ text-transform: uppercase;
131
+ color: var(--pa-text-strong);
132
+ margin-bottom: 0.8rem;
133
+ }
134
+
135
+ /* ----- Big value with 3-mode swap --------------------------------------
136
+ Author renders three .pa-kpi-tile__value siblings (data-mode="value" /
137
+ "delta" / "trend"); the active mode is selected via the
138
+ .pa-kpi-terminal[data-view="X"] attribute. JS toggles that attribute
139
+ when the segmented control is clicked. */
140
+ .pa-kpi-tile__values {
141
+ margin-bottom: 0.4rem;
142
+ }
143
+ .pa-kpi-tile__value {
144
+ display: none;
145
+ align-items: baseline;
146
+ gap: 0.3rem;
147
+ font-family: var(--base-font-family-mono);
148
+ line-height: 1;
149
+
150
+ &--very-positive .pa-kpi-tile__num { color: var(--pa-very-positive); }
151
+ &--positive .pa-kpi-tile__num { color: var(--pa-positive); }
152
+ &--neutral .pa-kpi-tile__num { color: var(--pa-neutral); }
153
+ &--negative .pa-kpi-tile__num { color: var(--pa-negative); }
154
+ &--very-negative .pa-kpi-tile__num { color: var(--pa-very-negative); }
155
+ }
156
+ .pa-kpi-terminal[data-view="value"] .pa-kpi-tile__value[data-mode="value"],
157
+ .pa-kpi-terminal[data-view="delta"] .pa-kpi-tile__value[data-mode="delta"],
158
+ .pa-kpi-terminal[data-view="trend"] .pa-kpi-tile__value[data-mode="trend"] {
159
+ display: inline-flex;
160
+ }
161
+
162
+ .pa-kpi-tile__num {
163
+ font-size: 3.8rem;
164
+ font-weight: 700;
165
+ letter-spacing: -0.02em;
166
+ color: var(--pa-text-color-1);
167
+ }
168
+ .pa-kpi-tile__unit {
169
+ font-size: 1.6rem;
170
+ font-weight: 500;
171
+ color: var(--pa-text-secondary);
172
+ }
173
+
174
+ /* ----- Previous-value + delta row -------------------------------------- */
175
+ .pa-kpi-tile__prev {
176
+ display: flex;
177
+ justify-content: space-between;
178
+ align-items: center;
179
+ font-family: var(--base-font-family-mono);
180
+ font-size: 1.3rem;
181
+ margin-top: auto; /* push to bottom of flex column */
182
+ margin-bottom: 0.4rem;
183
+ color: var(--pa-text-tertiary);
184
+ }
185
+ .pa-kpi-tile__delta {
186
+ &--very-positive { color: var(--pa-very-positive); }
187
+ &--positive { color: var(--pa-positive); }
188
+ &--neutral { color: var(--pa-neutral); }
189
+ &--negative { color: var(--pa-negative); }
190
+ &--very-negative { color: var(--pa-very-negative); }
191
+ }
192
+
193
+ /* ----- Sparkline -------------------------------------------------------- */
194
+ .pa-kpi-tile__spark {
195
+ display: block;
196
+ width: 100%;
197
+ height: var(--pa-chart-trendline-height);
198
+ overflow: visible;
199
+
200
+ polyline {
201
+ fill: none;
202
+ stroke: currentColor;
203
+ stroke-width: var(--pa-chart-trendline-stroke);
204
+ stroke-linecap: round;
205
+ stroke-linejoin: round;
206
+ }
207
+ }
208
+ /* Sentiment-coloured sparkline. Class names use up/down wording but pick
209
+ colours from the sentiment-of-the-change axis, not line shape — so an
210
+ error-rate metric whose line is falling but the change is good uses
211
+ --up. See showcase docs for the rationale. Colour is set on both the
212
+ SVG and the JS-inserted .pa-kpi-spark-wrap so currentColor resolves
213
+ for the SVG content (line) and the HTML dot (inside the wrap). */
214
+ .pa-kpi-tile {
215
+ &--up-strong .pa-kpi-tile__spark,
216
+ &--up-strong .pa-kpi-spark-wrap { color: var(--pa-very-positive); }
217
+
218
+ &--up .pa-kpi-tile__spark,
219
+ &--up .pa-kpi-spark-wrap { color: var(--pa-positive); }
220
+
221
+ &--flat .pa-kpi-tile__spark,
222
+ &--flat .pa-kpi-spark-wrap { color: var(--pa-neutral); }
223
+
224
+ &--down .pa-kpi-tile__spark,
225
+ &--down .pa-kpi-spark-wrap { color: var(--pa-negative); }
226
+
227
+ &--down-strong .pa-kpi-tile__spark,
228
+ &--down-strong .pa-kpi-spark-wrap { color: var(--pa-very-negative); }
229
+ }
@@ -73,7 +73,7 @@
73
73
 
74
74
  &::before {
75
75
  content: '✓';
76
- color: $success-bg;
76
+ color: var(--pa-success);
77
77
  font-weight: $font-weight-semibold;
78
78
  flex-shrink: 0;
79
79
  }
@@ -81,17 +81,17 @@
81
81
 
82
82
  &.pa-list-basic--danger li::before {
83
83
  content: '✗';
84
- color: $danger-bg;
84
+ color: var(--pa-danger);
85
85
  }
86
86
 
87
87
  &.pa-list-basic--info li::before {
88
88
  content: '→';
89
- color: $info-bg;
89
+ color: var(--pa-info);
90
90
  }
91
91
 
92
92
  &.pa-list-basic--warning li::before {
93
93
  content: '!';
94
- color: $warning-bg;
94
+ color: var(--pa-warning);
95
95
  }
96
96
  }
97
97
  }
@@ -66,12 +66,12 @@
66
66
  }
67
67
 
68
68
  &--and {
69
- border-color: $warning-bg;
69
+ border-color: var(--pa-warning);
70
70
  background: linear-gradient(135deg, #fff8e1 0%, #ffffff 100%);
71
71
  }
72
72
 
73
73
  &--or {
74
- border-color: $info-bg;
74
+ border-color: var(--pa-info);
75
75
  background: linear-gradient(135deg, #e3f2fd 0%, #ffffff 100%);
76
76
  }
77
77
 
@@ -106,6 +106,7 @@
106
106
  .pa-modal__footer {
107
107
  padding: $modal-footer-padding;
108
108
  border-top: $border-width-base solid var(--pa-border-color);
109
+ border-radius: 0 0 var(--pa-border-radius) var(--pa-border-radius); // Match container's bottom corners
109
110
  display: flex;
110
111
  justify-content: flex-end;
111
112
  gap: $spacing-md;
@@ -153,6 +154,74 @@
153
154
  }
154
155
  }
155
156
 
157
+ // Banded variant — header AND footer wear the role colour as filled bands,
158
+ // body and buttons stay neutral. Bands consume the alert tokens (15% mix
159
+ // in light mode, 45% in dark) so banded modals stay in lock-step with the
160
+ // alert palette across both themes — single source of truth.
161
+ //
162
+ // Compose with a role modifier to colour the bands:
163
+ // <div class="pa-modal pa-modal--success pa-modal--banded">
164
+ //
165
+ // Defined AFTER the role-only rules above so the compound selectors win on
166
+ // source order (specificity is identical, last-rule wins).
167
+ .pa-modal--banded {
168
+ .pa-modal__header {
169
+ background-color: var(--pa-modal-band-bg);
170
+ color: var(--pa-modal-band-text);
171
+ border-bottom-color: var(--pa-modal-band-border);
172
+
173
+ .pa-modal__title {
174
+ color: var(--pa-modal-band-text);
175
+ }
176
+ }
177
+
178
+ .pa-modal__footer {
179
+ background-color: var(--pa-modal-band-bg);
180
+ color: var(--pa-modal-band-text);
181
+ border-top-color: var(--pa-modal-band-border);
182
+ }
183
+
184
+ // Buttons inside the bands invert the modal's colour scheme so they
185
+ // pop against any role's band on either light or dark themes — using
186
+ // generic --light/--dark modifiers landed too close in luminance to
187
+ // the muted band on most themes.
188
+ .pa-modal__header .pa-btn,
189
+ .pa-modal__footer .pa-btn {
190
+ background-color: var(--pa-text-color-1);
191
+ color: var(--pa-modal-content-bg);
192
+ border-color: var(--pa-text-color-1);
193
+
194
+ &:hover {
195
+ background-color: color-mix(in srgb, var(--pa-text-color-1) 85%, transparent);
196
+ border-color: color-mix(in srgb, var(--pa-text-color-1) 85%, transparent);
197
+ }
198
+ }
199
+ }
200
+
201
+ .pa-modal--banded.pa-modal--success {
202
+ --pa-modal-band-bg: var(--pa-alert-success-bg);
203
+ --pa-modal-band-text: var(--pa-alert-success-text);
204
+ --pa-modal-band-border: var(--pa-alert-success-border);
205
+ }
206
+
207
+ .pa-modal--banded.pa-modal--warning {
208
+ --pa-modal-band-bg: var(--pa-alert-warning-bg);
209
+ --pa-modal-band-text: var(--pa-alert-warning-text);
210
+ --pa-modal-band-border: var(--pa-alert-warning-border);
211
+ }
212
+
213
+ .pa-modal--banded.pa-modal--danger {
214
+ --pa-modal-band-bg: var(--pa-alert-danger-bg);
215
+ --pa-modal-band-text: var(--pa-alert-danger-text);
216
+ --pa-modal-band-border: var(--pa-alert-danger-border);
217
+ }
218
+
219
+ .pa-modal--banded.pa-modal--info {
220
+ --pa-modal-band-bg: var(--pa-alert-info-bg);
221
+ --pa-modal-band-text: var(--pa-alert-info-text);
222
+ --pa-modal-band-border: var(--pa-alert-info-border);
223
+ }
224
+
156
225
  // Modal scrollable content
157
226
  .pa-modal__body--scrollable {
158
227
  max-height: $modal-body-scrollable-max-height;
@@ -106,14 +106,14 @@
106
106
  background: none;
107
107
  border: none;
108
108
  padding: 0;
109
- color: $accent-color;
109
+ color: var(--pa-accent);
110
110
  font-size: $font-size-sm;
111
111
  font-weight: $font-weight-medium;
112
112
  cursor: pointer;
113
113
  transition: color $transition-fast $easing-snappy;
114
114
 
115
115
  &:hover {
116
- color: $accent-hover;
116
+ color: var(--pa-accent-hover);
117
117
  }
118
118
  }
119
119
 
@@ -145,10 +145,10 @@
145
145
 
146
146
  // Unread notification (darker background)
147
147
  &--unread {
148
- background-color: rgba($accent-color, 0.05);
148
+ background-color: color-mix(in srgb, var(--pa-accent) 5%, transparent);
149
149
 
150
150
  &:hover {
151
- background-color: rgba($accent-color, 0.1);
151
+ background-color: color-mix(in srgb, var(--pa-accent) 10%, transparent);
152
152
  }
153
153
  }
154
154
  }
@@ -161,33 +161,33 @@
161
161
  display: flex;
162
162
  align-items: center;
163
163
  justify-content: center;
164
- border-radius: 50%;
165
164
  font-size: $font-size-base;
166
- background-color: var(--pa-accent-light);
167
165
  color: var(--pa-accent);
166
+ border: 1px solid transparent;
167
+ border-radius: var(--pa-border-radius);
168
168
 
169
169
  &--primary {
170
- background-color: rgba($btn-primary-bg, 0.1);
171
- color: $btn-primary-bg;
170
+ border-color: var(--pa-btn-primary-bg);
171
+ color: var(--pa-btn-primary-bg);
172
172
  }
173
173
 
174
174
  &--success {
175
- background-color: rgba($btn-success-bg, 0.1);
176
- color: $btn-success-bg;
175
+ border-color: var(--pa-success);
176
+ color: var(--pa-success);
177
177
  }
178
178
 
179
179
  &--warning {
180
- background-color: rgba($btn-warning-bg, 0.1);
181
- color: $btn-warning-bg;
180
+ border-color: var(--pa-warning);
181
+ color: var(--pa-warning);
182
182
  }
183
183
 
184
184
  &--danger {
185
- background-color: rgba($btn-danger-bg, 0.1);
186
- color: $btn-danger-bg;
185
+ border-color: var(--pa-danger);
186
+ color: var(--pa-danger);
187
187
  }
188
188
 
189
189
  &--secondary {
190
- background-color: rgba(var(--pa-text-color-2), 0.1);
190
+ border-color: var(--pa-text-color-2);
191
191
  color: var(--pa-text-color-2);
192
192
  }
193
193
  }
@@ -231,14 +231,14 @@
231
231
  text-align: center;
232
232
 
233
233
  a {
234
- color: $accent-color;
234
+ color: var(--pa-accent);
235
235
  font-size: $font-size-sm;
236
236
  font-weight: $font-weight-medium;
237
237
  text-decoration: none;
238
238
  transition: color $transition-fast $easing-snappy;
239
239
 
240
240
  &:hover {
241
- color: $accent-hover;
241
+ color: var(--pa-accent-hover);
242
242
  }
243
243
  }
244
244
  }
@@ -89,7 +89,7 @@
89
89
  display: flex;
90
90
  gap: $spacing-sm;
91
91
  justify-content: flex-end;
92
- background-color: rgba($border-color, 0.3);
92
+ background-color: color-mix(in srgb, var(--pa-border-color) 30%, transparent);
93
93
 
94
94
  .pa-btn {
95
95
  font-size: $font-size-xs;
@@ -19,24 +19,32 @@
19
19
  justify-content: center;
20
20
  font-size: $font-size-xl;
21
21
 
22
+ // Colour variants resolve via CSS custom properties so theme overrides
23
+ // (--pa-success-bg, --pa-warning-bg, etc.) cascade at runtime instead of
24
+ // baking at SCSS compile time.
22
25
  &--primary {
23
- background-color: rgba($accent-color, $opacity-light);
24
- color: $accent-color;
26
+ background-color: var(--pa-accent-light);
27
+ color: var(--pa-accent);
25
28
  }
26
29
 
27
30
  &--success {
28
- background-color: $success-bg-light;
29
- color: $success-bg;
31
+ background-color: var(--pa-success-bg-light);
32
+ color: var(--pa-success-bg);
30
33
  }
31
34
 
32
35
  &--warning {
33
- background-color: $warning-bg-light;
34
- color: $warning-bg;
36
+ background-color: var(--pa-warning-bg-light);
37
+ color: var(--pa-warning-bg);
38
+ }
39
+
40
+ &--danger {
41
+ background-color: var(--pa-danger-bg-light);
42
+ color: var(--pa-danger-bg);
35
43
  }
36
44
 
37
45
  &--info {
38
- background-color: $info-bg-light;
39
- color: $info-bg;
46
+ background-color: var(--pa-info-bg-light);
47
+ color: var(--pa-info-bg);
40
48
  }
41
49
  }
42
50
 
@@ -80,21 +88,19 @@
80
88
  margin-bottom: $spacing-xs;
81
89
  }
82
90
 
91
+ // Delta indicator — uses the 5-step sentiment scale (direction of change),
92
+ // distinct from role colours. --positive / --negative alias the success /
93
+ // danger roles so themes that retune those automatically retune deltas;
94
+ // --very-* are explicit darker stops.
83
95
  .pa-stat__change {
84
96
  font-size: $font-size-xs;
85
97
  font-weight: $font-weight-semibold;
86
98
 
87
- &--positive {
88
- color: $success-bg;
89
- }
90
-
91
- &--negative {
92
- color: $danger-bg;
93
- }
94
-
95
- &--neutral {
96
- color: var(--pa-text-color-2);
97
- }
99
+ &--very-positive { color: var(--pa-very-positive); }
100
+ &--positive { color: var(--pa-positive); }
101
+ &--neutral { color: var(--pa-neutral); }
102
+ &--negative { color: var(--pa-negative); }
103
+ &--very-negative { color: var(--pa-very-negative); }
98
104
  }
99
105
  }
100
106