style.css
⎇
Raw
1/* Custom layout and components on top of Pico.css */
2
3/* Scale down Pico's default sizing */
4html {
5 font-size: 16px;
6}
7
8/* Remove Pico's default bottom margin on button types */
9[type="button"],
10[type="reset"],
11[type="submit"] {
12 margin-bottom: 0;
13}
14
15/* Layout */
16body {
17 min-height: 100vh;
18 display: flex;
19 flex-direction: column;
20}
21
22header {
23 padding: 1rem 2rem;
24 position: sticky;
25 top: 0;
26 z-index: 100;
27 background-color: var(--pico-card-background-color);
28 border-bottom: 1px solid var(--pico-muted-border-color);
29 display: flex;
30 align-items: center;
31 gap: 2rem;
32}
33
34header h1 {
35 margin: 0;
36 flex-shrink: 0;
37 font-size: 1.5rem;
38}
39
40nav {
41 display: flex;
42 gap: 1rem;
43}
44
45nav a {
46 color: var(--pico-muted-color);
47 text-decoration: none;
48 padding: 0.5rem 1rem;
49 border-radius: var(--pico-border-radius);
50 transition: all 0.2s;
51}
52
53nav a:hover {
54 color: var(--pico-primary);
55 background-color: var(--pico-card-sectioning-background-color);
56}
57
58nav a.active {
59 background-color: var(--pico-primary);
60 color: var(--pico-primary-inverse);
61 font-weight: 600;
62}
63
64main {
65 flex: 1;
66 padding: 2rem 1rem;
67}
68
69main > * {
70 max-width: 1400px;
71 margin-left: auto;
72 margin-right: auto;
73}
74
75footer {
76 padding: 1rem 2rem;
77 text-align: center;
78 color: var(--pico-muted-color);
79 font-size: 0.9rem;
80 background-color: var(--pico-card-background-color);
81 border-top: 1px solid var(--pico-muted-border-color);
82}
83
84/* Page headers */
85.page-header {
86 display: flex;
87 justify-content: space-between;
88 align-items: center;
89 margin-bottom: 1.5rem;
90 flex-wrap: wrap;
91 gap: 1rem;
92}
93
94.page-header h2 {
95 font-size: 1.5rem;
96 margin: 0;
97}
98
99.actions {
100 display: flex;
101 gap: 1rem;
102 align-items: center;
103 flex-wrap: wrap;
104}
105
106/* Forms */
107.form-group {
108 margin-bottom: 1rem;
109}
110
111.form-row {
112 display: grid;
113 grid-template-columns: 1fr 1fr;
114 gap: 1rem;
115}
116
117.radio-group {
118 display: flex;
119 flex-wrap: wrap;
120 gap: 1rem;
121}
122
123.radio-group label {
124 display: flex;
125 align-items: center;
126 gap: 0.3rem;
127 cursor: pointer;
128 font-weight: normal;
129 margin-bottom: 0;
130}
131
132.form-actions {
133 display: flex;
134 gap: 1rem;
135 margin-top: 1.5rem;
136}
137
138/* Buttons */
139button.small,
140[role="button"].small {
141 padding: 0.3rem 0.6rem;
142 font-size: 0.85rem;
143}
144
145button.danger,
146button.danger:hover,
147button.danger:focus,
148[role="button"].danger,
149[role="button"].danger:hover,
150[role="button"].danger:focus {
151 --pico-background-color: #c62828;
152 --pico-border-color: #c62828;
153 --pico-color: white;
154}
155
156button.danger:hover,
157button.danger:focus,
158[role="button"].danger:hover,
159[role="button"].danger:focus {
160 --pico-background-color: #b71c1c;
161 --pico-border-color: #b71c1c;
162}
163
164button:disabled {
165 opacity: 0.5;
166 cursor: not-allowed;
167}
168
169/* Library */
170.breadcrumbs {
171 margin-bottom: 1rem;
172 display: flex;
173 align-items: center;
174 justify-content: flex-start;
175 gap: 0.35rem;
176 flex-wrap: wrap;
177 text-align: left;
178 width: 100%;
179}
180
181.breadcrumbs a,
182.breadcrumbs span {
183 flex-shrink: 0;
184}
185
186.breadcrumbs .current {
187 cursor: default;
188 pointer-events: none;
189}
190
191.library-grid {
192 display: grid;
193 grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
194 gap: 1rem;
195}
196
197.folder-item,
198.media-item {
199 background: var(--pico-card-background-color);
200 border: 1px solid var(--pico-card-border-color);
201 border-radius: var(--pico-border-radius);
202 overflow: hidden;
203 text-decoration: none;
204 color: var(--pico-color);
205 box-shadow: var(--pico-card-box-shadow);
206}
207
208.folder-item:hover,
209.media-item:hover {
210 background-color: var(--pico-primary-background);
211 border-color: var(--pico-primary-border);
212 box-shadow: var(--pico-card-box-shadow), 0 4px 12px rgba(0, 0, 0, 0.15);
213}
214
215.folder-item:hover .folder-name,
216.media-item:hover .media-info,
217.media-item:hover .media-title,
218.media-item:hover .media-path {
219 color: var(--pico-primary-inverse);
220}
221
222.media-info {
223 transition: color 0.2s;
224}
225
226.media-thumbnail {
227 aspect-ratio: 16/9;
228 background: var(--pico-card-sectioning-background-color);
229 display: flex;
230 align-items: center;
231 justify-content: center;
232 font-size: 3rem;
233 position: relative;
234}
235
236.media-thumbnail .media-duration {
237 position: absolute;
238 bottom: 0.4rem;
239 right: 0.4rem;
240 background: rgba(0, 0, 0, 0.75);
241 color: #fff;
242 padding: 0.15rem 0.4rem;
243 border-radius: var(--pico-border-radius);
244 font-size: 0.75rem;
245 font-weight: 500;
246 line-height: 1;
247}
248
249.folder-icon {
250 aspect-ratio: 16/9;
251 background: var(--pico-card-sectioning-background-color);
252 display: flex;
253 align-items: center;
254 justify-content: center;
255 font-size: 3rem;
256}
257
258.media-thumbnail img {
259 width: 100%;
260 height: 100%;
261 object-fit: cover;
262}
263
264.media-thumbnail img[src$=".svg"] {
265 width: 48px;
266 height: 48px;
267 opacity: 0.5;
268 object-fit: contain;
269}
270
271.folder-name,
272.media-info {
273 padding: 0.75rem;
274}
275
276.media-title {
277 font-weight: 500;
278 margin-bottom: 0.3rem;
279 overflow: hidden;
280 text-overflow: ellipsis;
281 white-space: nowrap;
282}
283
284.media-path {
285 font-size: 0.85rem;
286}
287
288.media-info .media-duration {
289 display: none;
290}
291
292/* Detail view */
293.detail-view {
294 max-width: 900px;
295}
296
297.detail-header {
298 display: flex;
299 justify-content: space-between;
300 align-items: center;
301 margin-bottom: 1.5rem;
302 flex-wrap: wrap;
303 gap: 1rem;
304}
305
306.player-container {
307 background: #000;
308 border-radius: var(--pico-border-radius);
309 overflow: hidden;
310 margin-bottom: 1.5rem;
311 max-height: 80vh;
312 display: flex;
313 align-items: center;
314 justify-content: center;
315}
316
317.player-container video,
318.player-container audio {
319 max-width: 100%;
320 max-height: 80vh;
321 width: auto;
322 height: auto;
323 display: block;
324}
325
326.detail-meta dl {
327 display: grid;
328 grid-template-columns: 150px 1fr;
329 gap: 0.5rem 1rem;
330}
331
332.detail-meta dd {
333 word-break: break-all;
334 margin: 0;
335}
336
337/* Queue */
338.queue-list {
339 display: flex;
340 flex-direction: column;
341 gap: 1rem;
342}
343
344.queue-item {
345 padding: 1rem;
346 margin-bottom: 0;
347}
348
349.queue-header {
350 display: flex;
351 justify-content: space-between;
352 align-items: flex-start;
353 gap: 1rem;
354 flex-wrap: wrap;
355 margin-bottom: 0.5rem;
356}
357
358.queue-url {
359 flex: 1;
360 min-width: 0;
361}
362
363.queue-url a {
364 word-break: break-all;
365}
366
367.queue-status {
368 display: flex;
369 gap: 0.5rem;
370 align-items: center;
371 flex-wrap: wrap;
372}
373
374.status-badge {
375 padding: 0.2rem 0.6rem;
376 border-radius: 12px;
377 font-size: 0.85rem;
378 font-weight: 500;
379}
380
381.status-badge.queued {
382 background: var(--pico-card-sectioning-background-color);
383 color: var(--pico-muted-color);
384}
385
386.status-badge.downloading {
387 background: var(--pico-primary);
388 color: var(--pico-primary-inverse);
389}
390
391.status-badge.completed {
392 background: var(--pico-ins-color);
393 color: white;
394}
395
396.status-badge.error {
397 background: var(--pico-del-color);
398 color: white;
399}
400
401.error-message {
402 color: var(--pico-del-color);
403 font-size: 0.9rem;
404 margin-top: 0.5rem;
405}
406
407.error-text {
408 color: var(--pico-del-color);
409}
410
411.queue-meta {
412 margin-top: 0.5rem;
413 font-size: 0.85rem;
414 display: flex;
415 gap: 1rem;
416}
417
418/* Logs */
419.logs {
420 max-height: 500px;
421 overflow-y: auto;
422 white-space: pre-wrap;
423 word-break: break-all;
424}
425
426pre {
427 padding: 1.5rem;
428}
429
430/* Settings */
431.settings-layout {
432 display: flex;
433 flex-direction: column;
434 gap: 2rem;
435}
436
437.presets-list {
438 display: flex;
439 flex-direction: column;
440 gap: 1rem;
441 margin-bottom: 1.5rem;
442}
443
444.preset-header {
445 display: flex;
446 justify-content: space-between;
447 align-items: center;
448 margin-bottom: 0.5rem;
449}
450
451.preset-header h4 {
452 display: flex;
453 align-items: center;
454 gap: 0.5rem;
455 margin: 0;
456}
457
458.badge {
459 background: var(--pico-primary);
460 color: var(--pico-primary-inverse);
461 padding: 0.1rem 0.4rem;
462 border-radius: 10px;
463 font-size: 0.75rem;
464}
465
466.preset-actions {
467 display: flex;
468 gap: 0.5rem;
469 flex-wrap: wrap;
470 margin-top: 1rem;
471}
472
473.preset-description {
474 margin-bottom: 0.5rem;
475}
476
477.preset-flags {
478 margin-top: 0.5rem;
479}
480
481.preset-flags pre {
482 overflow-x: auto;
483 margin-top: 0.3rem;
484 margin-bottom: 0;
485}
486
487/* Nested articles need visible borders */
488.presets-list article {
489 border: 1px solid var(--pico-muted-border-color);
490}
491
492/* Suppress Pico details marker for button-like summaries */
493details:has(summary[role="button"]) summary::after {
494 display: none !important;
495}
496
497details summary[role="button"] {
498 display: inline-flex;
499 width: auto;
500}
501
502/* Toggle text: Edit vs Cancel */
503details .cancel-text {
504 display: none;
505}
506
507details[open] .edit-text {
508 display: none;
509}
510
511details[open] .cancel-text {
512 display: inline;
513}
514
515/* Delete overlay popover */
516.delete-overlay {
517 position: relative;
518}
519
520.delete-overlay summary {
521 list-style: none;
522 cursor: pointer;
523}
524
525.delete-overlay summary::-webkit-details-marker {
526 display: none;
527}
528
529.delete-overlay summary::after {
530 display: none !important;
531}
532
533.delete-overlay .overlay-panel {
534 position: absolute;
535 top: calc(100% + 0.5rem);
536 right: 0;
537 z-index: 1000;
538 width: 260px;
539 background: var(--pico-background-color);
540 border: 1px solid var(--pico-muted-border-color);
541 border-radius: var(--pico-border-radius);
542 padding: 1rem;
543 box-shadow: var(--pico-dropdown-box-shadow);
544}
545
546/* Popover arrow */
547.delete-overlay .overlay-panel::before {
548 content: '';
549 position: absolute;
550 top: -6px;
551 right: 1rem;
552 width: 10px;
553 height: 10px;
554 background: var(--pico-background-color);
555 border-left: 1px solid var(--pico-muted-border-color);
556 border-top: 1px solid var(--pico-muted-border-color);
557 transform: rotate(45deg);
558}
559
560.delete-overlay .overlay-panel p {
561 margin-bottom: 1rem;
562 font-size: 0.9rem;
563}
564
565.delete-overlay .overlay-panel .form-actions {
566 margin-top: 0;
567 justify-content: flex-end;
568}
569
570/* Inline edit form */
571.edit-form {
572 padding: 1rem;
573}
574
575/* Conditional sections (no-JS show/hide) */
576.conditional-section {
577 margin-bottom: 1rem;
578}
579
580.conditional-section .conditional-content {
581 display: none;
582 margin-top: 1rem;
583}
584
585/* Checkbox triggers */
586.conditional-section:has(input[type="checkbox"].conditional-trigger:checked) .conditional-content {
587 display: block;
588}
589
590/* Radio triggers for format mode */
591.conditional-section:has(.radio-group input[value="preset"]:checked) .conditional-content.format-preset,
592.conditional-section:has(.radio-group input[value="custom"]:checked) .conditional-content.format-custom {
593 display: block;
594}
595
596/* Format mode selector - hidden radio inputs with button-like labels */
597.radio-group.format-mode {
598 display: flex;
599 gap: 0.5rem;
600 margin-top: 0.5rem;
601 flex-wrap: wrap;
602}
603
604.radio-group.format-mode input[type="radio"] {
605 position: absolute;
606 opacity: 0;
607 width: 0;
608 height: 0;
609}
610
611.radio-group.format-mode label {
612 padding: 0.4rem 0.8rem;
613 border: 1px solid var(--pico-muted-border-color);
614 border-radius: var(--pico-border-radius);
615 cursor: pointer;
616 background: var(--pico-background-color);
617 transition: all 0.2s;
618 font-weight: normal;
619}
620
621.radio-group.format-mode label:hover {
622 background: var(--pico-card-sectioning-background-color);
623}
624
625.radio-group.format-mode label:has(input:checked) {
626 background: var(--pico-primary);
627 color: var(--pico-primary-inverse);
628 border-color: var(--pico-primary);
629}
630
631/* Theme selector */
632.theme-selector {
633 display: flex;
634 gap: 0.5rem;
635}
636
637.theme-selector button {
638 min-width: 80px;
639}
640
641.theme-selector button.active {
642 background: var(--pico-primary);
643 color: var(--pico-primary-inverse);
644 border-color: var(--pico-primary);
645}
646
647/* Download form */
648.download-form {
649 max-width: 900px;
650}
651
652.download-form form {
653 max-width: 800px;
654}
655
656/* Filter form inline */
657.filter-form {
658 display: flex;
659 gap: 0.5rem;
660 align-items: center;
661}
662
663.filter-form input,
664.filter-form select,
665.filter-form button {
666 margin-bottom: 0;
667 width: auto;
668 display: inline-block;
669 flex-shrink: 0;
670}
671
672.filter-form input[type="text"] {
673 min-width: 200px;
674}
675
676/* Empty state */
677.empty-state {
678 text-align: center;
679 padding: 3rem;
680}
681
682/* Utility */
683.inline {
684 display: inline;
685}
686
687/* Responsive */
688@media (max-width: 768px) {
689 main {
690 padding: 1rem;
691 }
692
693 .page-header {
694 flex-direction: column;
695 align-items: flex-start;
696 }
697
698 .library-grid {
699 grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
700 }
701
702 .form-row {
703 grid-template-columns: 1fr;
704 }
705
706 .detail-meta dl {
707 grid-template-columns: 1fr;
708 }
709
710 .queue-header {
711 flex-direction: column;
712 }
713
714 header {
715 flex-direction: column;
716 gap: 1rem;
717 padding: 1rem;
718 }
719
720 nav {
721 width: 100%;
722 justify-content: center;
723 }
724}
725