@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.
@@ -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
+ }