@keenmate/pure-admin-core 2.7.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.
@@ -0,0 +1,210 @@
1
+ /* ========================================
2
+ KPI · Hero + supporting
3
+ Marketing/exec dashboard pattern: one headline metric on the left (huge
4
+ container-query-relative value, inline meta row, big filled-area
5
+ sparkline), and a vertical rail of compact supporting tiles on the
6
+ right. Container query collapses to single column on narrow cards.
7
+ ======================================== */
8
+ @use '../variables' as *;
9
+
10
+ .pa-kpi-hero-list {
11
+ container-type: inline-size;
12
+ }
13
+ .pa-kpi-hero-list__body { padding: 1.6rem; }
14
+
15
+ /* ----- Layout: hero left, rail right ------------------------------------ */
16
+ .pa-kpi-hero-list__layout {
17
+ display: grid;
18
+ grid-template-columns: 1fr 1fr;
19
+ gap: 1.4rem;
20
+ }
21
+ @container (max-width: 700px) {
22
+ .pa-kpi-hero-list__layout {
23
+ grid-template-columns: 1fr;
24
+ }
25
+ }
26
+ .pa-kpi-hero-list__rail {
27
+ display: flex;
28
+ flex-direction: column;
29
+ gap: 1rem;
30
+ }
31
+
32
+ /* ----- HERO panel (label · big value · meta row · sparkline) ------------ */
33
+ .pa-kpi-hero-main {
34
+ position: relative;
35
+ padding: 1.8rem 2rem;
36
+ border: 1px solid var(--pa-border-color);
37
+ display: flex;
38
+ flex-direction: column;
39
+ gap: 0.6rem;
40
+
41
+ /* Sentiment cascade — chart and delta inherit via currentColor. */
42
+ --pa-kpi-accent: var(--pa-positive);
43
+
44
+ &--positive { --pa-kpi-accent: var(--pa-positive); }
45
+ &--negative { --pa-kpi-accent: var(--pa-negative); }
46
+ &--neutral { --pa-kpi-accent: var(--pa-neutral); }
47
+ &--up-strong { --pa-kpi-accent: var(--pa-very-positive); }
48
+ }
49
+
50
+ .pa-kpi-hero-main__label {
51
+ font-family: var(--base-font-family-mono);
52
+ font-size: 1.4rem;
53
+ font-weight: 700;
54
+ letter-spacing: 0.1em;
55
+ text-transform: uppercase;
56
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
57
+ }
58
+
59
+ .pa-kpi-hero-main__value {
60
+ display: inline-flex;
61
+ align-items: baseline;
62
+ font-family: var(--base-font-family-mono);
63
+ line-height: 1;
64
+ margin: 0.4rem 0 0.6rem;
65
+ }
66
+ .pa-kpi-hero-main__num {
67
+ /* Container-query-relative: scales with the hero panel's width.
68
+ Floor keeps it readable in narrow page-grid cards. */
69
+ font-size: clamp(4rem, 17cqi, 7rem);
70
+ font-weight: 700;
71
+ letter-spacing: -0.02em;
72
+ color: var(--pa-text-color-1);
73
+ }
74
+ .pa-kpi-hero-main__unit {
75
+ font-size: clamp(1.8rem, 8cqi, 3rem);
76
+ font-weight: 500;
77
+ color: var(--pa-text-secondary);
78
+ margin: 0 0.1rem;
79
+ }
80
+
81
+ /* Meta row: inline delta + period text + target on one baseline. */
82
+ .pa-kpi-hero-main__meta {
83
+ display: grid;
84
+ grid-template-columns: auto minmax(0, 1fr) auto;
85
+ align-items: baseline;
86
+ gap: 0.4rem 1.4rem;
87
+ font-family: var(--base-font-family-mono);
88
+ font-size: 1.3rem;
89
+ }
90
+ .pa-kpi-hero-main__delta {
91
+ display: inline-flex;
92
+ align-items: baseline;
93
+ gap: 0.4rem;
94
+ color: var(--pa-kpi-accent);
95
+ font-weight: 700;
96
+ font-size: 1.5rem;
97
+ line-height: 1.2;
98
+ }
99
+ .pa-kpi-hero-main__period {
100
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
101
+ line-height: 1.3;
102
+ }
103
+ .pa-kpi-hero-main__target {
104
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
105
+ text-align: end;
106
+ }
107
+
108
+ /* Hero sparkline — chart container fills extra vertical space (when the
109
+ rail forces a tall row); SVG inside stays at fixed pixel height so the
110
+ line shape is never distorted along Y. The SVG uses
111
+ preserveAspectRatio="none", so any element that should keep its shape
112
+ needs to live outside that scaling or have fixed pixel dimensions. */
113
+ .pa-kpi-hero-main__chart {
114
+ flex: 1 1 10rem;
115
+ min-height: 10rem;
116
+ margin-top: 0.6rem;
117
+ color: var(--pa-kpi-accent);
118
+ display: flex;
119
+ align-items: flex-end; /* SVG wrap pinned to bottom */
120
+
121
+ polyline {
122
+ fill: none;
123
+ stroke: currentColor;
124
+ stroke-width: var(--pa-chart-trendline-stroke);
125
+ stroke-linecap: round;
126
+ stroke-linejoin: round;
127
+ }
128
+
129
+ polygon {
130
+ fill: currentColor;
131
+ fill-opacity: 0.18;
132
+ stroke: none;
133
+ }
134
+ }
135
+ .pa-kpi-hero-main__chart-svg {
136
+ position: relative;
137
+ display: block;
138
+ width: 100%;
139
+ height: var(--pa-chart-trendline-height);
140
+
141
+ svg {
142
+ display: block;
143
+ width: 100%;
144
+ height: 100%;
145
+ overflow: visible;
146
+ }
147
+ }
148
+
149
+ /* ----- SIDE rail tile (2×2 grid, value spans both rows on right) -------- */
150
+ .pa-kpi-hero-side {
151
+ position: relative;
152
+ padding: 1.2rem 1.4rem;
153
+ border: 1px solid var(--pa-border-color);
154
+ display: grid;
155
+ grid-template-columns: minmax(0, 1fr) auto;
156
+ grid-template-areas:
157
+ "label value"
158
+ "delta value";
159
+ column-gap: 1.2rem;
160
+ row-gap: 0.4rem;
161
+ align-items: center;
162
+
163
+ --pa-kpi-accent: var(--pa-positive);
164
+
165
+ &--positive { --pa-kpi-accent: var(--pa-positive); }
166
+ &--negative { --pa-kpi-accent: var(--pa-negative); }
167
+ &--neutral { --pa-kpi-accent: var(--pa-neutral); }
168
+ &--up-strong { --pa-kpi-accent: var(--pa-very-positive); }
169
+ }
170
+
171
+ .pa-kpi-hero-side__label {
172
+ grid-area: label;
173
+ align-self: end; /* tucks against the vertically-centred value */
174
+ font-family: var(--base-font-family-mono);
175
+ font-size: 1.3rem;
176
+ font-weight: 700;
177
+ letter-spacing: 0.1em;
178
+ text-transform: uppercase;
179
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
180
+ line-height: 1.25;
181
+ }
182
+ .pa-kpi-hero-side__value {
183
+ grid-area: value;
184
+ align-self: center;
185
+ font-family: var(--base-font-family-mono);
186
+ line-height: 1;
187
+ white-space: nowrap;
188
+ display: inline-flex;
189
+ align-items: baseline;
190
+ }
191
+ .pa-kpi-hero-side__num {
192
+ font-size: 2.4rem;
193
+ font-weight: 700;
194
+ letter-spacing: -0.02em;
195
+ color: var(--pa-text-color-1);
196
+ }
197
+ .pa-kpi-hero-side__unit {
198
+ font-size: 1.5rem;
199
+ font-weight: 500;
200
+ color: var(--pa-text-secondary);
201
+ margin: 0 0.35rem;
202
+ }
203
+ .pa-kpi-hero-side__delta {
204
+ grid-area: delta;
205
+ align-self: start;
206
+ font-family: var(--base-font-family-mono);
207
+ font-size: 1.2rem;
208
+ font-weight: 600;
209
+ color: var(--pa-kpi-accent);
210
+ }
@@ -0,0 +1,154 @@
1
+ /* ========================================
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
6
+ without needing subgrid. Wide-only by design — no responsive collapse;
7
+ narrow placements should use Comparison gauges instead.
8
+ ======================================== */
9
+ @use '../variables' as *;
10
+
11
+ .pa-kpi-strip__body { padding: 0; }
12
+
13
+ /* ----- Table layout ----------------------------------------------------- */
14
+ .pa-kpi-strip__row,
15
+ .pa-kpi-strip__head-row {
16
+ display: grid;
17
+ 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) */
23
+ column-gap: 1.6rem;
24
+ align-items: center;
25
+ position: relative;
26
+ padding: 1.1rem 1.6rem;
27
+ }
28
+
29
+ /* Divider between data rows / between head and first row only. */
30
+ .pa-kpi-strip__row + .pa-kpi-strip__row,
31
+ .pa-kpi-strip__head-row + .pa-kpi-strip__row {
32
+ border-top: 1px solid var(--pa-border-color);
33
+ }
34
+
35
+ /* Hover host: subtle row highlight so the cursor-anchored popover has a
36
+ clear "this row" anchor. */
37
+ .pa-kpi-strip__row:hover {
38
+ background: var(--pa-surface-hover);
39
+ }
40
+
41
+ /* ----- Header cells (uppercase mono captions) --------------------------- */
42
+ .pa-kpi-strip__head {
43
+ font-family: var(--base-font-family-mono);
44
+ font-size: 1.1rem;
45
+ font-weight: 700;
46
+ letter-spacing: 0.1em;
47
+ text-transform: uppercase;
48
+ color: var(--pa-text-tertiary);
49
+
50
+ &--num { text-align: end; }
51
+ }
52
+
53
+ /* ----- Metric label ----------------------------------------------------- */
54
+ .pa-kpi-strip__metric {
55
+ font-family: var(--base-font-family-mono);
56
+ font-size: 1.4rem;
57
+ font-weight: 700;
58
+ letter-spacing: 0.08em;
59
+ text-transform: uppercase;
60
+ color: color-mix(in srgb, var(--pa-text-color-1) 78%, transparent);
61
+ line-height: 1.2;
62
+ }
63
+
64
+ /* ----- NOW (focal value) ------------------------------------------------ */
65
+ .pa-kpi-strip__now {
66
+ font-family: var(--base-font-family-mono);
67
+ font-size: 1.8rem;
68
+ font-weight: 700;
69
+ text-align: end;
70
+ display: flex;
71
+ align-items: baseline;
72
+ justify-content: flex-end;
73
+ color: var(--pa-text-color-1);
74
+ white-space: nowrap;
75
+ line-height: 1;
76
+
77
+ .pa-kpi-strip__num { font-weight: 700; }
78
+ .pa-kpi-strip__unit {
79
+ font-size: 1.1rem;
80
+ font-weight: 500;
81
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
82
+ margin: 0 0.2rem;
83
+ }
84
+ }
85
+
86
+ /* ----- PREV (reference data, low contrast) ------------------------------ */
87
+ .pa-kpi-strip__prev {
88
+ font-family: var(--base-font-family-mono);
89
+ font-size: 1.5rem;
90
+ font-weight: 500;
91
+ color: var(--pa-text-tertiary);
92
+ text-align: end;
93
+ white-space: nowrap;
94
+ }
95
+
96
+ /* ----- Δ% (sentiment-coloured) ------------------------------------------ */
97
+ .pa-kpi-strip__delta {
98
+ font-family: var(--base-font-family-mono);
99
+ font-size: 1.5rem;
100
+ font-weight: 700;
101
+ text-align: end;
102
+ white-space: nowrap;
103
+ color: var(--pa-positive);
104
+
105
+ &--positive { color: var(--pa-positive); }
106
+ &--negative { color: var(--pa-negative); }
107
+ &--neutral { color: var(--pa-neutral); }
108
+ &--up-strong { color: var(--pa-very-positive); }
109
+ &--down-strong { color: var(--pa-very-negative); }
110
+ }
111
+
112
+ /* ----- VS TARGET (bar + pct stacked, capped at 100% visual) -------------
113
+ Theme-neutral grey fill — the pct value itself signals overshoot /
114
+ undershoot ("97" vs "108" vs "54") so colour reinforcement isn't
115
+ needed. The bar visually caps at 100% via overflow: hidden so an
116
+ over-target metric reads as "fully filled" rather than overflowing. */
117
+ .pa-kpi-strip__target {
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: 0.35rem;
121
+ }
122
+ .pa-kpi-strip__bar {
123
+ position: relative;
124
+ height: 0.5rem;
125
+ background: var(--pa-surface-track);
126
+ overflow: hidden;
127
+ border-radius: 0.1rem;
128
+ }
129
+ .pa-kpi-strip__fill {
130
+ position: absolute;
131
+ top: 0;
132
+ left: 0;
133
+ bottom: 0;
134
+ background: color-mix(in srgb, var(--pa-text-color-1) 40%, transparent);
135
+ }
136
+ .pa-kpi-strip__bar-pct {
137
+ font-family: var(--base-font-family-mono);
138
+ font-size: 1.05rem;
139
+ font-weight: 600;
140
+ color: var(--pa-text-secondary);
141
+ align-self: flex-end;
142
+ line-height: 1;
143
+ }
144
+
145
+ /* ----- --no-prev: 4-col layout (metric / now / Δ% / target) -------------
146
+ Useful for narrow placements; markup omits the prev cells. */
147
+ .pa-kpi-strip--no-prev .pa-kpi-strip__row,
148
+ .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);
154
+ }
@@ -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
+ }