@klodd/ds 3.21.5 → 4.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/css/base/typography.css +2 -2
- package/css/components/hero-roll.css +33 -33
- package/css/components/hero.css +23 -12
- package/js/hero-roll.js +5 -5
- package/js/turbo-nav.js +1 -1
- package/package.json +1 -1
- package/references/02-components.md +9 -6
- package/references/04-locked-decisions/0018-outline-offset-list-rows.md +122 -0
- package/references/04-locked-decisions/0019-hero-amount-bem-canonical.md +153 -0
- package/references/DESIGN-LANGUAGE.md +29 -4
package/css/base/typography.css
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
anvand dessa klasser eller (i komponentens CSS) primitive-tokens
|
|
18
18
|
med kommentar om varfor en bundlad klass inte rackte.
|
|
19
19
|
|
|
20
|
-
Undantag: .
|
|
21
|
-
undantag i components/hero
|
|
20
|
+
Undantag: .hero__amount (display-siffra, fw 600) - dokumenterat
|
|
21
|
+
undantag i components/hero.css per ADR 0019.
|
|
22
22
|
================================================================ */
|
|
23
23
|
|
|
24
24
|
|
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
/* ================================================================
|
|
2
2
|
components/hero-roll.css
|
|
3
3
|
Delad animationskomponent: kodlas-rullnings-animation pa hero-siffror.
|
|
4
|
-
Aktiveras via <p class="
|
|
4
|
+
Aktiveras via <p class="hero__amount" data-animate-roll="N">N kr</p>
|
|
5
5
|
och hero-roll.js. Ursprung: Ekonom Sprint J + J.3-J.6 (2026-04-27 ff).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
v4.0.0 (2026-05-14, ADR 0019): `.hero-amount`-compound raderad.
|
|
8
|
+
`.hero__amount` ar nu canonisk i hero.css. Digit-roller-children
|
|
9
|
+
ar BEM-element av separat block `.hero-roll` (anti-3-niva-nesting).
|
|
10
|
+
|
|
11
|
+
Helt app-token-fri - refererar bara primitives. Samma styling for
|
|
12
|
+
Jubb och Ekonom.
|
|
11
13
|
|
|
12
14
|
Krav for korrekt rendering:
|
|
13
15
|
- font-variant-numeric: tabular-nums (sa 0-9-glyfer ar lika breda)
|
|
14
16
|
- --lh-tight definierad i primitives (anvands som rullhojd)
|
|
15
17
|
- prefers-reduced-motion respekteras (hoppar direkt till slut-state)
|
|
18
|
+
|
|
19
|
+
Blocks:
|
|
20
|
+
.hero-roll - animation-mekanism (bara BEM-children, ingen baseregel)
|
|
21
|
+
|
|
22
|
+
Element:
|
|
23
|
+
.hero-roll__digit-roller - en per siffra, overflow:hidden-clipper
|
|
24
|
+
.hero-roll__digit-track - vertikal stack av siffer-spans
|
|
25
|
+
.hero-roll__static-char - mellanslag och "kr"-suffix mellan rollers
|
|
16
26
|
================================================================ */
|
|
17
27
|
|
|
18
28
|
|
|
19
29
|
/* ================================================================
|
|
20
|
-
====
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
==== HERO__AMOUNT INLINE-FLEX VID ANIMATION
|
|
31
|
+
.hero__amount ar default block-flow (text-content). Nar JS bygger
|
|
32
|
+
roller-children-DOM aktiveras inline-flex via :has()-selector sa
|
|
33
|
+
rollerna staplas horizontellt. Server-text + post-animation-restore
|
|
34
|
+
behaller default block-flow.
|
|
25
35
|
================================================================ */
|
|
26
|
-
.hero-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
font-size: var(--hero-amount-fz, var(--fs-100));
|
|
30
|
-
/* Undantag fran 400/500-policy: display-siffra, inte UI-text.
|
|
31
|
-
600 ger nodvandig visuell tyngd pa hero-storlekar. */
|
|
32
|
-
font-weight: 600;
|
|
33
|
-
letter-spacing: var(--ls-tightest);
|
|
34
|
-
color: var(--text-default);
|
|
35
|
-
line-height: var(--lh-tight);
|
|
36
|
-
/* Tabular-nums kritisk for rullnings-animationen: alla 0-9-glyfer
|
|
37
|
-
packas identiskt sa layout inte vibrerar nar siffran roterar. */
|
|
38
|
-
font-variant-numeric: tabular-nums;
|
|
36
|
+
.hero__amount[data-animate-roll]:has(.hero-roll__digit-roller) {
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
align-items: baseline;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
En per siffra. Containrar en track av siffror som translateY:as
|
|
45
45
|
till slutvardet. Overflow:hidden klipper allt utom synlig position.
|
|
46
46
|
================================================================ */
|
|
47
|
-
.hero-
|
|
47
|
+
.hero-roll__digit-roller {
|
|
48
48
|
display: inline-block;
|
|
49
49
|
height: var(--hero-digit-height, 1em);
|
|
50
50
|
line-height: var(--hero-digit-height, 1em);
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
/* Vignett over och under sa siffrorna fadar ut nar de rullar in/ut.
|
|
57
57
|
--hero-vignette-color satts av app-konsumenten om subtle fade
|
|
58
58
|
onskas mot bg, default transparent. */
|
|
59
|
-
.hero-
|
|
60
|
-
.hero-
|
|
59
|
+
.hero-roll__digit-roller::before,
|
|
60
|
+
.hero-roll__digit-roller::after {
|
|
61
61
|
content: '';
|
|
62
62
|
position: absolute;
|
|
63
63
|
left: 0;
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
background: transparent;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
.hero-
|
|
71
|
+
.hero-roll__digit-roller::before {
|
|
72
72
|
top: 0;
|
|
73
73
|
background: linear-gradient(
|
|
74
74
|
to bottom,
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
.hero-
|
|
80
|
+
.hero-roll__digit-roller::after {
|
|
81
81
|
bottom: 0;
|
|
82
82
|
background: linear-gradient(
|
|
83
83
|
to top,
|
|
@@ -91,15 +91,15 @@
|
|
|
91
91
|
==== DIGIT-TRACK
|
|
92
92
|
Vertikal stack av siffer-spans. translateY-styrt av JS via inline
|
|
93
93
|
transition + transform. tabular-nums upprepad pa span-niva som
|
|
94
|
-
safety-net om
|
|
94
|
+
safety-net om hero__amount-arvet skulle brytas av nan parent.
|
|
95
95
|
================================================================ */
|
|
96
|
-
.hero-
|
|
96
|
+
.hero-roll__digit-track {
|
|
97
97
|
display: flex;
|
|
98
98
|
flex-direction: column;
|
|
99
99
|
will-change: transform;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
.hero-
|
|
102
|
+
.hero-roll__digit-track > span {
|
|
103
103
|
height: var(--hero-digit-height, 1em);
|
|
104
104
|
line-height: var(--hero-digit-height, 1em);
|
|
105
105
|
display: block;
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
Mellanslag och "kr"-suffix runt rollers. white-space: pre bevarar
|
|
113
113
|
exakta blanksteg.
|
|
114
114
|
================================================================ */
|
|
115
|
-
.hero-
|
|
115
|
+
.hero-roll__static-char {
|
|
116
116
|
display: inline-block;
|
|
117
117
|
white-space: pre;
|
|
118
118
|
}
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
denna regel bara safety-net for befintliga inline-transitions.
|
|
126
126
|
================================================================ */
|
|
127
127
|
@media (prefers-reduced-motion: reduce) {
|
|
128
|
-
.hero-
|
|
128
|
+
.hero-roll__digit-track {
|
|
129
129
|
transition: none !important;
|
|
130
130
|
}
|
|
131
131
|
}
|
package/css/components/hero.css
CHANGED
|
@@ -3,21 +3,26 @@
|
|
|
3
3
|
Stora display-rubriker med amount + meta. Används på dashboard,
|
|
4
4
|
detaljvyer, primary-page-toppar.
|
|
5
5
|
|
|
6
|
+
v4.0.0 (2026-05-14, ADR 0019): `.hero-amount`-compound-block raderat.
|
|
7
|
+
`.hero__amount` är canonisk klass för alla hero-display-siffror
|
|
8
|
+
(animerade och statiska). Digit-roller-animationen bygger
|
|
9
|
+
`.hero-roll__*`-children inom `.hero__amount[data-animate-roll]` -
|
|
10
|
+
se components/hero-roll.css.
|
|
11
|
+
|
|
6
12
|
Blocks:
|
|
7
|
-
.hero
|
|
8
|
-
.hero-amount - sibling-block: hero-roll-animations-target
|
|
13
|
+
.hero - wrapper med padding och text-alignment (huvud-block)
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
inte .hero__amount. Definieras även i hero-roll.css (animation-styling).
|
|
15
|
+
Element:
|
|
16
|
+
.hero__heading, .hero__label, .hero__amount, .hero__amount-row,
|
|
17
|
+
.hero__meta, .hero__actions
|
|
14
18
|
|
|
15
19
|
Modifiers:
|
|
16
|
-
.hero__amount --card (40px sekundär), --fluid (clamp 40-80px)
|
|
17
20
|
.hero__label --muted
|
|
21
|
+
.hero__amount --card (40px sekundär), --fluid (clamp 40-80px)
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
.hero__amount font-weight 600 är dokumenterat undantag från
|
|
24
|
+
400/500-policyn (regel 5) - display-siffer-tyngd ärvd från tidigare
|
|
25
|
+
`.hero-amount`-implementation per ADR 0019.
|
|
21
26
|
================================================================ */
|
|
22
27
|
.hero {
|
|
23
28
|
/* 2026-05-13: text-align center for hero-sektion. Hero-content
|
|
@@ -56,9 +61,15 @@
|
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
.hero__amount {
|
|
59
|
-
/* 2026-05-13: fs-80 → fs-100 (25% storre) for mer display-tyngd.
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
/* 2026-05-13: fs-80 → fs-100 (25% storre) for mer display-tyngd.
|
|
65
|
+
2026-05-14 (ADR 0019): font-size konsumerar --hero-amount-fz-token
|
|
66
|
+
for app-override-mojlighet. */
|
|
67
|
+
font-size: var(--hero-amount-fz, var(--fs-100));
|
|
68
|
+
/* Undantag fran 400/500-policy (regel 5): display-siffra, inte
|
|
69
|
+
UI-text. 600 ger nodvandig visuell tyngd pa hero-storlekar.
|
|
70
|
+
Per ADR 0019 garlder weight 600 alla hero__amount-instances
|
|
71
|
+
(animerade och statiska) for konsekvens. */
|
|
72
|
+
font-weight: 600;
|
|
62
73
|
letter-spacing: var(--ls-tightest, -0.04em);
|
|
63
74
|
color: var(--text-default);
|
|
64
75
|
line-height: var(--lh-tight);
|
package/js/hero-roll.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Hero Roll Animation - delad komponent
|
|
2
|
-
* Krav: .
|
|
2
|
+
* Krav: .hero__amount[data-animate-roll] i HTML
|
|
3
3
|
* Krav: font-variant-numeric: tabular-nums pa elementet
|
|
4
4
|
* Krav: --lh-tight definierad i CSS (anvands som rullhojd)
|
|
5
5
|
* Turbo: triggar via turbo:swap-event om Turbo finns, annars fresh-session only
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
let lastSeenUrl = window.location.href;
|
|
69
69
|
|
|
70
70
|
function getHeroEl () {
|
|
71
|
-
return document.querySelector( '.
|
|
71
|
+
return document.querySelector( '.hero__amount[data-animate-roll]' );
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
function prefersReducedMotion () {
|
|
@@ -77,10 +77,10 @@
|
|
|
77
77
|
|
|
78
78
|
function buildRoller ( targetDigit ) {
|
|
79
79
|
const roller = document.createElement( 'span' );
|
|
80
|
-
roller.className = 'hero-
|
|
80
|
+
roller.className = 'hero-roll__digit-roller';
|
|
81
81
|
|
|
82
82
|
const track = document.createElement( 'span' );
|
|
83
|
-
track.className = 'hero-
|
|
83
|
+
track.className = 'hero-roll__digit-track';
|
|
84
84
|
|
|
85
85
|
const totalSteps = targetDigit + ( EXTRA_ROUNDS * 10 );
|
|
86
86
|
for ( let i = 0; i <= totalSteps; i++ ) {
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
|
|
96
96
|
function buildStaticChar ( text ) {
|
|
97
97
|
const span = document.createElement( 'span' );
|
|
98
|
-
span.className = 'hero-
|
|
98
|
+
span.className = 'hero-roll__static-char';
|
|
99
99
|
span.textContent = text;
|
|
100
100
|
return span;
|
|
101
101
|
}
|
package/js/turbo-nav.js
CHANGED
|
@@ -256,7 +256,7 @@
|
|
|
256
256
|
performSwap( doc );
|
|
257
257
|
// Sprint J.3: dispatcha SYNKRON 'turbo:swap'-event INOM
|
|
258
258
|
// swap-callback fore VT slut-snapshot tas. Subscribers (hero-
|
|
259
|
-
// roll.js) kan modifiera nya DOM (t.ex. byta ut
|
|
259
|
+
// roll.js) kan modifiera nya DOM (t.ex. byta ut hero__amount
|
|
260
260
|
// till digit-rollers) sa VT slut-state inkluderar deras
|
|
261
261
|
// modifikationer. Forhindrar 0.2s-flash av server-text
|
|
262
262
|
// under VT-fade vid month-change.
|
package/package.json
CHANGED
|
@@ -80,12 +80,13 @@ För varje entry gäller:
|
|
|
80
80
|
- **Tokens:** `currentColor` - parent-element styr fargen
|
|
81
81
|
|
|
82
82
|
### hero-roll (`hero-roll.css`)
|
|
83
|
-
- **Blocks:** `.hero-
|
|
84
|
-
- **Element:** `.hero-
|
|
85
|
-
- **States:** `[data-animate-roll]` pa `.
|
|
83
|
+
- **Blocks:** `.hero-roll` (animation-mekanism - bara BEM-children, ingen baseregel; agglomereras inom `.hero__amount[data-animate-roll]`)
|
|
84
|
+
- **Element:** `.hero-roll__digit-roller`, `.hero-roll__digit-track`, `.hero-roll__static-char`
|
|
85
|
+
- **States:** `[data-animate-roll]` pa `.hero__amount` (JS triggar animation, bygger `.hero-roll__*`-children)
|
|
86
86
|
- **Anvand:** Display-siffer-animation pa hero-vyer (kodlas-rullning). `__digit-roller` ar wrapper med ::before/::after-faders, `__digit-track` ar siffran-stack som flyttas vertikalt, `__static-char` ar statiska tecken (kr-suffix, kommatecken) utanfor rullnings-mekaniken.
|
|
87
87
|
- **INTE:** Vanliga numeriska varden (anvand `.hero__amount` utan data-animate-roll)
|
|
88
|
-
- **Tokens:** `--text-default`, `--lh-tight`, `--ls-tightest
|
|
88
|
+
- **Tokens:** `--text-default`, `--lh-tight`, `--ls-tightest`. CSS aktiverar `display: inline-flex` pa `.hero__amount[data-animate-roll]:has(.hero-roll__digit-roller)` sa rollerna staplas horizontellt.
|
|
89
|
+
- **Konvention (ADR 0019, v4.0.0):** `.hero-roll` ar separat block fran `.hero__amount` for att undvika 3-niva-BEM-nesting (`.block__elem__sub` ar forbjudet per ADR 0011). JS bygger `.hero-roll__*`-children inom `.hero__amount[data-animate-roll]` vid trigger.
|
|
89
90
|
|
|
90
91
|
### divider (`divider.css`)
|
|
91
92
|
- **Blocks:** `.divider`
|
|
@@ -194,11 +195,13 @@ För varje entry gäller:
|
|
|
194
195
|
- **INTE:** Single-bar-progress (anvand `.hbar`)
|
|
195
196
|
|
|
196
197
|
### hero (`hero.css`)
|
|
197
|
-
- **Blocks:** `.hero
|
|
198
|
+
- **Blocks:** `.hero`
|
|
198
199
|
- **Element:** `.hero__heading`, `.hero__amount`, `.hero__amount-row`, `.hero__label`, `.hero__meta`, `.hero__actions`
|
|
199
200
|
- **Modifiers:** `.hero__amount--card`, `.hero__amount--fluid`, `.hero__label--muted`
|
|
200
|
-
- **Anvand:** Stora display-rubriker (
|
|
201
|
+
- **Anvand:** Stora display-rubriker. `.hero__amount` ar default 100px (via `--hero-amount-fz`-token), `--card` 40px, `--fluid` clamp(40, 12vw, 80).
|
|
201
202
|
- **INTE:** Vanlig `<h1>` (anvand `.heading-1`)
|
|
203
|
+
- **Animation:** `.hero__amount[data-animate-roll="N"]` triggar digit-roller-animation via hero-roll.js + hero-roll.css. Se `hero-roll`-entry ovan.
|
|
204
|
+
- **Konvention (ADR 0019, v4.0.0):** `.hero__amount` ar canonisk klass for alla hero-display-siffror (animerade och statiska). Tidigare `.hero-amount`-compound-block raderat. `.hero__amount` har `font-weight: 600` som dokumenterat undantag fran 400/500-policyn (regel 5) - display-tyngd.
|
|
202
205
|
|
|
203
206
|
### chip (`chip.css`)
|
|
204
207
|
- **Blocks:** `.chip`, `.chip-list` (sibling-wrapper for chips i rad), `.brand-pill`, `.month-pill`
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# 0018 - Outline-offset: list-rows får använda -2px (anti-clipping)
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
Locked.
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
DESIGN-LANGUAGE.md sektion 9 (etablerad 2026-05-12 via ADR 0013)
|
|
9
|
+
specificerade `outline: 2px solid var(--border-focus); outline-offset: 2px`
|
|
10
|
+
som regel för alla interaktiva element.
|
|
11
|
+
|
|
12
|
+
Audit 2026-05-14 (Ekonom UI/UX/CSS-cykel) visade att paketet självt
|
|
13
|
+
använder `outline-offset: -2px` på 5 komponenter:
|
|
14
|
+
|
|
15
|
+
| Fil | Selector | Värde |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `collapsible.css:50` | `.collapsible__header:focus-visible` | `-2px` |
|
|
18
|
+
| `setting-row.css:63` | `.setting-row:focus-visible` | `-2px` |
|
|
19
|
+
| `sheet-content.css:101` | `.sheet__item:focus-visible` | `-1px` |
|
|
20
|
+
| `sheet-content.css:150` | `.sheet__btn--save:focus-visible` | `-1px` |
|
|
21
|
+
| `dropdown.css:72` | `.dropdown__item:focus-visible` | `-2px` |
|
|
22
|
+
|
|
23
|
+
Plus i Ekonom-domain:
|
|
24
|
+
- `ekonom.css:162` `.cat-row__header:focus-visible` `-2px`
|
|
25
|
+
- `ekonom.css:229` `.cat-list-row[data-tx-id]:focus-visible` `-2px`
|
|
26
|
+
- `ekonom.css:319` `.sub-row:focus-visible` `-2px`
|
|
27
|
+
|
|
28
|
+
Detta är **inte** slarv. Negative offset placerar outline _innanför_
|
|
29
|
+
elementets borders, vilket är nödvändigt för:
|
|
30
|
+
|
|
31
|
+
1. **List-rows utan visible border**: outline +2px hänger utanför
|
|
32
|
+
elementet och clip:as av parent-list-row's border eller scroll-
|
|
33
|
+
container's `overflow: hidden`. Result: outline syns delvis eller
|
|
34
|
+
inte alls vid keyboard-fokus.
|
|
35
|
+
2. **Dropdown-items inom radius-14-clippad menu**: outline +2px på
|
|
36
|
+
sista item:t clip:as av menu-borderns BR.
|
|
37
|
+
3. **Items inom collapsible/sheet/card med BR-clipping**: samma
|
|
38
|
+
problem.
|
|
39
|
+
|
|
40
|
+
Standalone-element (knappar, inputs, links i text) har visible borders
|
|
41
|
+
eller tillräcklig spacing för outline +2px.
|
|
42
|
+
|
|
43
|
+
## Decision
|
|
44
|
+
|
|
45
|
+
**Outline-offset är context-sensitive:**
|
|
46
|
+
|
|
47
|
+
### -2px (inom-element, "inset")
|
|
48
|
+
|
|
49
|
+
Använd för items inom radius-clippade containers eller list-rader
|
|
50
|
+
utan visible border:
|
|
51
|
+
- `.list-row:focus-visible`
|
|
52
|
+
- `.setting-row:focus-visible`
|
|
53
|
+
- `.collapsible__header:focus-visible`
|
|
54
|
+
- `.sheet__item:focus-visible` (`-1px` pga tightare BR)
|
|
55
|
+
- `.sheet__btn--save:focus-visible` (`-1px`)
|
|
56
|
+
- `.dropdown__item:focus-visible`
|
|
57
|
+
- App-domain list-rows (Ekonom: `.cat-row__header`, `.cat-list-row[data-tx-id]`,
|
|
58
|
+
`.sub-row`; Jubb: motsvarande list-row-typer)
|
|
59
|
+
|
|
60
|
+
### +2px (utanför-element, "outset")
|
|
61
|
+
|
|
62
|
+
Använd för standalone-element med visible border eller tillräcklig
|
|
63
|
+
spacing:
|
|
64
|
+
- `.btn:focus-visible`
|
|
65
|
+
- `.input:focus-visible` (text-input-undantag - använder `:focus`
|
|
66
|
+
istället per ADR-konvention, men om outline används är det +2px)
|
|
67
|
+
- `.skip-link:focus`
|
|
68
|
+
- `.chip:focus-visible`
|
|
69
|
+
- `.month-pill a:focus-visible`
|
|
70
|
+
- Topbar-actions (`.btn--icon`, `.pwa-avatar`, `.avatar` med focus)
|
|
71
|
+
- Generic `:focus-visible`-default i app/base.css
|
|
72
|
+
|
|
73
|
+
### Komposita BR (för list-row med inset)
|
|
74
|
+
|
|
75
|
+
`border-radius: var(--radius-4)` på outline:n så den följer en mjuk
|
|
76
|
+
form, inte fyrkantig 0px. Per app/base.css default `:focus-visible`-rule.
|
|
77
|
+
|
|
78
|
+
### Outline-bredd
|
|
79
|
+
|
|
80
|
+
`2px solid var(--accent-9)` (paket) eller `var(--border-focus)` (ADR
|
|
81
|
+
0013-spec). Båda är acceptabla token-konsumenter.
|
|
82
|
+
|
|
83
|
+
## Konsekvenser
|
|
84
|
+
|
|
85
|
+
**Bra:**
|
|
86
|
+
- Doktrinen matchar paketets faktiska implementation
|
|
87
|
+
- Future-CC har klar regel för var att applicera vilken offset
|
|
88
|
+
- Inset-värdet är _korrekt_ för list-rows (anti-clipping är funktionell
|
|
89
|
+
design, inte slarv)
|
|
90
|
+
- Stylelint-plugin (Sprint 5) kan validera per-komponent-typ
|
|
91
|
+
|
|
92
|
+
**Trade-offs:**
|
|
93
|
+
- Två varianter av outline-offset i systemet (-2/-1 vs +2). Komplexitet
|
|
94
|
+
ökar marginellt.
|
|
95
|
+
- Visuell skillnad mellan inset och outset är märkbar för
|
|
96
|
+
keyboard-användare. Outset är mer prominent, inset är mer subtil.
|
|
97
|
+
Trade-off: subtilitet vs synlighet. Anti-clipping vinner.
|
|
98
|
+
|
|
99
|
+
## Migration
|
|
100
|
+
|
|
101
|
+
**Ingen migration krävs.** Paketet och appar är redan i låst state.
|
|
102
|
+
ADR dokumenterar bara regeln post-hoc.
|
|
103
|
+
|
|
104
|
+
Sprint F i Ekonom-auditen 2026-05-14 skippades baserat på denna analys -
|
|
105
|
+
fyndet var inte ett brott utan en medveten design-pattern som doktrinen
|
|
106
|
+
inte hade dokumenterat.
|
|
107
|
+
|
|
108
|
+
## Stylelint-plugin (Sprint 5, framtida)
|
|
109
|
+
|
|
110
|
+
Validera:
|
|
111
|
+
- `outline-offset: -1px` eller `-2px` är endast tillåtet på selectors
|
|
112
|
+
som matchar list-row-/dropdown-item-/sheet-item-pattern
|
|
113
|
+
- `outline-offset: 2px` på alla andra :focus-visible-regler
|
|
114
|
+
- Varning vid avvikelse från ADR-tabellen ovan
|
|
115
|
+
|
|
116
|
+
## References
|
|
117
|
+
|
|
118
|
+
- DESIGN-LANGUAGE.md sektion 9 (uppdaterad 2026-05-14)
|
|
119
|
+
- ADR 0013 (visual-coherence-doctrine) - tidigare regel
|
|
120
|
+
- Ekonom DECISIONS 2026-05-14 (audit-cykel skip-rationale för Sprint F)
|
|
121
|
+
- Audit-not: paketets faktiska outline-offset-värden inventoradde
|
|
122
|
+
2026-05-14 via `grep -rEn "outline-offset:\s*-" app/static/css/ds/`
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# 0019 - Hero-amount: en BEM-klass + hero-roll som separat block
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
Locked. Major version-bump (4.0.0) - breaking change.
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Designsystemet hade två parallella klasser för samma display-siffer-koncept:
|
|
9
|
+
|
|
10
|
+
- `.hero-amount` (compound block i `hero-roll.css`, font-weight 600,
|
|
11
|
+
Tier D-undantag) - target för digit-roller-animation via
|
|
12
|
+
`[data-animate-roll]`-attribut.
|
|
13
|
+
- `.hero__amount` (BEM-element av `.hero` i `hero.css`, font-weight 500
|
|
14
|
+
via `--fw-medium`) - statisk display-text utan animation.
|
|
15
|
+
|
|
16
|
+
Plus modifiers `.hero__amount--card` (fs-40), `.hero__amount--fluid`
|
|
17
|
+
(clamp), `.hero__amount-row` (flex-wrapper) - alla på `.hero__amount`-
|
|
18
|
+
namespacet, inte `.hero-amount`.
|
|
19
|
+
|
|
20
|
+
Detta gav templates inkonsekvent klassanvändning:
|
|
21
|
+
|
|
22
|
+
| Template | Klass | Konsekvens |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `avstamning.html` | `.hero-amount[data-animate-roll]` | Animerad, fw-600 |
|
|
25
|
+
| `bolan.html:22` | `.hero-amount.hero__amount--fluid` | Modifier på fel block |
|
|
26
|
+
| `bolan.html:130` | `.hero-amount.hero__amount--fluid` | Samma |
|
|
27
|
+
| `var_bostad.html:34` | `.hero-amount` | Statisk men fw-600 |
|
|
28
|
+
| `dashboard.html` (Jubb) | `.hero-amount[data-animate-roll]` | Animerad, fw-600 |
|
|
29
|
+
|
|
30
|
+
Per ADR 0011 ska modifier sitta på samma block som basklassen.
|
|
31
|
+
`.hero__amount--fluid` på `.hero-amount` är teknisk inkorrekt CSS.
|
|
32
|
+
|
|
33
|
+
Plus: `.hero-amount__digit-roller`, `.hero-amount__digit-track`,
|
|
34
|
+
`.hero-amount__static-char` är BEM-children med dubbel-segment som
|
|
35
|
+
i praktiken bryter ADR 0011-syntax-regeln (BEM stödjer inte 3-nivå-
|
|
36
|
+
nesting `.block__elem__sub`).
|
|
37
|
+
|
|
38
|
+
Per Calle 2026-05-14: "Vi kör konsekvent hero__amount. Det ska vara
|
|
39
|
+
samma. Ingen split."
|
|
40
|
+
|
|
41
|
+
## Decision
|
|
42
|
+
|
|
43
|
+
### En klass: `.hero__amount`
|
|
44
|
+
|
|
45
|
+
Alla hero-display-siffror använder `.hero__amount` (BEM-element av
|
|
46
|
+
`.hero`). `.hero-amount`-compound-block raderas helt. Modifiers
|
|
47
|
+
(`--card`, `--fluid`) appliceras på `.hero__amount`.
|
|
48
|
+
|
|
49
|
+
`.hero__amount` har **font-weight 600** som dokumenterat undantag från
|
|
50
|
+
400/500-policyn (per Calle: behåll display-tyngd från tidigare
|
|
51
|
+
`.hero-amount`-implementation). Detta ärver behovet hero-roll-display-
|
|
52
|
+
siffror har för visuell tyngd.
|
|
53
|
+
|
|
54
|
+
### Digit-roller children: `.hero-roll`-block
|
|
55
|
+
|
|
56
|
+
Animation-mekanismen renderar children som JS bygger inom `.hero__amount`.
|
|
57
|
+
Dessa namnges som BEM-children av ett separat block `.hero-roll`:
|
|
58
|
+
|
|
59
|
+
- `.hero-roll__digit-roller` (var: `.hero-amount__digit-roller`)
|
|
60
|
+
- `.hero-roll__digit-track` (var: `.hero-amount__digit-track`)
|
|
61
|
+
- `.hero-roll__static-char` (var: `.hero-amount__static-char`)
|
|
62
|
+
|
|
63
|
+
`.hero-roll` är ett separat block (bara children, ingen egen base-rule)
|
|
64
|
+
för att undvika 3-nivå-BEM-nesting (`.block__elem__sub` är förbjudet
|
|
65
|
+
per ADR 0011-syntax-regex).
|
|
66
|
+
|
|
67
|
+
JS-modulen `hero-roll.js` adresserar `.hero__amount[data-animate-roll]`
|
|
68
|
+
och bygger `.hero-roll__*`-children inom det elementet.
|
|
69
|
+
|
|
70
|
+
### Filerna efter migration
|
|
71
|
+
|
|
72
|
+
`hero.css` äger `.hero__amount` baseregeln + alla modifiers
|
|
73
|
+
(`--card`, `--fluid`, samt `.hero__amount-row` wrapper).
|
|
74
|
+
|
|
75
|
+
`hero-roll.css` äger bara `.hero-roll__*`-children + `:has()`-aktivering
|
|
76
|
+
av `display: inline-flex` på `.hero__amount[data-animate-roll]:has(.hero-roll__digit-roller)`.
|
|
77
|
+
|
|
78
|
+
### CSS-disciplin
|
|
79
|
+
|
|
80
|
+
`.hero__amount` font-weight 600-undantaget dokumenteras i `hero.css`-
|
|
81
|
+
JSDoc-header och i regel 5 (font-weight 400/500-policyn).
|
|
82
|
+
|
|
83
|
+
## Migration
|
|
84
|
+
|
|
85
|
+
### Klodd-ds (4.0.0)
|
|
86
|
+
|
|
87
|
+
- `css/components/hero.css` - `.hero__amount` font-weight 500 → 600 +
|
|
88
|
+
font-size använder `var(--hero-amount-fz, var(--fs-100))` (override-
|
|
89
|
+
vänligt)
|
|
90
|
+
- `css/components/hero-roll.css`:
|
|
91
|
+
- Radera `.hero-amount` baseregeln
|
|
92
|
+
- Rename `.hero-amount__digit-roller` → `.hero-roll__digit-roller`
|
|
93
|
+
- Rename `.hero-amount__digit-track` → `.hero-roll__digit-track`
|
|
94
|
+
- Rename `.hero-amount__static-char` → `.hero-roll__static-char`
|
|
95
|
+
- Update `:has()`-selector
|
|
96
|
+
- `css/base/typography.css` - kommentar uppdateras
|
|
97
|
+
- `js/hero-roll.js`:
|
|
98
|
+
- Selector `.hero-amount[data-animate-roll]` → `.hero__amount[data-animate-roll]`
|
|
99
|
+
- Roller-element className → `hero-roll__digit-roller`
|
|
100
|
+
- Track-element className → `hero-roll__digit-track`
|
|
101
|
+
- Static-char-element className → `hero-roll__static-char`
|
|
102
|
+
- `js/turbo-nav.js` - kommentar
|
|
103
|
+
- `references/02-components.md` - hero-roll-entry komplett rename
|
|
104
|
+
- `references/DESIGN-LANGUAGE.md` sektion 3 - uppdatera Pattern A
|
|
105
|
+
|
|
106
|
+
### Ekonom
|
|
107
|
+
|
|
108
|
+
- 5 templates: `avstamning.html`, `bolan.html` (x2), `installningar/var_bostad.html`,
|
|
109
|
+
`design.html`. Klass `hero-amount` → `hero__amount`.
|
|
110
|
+
- `ds/base.css` 2 ställen: tabular-nums-selector + user-select-text
|
|
111
|
+
- SW VERSION + static_version bump
|
|
112
|
+
|
|
113
|
+
### Jubb
|
|
114
|
+
|
|
115
|
+
- 2 templates med klassanvändning: `dashboard.html`, `design.html` (line 853)
|
|
116
|
+
- Doc-kommentarer i 3 templates uppdateras (base.html, design.html line 859,
|
|
117
|
+
1336)
|
|
118
|
+
- `ds/base.css` 2 ställen
|
|
119
|
+
- `ds/jubb.css` rad 726-727 (desktop responsive overrides)
|
|
120
|
+
- `pages/design.css` rad 403 (.ds-anim-demo styling)
|
|
121
|
+
- SW VERSION + static_version bump
|
|
122
|
+
|
|
123
|
+
### Stylelint-regex
|
|
124
|
+
|
|
125
|
+
Oförändrad - `.hero__amount` (en `__`) + `.hero-roll__digit-roller` (en `__`)
|
|
126
|
+
matchar standard-BEM. Inga undantag behövs i regex.
|
|
127
|
+
|
|
128
|
+
## Konsekvenser
|
|
129
|
+
|
|
130
|
+
**Bra:**
|
|
131
|
+
- En klass per koncept - templates har inte längre val mellan
|
|
132
|
+
`.hero-amount` och `.hero__amount`
|
|
133
|
+
- Modifiers `--card`/`--fluid` används korrekt (på samma block som basklassen)
|
|
134
|
+
- BEM-konsekvens: ingen 3-nivå-nesting (`.block__elem__sub`)
|
|
135
|
+
- Future-CC kan inte introducera mismatch
|
|
136
|
+
- Stylelint fångar nya brott via existerande regex
|
|
137
|
+
|
|
138
|
+
**Trade-offs:**
|
|
139
|
+
- Major version-bump (4.0.0) - alla apparna måste uppdatera templates
|
|
140
|
+
+ lokala CSS-overrides parallellt
|
|
141
|
+
- Visuell paritet: `.hero__amount` får font-weight 600 (var 500). Statiska
|
|
142
|
+
hero-vyer (utan digit-roller) kommer rendera tjockare display-text. Per
|
|
143
|
+
Calle: "konsekvent hero__amount" - ingen split, weight 600 vinner.
|
|
144
|
+
- `.hero-roll`-namn introducerar nytt block-koncept. Dokumentera i 02-components.
|
|
145
|
+
|
|
146
|
+
## References
|
|
147
|
+
|
|
148
|
+
- ADR 0011 (strikt-bem-elementsyntax)
|
|
149
|
+
- ADR 0012 (stylelint-bem-enforcement)
|
|
150
|
+
- DESIGN-LANGUAGE.md sektion 3 (page-toppar)
|
|
151
|
+
- Ekonom DECISIONS 2026-05-14 (audit-cykel skip-rationale för Sprint D)
|
|
152
|
+
- Calle's beslut 2026-05-14: "Vi kör konsekvent hero__amount. Det ska
|
|
153
|
+
vara samma. Ingen split."
|
|
@@ -82,7 +82,7 @@ För dashboards där primär-data är en STOR siffer-display.
|
|
|
82
82
|
|
|
83
83
|
```html
|
|
84
84
|
<section class="hero">
|
|
85
|
-
<p class="hero__label">
|
|
85
|
+
<p class="hero__label">Sektion-label</p>
|
|
86
86
|
<p class="hero__amount">{stort-värde}</p>
|
|
87
87
|
<p class="hero__meta">{kontextuell-info}</p>
|
|
88
88
|
<div class="hero__actions">
|
|
@@ -91,12 +91,19 @@ För dashboards där primär-data är en STOR siffer-display.
|
|
|
91
91
|
</section>
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
`.hero__amount` är fs-
|
|
95
|
-
(fs-40) för hero inom panel.
|
|
96
|
-
för responsiv siffer-display.
|
|
94
|
+
`.hero__amount` är default fs-100 (`--hero-amount-fz`-token, override-vänlig
|
|
95
|
+
per app). `.hero__amount--card` (fs-40) för hero inom panel.
|
|
96
|
+
`.hero__amount--fluid` (clamp 40-80vw) för responsiv siffer-display.
|
|
97
|
+
Font-weight 600 dokumenterat undantag (display-tyngd, ADR 0019).
|
|
97
98
|
|
|
98
99
|
Hero har `padding: 20px 0 24px` (alignment med övriga container-element).
|
|
99
100
|
|
|
101
|
+
**Animation:** lägg till `data-animate-roll="N"` på `.hero__amount`
|
|
102
|
+
för digit-roller-animation vid fresh-session eller turbo:swap. JS i
|
|
103
|
+
hero-roll.js bygger `.hero-roll__*`-children inom elementet. Se
|
|
104
|
+
ADR 0019 (`.hero__amount` är canonisk klass - tidigare `.hero-amount`
|
|
105
|
+
raderat i v4.0.0).
|
|
106
|
+
|
|
100
107
|
### Pattern B: `.page-header` (utility-page med titel)
|
|
101
108
|
|
|
102
109
|
För pages utan primär-metric, bara navigation-kontext.
|
|
@@ -289,6 +296,24 @@ egna `margin-bottom`. Välj en mekanism per kontext.
|
|
|
289
296
|
- List-rows: `padding: 14px 0` minimum (= 50px height med text-line).
|
|
290
297
|
- Icon-buttons: `width: 44px; height: 44px`.
|
|
291
298
|
|
|
299
|
+
### Focus-ring (outline-offset context-sensitive)
|
|
300
|
+
|
|
301
|
+
Outline-offset är **inte** alltid `+2px`. Inom radius-clippade
|
|
302
|
+
containers eller list-rader utan visible border klipps `+2px` outline
|
|
303
|
+
av parent. Använd inset (`-2px` eller `-1px`) i dessa fall för
|
|
304
|
+
synlig fokus-ring.
|
|
305
|
+
|
|
306
|
+
| Kontext | offset | Komponenter |
|
|
307
|
+
|---|---|---|
|
|
308
|
+
| Standalone-element med border eller spacing | `+2px` | `.btn`, `.chip`, `.skip-link`, `.month-pill a`, topbar-actions, generic `:focus-visible` |
|
|
309
|
+
| List-rader utan visible border | `-2px` | `.list-row`, `.setting-row`, `.collapsible__header`, `.dropdown__item` |
|
|
310
|
+
| Items inom radius-clippad menu/sheet | `-1px` | `.sheet__item`, `.sheet__btn--save` (tightare BR kräver -1) |
|
|
311
|
+
| App-domain list-rows | `-2px` | Ekonom `.cat-row__header`/`.cat-list-row[data-tx-id]`/`.sub-row`; Jubb motsvarande |
|
|
312
|
+
|
|
313
|
+
`outline: 2px solid var(--accent-9)` (eller `var(--border-focus)`) +
|
|
314
|
+
`border-radius: var(--radius-4)` på outline:n så den följer en mjuk
|
|
315
|
+
form. Se ADR 0018 för full rationale.
|
|
316
|
+
|
|
292
317
|
### Mobile-first
|
|
293
318
|
|
|
294
319
|
- Default-styles är mobile (375-430 viewport).
|