+117
docs/frontend/design-tokens.md
+117
docs/frontend/design-tokens.md
···
1
+
# design tokens
2
+
3
+
CSS custom properties defined in `frontend/src/routes/+layout.svelte`. Use these instead of hardcoding values.
4
+
5
+
## border radius
6
+
7
+
```css
8
+
--radius-sm: 4px; /* tight corners (inputs, small elements) */
9
+
--radius-base: 6px; /* default for most elements */
10
+
--radius-md: 8px; /* cards, modals */
11
+
--radius-lg: 12px; /* larger containers */
12
+
--radius-xl: 16px; /* prominent elements */
13
+
--radius-2xl: 24px; /* hero elements */
14
+
--radius-full: 9999px; /* pills, circles */
15
+
```
16
+
17
+
## typography
18
+
19
+
```css
20
+
/* scale */
21
+
--text-xs: 0.75rem; /* 12px - hints, captions */
22
+
--text-sm: 0.85rem; /* 13.6px - labels, secondary */
23
+
--text-base: 0.9rem; /* 14.4px - body default */
24
+
--text-lg: 1rem; /* 16px - body emphasized */
25
+
--text-xl: 1.1rem; /* 17.6px - subheadings */
26
+
--text-2xl: 1.25rem; /* 20px - section headings */
27
+
--text-3xl: 1.5rem; /* 24px - page headings */
28
+
29
+
/* semantic aliases */
30
+
--text-page-heading: var(--text-3xl);
31
+
--text-section-heading: 1.2rem;
32
+
--text-body: var(--text-lg);
33
+
--text-small: var(--text-base);
34
+
```
35
+
36
+
## colors
37
+
38
+
### accent
39
+
40
+
```css
41
+
--accent: #6a9fff; /* primary brand color (user-customizable) */
42
+
--accent-hover: #8ab3ff; /* hover state */
43
+
--accent-muted: #4a7ddd; /* subdued variant */
44
+
--accent-rgb: 106, 159, 255; /* for rgba() usage */
45
+
```
46
+
47
+
### backgrounds
48
+
49
+
```css
50
+
/* dark theme */
51
+
--bg-primary: #0a0a0a; /* main background */
52
+
--bg-secondary: #141414; /* elevated surfaces */
53
+
--bg-tertiary: #1a1a1a; /* cards, modals */
54
+
--bg-hover: #1f1f1f; /* hover states */
55
+
56
+
/* light theme overrides these automatically */
57
+
```
58
+
59
+
### borders
60
+
61
+
```css
62
+
--border-subtle: #282828; /* barely visible */
63
+
--border-default: #333333; /* standard borders */
64
+
--border-emphasis: #444444; /* highlighted borders */
65
+
```
66
+
67
+
### text
68
+
69
+
```css
70
+
--text-primary; /* high contrast */
71
+
--text-secondary; /* medium contrast */
72
+
--text-tertiary; /* low contrast */
73
+
--text-muted; /* very low contrast */
74
+
```
75
+
76
+
### semantic
77
+
78
+
```css
79
+
--success: #22c55e;
80
+
--warning: #f59e0b;
81
+
--error: #ef4444;
82
+
```
83
+
84
+
## usage
85
+
86
+
```svelte
87
+
<style>
88
+
.card {
89
+
border-radius: var(--radius-md);
90
+
background: var(--bg-tertiary);
91
+
border: 1px solid var(--border-default);
92
+
}
93
+
94
+
.label {
95
+
font-size: var(--text-sm);
96
+
color: var(--text-secondary);
97
+
}
98
+
99
+
input:focus {
100
+
border-color: var(--accent);
101
+
}
102
+
</style>
103
+
```
104
+
105
+
## anti-patterns
106
+
107
+
```css
108
+
/* bad - hardcoded values */
109
+
border-radius: 8px;
110
+
font-size: 14px;
111
+
background: #1a1a1a;
112
+
113
+
/* good - use tokens */
114
+
border-radius: var(--radius-md);
115
+
font-size: var(--text-base);
116
+
background: var(--bg-tertiary);
117
+
```
+1
frontend/CLAUDE.md
+1
frontend/CLAUDE.md
···
6
6
- **state**: global managers in `lib/*.svelte.ts` using `$state` runes (player, queue, uploader, tracks cache)
7
7
- **components**: reusable ui in `lib/components/` (LikeButton, Toast, Player, etc)
8
8
- **routes**: pages in `routes/` with `+page.svelte` and `+page.ts` for data loading
9
+
- **design tokens**: use CSS variables from `+layout.svelte` - never hardcode colors, radii, or font sizes (see `docs/frontend/design-tokens.md`)
9
10
10
11
gotchas:
11
12
- **svelte 5 runes mode**: component-local state MUST use `$state()` - plain `let` has no reactivity (see `docs/frontend/state-management.md`)
+9
-22
frontend/src/routes/portal/+page.svelte
+9
-22
frontend/src/routes/portal/+page.svelte
···
1262
1262
font-size: var(--text-sm);
1263
1263
}
1264
1264
1265
-
input[type='text'] {
1265
+
input[type='text'],
1266
+
input[type='url'],
1267
+
textarea {
1266
1268
width: 100%;
1267
1269
padding: 0.6rem 0.75rem;
1268
1270
background: var(--bg-primary);
···
1274
1276
transition: all 0.15s;
1275
1277
}
1276
1278
1277
-
input[type='text']:focus {
1279
+
input[type='text']:focus,
1280
+
input[type='url']:focus,
1281
+
textarea:focus {
1278
1282
outline: none;
1279
1283
border-color: var(--accent);
1280
1284
}
1281
1285
1282
-
input[type='text']:disabled {
1286
+
input[type='text']:disabled,
1287
+
input[type='url']:disabled,
1288
+
textarea:disabled {
1283
1289
opacity: 0.5;
1284
1290
cursor: not-allowed;
1285
1291
}
1286
1292
1287
1293
textarea {
1288
-
width: 100%;
1289
-
padding: 0.6rem 0.75rem;
1290
-
background: var(--bg-primary);
1291
-
border: 1px solid var(--border-default);
1292
-
border-radius: var(--radius-sm);
1293
-
color: var(--text-primary);
1294
-
font-size: var(--text-base);
1295
-
font-family: inherit;
1296
-
transition: all 0.15s;
1297
1294
resize: vertical;
1298
1295
min-height: 80px;
1299
-
}
1300
-
1301
-
textarea:focus {
1302
-
outline: none;
1303
-
border-color: var(--accent);
1304
-
}
1305
-
1306
-
textarea:disabled {
1307
-
opacity: 0.5;
1308
-
cursor: not-allowed;
1309
1296
}
1310
1297
1311
1298
.hint {