irongit

Open source voice dictation for your whole desktop. Local whisper.cpp transcription, global hotkeys, optional LLM enhancement via Claude Code, Gemini CLI, Codex, or any API. Wayland-first.

mutterbox/src/styles.css
685 lines11 KBCSS
1:root {
2 --bg: #131110;
3 --bg-raised: #1c1917;
4 --bg-inset: #0d0c0b;
5 --line: #2c2825;
6 --text: #ece4d8;
7 --text-dim: #93897c;
8 --accent: #ff5c1f;
9 --accent-soft: #b8410f;
10 --ok: #7dbb6a;
11 --danger: #e05646;
12 --mono: "JetBrains Mono", "Fira Code", "Iosevka", ui-monospace, "Cascadia Code",
13 monospace;
14}
15
16* {
17 margin: 0;
18 padding: 0;
19 box-sizing: border-box;
20}
21
22html,
23body,
24#root {
25 height: 100%;
26}
27
28body {
29 font-family: var(--mono);
30 font-size: 13px;
31 line-height: 1.55;
32 background: var(--bg);
33 color: var(--text);
34 -webkit-font-smoothing: antialiased;
35 user-select: none;
36}
37
38code {
39 color: var(--accent);
40 font-size: 12px;
41}
42
43/* ---------- shell ---------- */
44
45.shell {
46 display: flex;
47 height: 100%;
48}
49
50.sidebar {
51 width: 220px;
52 flex-shrink: 0;
53 display: flex;
54 flex-direction: column;
55 border-right: 1px solid var(--line);
56 background: var(--bg-inset);
57 padding: 24px 0;
58}
59
60.wordmark {
61 padding: 0 20px 24px;
62 border-bottom: 1px solid var(--line);
63}
64
65.wordmark-glyph {
66 color: var(--accent);
67 font-size: 15px;
68 letter-spacing: 2px;
69}
70
71.wordmark h1 {
72 font-size: 19px;
73 font-weight: 700;
74 letter-spacing: -0.5px;
75 margin-top: 6px;
76}
77
78.tagline {
79 color: var(--text-dim);
80 font-size: 11px;
81 margin-top: 2px;
82}
83
84nav {
85 display: flex;
86 flex-direction: column;
87 padding: 16px 8px;
88 gap: 2px;
89 flex: 1;
90}
91
92.nav-item {
93 display: flex;
94 gap: 8px;
95 align-items: center;
96 background: none;
97 border: none;
98 color: var(--text-dim);
99 font: inherit;
100 text-align: left;
101 padding: 8px 12px;
102 border-radius: 6px;
103 cursor: pointer;
104}
105
106.nav-item:hover {
107 color: var(--text);
108 background: var(--bg-raised);
109}
110
111.nav-item.active {
112 color: var(--text);
113 background: var(--bg-raised);
114}
115
116.nav-marker {
117 color: var(--accent);
118 width: 10px;
119 display: inline-block;
120}
121
122.sidebar-foot {
123 padding: 16px 20px 0;
124 border-top: 1px solid var(--line);
125}
126
127.dictate-btn {
128 width: 100%;
129 padding: 10px;
130 font: inherit;
131 font-weight: 700;
132 border-radius: 8px;
133 border: 1px solid var(--accent-soft);
134 background: transparent;
135 color: var(--accent);
136 cursor: pointer;
137 transition: background 120ms ease;
138}
139
140.dictate-btn:hover {
141 background: rgba(255, 92, 31, 0.12);
142}
143
144.dictate-btn.state-recording {
145 background: var(--accent);
146 color: #170b05;
147 animation: pulse-bg 1.4s ease-in-out infinite;
148}
149
150.dictate-btn.state-processing {
151 color: var(--text-dim);
152 border-color: var(--line);
153}
154
155@keyframes pulse-bg {
156 50% {
157 filter: brightness(1.25);
158 }
159}
160
161/* ---------- content ---------- */
162
163.content {
164 flex: 1;
165 overflow-y: auto;
166 padding: 36px 44px 60px;
167}
168
169.page-head {
170 margin-bottom: 28px;
171}
172
173.page-head h2 {
174 font-size: 17px;
175 letter-spacing: -0.3px;
176}
177
178.page-head h2::before {
179 content: "# ";
180 color: var(--accent);
181}
182
183.page-head p {
184 color: var(--text-dim);
185 margin-top: 4px;
186 max-width: 52ch;
187}
188
189.field {
190 margin-bottom: 22px;
191 max-width: 560px;
192}
193
194.field > label {
195 display: block;
196 font-size: 11px;
197 text-transform: uppercase;
198 letter-spacing: 1.5px;
199 color: var(--text-dim);
200 margin-bottom: 8px;
201}
202
203.field-row {
204 display: flex;
205 gap: 8px;
206}
207
208input[type="text"],
209input:not([type]),
210select,
211textarea {
212 width: 100%;
213 font: inherit;
214 color: var(--text);
215 background: var(--bg-raised);
216 border: 1px solid var(--line);
217 border-radius: 6px;
218 padding: 9px 12px;
219 outline: none;
220 user-select: text;
221}
222
223input:focus,
224select:focus,
225textarea:focus {
226 border-color: var(--accent-soft);
227}
228
229/* WebKitGTK renders the native dropdown on a light surface, so option colors
230 must be set explicitly or they inherit light text: white on white. */
231select {
232 appearance: none;
233 -webkit-appearance: none;
234 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2393897c' fill='none' stroke-width='1.5'/%3E%3C/svg%3E");
235 background-repeat: no-repeat;
236 background-position: right 12px center;
237 padding-right: 32px;
238 cursor: pointer;
239}
240
241select option {
242 background: #ffffff;
243 color: #1c1917;
244}
245
246textarea {
247 resize: vertical;
248 line-height: 1.6;
249}
250
251.check {
252 display: flex;
253 align-items: center;
254 gap: 10px;
255 cursor: pointer;
256 font-size: 13px;
257 color: var(--text);
258}
259
260.check input {
261 accent-color: var(--accent);
262 width: 14px;
263 height: 14px;
264}
265
266.btn {
267 font: inherit;
268 padding: 9px 16px;
269 border-radius: 6px;
270 border: 1px solid var(--accent-soft);
271 background: transparent;
272 color: var(--accent);
273 cursor: pointer;
274 white-space: nowrap;
275}
276
277.btn:hover:not(:disabled) {
278 background: rgba(255, 92, 31, 0.12);
279}
280
281.btn:disabled {
282 opacity: 0.4;
283 cursor: default;
284}
285
286.btn.ghost {
287 border-color: var(--line);
288 color: var(--text-dim);
289}
290
291.btn.ghost:hover {
292 color: var(--danger);
293 border-color: var(--danger);
294 background: transparent;
295}
296
297.segmented {
298 display: inline-flex;
299 border: 1px solid var(--line);
300 border-radius: 6px;
301 overflow: hidden;
302}
303
304.seg {
305 font: inherit;
306 background: transparent;
307 color: var(--text-dim);
308 border: none;
309 padding: 8px 16px;
310 cursor: pointer;
311}
312
313.seg + .seg {
314 border-left: 1px solid var(--line);
315}
316
317.seg.active {
318 background: var(--bg-raised);
319 color: var(--accent);
320}
321
322.hint {
323 color: var(--text-dim);
324 font-size: 11.5px;
325 margin-top: 7px;
326 max-width: 52ch;
327}
328
329.ok-text {
330 color: var(--ok);
331}
332
333.error-text {
334 color: var(--danger);
335}
336
337.dimmed {
338 opacity: 0.45;
339 pointer-events: none;
340}
341
342.banner {
343 padding: 10px 14px;
344 border-radius: 6px;
345 margin-bottom: 20px;
346 display: flex;
347 justify-content: space-between;
348 gap: 12px;
349 align-items: baseline;
350}
351
352.banner.error {
353 border: 1px solid rgba(224, 86, 70, 0.5);
354 color: var(--danger);
355 background: rgba(224, 86, 70, 0.08);
356}
357
358.banner-dismiss {
359 background: none;
360 border: none;
361 color: inherit;
362 cursor: pointer;
363 font: inherit;
364}
365
366.hotkey-capture {
367 flex: 1;
368 font: inherit;
369 text-align: left;
370 color: var(--text);
371 background: var(--bg-raised);
372 border: 1px solid var(--line);
373 border-radius: 6px;
374 padding: 9px 12px;
375 cursor: pointer;
376}
377
378.hotkey-capture:hover {
379 border-color: var(--accent-soft);
380}
381
382.hotkey-capture.capturing {
383 border-color: var(--accent);
384 color: var(--text-dim);
385 animation: pulse-border 1.2s ease-in-out infinite;
386}
387
388@keyframes pulse-border {
389 50% {
390 border-color: var(--accent-soft);
391 }
392}
393
394.chip-row {
395 display: flex;
396 flex-wrap: wrap;
397 gap: 8px;
398}
399
400.chip {
401 font: inherit;
402 font-size: 12px;
403 padding: 6px 14px;
404 border-radius: 99px;
405 border: 1px solid var(--line);
406 background: var(--bg-raised);
407 color: var(--text-dim);
408 cursor: pointer;
409}
410
411.chip:hover {
412 color: var(--text);
413}
414
415.chip.active {
416 color: var(--accent);
417 border-color: var(--accent-soft);
418 background: rgba(255, 92, 31, 0.08);
419}
420
421.chip.add {
422 border-style: dashed;
423 background: transparent;
424}
425
426.optional {
427 text-transform: none;
428 letter-spacing: 0;
429 opacity: 0.7;
430}
431
432/* ---------- modal ---------- */
433
434.modal-backdrop {
435 position: fixed;
436 inset: 0;
437 background: rgba(0, 0, 0, 0.6);
438 display: flex;
439 align-items: center;
440 justify-content: center;
441 z-index: 50;
442}
443
444.modal {
445 width: min(560px, calc(100vw - 48px));
446 background: var(--bg-raised);
447 border: 1px solid var(--line);
448 border-radius: 10px;
449 padding: 24px;
450 box-shadow: 0 16px 50px rgba(0, 0, 0, 0.55);
451}
452
453.modal h3 {
454 font-size: 15px;
455 margin-bottom: 8px;
456}
457
458.modal h3::before {
459 content: "# ";
460 color: var(--accent);
461}
462
463.modal .btn {
464 margin-top: 16px;
465}
466
467.code-block {
468 position: relative;
469 background: var(--bg-inset);
470 border: 1px solid var(--line);
471 border-radius: 6px;
472 padding: 14px;
473 margin-top: 12px;
474 user-select: text;
475}
476
477.code-block pre {
478 font: inherit;
479 font-size: 12px;
480 color: var(--accent);
481 white-space: pre-wrap;
482 word-break: break-all;
483 padding-right: 72px;
484}
485
486.copy-btn {
487 position: absolute;
488 top: 8px;
489 right: 8px;
490 font: inherit;
491 font-size: 11px;
492 color: var(--text-dim);
493 background: var(--bg-raised);
494 border: 1px solid var(--line);
495 border-radius: 5px;
496 padding: 4px 9px;
497 cursor: pointer;
498}
499
500.copy-btn:hover {
501 color: var(--text);
502 border-color: var(--accent-soft);
503}
504
505.transcript-box {
506 background: var(--bg-inset);
507 border: 1px solid var(--line);
508 border-radius: 6px;
509 padding: 14px;
510 min-height: 64px;
511 user-select: text;
512}
513
514.transcript-box p + p {
515 margin-top: 8px;
516}
517
518/* ---------- models ---------- */
519
520.model-list {
521 list-style: none;
522 max-width: 640px;
523}
524
525.model {
526 display: flex;
527 gap: 16px;
528 align-items: flex-start;
529 justify-content: space-between;
530 padding: 16px 18px;
531 border: 1px solid var(--line);
532 border-radius: 8px;
533 margin-bottom: 10px;
534 background: var(--bg-raised);
535}
536
537.model.active {
538 border-color: var(--accent-soft);
539}
540
541.model-title {
542 display: flex;
543 align-items: baseline;
544 gap: 8px;
545 flex-wrap: wrap;
546}
547
548.model-main {
549 flex: 1;
550}
551
552.model-actions {
553 display: flex;
554 gap: 8px;
555 flex-shrink: 0;
556 align-items: center;
557}
558
559.badge {
560 font-size: 10.5px;
561 padding: 1px 7px;
562 border-radius: 99px;
563 border: 1px solid var(--line);
564 color: var(--text-dim);
565}
566
567.badge.accent {
568 color: var(--accent);
569 border-color: var(--accent-soft);
570}
571
572.badge.dim {
573 opacity: 0.8;
574}
575
576.progress {
577 height: 4px;
578 border-radius: 2px;
579 background: var(--bg-inset);
580 margin-top: 10px;
581 overflow: hidden;
582}
583
584.progress-fill {
585 height: 100%;
586 background: var(--accent);
587 border-radius: 2px;
588 transition: width 300ms ease;
589}
590
591/* ---------- overlay pill ---------- */
592
593html.overlay-mode,
594html.overlay-mode body,
595html.overlay-mode #root {
596 background: transparent;
597 overflow: hidden;
598}
599
600.overlay-pill {
601 position: fixed;
602 inset: 8px;
603 display: flex;
604 align-items: center;
605 justify-content: center;
606 gap: 12px;
607 background: rgba(19, 17, 16, 0.94);
608 border: 1px solid var(--line);
609 border-radius: 99px;
610 box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
611}
612
613.overlay-dot {
614 width: 9px;
615 height: 9px;
616 border-radius: 50%;
617 background: var(--accent);
618}
619
620.overlay-recording .overlay-dot {
621 animation: blink 1.2s ease-in-out infinite;
622}
623
624.overlay-processing .overlay-dot {
625 background: var(--text-dim);
626 animation: blink 0.5s ease-in-out infinite;
627}
628
629@keyframes blink {
630 50% {
631 opacity: 0.25;
632 }
633}
634
635.overlay-label {
636 font-size: 12px;
637 letter-spacing: 2px;
638 text-transform: uppercase;
639}
640
641.overlay-bars {
642 display: flex;
643 gap: 3px;
644 align-items: center;
645 height: 18px;
646}
647
648.overlay-bars i {
649 width: 3px;
650 border-radius: 2px;
651 background: var(--accent);
652 animation: bounce 0.9s ease-in-out infinite;
653}
654
655.overlay-bars i:nth-child(1) {
656 height: 40%;
657 animation-delay: 0s;
658}
659.overlay-bars i:nth-child(2) {
660 height: 80%;
661 animation-delay: 0.15s;
662}
663.overlay-bars i:nth-child(3) {
664 height: 100%;
665 animation-delay: 0.3s;
666}
667.overlay-bars i:nth-child(4) {
668 height: 70%;
669 animation-delay: 0.45s;
670}
671.overlay-bars i:nth-child(5) {
672 height: 45%;
673 animation-delay: 0.6s;
674}
675
676@keyframes bounce {
677 50% {
678 transform: scaleY(0.4);
679 }
680}
681
682.overlay-processing .overlay-bars i {
683 background: var(--text-dim);
684 animation-duration: 0.5s;
685}