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
647 lines10 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/* ---------- modal ---------- */
395
396.modal-backdrop {
397 position: fixed;
398 inset: 0;
399 background: rgba(0, 0, 0, 0.6);
400 display: flex;
401 align-items: center;
402 justify-content: center;
403 z-index: 50;
404}
405
406.modal {
407 width: min(560px, calc(100vw - 48px));
408 background: var(--bg-raised);
409 border: 1px solid var(--line);
410 border-radius: 10px;
411 padding: 24px;
412 box-shadow: 0 16px 50px rgba(0, 0, 0, 0.55);
413}
414
415.modal h3 {
416 font-size: 15px;
417 margin-bottom: 8px;
418}
419
420.modal h3::before {
421 content: "# ";
422 color: var(--accent);
423}
424
425.modal .btn {
426 margin-top: 16px;
427}
428
429.code-block {
430 position: relative;
431 background: var(--bg-inset);
432 border: 1px solid var(--line);
433 border-radius: 6px;
434 padding: 14px;
435 margin-top: 12px;
436 user-select: text;
437}
438
439.code-block pre {
440 font: inherit;
441 font-size: 12px;
442 color: var(--accent);
443 white-space: pre-wrap;
444 word-break: break-all;
445 padding-right: 72px;
446}
447
448.copy-btn {
449 position: absolute;
450 top: 8px;
451 right: 8px;
452 font: inherit;
453 font-size: 11px;
454 color: var(--text-dim);
455 background: var(--bg-raised);
456 border: 1px solid var(--line);
457 border-radius: 5px;
458 padding: 4px 9px;
459 cursor: pointer;
460}
461
462.copy-btn:hover {
463 color: var(--text);
464 border-color: var(--accent-soft);
465}
466
467.transcript-box {
468 background: var(--bg-inset);
469 border: 1px solid var(--line);
470 border-radius: 6px;
471 padding: 14px;
472 min-height: 64px;
473 user-select: text;
474}
475
476.transcript-box p + p {
477 margin-top: 8px;
478}
479
480/* ---------- models ---------- */
481
482.model-list {
483 list-style: none;
484 max-width: 640px;
485}
486
487.model {
488 display: flex;
489 gap: 16px;
490 align-items: flex-start;
491 justify-content: space-between;
492 padding: 16px 18px;
493 border: 1px solid var(--line);
494 border-radius: 8px;
495 margin-bottom: 10px;
496 background: var(--bg-raised);
497}
498
499.model.active {
500 border-color: var(--accent-soft);
501}
502
503.model-title {
504 display: flex;
505 align-items: baseline;
506 gap: 8px;
507 flex-wrap: wrap;
508}
509
510.model-main {
511 flex: 1;
512}
513
514.model-actions {
515 display: flex;
516 gap: 8px;
517 flex-shrink: 0;
518 align-items: center;
519}
520
521.badge {
522 font-size: 10.5px;
523 padding: 1px 7px;
524 border-radius: 99px;
525 border: 1px solid var(--line);
526 color: var(--text-dim);
527}
528
529.badge.accent {
530 color: var(--accent);
531 border-color: var(--accent-soft);
532}
533
534.badge.dim {
535 opacity: 0.8;
536}
537
538.progress {
539 height: 4px;
540 border-radius: 2px;
541 background: var(--bg-inset);
542 margin-top: 10px;
543 overflow: hidden;
544}
545
546.progress-fill {
547 height: 100%;
548 background: var(--accent);
549 border-radius: 2px;
550 transition: width 300ms ease;
551}
552
553/* ---------- overlay pill ---------- */
554
555html.overlay-mode,
556html.overlay-mode body,
557html.overlay-mode #root {
558 background: transparent;
559 overflow: hidden;
560}
561
562.overlay-pill {
563 position: fixed;
564 inset: 8px;
565 display: flex;
566 align-items: center;
567 justify-content: center;
568 gap: 12px;
569 background: rgba(19, 17, 16, 0.94);
570 border: 1px solid var(--line);
571 border-radius: 99px;
572 box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
573}
574
575.overlay-dot {
576 width: 9px;
577 height: 9px;
578 border-radius: 50%;
579 background: var(--accent);
580}
581
582.overlay-recording .overlay-dot {
583 animation: blink 1.2s ease-in-out infinite;
584}
585
586.overlay-processing .overlay-dot {
587 background: var(--text-dim);
588 animation: blink 0.5s ease-in-out infinite;
589}
590
591@keyframes blink {
592 50% {
593 opacity: 0.25;
594 }
595}
596
597.overlay-label {
598 font-size: 12px;
599 letter-spacing: 2px;
600 text-transform: uppercase;
601}
602
603.overlay-bars {
604 display: flex;
605 gap: 3px;
606 align-items: center;
607 height: 18px;
608}
609
610.overlay-bars i {
611 width: 3px;
612 border-radius: 2px;
613 background: var(--accent);
614 animation: bounce 0.9s ease-in-out infinite;
615}
616
617.overlay-bars i:nth-child(1) {
618 height: 40%;
619 animation-delay: 0s;
620}
621.overlay-bars i:nth-child(2) {
622 height: 80%;
623 animation-delay: 0.15s;
624}
625.overlay-bars i:nth-child(3) {
626 height: 100%;
627 animation-delay: 0.3s;
628}
629.overlay-bars i:nth-child(4) {
630 height: 70%;
631 animation-delay: 0.45s;
632}
633.overlay-bars i:nth-child(5) {
634 height: 45%;
635 animation-delay: 0.6s;
636}
637
638@keyframes bounce {
639 50% {
640 transform: scaleY(0.4);
641 }
642}
643
644.overlay-processing .overlay-bars i {
645 background: var(--text-dim);
646 animation-duration: 0.5s;
647}