@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.
- package/README.md +24 -16
- package/dist/css/main.css +1865 -257
- package/package.json +1 -1
- package/src/scss/_base-css-variables.scss +37 -19
- package/src/scss/_core.scss +11 -0
- package/src/scss/core-components/_alerts.scss +2 -2
- package/src/scss/core-components/_base.scss +19 -2
- package/src/scss/core-components/_buttons.scss +12 -8
- package/src/scss/core-components/_callouts.scss +1 -1
- package/src/scss/core-components/_cards.scss +4 -4
- package/src/scss/core-components/_checkbox-lists.scss +2 -2
- package/src/scss/core-components/_comparison.scss +7 -4
- package/src/scss/core-components/_data-display.scss +24 -15
- package/src/scss/core-components/_data-viz.scss +139 -131
- package/src/scss/core-components/_file-selector.scss +34 -34
- package/src/scss/core-components/_kpi-base.scss +169 -0
- package/src/scss/core-components/_kpi-bento.scss +182 -0
- package/src/scss/core-components/_kpi-comparison-gauges.scss +132 -0
- package/src/scss/core-components/_kpi-editorial-minimal.scss +128 -0
- package/src/scss/core-components/_kpi-hero-supporting.scss +210 -0
- package/src/scss/core-components/_kpi-numeric-strip.scss +154 -0
- package/src/scss/core-components/_kpi-sparkline-list.scss +171 -0
- package/src/scss/core-components/_kpi-terminal.scss +229 -0
- package/src/scss/core-components/_lists.scss +4 -4
- package/src/scss/core-components/_logic-tree.scss +2 -2
- package/src/scss/core-components/_modals.scss +69 -0
- package/src/scss/core-components/_notifications.scss +17 -17
- package/src/scss/core-components/_popconfirm.scss +1 -1
- package/src/scss/core-components/_statistics.scss +25 -19
- package/src/scss/core-components/_tabs.scss +12 -12
- package/src/scss/core-components/_timeline.scss +30 -30
- package/src/scss/core-components/badges/_composite-badge-variants.scss +7 -7
- package/src/scss/core-components/badges/_composite-badge.scss +1 -1
- package/src/scss/core-components/badges/_labels.scss +6 -6
- package/src/scss/core-components/forms/_input-wrapper.scss +1 -1
- package/src/scss/core-components/forms/_query-editor.scss +10 -10
- package/src/scss/core-components/layout/_sidebar-states.scss +1 -0
- package/src/scss/core-components/layout/_sidebar.scss +1 -0
- package/src/scss/variables/_colors.scss +1 -0
- package/src/scss/variables/_components.scss +3 -2
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* ========================================
|
|
2
|
+
KPI · Comparison gauges
|
|
3
|
+
Goal-oriented progress bars. Each KPI shows label · value, a bar with a
|
|
4
|
+
target tick, and a 0 · tgt scale below. Bar fill = value/target * 100%,
|
|
5
|
+
capped visually so overshoots are signalled by colour, not overflow.
|
|
6
|
+
======================================== */
|
|
7
|
+
@use '../variables' as *;
|
|
8
|
+
|
|
9
|
+
.pa-kpi-gauge-list {
|
|
10
|
+
container-type: inline-size;
|
|
11
|
+
}
|
|
12
|
+
.pa-kpi-gauge-list__body { padding: 0; }
|
|
13
|
+
|
|
14
|
+
/* 2-column internal grid by default; collapses to 1-col when card narrows. */
|
|
15
|
+
.pa-kpi-gauge-list__grid {
|
|
16
|
+
display: grid;
|
|
17
|
+
grid-template-columns: repeat(2, 1fr);
|
|
18
|
+
}
|
|
19
|
+
@container (max-width: 600px) {
|
|
20
|
+
.pa-kpi-gauge-list__grid {
|
|
21
|
+
grid-template-columns: 1fr;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* ----- Gauge tile (label/value head · bar · 0/tgt scale) ---------------- */
|
|
26
|
+
.pa-kpi-gauge {
|
|
27
|
+
position: relative;
|
|
28
|
+
padding: 1.6rem 2rem;
|
|
29
|
+
border-bottom: 1px solid var(--pa-border-color);
|
|
30
|
+
border-right: 1px solid var(--pa-border-color);
|
|
31
|
+
|
|
32
|
+
/* Per-tile bar colour cascade — modifiers below set the var, the fill
|
|
33
|
+
reads it. Cleaner than per-modifier-per-element rules; host apps can
|
|
34
|
+
override at the tile level via inline style="--pa-kpi-bar-color: …". */
|
|
35
|
+
--pa-kpi-bar-color: var(--pa-positive);
|
|
36
|
+
|
|
37
|
+
&:nth-child(2n) { border-right: 0; }
|
|
38
|
+
&:nth-last-child(-n+2) { border-bottom: 0; }
|
|
39
|
+
|
|
40
|
+
&--positive { --pa-kpi-bar-color: var(--pa-positive); }
|
|
41
|
+
&--warning { --pa-kpi-bar-color: var(--pa-warning); }
|
|
42
|
+
&--negative { --pa-kpi-bar-color: var(--pa-negative); }
|
|
43
|
+
&--neutral { --pa-kpi-bar-color: color-mix(in srgb, var(--pa-text-color-1) 70%, transparent); }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@container (max-width: 600px) {
|
|
47
|
+
.pa-kpi-gauge {
|
|
48
|
+
border-right: 0;
|
|
49
|
+
|
|
50
|
+
&:nth-last-child(-n+2) { border-bottom: 1px solid var(--pa-border-color); }
|
|
51
|
+
&:last-child { border-bottom: 0; }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ----- Head: label left, value right (baseline-aligned) ----------------- */
|
|
56
|
+
.pa-kpi-gauge__head {
|
|
57
|
+
display: flex;
|
|
58
|
+
justify-content: space-between;
|
|
59
|
+
align-items: baseline;
|
|
60
|
+
gap: 1.2rem;
|
|
61
|
+
margin-bottom: 0.8rem;
|
|
62
|
+
}
|
|
63
|
+
.pa-kpi-gauge__label {
|
|
64
|
+
font-family: var(--base-font-family-mono);
|
|
65
|
+
font-size: 1.4rem;
|
|
66
|
+
font-weight: 700;
|
|
67
|
+
letter-spacing: 0.1em;
|
|
68
|
+
text-transform: uppercase;
|
|
69
|
+
color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
|
|
70
|
+
}
|
|
71
|
+
.pa-kpi-gauge__value {
|
|
72
|
+
font-family: var(--base-font-family-mono);
|
|
73
|
+
line-height: 1;
|
|
74
|
+
}
|
|
75
|
+
.pa-kpi-gauge__num {
|
|
76
|
+
font-size: 2.6rem;
|
|
77
|
+
font-weight: 700;
|
|
78
|
+
letter-spacing: -0.02em;
|
|
79
|
+
color: var(--pa-text-color-1);
|
|
80
|
+
}
|
|
81
|
+
.pa-kpi-gauge__unit {
|
|
82
|
+
font-size: 1.3rem;
|
|
83
|
+
font-weight: 500;
|
|
84
|
+
color: var(--pa-text-secondary);
|
|
85
|
+
margin-left: 0.2rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* ----- Bar (track + sentiment-coloured fill + target tick) -------------- */
|
|
89
|
+
.pa-kpi-gauge__bar {
|
|
90
|
+
/* Author-controlled tick position (default 100% — the target sits at the
|
|
91
|
+
right edge of the bar's "0 → target" scale). Override per-tile via
|
|
92
|
+
style="--pa-kpi-gauge-tick-pos: 80%" if the bar represents a wider
|
|
93
|
+
scale where the target sits inside the bar. */
|
|
94
|
+
--pa-kpi-gauge-tick-pos: 100%;
|
|
95
|
+
--pa-kpi-gauge-tick-color: var(--pa-text-color-1);
|
|
96
|
+
|
|
97
|
+
position: relative;
|
|
98
|
+
height: 0.7rem;
|
|
99
|
+
background: var(--pa-surface-track);
|
|
100
|
+
margin-bottom: 0.5rem;
|
|
101
|
+
overflow: visible; /* tick can sit just outside the track */
|
|
102
|
+
|
|
103
|
+
/* Target tick — small bar slightly taller than the track. Centred on
|
|
104
|
+
--pa-kpi-gauge-tick-pos via the negative margin-left (matches half
|
|
105
|
+
the tick width). Full opacity so it stays readable on dark themes
|
|
106
|
+
against bright orange/green fills. */
|
|
107
|
+
&::after {
|
|
108
|
+
content: '';
|
|
109
|
+
position: absolute;
|
|
110
|
+
left: var(--pa-kpi-gauge-tick-pos);
|
|
111
|
+
margin-left: -0.15rem;
|
|
112
|
+
top: -0.2rem;
|
|
113
|
+
bottom: -0.2rem;
|
|
114
|
+
width: 0.3rem;
|
|
115
|
+
background: var(--pa-kpi-gauge-tick-color);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
.pa-kpi-gauge__fill {
|
|
119
|
+
height: 100%;
|
|
120
|
+
background: var(--pa-kpi-bar-color);
|
|
121
|
+
/* Width set inline per tile via style="width: X%". */
|
|
122
|
+
transition: width 0.3s ease;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* ----- Scale row (0 left · tgt XYZ right) ------------------------------- */
|
|
126
|
+
.pa-kpi-gauge__scale {
|
|
127
|
+
display: flex;
|
|
128
|
+
justify-content: space-between;
|
|
129
|
+
font-family: var(--base-font-family-mono);
|
|
130
|
+
font-size: 1.2rem;
|
|
131
|
+
color: var(--pa-text-tertiary);
|
|
132
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* ========================================
|
|
2
|
+
KPI · Editorial minimal
|
|
3
|
+
Six KPIs in a 2×3 / 3×2 grid with hairline rules between cells,
|
|
4
|
+
generous space, and an extra-light-weight number as the focal point per
|
|
5
|
+
tile. No charts, no pills — the design's whole identity is the thin
|
|
6
|
+
numeral. Renders as a table card (zero card-body padding) so hairlines
|
|
7
|
+
go edge-to-edge.
|
|
8
|
+
======================================== */
|
|
9
|
+
@use '../variables' as *;
|
|
10
|
+
|
|
11
|
+
.pa-kpi-edit__body { padding: 0; }
|
|
12
|
+
|
|
13
|
+
/* ----- Grid + hairline rules -------------------------------------------
|
|
14
|
+
`gap: 1px` over `background: var(--pa-border-color)` with each tile
|
|
15
|
+
painting `background: var(--pa-card-bg)` on top. Only the gap shows
|
|
16
|
+
through, giving perfect single-pixel hairlines on every interior
|
|
17
|
+
boundary (vertical and horizontal) for free, regardless of column
|
|
18
|
+
count. The card's outer border supplies the perimeter. */
|
|
19
|
+
.pa-kpi-edit__grid {
|
|
20
|
+
display: grid;
|
|
21
|
+
grid-template-columns: repeat(3, 1fr);
|
|
22
|
+
gap: 1px;
|
|
23
|
+
background: var(--pa-border-color);
|
|
24
|
+
container-type: inline-size;
|
|
25
|
+
}
|
|
26
|
+
@container (max-width: 640px) {
|
|
27
|
+
.pa-kpi-edit__grid { grid-template-columns: repeat(2, 1fr); }
|
|
28
|
+
}
|
|
29
|
+
@container (max-width: 360px) {
|
|
30
|
+
.pa-kpi-edit__grid { grid-template-columns: 1fr; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Modifier: force 2-col regardless of card width. For placements wanting
|
|
34
|
+
a deterministic 2×N layout (e.g. 4 tiles as clean 2×2) instead of
|
|
35
|
+
relying on the container query to land in the right bucket. */
|
|
36
|
+
.pa-kpi-edit__grid--2col { grid-template-columns: repeat(2, 1fr); }
|
|
37
|
+
|
|
38
|
+
/* ----- Tile (editorial spacing leans on the 2.4rem padding) ------------- */
|
|
39
|
+
.pa-kpi-edit__tile {
|
|
40
|
+
background: var(--pa-card-bg);
|
|
41
|
+
padding: 2.4rem 2rem;
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
gap: 1.2rem;
|
|
45
|
+
position: relative;
|
|
46
|
+
min-width: 0;
|
|
47
|
+
|
|
48
|
+
/* Hover host: faint wash so the cursor-anchored popover has a clear
|
|
49
|
+
"this tile" anchor. */
|
|
50
|
+
&:hover {
|
|
51
|
+
background: color-mix(in srgb, var(--pa-text-color-1) 4%, var(--pa-card-bg));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ----- Label (uppercase mono caption) ----------------------------------- */
|
|
56
|
+
.pa-kpi-edit__label {
|
|
57
|
+
font-family: var(--base-font-family-mono);
|
|
58
|
+
font-size: 1.2rem;
|
|
59
|
+
font-weight: 600;
|
|
60
|
+
letter-spacing: 0.12em;
|
|
61
|
+
text-transform: uppercase;
|
|
62
|
+
color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
|
|
63
|
+
line-height: 1.3;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* ----- Value (the editorial signature) ---------------------------------
|
|
67
|
+
- NOT mono — mono fonts rarely have a true 200 weight; using the body
|
|
68
|
+
sans gives proper thin glyphs that read as "editorial" rather than
|
|
69
|
+
"monospace at low contrast".
|
|
70
|
+
- font-weight: 200 (extra-light). 300 was tested first but didn't read
|
|
71
|
+
distinctly enough as "light" against the body's 400 default.
|
|
72
|
+
- clamp() lets the number shrink in narrow 25% page-grid cells without
|
|
73
|
+
manual breakpoints. */
|
|
74
|
+
.pa-kpi-edit__value {
|
|
75
|
+
font-family: var(--base-font-family);
|
|
76
|
+
font-size: clamp(3.2rem, 18cqi, 5.6rem);
|
|
77
|
+
font-weight: 200;
|
|
78
|
+
letter-spacing: -0.02em;
|
|
79
|
+
line-height: 1;
|
|
80
|
+
color: var(--pa-text-color-1);
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: baseline;
|
|
83
|
+
gap: 0.2rem;
|
|
84
|
+
flex-wrap: wrap;
|
|
85
|
+
}
|
|
86
|
+
.pa-kpi-edit__num {
|
|
87
|
+
font-weight: 200;
|
|
88
|
+
font-variant-numeric: tabular-nums;
|
|
89
|
+
}
|
|
90
|
+
.pa-kpi-edit__unit {
|
|
91
|
+
font-size: 0.4em;
|
|
92
|
+
font-weight: 300;
|
|
93
|
+
color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
|
|
94
|
+
letter-spacing: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ----- Meta row (delta + tgt) ------------------------------------------ */
|
|
98
|
+
.pa-kpi-edit__meta {
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: baseline;
|
|
101
|
+
gap: 1.4rem;
|
|
102
|
+
font-family: var(--base-font-family-mono);
|
|
103
|
+
font-size: 1.3rem;
|
|
104
|
+
line-height: 1.3;
|
|
105
|
+
flex-wrap: wrap;
|
|
106
|
+
}
|
|
107
|
+
.pa-kpi-edit__delta {
|
|
108
|
+
font-weight: 700;
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
color: var(--pa-positive);
|
|
111
|
+
|
|
112
|
+
&--positive { color: var(--pa-positive); }
|
|
113
|
+
&--negative { color: var(--pa-negative); }
|
|
114
|
+
&--neutral { color: var(--pa-neutral); }
|
|
115
|
+
&--up-strong { color: var(--pa-very-positive); }
|
|
116
|
+
&--down-strong { color: var(--pa-very-negative); }
|
|
117
|
+
}
|
|
118
|
+
.pa-kpi-edit__target {
|
|
119
|
+
font-weight: 500;
|
|
120
|
+
color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
|
|
121
|
+
white-space: nowrap;
|
|
122
|
+
|
|
123
|
+
em {
|
|
124
|
+
font-style: normal;
|
|
125
|
+
color: color-mix(in srgb, var(--pa-text-color-1) 38%, transparent);
|
|
126
|
+
margin-right: 0.5rem;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -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
|
+
}
|