1
2
3:root[data-theme="dark"] {
4 color-scheme: dark;
5 --page: #0d0d0d;
6 --surface-1: #1a1a19;
7 --surface-2: #222221;
8 --text-primary: #ffffff;
9 --text-secondary: #c3c2b7;
10 --text-muted: #898781;
11 --grid: #2c2c2a;
12 --baseline: #383835;
13 --border: rgba(255, 255, 255, 0.10);
14 --series-1: #3987e5;
15 --series-1-track: rgba(57, 135, 229, 0.18);
16 --status-good: #0ca30c;
17 --status-warning: #fab219;
18 --status-warning-track: rgba(250, 178, 25, 0.18);
19 --status-critical: #d03b3b;
20 --status-critical-track: rgba(208, 59, 59, 0.20);
21}
22
23* { box-sizing: border-box; }
24
25html, body {
26 margin: 0;
27 padding: 0;
28}
29
30body {
31 background: var(--page);
32 color: var(--text-primary);
33 font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
34 font-size: 14px;
35 line-height: 1.45;
36}
37
38.app {
39 max-width: 1080px;
40 margin: 0 auto;
41 padding: 24px 20px 48px;
42 display: flex;
43 flex-direction: column;
44 gap: 16px;
45}
46
47
48
49.app-header {
50 display: flex;
51 align-items: center;
52 justify-content: space-between;
53 gap: 16px;
54 flex-wrap: wrap;
55}
56
57.brand {
58 display: flex;
59 align-items: center;
60 gap: 10px;
61}
62
63.brand-mark {
64 width: 14px;
65 height: 14px;
66 border-radius: 4px;
67 background: var(--series-1);
68}
69
70.brand h1 {
71 font-size: 18px;
72 font-weight: 650;
73 margin: 0;
74 letter-spacing: -0.01em;
75}
76
77.header-actions {
78 display: flex;
79 gap: 8px;
80 align-items: center;
81}
82
83
84
85button {
86 font: inherit;
87 cursor: pointer;
88 border-radius: 8px;
89 border: 1px solid transparent;
90 padding: 7px 14px;
91 transition: background 0.12s ease, border-color 0.12s ease;
92}
93
94.btn-primary {
95 background: var(--series-1);
96 color: #ffffff;
97 font-weight: 600;
98}
99.btn-primary:hover { background: #2a78d6; }
100
101.btn-ghost {
102 background: transparent;
103 color: var(--text-secondary);
104 border-color: var(--border);
105}
106.btn-ghost:hover {
107 background: var(--surface-2);
108 color: var(--text-primary);
109}
110
111.btn-icon {
112 padding: 7px 11px;
113 font-weight: 600;
114}
115
116.btn-small {
117 padding: 3px 9px;
118 font-size: 12.5px;
119 border-radius: 6px;
120}
121
122.btn-danger:hover {
123 color: #e66767;
124 border-color: rgba(230, 103, 103, 0.4);
125}
126
127
128
129.stat-row {
130 display: grid;
131 grid-template-columns: 1.6fr 1fr 1fr;
132 gap: 12px;
133}
134
135.stat-tile {
136 background: var(--surface-1);
137 border: 1px solid var(--border);
138 border-radius: 12px;
139 padding: 16px 18px;
140 display: flex;
141 flex-direction: column;
142 gap: 4px;
143}
144
145.stat-label {
146 color: var(--text-muted);
147 font-size: 12.5px;
148}
149
150.stat-value {
151 font-size: 48px;
152 font-weight: 600;
153 letter-spacing: -0.02em;
154 line-height: 1.1;
155}
156
157.stat-value-sm {
158 font-size: 26px;
159}
160
161.stat-value.is-negative { color: var(--status-critical); }
162
163
164
165.grid-2 {
166 display: grid;
167 grid-template-columns: 3fr 2fr;
168 gap: 12px;
169}
170
171.card {
172 background: var(--surface-1);
173 border: 1px solid var(--border);
174 border-radius: 12px;
175 padding: 16px 18px;
176}
177
178.card-head {
179 display: flex;
180 align-items: baseline;
181 justify-content: space-between;
182 gap: 12px;
183 margin-bottom: 12px;
184}
185
186.card-head h2 {
187 font-size: 14.5px;
188 font-weight: 650;
189 margin: 0;
190}
191
192.card-sub {
193 color: var(--text-muted);
194 font-size: 12.5px;
195}
196
197.card-foot {
198 color: var(--text-muted);
199 font-size: 12px;
200 margin: 10px 0 0;
201}
202
203
204
205.chart-wrap {
206 position: relative;
207 height: 260px;
208}
209
210#spending-chart {
211 width: 100%;
212 height: 100%;
213 display: block;
214}
215
216.chart-tooltip {
217 position: absolute;
218 pointer-events: none;
219 background: var(--surface-2);
220 border: 1px solid var(--border);
221 border-radius: 8px;
222 padding: 8px 10px;
223 font-size: 12.5px;
224 color: var(--text-secondary);
225 display: flex;
226 flex-direction: column;
227 gap: 2px;
228 white-space: nowrap;
229 box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
230 z-index: 5;
231}
232
233.chart-tooltip .tt-title {
234 color: var(--text-primary);
235 font-weight: 600;
236}
237
238.chart-tooltip .tt-row { display: flex; align-items: center; gap: 6px; }
239.chart-tooltip .tt-row b { color: var(--text-primary); font-weight: 600; }
240.chart-tooltip .tt-dim { color: var(--text-muted); }
241
242.chart-tooltip .tt-key {
243 width: 8px;
244 height: 8px;
245 border-radius: 50%;
246 background: var(--series-1);
247 display: inline-block;
248}
249
250
251
252.budget-list {
253 display: flex;
254 flex-direction: column;
255 gap: 12px;
256 max-height: 232px;
257 overflow-y: auto;
258 padding-right: 4px;
259}
260
261.budget-row { user-select: none; }
262.budget-editable { cursor: pointer; }
263.budget-editable:hover .budget-name { color: var(--text-primary); }
264
265.budget-head {
266 display: flex;
267 justify-content: space-between;
268 align-items: baseline;
269 gap: 8px;
270 margin-bottom: 4px;
271}
272
273.budget-name {
274 color: var(--text-secondary);
275 font-size: 13px;
276}
277
278.budget-val {
279 color: var(--text-muted);
280 font-size: 12px;
281 font-variant-numeric: tabular-nums;
282}
283
284.budget-val b {
285 color: var(--text-secondary);
286 font-weight: 600;
287}
288
289.budget-pct { margin-right: 4px; }
290
291.badge-over {
292 color: var(--status-critical);
293 font-weight: 600;
294}
295
296
297.budget-track {
298 height: 10px;
299 border-radius: 5px;
300 background: var(--series-1-track);
301 overflow: hidden;
302}
303
304.budget-fill {
305 height: 100%;
306 background: var(--series-1);
307 border-radius: 0 4px 4px 0;
− min-width: 2px;
308}
309
310.budget-near .budget-track { background: var(--status-warning-track); }
311.budget-near .budget-fill { background: var(--status-warning); }
312.budget-over .budget-track { background: var(--status-critical-track); }
313.budget-over .budget-fill { background: var(--status-critical); border-radius: 0; }
314
315
316
317.filter-row {
318 display: flex;
319 gap: 8px;
320 margin-bottom: 12px;
321 flex-wrap: wrap;
322}
323
324input[type="search"],
325input[type="text"],
326input[type="date"],
327input[type="number"],
328select {
329 font: inherit;
330 color: var(--text-primary);
331 background: var(--surface-2);
332 border: 1px solid var(--border);
333 border-radius: 8px;
334 padding: 7px 10px;
335}
336
337input:focus-visible, select:focus-visible, button:focus-visible {
338 outline: 2px solid var(--series-1);
339 outline-offset: 1px;
340}
341
342#filter-search { flex: 1; min-width: 180px; }
343
344
345
346.table-wrap { overflow-x: auto; }
347
348.tx-table {
349 width: 100%;
350 border-collapse: collapse;
351}
352
353.tx-table th {
354 text-align: left;
355 color: var(--text-muted);
356 font-size: 12px;
357 font-weight: 500;
358 padding: 6px 10px;
359 border-bottom: 1px solid var(--baseline);
360}
361
362.tx-table td {
363 padding: 8px 10px;
364 border-bottom: 1px solid var(--grid);
365 vertical-align: middle;
366}
367
368.tx-table tbody tr:hover { background: var(--surface-2); }
369
370.col-date {
371 color: var(--text-muted);
372 font-variant-numeric: tabular-nums;
373 white-space: nowrap;
374}
375
376.col-desc { color: var(--text-secondary); }
377
378.col-amount {
379 text-align: right;
380 font-variant-numeric: tabular-nums;
381 white-space: nowrap;
382}
383
384th.col-amount { text-align: right; }
385
386.col-amount.is-income { color: var(--status-good); }
387
388.col-actions {
389 text-align: right;
390 white-space: nowrap;
391 width: 120px;
392}
393
394.col-actions button { opacity: 0; }
395.tx-table tr:hover .col-actions button,
396.col-actions button:focus-visible { opacity: 1; }
397
398.cat-chip {
399 display: inline-block;
400 padding: 2px 9px;
401 border-radius: 999px;
402 background: var(--surface-2);
403 border: 1px solid var(--border);
404 color: var(--text-secondary);
405 font-size: 12px;
406}
407
408.empty-note {
409 color: var(--text-muted);
410 text-align: center;
411 padding: 24px 10px;
412}
413
414
415
416.overlay {
417 position: fixed;
418 inset: 0;
419 background: rgba(0, 0, 0, 0.55);
420 display: flex;
421 align-items: flex-start;
422 justify-content: center;
423 padding: 10vh 20px 20px;
424 z-index: 20;
425}
426
427.panel-card {
428 background: var(--surface-1);
429 border: 1px solid var(--border);
430 border-radius: 14px;
431 padding: 20px 22px;
432 width: 100%;
433 max-width: 420px;
434 display: flex;
435 flex-direction: column;
436 gap: 12px;
437 box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
438}
439
440.panel-card h2 {
441 margin: 0;
442 font-size: 16px;
443}
444
445.panel-card label {
446 display: flex;
447 flex-direction: column;
448 gap: 4px;
449}
450
451.panel-card label > span {
452 color: var(--text-muted);
453 font-size: 12.5px;
454}
455
456.panel-card small {
457 color: var(--text-muted);
458 font-size: 11.5px;
459}
460
461.panel-actions {
462 display: flex;
463 justify-content: flex-end;
464 gap: 8px;
465 margin-top: 4px;
466}
467
468.form-error {
469 color: var(--status-critical);
470 background: var(--status-critical-track);
471 border-radius: 8px;
472 padding: 8px 10px;
473 margin: 0;
474 font-size: 13px;
475}
476
477.shortcut-list {
478 margin: 0;
479 display: flex;
480 flex-direction: column;
481 gap: 8px;
482}
483
484.shortcut-list > div {
485 display: flex;
486 align-items: center;
487 gap: 12px;
488}
489
490.shortcut-list dt { min-width: 70px; }
491.shortcut-list dd { margin: 0; color: var(--text-secondary); }
492
493kbd {
494 background: var(--surface-2);
495 border: 1px solid var(--border);
496 border-bottom-width: 2px;
497 border-radius: 5px;
498 padding: 1px 6px;
499 font-family: inherit;
500 font-size: 12px;
501 color: var(--text-secondary);
502}
503
504
505
506.app-footer {
507 display: flex;
508 justify-content: space-between;
509 align-items: center;
510 gap: 12px;
511 color: var(--text-muted);
512 font-size: 12.5px;
513 padding-top: 4px;
514 flex-wrap: wrap;
515}
516
517.app-footer a { color: var(--text-secondary); }
518
519.toast {
520 position: fixed;
521 left: 50%;
522 bottom: 24px;
523 transform: translateX(-50%) translateY(12px);
524 background: var(--surface-2);
525 color: var(--text-primary);
526 border: 1px solid var(--border);
527 border-radius: 10px;
528 padding: 10px 16px;
529 font-size: 13px;
530 opacity: 0;
531 pointer-events: none;
532 transition: opacity 0.18s ease, transform 0.18s ease;
533 z-index: 30;
534 box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
535}
536
537.toast.show {
538 opacity: 1;
539 transform: translateX(-50%) translateY(0);
540}
541
542.visually-hidden {
543 position: absolute;
544 width: 1px;
545 height: 1px;
546 overflow: hidden;
547 clip: rect(0 0 0 0);
548 white-space: nowrap;
549}
550
551
552
553@media (max-width: 820px) {
554 .grid-2 { grid-template-columns: 1fr; }
555 .stat-row { grid-template-columns: 1fr; }
556 .stat-value { font-size: 36px; }
557 .col-actions button { opacity: 1; }
558}
559
Discussion
1 comment on this trajectory. Recorded by @patrick-toulme.