@klodd/ds 1.0.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 +120 -0
- package/css/00-primitives.css +337 -0
- package/css/10-semantic.css +432 -0
- package/css/apps/ekonom.css +17 -0
- package/css/apps/jubb.css +15 -0
- package/css/base/pwa.css +53 -0
- package/css/base/typography.css +122 -0
- package/css/components/badge.css +98 -0
- package/css/components/button.css +200 -0
- package/css/components/card.css +86 -0
- package/css/components/feedback.css +187 -0
- package/css/components/hero-roll.css +129 -0
- package/css/components/icon.css +60 -0
- package/css/components/input.css +144 -0
- package/css/components/nav.css +192 -0
- package/css/components/overlay.css +128 -0
- package/css/index.css +28 -0
- package/package.json +23 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* ================================================================
|
|
2
|
+
components/badge.css
|
|
3
|
+
.badge - generisk pill med variants for status.
|
|
4
|
+
.score-pill - tabular-nums-pill speciellt for relevance-scores.
|
|
5
|
+
|
|
6
|
+
BEM-konvention med double-dash for varianter.
|
|
7
|
+
================================================================ */
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/* ================================================================
|
|
11
|
+
==== BADGE (generisk)
|
|
12
|
+
Inline-flex pill med valfri ikon + text. Default = neutral surface.
|
|
13
|
+
================================================================ */
|
|
14
|
+
.badge {
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: var(--space-6);
|
|
18
|
+
padding: var(--space-2) var(--space-10);
|
|
19
|
+
font-size: var(--fs-11);
|
|
20
|
+
font-weight: var(--fw-medium);
|
|
21
|
+
line-height: 1.4;
|
|
22
|
+
letter-spacing: 0.01em;
|
|
23
|
+
color: var(--text-subtle);
|
|
24
|
+
background: var(--surface-default);
|
|
25
|
+
border: 1px solid var(--border-subtle);
|
|
26
|
+
border-radius: var(--radius-full);
|
|
27
|
+
white-space: nowrap;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.badge--neutral {
|
|
31
|
+
color: var(--text-subtle);
|
|
32
|
+
background: var(--surface-default);
|
|
33
|
+
border-color: var(--border-subtle);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.badge--success {
|
|
37
|
+
color: var(--positive);
|
|
38
|
+
background: var(--positive-dim);
|
|
39
|
+
border-color: var(--positive-border);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.badge--warning {
|
|
43
|
+
color: var(--warning);
|
|
44
|
+
background: var(--warning-dim);
|
|
45
|
+
border-color: var(--warning-border);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.badge--danger {
|
|
49
|
+
color: var(--accent-danger);
|
|
50
|
+
background: var(--accent-danger-dim);
|
|
51
|
+
border-color: var(--accent-danger-border);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.badge--accent {
|
|
55
|
+
color: var(--accent-text);
|
|
56
|
+
background: var(--accent-a10);
|
|
57
|
+
border-color: var(--accent-a40);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
/* ================================================================
|
|
62
|
+
==== SCORE-PILL (tabular-nums for justering)
|
|
63
|
+
Anvands pa relevance-scores i jobblistan, triage, job-detail.
|
|
64
|
+
Modifiers: --strong (gron), --medium (gul), --low (gra).
|
|
65
|
+
================================================================ */
|
|
66
|
+
.score-pill {
|
|
67
|
+
display: inline-flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
justify-content: center;
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
padding: var(--space-2) var(--space-8);
|
|
72
|
+
font-size: var(--fs-11);
|
|
73
|
+
font-weight: var(--fw-medium);
|
|
74
|
+
font-variant-numeric: tabular-nums;
|
|
75
|
+
letter-spacing: 0.01em;
|
|
76
|
+
line-height: 1.4;
|
|
77
|
+
border: 1px solid transparent;
|
|
78
|
+
border-radius: var(--radius-full);
|
|
79
|
+
white-space: nowrap;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.score-pill--strong {
|
|
83
|
+
color: var(--positive);
|
|
84
|
+
background: var(--positive-dim);
|
|
85
|
+
border-color: var(--positive-border);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.score-pill--medium {
|
|
89
|
+
color: var(--warning);
|
|
90
|
+
background: var(--warning-dim);
|
|
91
|
+
border-color: var(--warning-border);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.score-pill--low {
|
|
95
|
+
color: var(--text-muted);
|
|
96
|
+
background: var(--surface-default);
|
|
97
|
+
border-color: var(--border-subtle);
|
|
98
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/* ================================================================
|
|
2
|
+
components/button.css
|
|
3
|
+
BEM-konvention: .btn (bas), .btn--<variant>, .btn--<size>, .btn--<state>.
|
|
4
|
+
States via :hover, :active, :focus-visible, [disabled], .btn--loading.
|
|
5
|
+
|
|
6
|
+
Standardvarianter:
|
|
7
|
+
- --primary solid accent
|
|
8
|
+
- --secondary surface med border
|
|
9
|
+
- --ghost transparent text
|
|
10
|
+
- --danger solid danger
|
|
11
|
+
|
|
12
|
+
Standardstorlekar:
|
|
13
|
+
- --sm 36px tall, fs-13
|
|
14
|
+
- --md 44px tall, fs-14 (default - inget --md klass behovs)
|
|
15
|
+
- --lg 52px tall, fs-15
|
|
16
|
+
|
|
17
|
+
Tilläggsformer:
|
|
18
|
+
- --icon kvadratisk, bara ikon
|
|
19
|
+
- --circle rund (pill)
|
|
20
|
+
================================================================ */
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
/* ================================================================
|
|
24
|
+
==== BAS
|
|
25
|
+
Alla knappar har samma fundamental: 44px touch-target, medium-vikt,
|
|
26
|
+
10px radius, smooth transitions, fokusring.
|
|
27
|
+
================================================================ */
|
|
28
|
+
.btn {
|
|
29
|
+
display: inline-flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
gap: var(--space-8);
|
|
33
|
+
min-height: var(--touch-min);
|
|
34
|
+
padding: 0 var(--space-16);
|
|
35
|
+
font-family: inherit;
|
|
36
|
+
font-size: var(--fs-14);
|
|
37
|
+
font-weight: var(--fw-medium);
|
|
38
|
+
line-height: 1;
|
|
39
|
+
color: var(--text-default);
|
|
40
|
+
background: transparent;
|
|
41
|
+
border: 1px solid transparent;
|
|
42
|
+
border-radius: var(--radius-10);
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
text-decoration: none;
|
|
45
|
+
white-space: nowrap;
|
|
46
|
+
user-select: none;
|
|
47
|
+
-webkit-tap-highlight-color: transparent;
|
|
48
|
+
touch-action: manipulation;
|
|
49
|
+
transition:
|
|
50
|
+
background var(--dur-base) var(--ease-spring-snappy),
|
|
51
|
+
border-color var(--dur-base) var(--ease-spring-snappy),
|
|
52
|
+
color var(--dur-base) var(--ease-spring-snappy),
|
|
53
|
+
transform var(--press-out-duration) var(--press-out-easing);
|
|
54
|
+
position: relative;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.btn:focus-visible {
|
|
58
|
+
outline: 2px solid var(--border-focus);
|
|
59
|
+
outline-offset: 2px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.btn:active {
|
|
63
|
+
transform: scale(0.97);
|
|
64
|
+
transition:
|
|
65
|
+
background var(--press-in-duration) var(--press-in-easing),
|
|
66
|
+
transform var(--press-in-duration) var(--press-in-easing);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.btn[disabled],
|
|
70
|
+
.btn:disabled {
|
|
71
|
+
opacity: 0.4;
|
|
72
|
+
cursor: not-allowed;
|
|
73
|
+
pointer-events: none;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
/* ================================================================
|
|
78
|
+
==== VARIANTER
|
|
79
|
+
================================================================ */
|
|
80
|
+
|
|
81
|
+
/* --primary: solid accent. Default action. */
|
|
82
|
+
.btn--primary {
|
|
83
|
+
background: var(--accent-9);
|
|
84
|
+
color: var(--text-on-accent);
|
|
85
|
+
border-color: var(--accent-9);
|
|
86
|
+
}
|
|
87
|
+
@media (hover: hover) and (pointer: fine) {
|
|
88
|
+
.btn--primary:hover {
|
|
89
|
+
background: var(--accent-10);
|
|
90
|
+
border-color: var(--accent-10);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* --secondary: surface med border. Sekundar action. */
|
|
95
|
+
.btn--secondary {
|
|
96
|
+
background: var(--surface-default);
|
|
97
|
+
color: var(--text-default);
|
|
98
|
+
border-color: var(--border-default);
|
|
99
|
+
}
|
|
100
|
+
@media (hover: hover) and (pointer: fine) {
|
|
101
|
+
.btn--secondary:hover {
|
|
102
|
+
background: var(--surface-hover);
|
|
103
|
+
border-color: var(--border-strong);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* --ghost: transparent. Tertiar action / cancel. */
|
|
108
|
+
.btn--ghost {
|
|
109
|
+
background: transparent;
|
|
110
|
+
color: var(--text-subtle);
|
|
111
|
+
border-color: transparent;
|
|
112
|
+
}
|
|
113
|
+
@media (hover: hover) and (pointer: fine) {
|
|
114
|
+
.btn--ghost:hover {
|
|
115
|
+
background: var(--surface-hover);
|
|
116
|
+
color: var(--text-default);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* --danger: solid danger. Destruktiv action. */
|
|
121
|
+
.btn--danger {
|
|
122
|
+
background: var(--bg-danger);
|
|
123
|
+
color: var(--text-on-status);
|
|
124
|
+
border-color: var(--bg-danger);
|
|
125
|
+
}
|
|
126
|
+
@media (hover: hover) and (pointer: fine) {
|
|
127
|
+
.btn--danger:hover {
|
|
128
|
+
filter: brightness(1.1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
/* ================================================================
|
|
134
|
+
==== STORLEKAR
|
|
135
|
+
--md ar default (44px). Anvand bara --sm eller --lg explicit.
|
|
136
|
+
================================================================ */
|
|
137
|
+
.btn--sm {
|
|
138
|
+
min-height: 36px;
|
|
139
|
+
padding: 0 var(--space-12);
|
|
140
|
+
font-size: var(--fs-13);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.btn--lg {
|
|
144
|
+
min-height: 52px;
|
|
145
|
+
padding: 0 var(--space-20);
|
|
146
|
+
font-size: var(--fs-15);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
/* ================================================================
|
|
151
|
+
==== FORMER
|
|
152
|
+
--icon: kvadratisk for bara ikon (32x32 sm, 44x44 md, 52x52 lg).
|
|
153
|
+
--circle: rund (pill via radius-full). Anvands for cirkel-cta.
|
|
154
|
+
================================================================ */
|
|
155
|
+
.btn--icon {
|
|
156
|
+
width: var(--touch-min);
|
|
157
|
+
height: var(--touch-min);
|
|
158
|
+
padding: 0;
|
|
159
|
+
}
|
|
160
|
+
.btn--icon.btn--sm { width: 36px; height: 36px; }
|
|
161
|
+
.btn--icon.btn--lg { width: 52px; height: 52px; }
|
|
162
|
+
|
|
163
|
+
.btn--circle {
|
|
164
|
+
border-radius: var(--radius-full);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
/* ================================================================
|
|
169
|
+
==== LOADING-STATE
|
|
170
|
+
Doljer text och visar spinner via ::after. Behaller knappens
|
|
171
|
+
bredd sa layout inte hoppar.
|
|
172
|
+
================================================================ */
|
|
173
|
+
.btn--loading {
|
|
174
|
+
color: transparent !important;
|
|
175
|
+
pointer-events: none;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.btn--loading::after {
|
|
179
|
+
content: "";
|
|
180
|
+
position: absolute;
|
|
181
|
+
inset: 50% auto auto 50%;
|
|
182
|
+
width: 16px;
|
|
183
|
+
height: 16px;
|
|
184
|
+
margin: -8px 0 0 -8px;
|
|
185
|
+
border: 2px solid currentColor;
|
|
186
|
+
border-top-color: transparent;
|
|
187
|
+
border-radius: var(--radius-full);
|
|
188
|
+
color: var(--text-default);
|
|
189
|
+
animation: btn-spin 0.6s linear infinite;
|
|
190
|
+
opacity: 1;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.btn--primary.btn--loading::after,
|
|
194
|
+
.btn--danger.btn--loading::after {
|
|
195
|
+
color: var(--text-on-accent);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@keyframes btn-spin {
|
|
199
|
+
to { transform: rotate(360deg); }
|
|
200
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* ================================================================
|
|
2
|
+
components/card.css
|
|
3
|
+
.card - upphojd yta med subtil border. Standardcontainer for
|
|
4
|
+
panel-stylade sektioner. .card--interactive ger hover/active-feel
|
|
5
|
+
pa klickbara kort. .card--flush tar bort padding (anvand for
|
|
6
|
+
inbaddat innehall som hanterar egen padding).
|
|
7
|
+
|
|
8
|
+
Slot-konvention: .card-header / .card-body / .card-footer for
|
|
9
|
+
strukturerat innehall. .card-divider for horisontell separator.
|
|
10
|
+
================================================================ */
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/* ================================================================
|
|
14
|
+
==== BAS
|
|
15
|
+
================================================================ */
|
|
16
|
+
.card {
|
|
17
|
+
background: var(--surface-raised);
|
|
18
|
+
color: var(--text-default);
|
|
19
|
+
border: 1px solid var(--border-subtle);
|
|
20
|
+
border-radius: var(--radius-14);
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
transition:
|
|
23
|
+
background var(--dur-base) var(--ease-spring-snappy),
|
|
24
|
+
border-color var(--dur-base) var(--ease-spring-snappy),
|
|
25
|
+
transform var(--press-out-duration) var(--press-out-easing);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
/* ================================================================
|
|
30
|
+
==== VARIANTER
|
|
31
|
+
================================================================ */
|
|
32
|
+
.card--interactive {
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
-webkit-tap-highlight-color: transparent;
|
|
35
|
+
touch-action: manipulation;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@media (hover: hover) and (pointer: fine) {
|
|
39
|
+
.card--interactive:hover {
|
|
40
|
+
background: var(--surface-overlay);
|
|
41
|
+
border-color: var(--border-default);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.card--interactive:active {
|
|
46
|
+
transform: scale(0.99);
|
|
47
|
+
transition: transform var(--press-in-duration) var(--press-in-easing);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.card--interactive:focus-visible {
|
|
51
|
+
outline: 2px solid var(--border-focus);
|
|
52
|
+
outline-offset: 2px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* --flush tar bort default padding sa innehallet kan ga edge-to-edge.
|
|
56
|
+
Anvand nar barn-element har egen padding (ex. lista med list-rows). */
|
|
57
|
+
.card--flush > .card-body {
|
|
58
|
+
padding: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/* ================================================================
|
|
63
|
+
==== SLOTS
|
|
64
|
+
Header och footer har samma padding som body men separator-linjer.
|
|
65
|
+
Anvand .card-divider for visuell uppdelning inom samma slot.
|
|
66
|
+
================================================================ */
|
|
67
|
+
.card-header {
|
|
68
|
+
padding: var(--space-16) var(--space-16) var(--space-12);
|
|
69
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.card-body {
|
|
73
|
+
padding: var(--space-16);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.card-footer {
|
|
77
|
+
padding: var(--space-12) var(--space-16);
|
|
78
|
+
border-top: 1px solid var(--border-subtle);
|
|
79
|
+
background: var(--surface-default);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.card-divider {
|
|
83
|
+
height: 1px;
|
|
84
|
+
background: var(--border-subtle);
|
|
85
|
+
margin: 0 calc(-1 * var(--space-16));
|
|
86
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* ================================================================
|
|
2
|
+
components/feedback.css
|
|
3
|
+
Toast, empty-state, skeleton-loader, spinner.
|
|
4
|
+
Komponenter for transient feedback och loading-states.
|
|
5
|
+
================================================================ */
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/* ================================================================
|
|
9
|
+
==== TOAST
|
|
10
|
+
Transient meddelande, oftast ankrat top-center via .toast-region
|
|
11
|
+
wrapper. Fyra varianter: default (neutral), --success, --error,
|
|
12
|
+
--warning. Animerad in via @keyframes toast-in.
|
|
13
|
+
================================================================ */
|
|
14
|
+
.toast {
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: var(--space-10);
|
|
18
|
+
padding: var(--space-12) var(--space-16);
|
|
19
|
+
font-size: var(--fs-14);
|
|
20
|
+
line-height: 1.4;
|
|
21
|
+
color: var(--text-default);
|
|
22
|
+
background: var(--surface-overlay);
|
|
23
|
+
border: 1px solid var(--border-subtle);
|
|
24
|
+
border-radius: var(--radius-12);
|
|
25
|
+
box-shadow: var(--shadow-float);
|
|
26
|
+
-webkit-backdrop-filter: blur(20px);
|
|
27
|
+
backdrop-filter: blur(20px);
|
|
28
|
+
pointer-events: auto;
|
|
29
|
+
max-width: 90vw;
|
|
30
|
+
animation: toast-in 0.32s var(--ease-spring-bounce);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.toast--success {
|
|
34
|
+
background: var(--bg-success);
|
|
35
|
+
border-color: var(--positive-border);
|
|
36
|
+
color: var(--text-on-status);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.toast--error {
|
|
40
|
+
background: var(--bg-danger);
|
|
41
|
+
border-color: var(--accent-danger-border);
|
|
42
|
+
color: var(--text-on-status);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.toast--warning {
|
|
46
|
+
background: var(--bg-warning);
|
|
47
|
+
border-color: var(--warning-border);
|
|
48
|
+
color: var(--text-on-status);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@keyframes toast-in {
|
|
52
|
+
0% { opacity: 0; transform: translateY(-8px) scale(0.96); }
|
|
53
|
+
100% { opacity: 1; transform: translateY(0) scale(1); }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
/* ================================================================
|
|
58
|
+
==== EMPTY-STATE
|
|
59
|
+
Centrerad ikon + rubrik + text + valfri knapp. Anvands i listor
|
|
60
|
+
utan resultat, dashboards utan data, eller efter genomforda
|
|
61
|
+
actions ("Allt klart").
|
|
62
|
+
================================================================ */
|
|
63
|
+
.empty-state {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
gap: var(--space-12);
|
|
69
|
+
padding: var(--space-40) var(--space-20);
|
|
70
|
+
text-align: center;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.empty-state__icon {
|
|
74
|
+
display: inline-flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
justify-content: center;
|
|
77
|
+
width: 56px;
|
|
78
|
+
height: 56px;
|
|
79
|
+
background: var(--surface-default);
|
|
80
|
+
color: var(--text-muted);
|
|
81
|
+
border-radius: var(--radius-full);
|
|
82
|
+
margin-bottom: var(--space-4);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.empty-state__title {
|
|
86
|
+
font-size: var(--fs-17);
|
|
87
|
+
font-weight: var(--fw-medium);
|
|
88
|
+
line-height: var(--lh-snug);
|
|
89
|
+
color: var(--text-default);
|
|
90
|
+
margin: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.empty-state__text {
|
|
94
|
+
font-size: var(--fs-14);
|
|
95
|
+
line-height: var(--lh-base);
|
|
96
|
+
color: var(--text-subtle);
|
|
97
|
+
max-width: 320px;
|
|
98
|
+
margin: 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
/* ================================================================
|
|
103
|
+
==== SKELETON
|
|
104
|
+
Loading-placeholder med subtle pulse-animation. Storleksvarianter
|
|
105
|
+
for vanliga formfaktorer: --text (en rad), --circle (avatar),
|
|
106
|
+
--card (full kort).
|
|
107
|
+
================================================================ */
|
|
108
|
+
.skeleton {
|
|
109
|
+
display: block;
|
|
110
|
+
background: linear-gradient(
|
|
111
|
+
90deg,
|
|
112
|
+
var(--surface-default) 0%,
|
|
113
|
+
var(--surface-overlay) 50%,
|
|
114
|
+
var(--surface-default) 100%
|
|
115
|
+
);
|
|
116
|
+
background-size: 200% 100%;
|
|
117
|
+
border-radius: var(--radius-6);
|
|
118
|
+
animation: skeleton-shimmer 1.4s ease-in-out infinite;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.skeleton--text {
|
|
122
|
+
height: var(--space-12);
|
|
123
|
+
border-radius: var(--radius-4);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.skeleton--circle {
|
|
127
|
+
width: 36px;
|
|
128
|
+
height: 36px;
|
|
129
|
+
border-radius: var(--radius-full);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.skeleton--card {
|
|
133
|
+
height: 80px;
|
|
134
|
+
border-radius: var(--radius-14);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@keyframes skeleton-shimmer {
|
|
138
|
+
0% { background-position: 200% 0; }
|
|
139
|
+
100% { background-position: -200% 0; }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@media (prefers-reduced-motion: reduce) {
|
|
143
|
+
.skeleton {
|
|
144
|
+
animation: none;
|
|
145
|
+
background: var(--surface-default);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
/* ================================================================
|
|
151
|
+
==== SPINNER
|
|
152
|
+
Roterande loader. Tre storlekar via --sm/--lg modifiers (default md).
|
|
153
|
+
Anvander currentColor sa parent kan styra fargen.
|
|
154
|
+
================================================================ */
|
|
155
|
+
.spinner {
|
|
156
|
+
display: inline-block;
|
|
157
|
+
width: 24px;
|
|
158
|
+
height: 24px;
|
|
159
|
+
border: 2px solid currentColor;
|
|
160
|
+
border-top-color: transparent;
|
|
161
|
+
border-radius: var(--radius-full);
|
|
162
|
+
color: var(--accent-9);
|
|
163
|
+
animation: spinner-rotate 0.7s linear infinite;
|
|
164
|
+
vertical-align: middle;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.spinner--sm {
|
|
168
|
+
width: 16px;
|
|
169
|
+
height: 16px;
|
|
170
|
+
border-width: 2px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.spinner--lg {
|
|
174
|
+
width: 40px;
|
|
175
|
+
height: 40px;
|
|
176
|
+
border-width: 3px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@keyframes spinner-rotate {
|
|
180
|
+
to { transform: rotate(360deg); }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@media (prefers-reduced-motion: reduce) {
|
|
184
|
+
.spinner {
|
|
185
|
+
animation-duration: 2s;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/* ================================================================
|
|
2
|
+
components/hero-roll.css
|
|
3
|
+
Delad animationskomponent: kodlas-rullnings-animation pa hero-siffror.
|
|
4
|
+
Aktiveras via <p class="hero-amount" data-animate-roll="N">N kr</p>
|
|
5
|
+
och hero-roll.js. Ursprung: Ekonom Sprint J + J.3-J.6 (2026-04-27 ff).
|
|
6
|
+
|
|
7
|
+
Helt app-token-fri - referar bara primitives (--lh-tight, --space-N,
|
|
8
|
+
--fs-N, --fw-medium) och en text-color (--text-default). Inga
|
|
9
|
+
--accent-*, --blue-*, --purple-* eller andra app-overrides. Samma
|
|
10
|
+
styling for Jubb och Ekonom.
|
|
11
|
+
|
|
12
|
+
Krav for korrekt rendering:
|
|
13
|
+
- font-variant-numeric: tabular-nums (sa 0-9-glyfer ar lika breda)
|
|
14
|
+
- --lh-tight definierad i primitives (anvands som rullhojd)
|
|
15
|
+
- prefers-reduced-motion respekteras (hoppar direkt till slut-state)
|
|
16
|
+
================================================================ */
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/* ================================================================
|
|
20
|
+
==== HERO-AMOUNT BAS
|
|
21
|
+
Display-siffran. Storre an UI-text och tyngre vikt for visuell
|
|
22
|
+
prioritet. Server renderar slutvardet som text - JS bygger
|
|
23
|
+
digit-roller-DOM vid trigger och restoreraar tillbaka till server-
|
|
24
|
+
text vid done.
|
|
25
|
+
================================================================ */
|
|
26
|
+
.hero-amount {
|
|
27
|
+
font-size: var(--hero-amount-fz, var(--fs-80));
|
|
28
|
+
/* Undantag fran 400/500-policy: display-siffra, inte UI-text.
|
|
29
|
+
600 ger nodvandig visuell tyngd pa hero-storlekar. */
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
letter-spacing: var(--ls-tightest);
|
|
32
|
+
color: var(--text-default);
|
|
33
|
+
line-height: var(--lh-tight);
|
|
34
|
+
/* Tabular-nums kritisk for rullnings-animationen: alla 0-9-glyfer
|
|
35
|
+
packas identiskt sa layout inte vibrerar nar siffran roterar. */
|
|
36
|
+
font-variant-numeric: tabular-nums;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
/* ================================================================
|
|
41
|
+
==== DIGIT-ROLLER
|
|
42
|
+
En per siffra. Containrar en track av siffror som translateY:as
|
|
43
|
+
till slutvardet. Overflow:hidden klipper allt utom synlig position.
|
|
44
|
+
================================================================ */
|
|
45
|
+
.hero-digit-roller {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
height: var(--hero-digit-height, 1em);
|
|
48
|
+
line-height: var(--hero-digit-height, 1em);
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
position: relative;
|
|
51
|
+
vertical-align: top;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Vignett over och under sa siffrorna fadar ut nar de rullar in/ut.
|
|
55
|
+
--hero-vignette-color satts av app-konsumenten om subtle fade
|
|
56
|
+
onskas mot bg, default transparent. */
|
|
57
|
+
.hero-digit-roller::before,
|
|
58
|
+
.hero-digit-roller::after {
|
|
59
|
+
content: '';
|
|
60
|
+
position: absolute;
|
|
61
|
+
left: 0;
|
|
62
|
+
right: 0;
|
|
63
|
+
height: 14px;
|
|
64
|
+
pointer-events: none;
|
|
65
|
+
z-index: 1;
|
|
66
|
+
background: transparent;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.hero-digit-roller::before {
|
|
70
|
+
top: 0;
|
|
71
|
+
background: linear-gradient(
|
|
72
|
+
to bottom,
|
|
73
|
+
var(--hero-vignette-color, transparent) 0%,
|
|
74
|
+
transparent 100%
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.hero-digit-roller::after {
|
|
79
|
+
bottom: 0;
|
|
80
|
+
background: linear-gradient(
|
|
81
|
+
to top,
|
|
82
|
+
var(--hero-vignette-color, transparent) 0%,
|
|
83
|
+
transparent 100%
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
/* ================================================================
|
|
89
|
+
==== DIGIT-TRACK
|
|
90
|
+
Vertikal stack av siffer-spans. translateY-styrt av JS via inline
|
|
91
|
+
transition + transform. tabular-nums upprepad pa span-niva som
|
|
92
|
+
safety-net om hero-amount-arvet skulle brytas av nan parent.
|
|
93
|
+
================================================================ */
|
|
94
|
+
.hero-digit-track {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
will-change: transform;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.hero-digit-track > span {
|
|
101
|
+
height: var(--hero-digit-height, 1em);
|
|
102
|
+
line-height: var(--hero-digit-height, 1em);
|
|
103
|
+
display: block;
|
|
104
|
+
font-variant-numeric: tabular-nums;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
/* ================================================================
|
|
109
|
+
==== STATIC CHARS
|
|
110
|
+
Mellanslag och "kr"-suffix runt rollers. white-space: pre bevarar
|
|
111
|
+
exakta blanksteg.
|
|
112
|
+
================================================================ */
|
|
113
|
+
.hero-static-char {
|
|
114
|
+
display: inline-block;
|
|
115
|
+
white-space: pre;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
/* ================================================================
|
|
120
|
+
==== REDUCED-MOTION
|
|
121
|
+
Hoppa direkt till slut-state utan transition. JS hanterar
|
|
122
|
+
transformen sjalv (translateY till slutposition utan animation),
|
|
123
|
+
denna regel bara safety-net for befintliga inline-transitions.
|
|
124
|
+
================================================================ */
|
|
125
|
+
@media (prefers-reduced-motion: reduce) {
|
|
126
|
+
.hero-digit-track {
|
|
127
|
+
transition: none !important;
|
|
128
|
+
}
|
|
129
|
+
}
|