@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,60 @@
1
+ /* ================================================================
2
+ components/icon.css
3
+ .icon - generisk ikon-wrapper med fem size-modifiers.
4
+ Anvands for bade Lucide-ikoner (via <i data-lucide="X" class="icon icon--md">)
5
+ och custom inline-SVG (<svg class="icon icon--md">).
6
+
7
+ color: currentColor sa parent-elementet styr fargen via color-property.
8
+
9
+ Custom SVG-monster (.icon-custom svg) ger standardiserade
10
+ stroke-attribut for handritade ikoner som Lucide saknar.
11
+ ================================================================ */
12
+
13
+
14
+ /* ================================================================
15
+ ==== BAS
16
+ ================================================================ */
17
+ .icon {
18
+ display: inline-flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ flex-shrink: 0;
22
+ color: currentColor;
23
+ vertical-align: middle;
24
+ }
25
+
26
+ /* Lucide ersatter <i data-lucide="X"> med <svg>. Tvinga svg att fylla
27
+ wrappern sa size-modifiers fungerar konsekvent. */
28
+ .icon > svg {
29
+ width: 100%;
30
+ height: 100%;
31
+ }
32
+
33
+
34
+ /* ================================================================
35
+ ==== STORLEKAR
36
+ --md ar default storlek (20px). Anvand --xs/--sm/--lg/--xl explicit.
37
+ ================================================================ */
38
+ .icon--xs { width: var(--space-12); height: var(--space-12); }
39
+ .icon--sm { width: var(--space-16); height: var(--space-16); }
40
+ .icon--md { width: var(--space-20); height: var(--space-20); }
41
+ .icon--lg { width: var(--space-24); height: var(--space-24); }
42
+ .icon--xl { width: var(--space-32); height: var(--space-32); }
43
+
44
+
45
+ /* ================================================================
46
+ ==== CUSTOM SVG-MONSTER
47
+ App-specifika ikoner som Lucide saknar. Anvand som wrapper:
48
+ <span class="icon icon--md icon-custom">
49
+ <svg viewBox="0 0 24 24">...</svg>
50
+ </span>
51
+ stroke-width 1.5 matchar Lucide-default. linecap/linejoin round
52
+ ger samma mjuka look.
53
+ ================================================================ */
54
+ .icon-custom svg {
55
+ fill: none;
56
+ stroke: currentColor;
57
+ stroke-width: 1.5;
58
+ stroke-linecap: round;
59
+ stroke-linejoin: round;
60
+ }
@@ -0,0 +1,144 @@
1
+ /* ================================================================
2
+ components/input.css
3
+ .input + .textarea + .select med konsekvent styling.
4
+ States: :focus, :disabled, [aria-invalid="true"] (error).
5
+ Tillagg: .input-group (label + input + error), .input-icon (input
6
+ med leading ikon).
7
+
8
+ Regel: font-size 17px sa iOS Safari inte auto-zoomar in vid focus.
9
+ Aldrig under 16px pa input-element.
10
+ ================================================================ */
11
+
12
+
13
+ /* ================================================================
14
+ ==== BAS
15
+ ================================================================ */
16
+ .input,
17
+ .textarea,
18
+ .select {
19
+ display: block;
20
+ width: 100%;
21
+ min-height: var(--touch-min);
22
+ padding: 0 var(--space-14);
23
+ font-family: inherit;
24
+ font-size: var(--fs-17);
25
+ line-height: 1.4;
26
+ color: var(--text-default);
27
+ background: var(--surface-sunken);
28
+ border: 1px solid var(--border-default);
29
+ border-radius: var(--radius-10);
30
+ outline: none;
31
+ transition:
32
+ border-color var(--dur-base) var(--ease-spring-snappy),
33
+ box-shadow var(--dur-base) var(--ease-spring-snappy),
34
+ background var(--dur-base) var(--ease-spring-snappy);
35
+ -webkit-appearance: none;
36
+ appearance: none;
37
+ }
38
+
39
+ .textarea {
40
+ min-height: 96px;
41
+ padding: var(--space-12) var(--space-14);
42
+ line-height: var(--lh-base);
43
+ resize: vertical;
44
+ }
45
+
46
+ .select {
47
+ padding-right: var(--space-32);
48
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2390919E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
49
+ background-repeat: no-repeat;
50
+ background-position: right var(--space-12) center;
51
+ background-size: 14px;
52
+ cursor: pointer;
53
+ }
54
+
55
+ .input::placeholder,
56
+ .textarea::placeholder {
57
+ color: var(--text-muted);
58
+ }
59
+
60
+
61
+ /* ================================================================
62
+ ==== STATES
63
+ ================================================================ */
64
+ .input:focus,
65
+ .textarea:focus,
66
+ .select:focus {
67
+ border-color: var(--accent-9);
68
+ box-shadow: 0 0 0 3px var(--accent-a20);
69
+ background: var(--surface-default);
70
+ }
71
+
72
+ .input:disabled,
73
+ .textarea:disabled,
74
+ .select:disabled {
75
+ opacity: 0.5;
76
+ cursor: not-allowed;
77
+ background: var(--surface-default);
78
+ }
79
+
80
+ .input[aria-invalid="true"],
81
+ .textarea[aria-invalid="true"],
82
+ .select[aria-invalid="true"] {
83
+ border-color: var(--bg-danger);
84
+ }
85
+
86
+ .input[aria-invalid="true"]:focus,
87
+ .textarea[aria-invalid="true"]:focus,
88
+ .select[aria-invalid="true"]:focus {
89
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--bg-danger) 20%, transparent);
90
+ }
91
+
92
+
93
+ /* ================================================================
94
+ ==== INPUT-GROUP (label + input + error)
95
+ Wrapper for ett komplett input-falt med metadata.
96
+ ================================================================ */
97
+ .input-group {
98
+ display: flex;
99
+ flex-direction: column;
100
+ gap: var(--space-6);
101
+ }
102
+
103
+ .input-group__label {
104
+ font-size: var(--fs-12);
105
+ font-weight: var(--fw-medium);
106
+ color: var(--text-subtle);
107
+ letter-spacing: 0.02em;
108
+ }
109
+
110
+ .input-group__hint {
111
+ font-size: var(--fs-11);
112
+ color: var(--text-muted);
113
+ line-height: var(--lh-base);
114
+ }
115
+
116
+ .input-group__error {
117
+ font-size: var(--fs-11);
118
+ color: var(--bg-danger);
119
+ line-height: var(--lh-base);
120
+ }
121
+
122
+
123
+ /* ================================================================
124
+ ==== INPUT-ICON (leading icon)
125
+ Wrapper med ikon till vanster om input. Anvands for sok-falt eller
126
+ amount-input med suffix.
127
+ ================================================================ */
128
+ .input-icon {
129
+ position: relative;
130
+ display: flex;
131
+ align-items: center;
132
+ }
133
+
134
+ .input-icon > .icon,
135
+ .input-icon > svg {
136
+ position: absolute;
137
+ left: var(--space-12);
138
+ pointer-events: none;
139
+ color: var(--text-muted);
140
+ }
141
+
142
+ .input-icon > .input {
143
+ padding-left: var(--space-40);
144
+ }
@@ -0,0 +1,192 @@
1
+ /* ================================================================
2
+ components/nav.css
3
+ Bottom-nav, topbar och tab-bar - tre standardnavigations-monster.
4
+
5
+ Bottom-nav ar position:fixed och ALLTID synlig pa mobil. Topbar
6
+ ar in-flow under safe-area. Tab-bar ar inline (inom en panel).
7
+ ================================================================ */
8
+
9
+
10
+ /* ================================================================
11
+ ==== BOTTOM-NAV (fixed, mobil-primar)
12
+ Edge-to-edge pa botten med safe-area-padding for hemskarms-knapp.
13
+ N kolumner via grid (--bottom-nav-cols custom property om appen
14
+ vill annat an default 4).
15
+ ================================================================ */
16
+ .bottom-nav {
17
+ position: fixed;
18
+ bottom: 0;
19
+ left: 0;
20
+ right: 0;
21
+ z-index: var(--z-nav);
22
+ display: grid;
23
+ grid-template-columns: repeat(var(--bottom-nav-cols, 4), 1fr);
24
+ padding: 0;
25
+ padding-bottom: var(--safe-bottom);
26
+ margin: 0;
27
+ max-width: none;
28
+ background: var(--surface-default);
29
+ border: 0;
30
+ border-top: 1px solid var(--border-subtle);
31
+ border-radius: 0;
32
+ }
33
+
34
+ .bottom-nav-item {
35
+ display: flex;
36
+ flex-direction: column;
37
+ align-items: center;
38
+ justify-content: center;
39
+ gap: var(--space-4);
40
+ min-height: var(--touch-min);
41
+ padding: var(--space-8) var(--space-6);
42
+ font-size: var(--fs-10);
43
+ font-weight: var(--fw-medium);
44
+ color: var(--text-muted);
45
+ text-decoration: none;
46
+ -webkit-tap-highlight-color: transparent;
47
+ touch-action: manipulation;
48
+ transition:
49
+ color var(--dur-base) var(--ease-spring-snappy),
50
+ background var(--dur-base) var(--ease-spring-snappy);
51
+ }
52
+
53
+ @media (hover: hover) and (pointer: fine) {
54
+ .bottom-nav-item:hover {
55
+ color: var(--text-default);
56
+ }
57
+ }
58
+
59
+ .bottom-nav-item.active {
60
+ color: var(--accent-text);
61
+ }
62
+
63
+ .bottom-nav-item .icon,
64
+ .bottom-nav-item svg {
65
+ width: var(--space-20);
66
+ height: var(--space-20);
67
+ }
68
+
69
+
70
+ /* ================================================================
71
+ ==== TOPBAR (in-flow header)
72
+ Tre slots: back-knapp / titel / action. Display flex, full bredd.
73
+ ================================================================ */
74
+ .topbar {
75
+ display: flex;
76
+ align-items: center;
77
+ gap: var(--space-12);
78
+ min-height: var(--touch-min);
79
+ padding: 0 var(--space-4);
80
+ margin-bottom: var(--space-16);
81
+ }
82
+
83
+ .topbar-back {
84
+ display: inline-flex;
85
+ align-items: center;
86
+ justify-content: center;
87
+ width: 36px;
88
+ height: 36px;
89
+ padding: 0;
90
+ border: 1px solid var(--border-subtle);
91
+ border-radius: var(--radius-full);
92
+ background: var(--surface-default);
93
+ color: var(--text-default);
94
+ cursor: pointer;
95
+ flex-shrink: 0;
96
+ transition: background var(--dur-base) var(--ease-spring-snappy);
97
+ }
98
+
99
+ @media (hover: hover) and (pointer: fine) {
100
+ .topbar-back:hover {
101
+ background: var(--surface-hover);
102
+ }
103
+ }
104
+
105
+ .topbar-back:focus-visible {
106
+ outline: 2px solid var(--border-focus);
107
+ outline-offset: 2px;
108
+ }
109
+
110
+ .topbar-title {
111
+ flex: 1;
112
+ min-width: 0;
113
+ font-size: var(--fs-17);
114
+ font-weight: var(--fw-medium);
115
+ line-height: var(--lh-tight);
116
+ color: var(--text-default);
117
+ margin: 0;
118
+ overflow: hidden;
119
+ text-overflow: ellipsis;
120
+ white-space: nowrap;
121
+ }
122
+
123
+ .topbar-action {
124
+ display: inline-flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ min-width: 36px;
128
+ min-height: 36px;
129
+ padding: 0 var(--space-10);
130
+ background: transparent;
131
+ border: 0;
132
+ color: var(--text-subtle);
133
+ cursor: pointer;
134
+ flex-shrink: 0;
135
+ transition: color var(--dur-base) var(--ease-spring-snappy);
136
+ }
137
+
138
+ @media (hover: hover) and (pointer: fine) {
139
+ .topbar-action:hover {
140
+ color: var(--text-default);
141
+ }
142
+ }
143
+
144
+
145
+ /* ================================================================
146
+ ==== TAB-BAR (inline tabs i panel)
147
+ Segmented control monster. Active-tab har highlighted bg.
148
+ ================================================================ */
149
+ .tab-bar {
150
+ display: flex;
151
+ gap: var(--space-4);
152
+ padding: var(--space-4);
153
+ background: var(--surface-default);
154
+ border-radius: var(--radius-12);
155
+ }
156
+
157
+ .tab {
158
+ flex: 1;
159
+ display: inline-flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ min-height: 36px;
163
+ padding: 0 var(--space-12);
164
+ font-family: inherit;
165
+ font-size: var(--fs-13);
166
+ font-weight: var(--fw-medium);
167
+ color: var(--text-subtle);
168
+ background: transparent;
169
+ border: 0;
170
+ border-radius: var(--radius-8);
171
+ cursor: pointer;
172
+ text-decoration: none;
173
+ transition:
174
+ background var(--dur-fast) var(--ease-spring-snappy),
175
+ color var(--dur-fast) var(--ease-spring-snappy);
176
+ }
177
+
178
+ @media (hover: hover) and (pointer: fine) {
179
+ .tab:not(.active):hover {
180
+ color: var(--text-default);
181
+ }
182
+ }
183
+
184
+ .tab.active {
185
+ background: var(--surface-raised);
186
+ color: var(--text-default);
187
+ }
188
+
189
+ .tab:focus-visible {
190
+ outline: 2px solid var(--border-focus);
191
+ outline-offset: 1px;
192
+ }
@@ -0,0 +1,128 @@
1
+ /* ================================================================
2
+ components/overlay.css
3
+ Dialog/modal och bottom-sheet - tva overlay-monster.
4
+
5
+ Dialog: centrerad modal for forms och bekraftelser.
6
+ Sheet: bottom-attached for mobile-first sheet-monster.
7
+
8
+ Bagge anvander backdrop med blur for visuell hierarki.
9
+ ================================================================ */
10
+
11
+
12
+ /* ================================================================
13
+ ==== DIALOG-BACKDROP
14
+ Halvtransparent overlay med blur. Anvands av bade .dialog och
15
+ .sheet. native ::backdrop pseudo-element (om <dialog>) eller en
16
+ konkret <div class="dialog-backdrop"> for portal-baserade modaler.
17
+ ================================================================ */
18
+ .dialog-backdrop,
19
+ dialog.dialog::backdrop,
20
+ dialog.sheet::backdrop {
21
+ background: color-mix(in oklch, var(--surface-page) 60%, transparent);
22
+ -webkit-backdrop-filter: blur(4px);
23
+ backdrop-filter: blur(4px);
24
+ }
25
+
26
+
27
+ /* ================================================================
28
+ ==== DIALOG (centrerad modal)
29
+ Native <dialog>-element rekommenderas for fokus-trap + ESC-stang.
30
+ Klassen .dialog kan ocksa anvandas pa <div role="dialog"> nar
31
+ native inte passar (ex. icke-modal panel).
32
+ ================================================================ */
33
+ .dialog {
34
+ position: fixed;
35
+ top: 50%;
36
+ left: 50%;
37
+ transform: translate(-50%, -50%);
38
+ z-index: var(--z-overlay);
39
+ width: calc(100vw - 2 * var(--space-20));
40
+ max-width: 480px;
41
+ max-height: calc(100dvh - 2 * var(--space-40));
42
+ margin: 0;
43
+ padding: 0;
44
+ background: var(--surface-raised);
45
+ color: var(--text-default);
46
+ border: 1px solid var(--border-subtle);
47
+ border-radius: var(--radius-14);
48
+ box-shadow: var(--shadow-float);
49
+ overflow: hidden;
50
+ /* Native <dialog>: aterstall default-sytling. */
51
+ inset: auto;
52
+ }
53
+
54
+ .dialog-header {
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: space-between;
58
+ gap: var(--space-12);
59
+ padding: var(--space-16) var(--space-20);
60
+ border-bottom: 1px solid var(--border-subtle);
61
+ }
62
+
63
+ .dialog-body {
64
+ padding: var(--space-20);
65
+ overflow-y: auto;
66
+ max-height: 60vh;
67
+ }
68
+
69
+ .dialog-footer {
70
+ display: flex;
71
+ justify-content: flex-end;
72
+ gap: var(--space-8);
73
+ padding: var(--space-12) var(--space-20);
74
+ border-top: 1px solid var(--border-subtle);
75
+ background: var(--surface-default);
76
+ }
77
+
78
+
79
+ /* ================================================================
80
+ ==== SHEET (bottom-attached)
81
+ <dialog class="sheet"> ar standardpattern. Native focus-trap, ESC,
82
+ ::backdrop. JS anvander .showModal() / .close().
83
+
84
+ max-height 90svh sa sheet alltid laemnar lite plats overst sa
85
+ anvandaren ser kontexten bakom.
86
+ ================================================================ */
87
+ .sheet {
88
+ position: fixed;
89
+ bottom: 0;
90
+ left: 0;
91
+ right: 0;
92
+ top: auto;
93
+ z-index: var(--z-overlay);
94
+ width: 100%;
95
+ max-width: 600px;
96
+ max-height: 90svh;
97
+ margin: 0 auto;
98
+ padding: var(--space-8) var(--space-20) calc(var(--space-28) + var(--safe-bottom));
99
+ background: var(--surface-raised);
100
+ color: var(--text-default);
101
+ border: 1px solid var(--border-subtle);
102
+ border-bottom: 0;
103
+ border-radius: var(--radius-24) var(--radius-24) 0 0;
104
+ box-shadow: var(--shadow-float);
105
+ overflow-y: auto;
106
+ overscroll-behavior: contain;
107
+ -webkit-overflow-scrolling: touch;
108
+ inset: auto 0 0 0;
109
+ }
110
+
111
+ .sheet-handle {
112
+ width: 32px;
113
+ height: 4px;
114
+ margin: 0 auto var(--space-16);
115
+ background: var(--text-disabled);
116
+ border-radius: var(--radius-full);
117
+ }
118
+
119
+ .sheet-body {
120
+ padding-top: var(--space-4);
121
+ }
122
+
123
+ /* Sheet-section divider for grupper inom samma sheet. */
124
+ .sheet-divider {
125
+ height: 1px;
126
+ background: var(--border-subtle);
127
+ margin: var(--space-16) calc(-1 * var(--space-20));
128
+ }
package/css/index.css ADDED
@@ -0,0 +1,28 @@
1
+ /* ================================================================
2
+ @klodd/ds - convenience-import
3
+ Importerar hela designsystemet i kanonisk ordning. App-specifika
4
+ tema-filer (apps/jubb.css, apps/ekonom.css) importeras separat
5
+ eftersom bara en ar aktiv per pageload.
6
+
7
+ Anvandning:
8
+ <link rel="stylesheet" href="@klodd/ds/css/index.css">
9
+ <link rel="stylesheet" href="@klodd/ds/css/apps/jubb.css">
10
+
11
+ Eller via CSS:
12
+ @import '@klodd/ds/css/index.css';
13
+ @import '@klodd/ds/css/apps/jubb.css';
14
+ ================================================================ */
15
+
16
+ @import './00-primitives.css';
17
+ @import './10-semantic.css';
18
+ @import './base/pwa.css';
19
+ @import './base/typography.css';
20
+ @import './components/button.css';
21
+ @import './components/input.css';
22
+ @import './components/badge.css';
23
+ @import './components/card.css';
24
+ @import './components/nav.css';
25
+ @import './components/feedback.css';
26
+ @import './components/overlay.css';
27
+ @import './components/icon.css';
28
+ @import './components/hero-roll.css';
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@klodd/ds",
3
+ "version": "1.0.0",
4
+ "description": "Klodd Design System - shared tokens, typography, and components for Jubb, Ekonom, and future apps",
5
+ "main": "css/index.css",
6
+ "files": [
7
+ "css/"
8
+ ],
9
+ "keywords": [
10
+ "design-system",
11
+ "css",
12
+ "tokens",
13
+ "dark-mode",
14
+ "radix-colors"
15
+ ],
16
+ "author": "Carl-Eric Persson",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/drawn124578/klodd-ds.git"
21
+ },
22
+ "homepage": "https://github.com/drawn124578/klodd-ds#readme"
23
+ }