1// Package theme provides GTK CSS styling for UI components.
2package theme
3
4import (
5 "fmt"
6 "strings"
7)
8
9// GenerateCSS creates GTK4 CSS using the provided palette with default scale.
10func GenerateCSS(p Palette) string {
11 return GenerateCSSWithScale(p, 1.0)
12}
13
14// GenerateCSSWithScale creates GTK4 CSS using the provided palette and UI scale factor.
15// Scale affects font sizes and widget padding/margins proportionally.
16func GenerateCSSWithScale(p Palette, scale float64) string {
17 return GenerateCSSWithScaleAndFonts(p, scale, DefaultFontConfig())
18}
19
20// FontConfig holds font settings for CSS generation.
21type FontConfig struct {
22 SansFont string
23 MonospaceFont string
24}
25
26// DefaultFontConfig returns safe font fallbacks.
27func DefaultFontConfig() FontConfig {
28 return FontConfig{
29 SansFont: "sans-serif",
30 MonospaceFont: "monospace",
31 }
32}
33
34// FontCSSVars generates CSS custom properties for fonts.
35func FontCSSVars(fonts FontConfig) string {
36 var sb strings.Builder
37 // Quote the configured family; include generic fallback.
38 fmt.Fprintf(&sb, " --font-sans: %q, sans-serif;\n", fonts.SansFont)
39 fmt.Fprintf(&sb, " --font-mono: %q, monospace;\n", fonts.MonospaceFont)
40 return sb.String()
41}
42
43func generateFontCSS() string {
44 return `/* Font styling */
45window, tooltip, popover {
46 font-family: var(--font-sans);
47}
48`
49}
50
51// GenerateCSSWithScaleAndFonts creates GTK4 CSS using the provided palette, UI scale factor and fonts.
52// Scale affects font sizes and widget padding/margins proportionally.
53// Uses default mode colors.
54func GenerateCSSWithScaleAndFonts(p Palette, scale float64, fonts FontConfig) string {
55 return GenerateCSSFull(p, scale, fonts, DefaultModeColors())
56}
57
58// GenerateCSSFull creates GTK4 CSS using all provided configuration.
59// Scale affects font sizes and widget padding/margins proportionally.
60func GenerateCSSFull(p Palette, scale float64, fonts FontConfig, modeColors ModeColors) string {
61 if scale <= 0 {
62 scale = 1.0
63 }
64
65 defaults := DefaultFontConfig()
66 fonts = FontConfig{
67 SansFont: Coalesce(fonts.SansFont, defaults.SansFont),
68 MonospaceFont: Coalesce(fonts.MonospaceFont, defaults.MonospaceFont),
69 }
70
71 var sb strings.Builder
72
73 // CSS custom properties (variables) - GTK4 uses :root selector
74 sb.WriteString("/* Theme variables */\n")
75 sb.WriteString(":root {\n")
76 sb.WriteString(p.ToCSSVars())
77 sb.WriteString(FontCSSVars(fonts))
78 sb.WriteString(modeColors.ToCSSVars())
79 sb.WriteString("}\n\n")
80
81 // Global font styling
82 sb.WriteString(generateFontCSS())
83 sb.WriteString("\n")
84
85 // Global UI scaling via font-size on all widgets
86 if scale != 1.0 {
87 sb.WriteString(generateScalingCSS(scale))
88 sb.WriteString("\n")
89 }
90
91 // Tab bar styling
92 sb.WriteString(generateTabBarCSS(p))
93 sb.WriteString("\n")
94
95 // Omnibox styling
96 sb.WriteString(generateOmniboxCSS(p))
97 sb.WriteString("\n")
98
99 // Find bar styling
100 sb.WriteString(generateFindBarCSS(p))
101 sb.WriteString("\n")
102
103 // Pane styling
104 sb.WriteString(generatePaneCSS(p))
105 sb.WriteString("\n")
106
107 // Stacked pane styling
108 sb.WriteString(generateStackedPaneCSS(p))
109 sb.WriteString("\n")
110
111 // Progress bar styling
112 sb.WriteString(generateProgressBarCSS(p))
113 sb.WriteString("\n")
114
115 // Toaster styling
116 sb.WriteString(generateToasterCSS(p))
117 sb.WriteString("\n")
118
119 // Link status overlay styling
120 sb.WriteString(generateLinkStatusCSS(p))
121 sb.WriteString("\n")
122
123 // Session manager styling
124 sb.WriteString(generateSessionManagerCSS(p))
125 sb.WriteString("\n")
126
127 // Tab picker styling
128 sb.WriteString(generateTabPickerCSS(p))
129 sb.WriteString("\n")
130
131 // Permission popup styling
132 sb.WriteString(generatePermissionPopupCSS(p))
133 sb.WriteString("\n")
134
135 // Floating pane styling
136 sb.WriteString(generateFloatingPaneCSS(p))
137 sb.WriteString("\n")
138
139 // WebRTC permission activity indicator styling
140 sb.WriteString(generateWebRTCPermissionIndicatorCSS(p))
141 sb.WriteString("\n")
142
143 // Accent picker styling
144 sb.WriteString(generateAccentPickerCSS(p))
145
146 return sb.String()
147}
148
149// generateScalingCSS creates CSS rules that scale the UI.
150// GTK4 CSS doesn't support transform:scale() well, so we scale font-size
151// and use relative units (em) for padding/margins in other rules.
152func generateScalingCSS(scale float64) string {
153 // Use 16px base so em conversions are correct:
154 // 1px = 0.0625em (1/16), 4px = 0.25em, etc.
155 basePx := int(16 * scale)
156 return fmt.Sprintf(`/* UI Scaling (%.0f%%) */
157window {
158 font-size: %dpx;
159}
160`, scale*100, basePx)
161}
162
163// generateTabBarCSS creates tab bar styles.
164// Uses em units for scalable UI.
165func generateTabBarCSS(p Palette) string {
166 return `/* Tab bar styling */
167.tab-bar {
168 background-color: var(--surface);
169 border-top: 0.125em solid var(--border);
170 padding: 0;
171 min-height: 2em;
172}
173
174/* Tab button styling */
175button.tab-button {
176 background-color: var(--surface-variant);
177 background-image: none;
178 border: none;
179 border-right: 0.0625em solid var(--border);
180 border-radius: 0;
181 padding: 0.25em 0.5em;
182 transition: background-color 200ms ease-in-out;
183}
184
185button.tab-button:hover {
186 background-color: shade(var(--surface-variant), 1.2);
187}
188
189button.tab-button.tab-button-active {
190 background-color: shade(var(--surface-variant), 1.2);
191 border-bottom: 0.125em solid var(--accent);
192 font-weight: 600;
193}
194
195/* Tab title text */
196.tab-title {
197 font-size: 0.75em;
198 color: var(--text);
199 font-weight: 500;
200}
201`
202}
203
204// generateOmniboxCSS creates omnibox styles.
205// Uses em units for scalable UI.
206func generateOmniboxCSS(p Palette) string {
207 return `/* ===== Omnibox Styling ===== */
208
209/* Omnibox outer container - for positioning in overlay */
210.omnibox-outer {
211 /* Positioning is handled via SetHalign/SetValign/SetMarginTop in Go */
212}
213
214/* Omnibox main container - the visible popup */
215.omnibox-container {
216 background-color: var(--surface-variant);
217 border: 0.0625em solid var(--border);
218 border-radius: 0.1875em;
219 padding: 0;
220}
221
222/* Header with History/Favorites toggle */
223.omnibox-header {
224 background-color: shade(var(--surface-variant), 1.1);
225 border-bottom: 0.0625em solid var(--border);
226 padding: 0.375em 0.75em;
227}
228
229.omnibox-header-btn {
230 background-color: transparent;
231 background-image: none;
232 border: none;
233 border-radius: 0.125em;
234 padding: 0.25em 0.75em;
235 margin-right: 0.5em;
236 font-size: 0.8125em;
237 font-weight: 500;
238 color: var(--muted);
239 transition: all 100ms ease-in-out;
240}
241
242.omnibox-header-btn:hover {
243 background-color: alpha(var(--accent), 0.15);
244 color: var(--text);
245}
246
247.omnibox-header-btn.omnibox-header-active {
248 background-color: alpha(var(--accent), 0.2);
249 color: var(--accent);
250 font-weight: 600;
251}
252
253/* Search entry field */
254.omnibox-entry {
255 background-color: var(--bg);
256 color: var(--text);
257 border: 0.0625em solid var(--border);
258 border-radius: 0.125em;
259 padding: 0.625em 0.75em;
260 margin: 0.5em 0.75em;
261 font-size: 1em;
262 caret-color: var(--accent);
263}
264
265entry.omnibox-entry:focus,
266entry.omnibox-entry:focus-within,
267entry.omnibox-entry:focus-visible {
268 border-color: var(--accent);
269 background-color: shade(var(--bg), 1.05);
270 outline-style: none;
271 outline-width: 0px;
272 outline-color: transparent;
273}
274
275/* Override any internal focus styling */
276entry.omnibox-entry > *:focus,
277entry.omnibox-entry > *:focus-visible {
278 outline-style: none;
279 outline-color: transparent;
280}
281
282/* Scrolled window for suggestions */
283.omnibox-scrolled {
284 background-color: shade(var(--surface-variant), 0.95);
285 border-top: 0.0625em solid var(--border);
286}
287
288/* List box */
289.omnibox-listbox {
290 background-color: transparent;
291}
292
293/* Suggestion rows */
294.omnibox-row {
295 padding: 0.5em 0.75em;
296 margin: 0;
297 border-radius: 0;
298 border-left: 0.1875em solid transparent;
299 border-bottom: 0.0625em solid alpha(var(--border), 0.5);
300 transition: background-color 100ms ease-in-out, border-left 100ms ease-in-out;
301 min-height: 2.75em;
302}
303
304.omnibox-row:last-child {
305 border-bottom: none;
306}
307
308.omnibox-row:hover {
309 background-color: alpha(var(--accent), 0.12);
310 border-left: 0.1875em solid var(--accent);
311}
312
313.omnibox-row:selected {
314 background-color: alpha(var(--accent), 0.2);
315 border-left: 0.1875em solid var(--accent);
316}
317
318/* Favorite indicator in history mode - yellowish accent */
319.omnibox-row.omnibox-row-favorite {
320 background-color: alpha(var(--warning), 0.08);
321 border-left: 0.1875em solid var(--warning);
322}
323
324.omnibox-row.omnibox-row-favorite:hover {
325 background-color: alpha(var(--warning), 0.15);
326 border-left: 0.1875em solid var(--warning);
327}
328
329.omnibox-row.omnibox-row-favorite:selected {
330 background-color: alpha(var(--warning), 0.2);
331 border-left: 0.1875em solid var(--warning);
332}
333
334/* Favicon in omnibox rows */
335.omnibox-favicon {
336 min-width: 1em;
337 min-height: 1em;
338 margin-right: 0.5em;
339}
340
341/* Suggestion title/URL */
342.omnibox-suggestion-title {
343 font-size: 0.875em;
344 color: var(--text);
345 font-weight: 400;
346}
347
348/* Also style labels inside omnibox rows directly */
349.omnibox-row label {
350 color: var(--text);
351}
352
353.omnibox-row:selected .omnibox-suggestion-title {
354 color: var(--text);
355 font-weight: 500;
356}
357
358/* URL text below title - use .omnibox-row prefix for specificity over .omnibox-row label */
359.omnibox-row .omnibox-suggestion-url {
360 font-size: 0.75em;
361 color: alpha(var(--muted), 0.65);
362 font-weight: 400;
363}
364
365.omnibox-row:selected .omnibox-suggestion-url {
366 color: alpha(var(--muted), 0.75);
367}
368
369/* Keyboard shortcut badge */
370.omnibox-shortcut-badge {
371 background-color: alpha(var(--muted), 0.3);
372 color: var(--muted);
373 border-radius: 0.25em;
374 padding: 0.125em 0.375em;
375 font-size: 0.625em;
376 font-weight: 500;
377 font-family: var(--font-mono);
378 margin-left: 0.5em;
379}
380
381.omnibox-row:hover .omnibox-shortcut-badge {
382 background-color: alpha(var(--accent), 0.2);
383 color: var(--accent);
384}
385
386.omnibox-row:selected .omnibox-shortcut-badge {
387 background-color: alpha(var(--accent), 0.3);
388 color: var(--accent);
389}
390
391/* Bang indicator badge in omnibox header */
392.omnibox-bang-badge {
393 background-color: alpha(var(--accent), 0.18);
394 color: var(--accent);
395 border-radius: 0.25em;
396 padding: 0.125em 0.5em;
397 font-size: 0.6875em;
398 font-weight: 600;
399}
400
401/* Zoom indicator in omnibox header */
402.omnibox-zoom-indicator {
403 background-color: alpha(var(--accent), 0.2);
404 color: var(--accent);
405 border-radius: 0.25em;
406 padding: 0.125em 0.5em;
407 font-size: 0.6875em;
408 font-weight: 600;
409 /* Note: margin-left: auto not supported in GTK4 CSS, use SetHalign(End) in code */
410}
411
412entry.omnibox-entry.omnibox-entry-bang-active,
413entry.omnibox-entry.omnibox-entry-bang-active:focus,
414entry.omnibox-entry.omnibox-entry-bang-active:focus-within,
415entry.omnibox-entry.omnibox-entry-bang-active:focus-visible {
416 border-color: var(--accent);
417 background-color: shade(var(--bg), 1.05);
418}
419
420.omnibox-row.omnibox-row-bang .omnibox-suggestion-title {
421 color: var(--accent);
422 font-weight: 600;
423}
424
425/* Ghost text autocomplete overlay - positioned to align with entry text */
426.omnibox-ghost {
427 font-size: 1em;
428 color: var(--muted);
429 margin-top: -0.0625em; /* Nudge up 1px to align with entry text baseline */
430 background-color: transparent;
431 /* Allow clicks to pass through to entry below */
432 pointer-events: none;
433}
434`
435}
436
437// generateFindBarCSS creates find bar styles.
438// Uses em units for scalable UI.
439func generateFindBarCSS(p Palette) string {
440 _ = p
441 return `/* ===== Find Bar Styling ===== */
442.find-bar-outer {
443 margin: 0.5em;
444}
445
446.find-bar-container {
447 background-color: var(--surface-variant);
448 border: 0.0625em solid var(--border);
449 border-radius: 0.25em;
450 box-shadow: 0 0.25em 0.75em alpha(black, 0.2);
451 padding: 0.5em;
452 min-width: 20em;
453}
454
455.find-bar-input-row {
456 margin-bottom: 0.25em;
457}
458
459.find-bar-entry {
460 background-color: var(--bg);
461 color: var(--text);
462 border: 0.0625em solid var(--border);
463 border-radius: 0.1875em;
464 padding: 0.4em 0.5em;
465 caret-color: var(--accent);
466}
467
468entry.find-bar-entry:focus,
469entry.find-bar-entry:focus-within,
470entry.find-bar-entry:focus-visible {
471 border-color: var(--accent);
472 outline-style: none;
473 outline-width: 0px;
474 outline-color: transparent;
475}
476
477.find-bar-entry.not-found {
478 background-color: alpha(#ef4444, 0.15);
479 border-color: #ef4444;
480}
481
482.find-bar-count {
483 font-size: 0.8em;
484 color: var(--muted);
485 min-width: 3.5em;
486}
487
488.find-bar-count.find-bar-count-has {
489 color: var(--accent);
490}
491
492.find-bar-nav,
493.find-bar-close,
494.find-bar-toggle {
495 background-image: none;
496 background-color: alpha(var(--bg), 0.6);
497 border: 0.0625em solid var(--border);
498 border-radius: 0.1875em;
499 padding: 0.25em 0.5em;
500 transition: background-color 120ms ease-in-out, border-color 120ms ease-in-out;
501}
502
503.find-bar-nav:hover,
504.find-bar-close:hover,
505.find-bar-toggle:hover {
506 background-color: alpha(var(--accent), 0.15);
507}
508
509.find-bar-toggle:checked {
510 background-color: alpha(var(--accent), 0.2);
511 border-color: var(--accent);
512 color: var(--accent);
513}
514`
515}
516
517// generatePaneCSS creates pane border styles.
518// Uses em units for scalable UI.
519func generatePaneCSS(p Palette) string {
520 return `/* ===== Pane Styling ===== */
521
522/* Pane overlay container - theme background prevents white flash */
523.pane-overlay {
524 background-color: var(--bg);
525}
526
527/* WebView widget - theme background prevents white flash during page load */
528.webview-themed {
529 background-color: var(--bg);
530}
531
532/* Loading skeleton - shown until WebView paints */
533.loading-skeleton {
534 background-color: var(--bg);
535}
536
537.loading-skeleton-spinner {
538 -gtk-icon-size: 32px;
539 min-width: 32px;
540 min-height: 32px;
541 color: var(--muted);
542 opacity: 0.4;
543}
544
545.loading-skeleton-logo {
546 opacity: 0.08;
547 min-width: 256px;
548 min-height: 256px;
549}
550
551.loading-skeleton-version {
552 opacity: 0.25;
553 font-size: 13px;
554 color: var(--muted);
555}
556
557/* Pane border - default transparent */
558.pane-border {
559 border: 0.0625em solid transparent;
560}
561
562/* Active pane - accent border */
563.pane-active {
564 border: 0.0625em solid var(--accent);
565}
566
567/* Hide active border when only one pane exists */
568.single-pane .pane-active {
569 border-color: transparent;
570}
571
572/* Pane mode active - thick inset border (for overlay) */
573.pane-mode-active {
574 background-color: transparent;
575 box-shadow: inset 0 0 0 0.25em var(--pane-mode-color);
576 border-radius: 0;
577}
578
579/* Tab mode active - thick inset border (for overlay) */
580.tab-mode-active {
581 background-color: transparent;
582 box-shadow: inset 0 0 0 0.25em var(--tab-mode-color);
583 border-radius: 0;
584}
585
586/* Resize mode active - thick inset border */
587.resize-mode-active {
588 background-color: transparent;
589 /* Prefer inset shadow, but also set outline for widgets that don't paint shadows */
590 box-shadow: inset 0 0 0 0.25em var(--resize-mode-color);
591 outline: 0.25em solid var(--resize-mode-color);
592 outline-offset: -0.25em;
593 border-radius: 0;
594}
595`
596}
597
598// generateStackedPaneCSS creates stacked pane (Zellij-style tabs within panes) styles.
599// Uses em units for scalable UI.
600func generateStackedPaneCSS(p Palette) string {
601 return `/* ===== Stacked Pane Styling ===== */
602
603/* Title bar for inactive panes in a stack */
604/* The title bar is now a Box with GestureClick, not wrapped in a button */
605.stacked-pane-titlebar {
606 background-color: var(--surface-variant);
607 border-bottom: 0.0625em solid var(--border);
608 padding: 0.25em 0.5em;
609 min-height: 1.5em;
610}
611
612/* Clickable title bar - hover styling */
613.stacked-pane-titlebar.stacked-pane-title-clickable {
614 transition: background-color 150ms ease-in-out;
615}
616
617.stacked-pane-titlebar.stacked-pane-title-clickable:hover {
618 background-color: shade(var(--surface-variant), 1.15);
619}
620
621/* Title bar content box */
622.stacked-pane-titlebar.active {
623 background-color: shade(var(--surface-variant), 1.2);
624 border-left: 0.1875em solid var(--accent);
625}
626
627/* Favicon image in title bar */
628.stacked-pane-titlebar image {
629 min-width: 1em;
630 min-height: 1em;
631 margin-right: 0.375em;
632}
633
634/* Title text in title bar */
635.stacked-pane-titlebar label {
636 color: var(--text);
637 font-size: 0.75em;
638 font-weight: 400;
639}
640
641/* Hover effect on title bar label */
642.stacked-pane-titlebar.stacked-pane-title-clickable:hover label {
643 color: var(--accent);
644}
645
646/* Close button in stacked pane title bar */
647.stacked-pane-close-button {
648 background: transparent;
649 border: none;
650 border-radius: 4px;
651 padding: 4px;
652 margin: 0 2px;
653 min-width: 20px;
654 min-height: 20px;
655 opacity: 0.4;
656 transition: opacity 100ms ease-in-out, background-color 100ms ease-in-out;
657}
658
659.stacked-pane-close-button > image {
660 margin: 0;
661 padding: 0;
662}
663
664.stacked-pane-close-button:hover {
665 opacity: 1.0;
666 background: alpha(var(--destructive), 0.2);
667}
668
669.stacked-pane-close-button:hover > image {
670 color: var(--destructive);
671}
672
673/* Show close button more prominently when hovering title bar */
674.stacked-pane-titlebar.stacked-pane-title-clickable:hover .stacked-pane-close-button {
675 opacity: 0.7;
676}
677
678.stacked-pane-close-button:focus {
679 outline: none;
680 box-shadow: none;
681}
682`
683}
684
685// generateProgressBarCSS creates progress bar styles for page loading indication.
686func generateProgressBarCSS(p Palette) string {
687 return `/* ===== Progress Bar Styling ===== */
688
689/* Native GtkProgressBar with osd class */
690progressbar.osd {
691 min-height: 4px;
692}
693
694progressbar.osd trough {
695 min-height: 4px;
696 min-width: 2px;
697 margin: 0;
698 padding: 0;
699 background-color: alpha(var(--bg), 0.3);
700}
701
702progressbar.osd progress {
703 min-height: 4px;
704 min-width: 2px;
705 margin: 0;
706 padding: 0;
707 background-color: var(--accent);
708}
709`
710}
711
712// generateSessionManagerCSS creates session manager modal styles.
713// Uses em units for scalable UI, matches omnibox styling patterns.
714func generateSessionManagerCSS(p Palette) string {
715 _ = p
716 return `/* ===== Session Manager Styling ===== */
717
718/* Session manager outer container - for positioning in overlay */
719.session-manager-outer {
720 /* Positioning is handled via SetHalign/SetValign in Go */
721}
722
723/* Session manager main container - the visible popup */
724.session-manager-container {
725 background-color: var(--surface-variant);
726 border: 0.0625em solid var(--border);
727 border-radius: 0.1875em;
728 padding: 0;
729 min-width: 28em;
730 /* Note: max-width not supported in GTK4 CSS, rely on container constraints */
731}
732
733/* Header with title */
734.session-manager-header {
735 background-color: shade(var(--surface-variant), 1.1);
736 border-bottom: 0.0625em solid var(--border);
737 padding: 0.5em 0.75em;
738}
739
740.session-manager-title {
741 font-size: 0.9375em;
742 font-weight: 600;
743 color: var(--text);
744}
745
746/* Scrolled window for session list */
747.session-manager-scrolled {
748 background-color: shade(var(--surface-variant), 0.95);
749}
750
751/* List box */
752.session-manager-list {
753 background-color: transparent;
754}
755
756/* Session rows */
757.session-manager-row {
758 padding: 0.5em 0.75em;
759 margin: 0;
760 border-radius: 0;
761 border-left: 0.1875em solid transparent;
762 border-bottom: 0.0625em solid alpha(var(--border), 0.5);
763 transition: background-color 100ms ease-in-out, border-left 100ms ease-in-out;
764 min-height: 2.75em;
765}
766
767.session-manager-row:last-child {
768 border-bottom: none;
769}
770
771.session-manager-row:hover {
772 background-color: alpha(var(--accent), 0.12);
773 border-left: 0.1875em solid var(--accent);
774}
775
776.session-manager-row:selected,
777row:selected .session-manager-row {
778 background-color: alpha(var(--accent), 0.2);
779 border-left: 0.1875em solid var(--accent);
780}
781
782/* Status indicator dot */
783.session-status {
784 font-size: 0.875em;
785 min-width: 1em;
786 margin-right: 0.5em;
787}
788
789/* Current session - accent color dot */
790.session-current .session-status {
791 color: var(--accent);
792}
793
794/* Active session (other instance) - muted dot */
795.session-active .session-status {
796 color: var(--muted);
797}
798
799/* Exited session - dimmed appearance with subtle stop icon */
800.session-exited {
801 opacity: 0.75;
802}
803
804.session-exited .session-status {
805 color: var(--error);
806 opacity: 0.5;
807}
808
809/* Session ID labels */
810.session-id-short {
811 font-size: 0.875em;
812 font-weight: 600;
813 color: var(--accent);
814 font-family: var(--font-mono);
815}
816
817.session-id-prefix {
818 font-size: 0.875em;
819 font-weight: 400;
820 color: var(--muted);
821 font-family: var(--font-mono);
822}
823
824.session-id-badge {
825 font-size: 0.75em;
826 font-weight: 500;
827 color: var(--accent);
828 margin-left: 0.5em;
829}
830
831/* Tab/pane count label */
832.session-count {
833 font-size: 0.75em;
834 color: var(--muted);
835}
836
837/* Relative time label */
838.session-time {
839 font-size: 0.75em;
840 color: var(--muted);
841 /* Note: margin-left: auto not supported in GTK4 CSS, use SetHalign(End) in code */
842}
843
844/* Section divider for EXITED sessions */
845.session-divider {
846 font-size: 0.6875em;
847 font-weight: 600;
848 color: var(--muted);
849 text-transform: uppercase;
850 letter-spacing: 0.05em;
851 padding: 0.5em 0.75em 0.25em 0.75em;
852 background-color: alpha(var(--border), 0.3);
853 border-bottom: 0.0625em solid var(--border);
854}
855
856/* Footer with keyboard shortcuts */
857.session-manager-footer {
858 background-color: shade(var(--surface-variant), 0.9);
859 border-top: 0.0625em solid var(--border);
860 padding: 0.375em 0.75em;
861 font-size: 0.6875em;
862 color: var(--muted);
863 font-family: var(--font-mono);
864}
865
866/* Session mode border */
867.session-mode-active {
868 background-color: transparent;
869 box-shadow: inset 0 0 0 0.25em var(--session-mode-color);
870 border-radius: 0;
871}
872`
873}
874
875// generateToasterCSS creates toast notification styles.
876// Uses em units for scalable UI.
877func generateToasterCSS(p Palette) string {
878 return `/* ===== Toaster Styling ===== */
879
880/* Toast notification container */
881.toast {
882 background-color: var(--surface-variant);
883 border-radius: 0.25em;
884 padding: 0.375em 0.625em;
885 margin: 0.5em;
886 font-size: 0.875em;
887 font-weight: 600;
888 box-shadow: 0 2px 8px alpha(black, 0.3);
889 min-width: 2.5em;
890}
891
892/* Toast level: info (default, accent color) */
893.toast-info {
894 background-color: alpha(var(--accent), 0.9);
895 color: var(--bg);
896}
897
898/* Toast level: success */
899.toast-success {
900 background-color: alpha(var(--success), 0.9);
901 color: var(--bg);
902}
903
904/* Toast level: warning */
905.toast-warning {
906 background-color: alpha(var(--warning), 0.9);
907 color: var(--bg);
908}
909
910/* Toast level: error */
911.toast-error {
912 background-color: alpha(var(--destructive), 0.9);
913 color: var(--bg);
914}
915
916/* Toast with custom styling (mode indicator toasters) */
917.toast-custom {
918 color: #ffffff;
919}
920
921/* Mode-specific toast colors */
922.toast-pane-mode {
923 background-color: var(--pane-mode-color);
924 color: #ffffff;
925}
926
927.toast-tab-mode {
928 background-color: var(--tab-mode-color);
929 color: #ffffff;
930}
931
932.toast-session-mode {
933 background-color: var(--session-mode-color);
934 color: #ffffff;
935}
936
937.toast-resize-mode {
938 background-color: var(--resize-mode-color);
939 color: #ffffff;
940}
941`
942}
943
944// generateLinkStatusCSS creates link status overlay styles.
945// Uses CSS transitions for fade-in/fade-out effect.
946func generateLinkStatusCSS(p Palette) string {
947 return `/* ===== Link Status Overlay Styling ===== */
948
949/* Link status container - bottom left positioning */
950.link-status {
951 background-color: alpha(var(--surface-variant), 0.95);
952 border-radius: 0.25em 0.25em 0 0;
953 padding: 0.25em 0.5em;
954 margin: 0;
955 font-size: 0.75em;
956 /* Note: max-width not supported in GTK4 CSS, use label SetMaxWidthChars instead */
957 box-shadow: 0 -1px 4px alpha(black, 0.15);
958
959 /* Fade transition - hidden by default */
960 opacity: 0;
961 transition: opacity 150ms ease-in-out;
962}
963
964/* Visible state - triggered by adding .visible class */
965.link-status.visible {
966 opacity: 1;
967}
968
969/* Link status label text */
970.link-status label {
971 color: var(--muted);
972}
973`
974}
975
976func generateFloatingPaneCSS(p Palette) string {
977 _ = p
978 return `/* ===== Floating Pane Styling ===== */
979.floating-pane-outer {
980 /* Positioning handled in Go via overlay alignment and margins. */
981}
982
983.floating-pane-container {
984 background-color: var(--surface-variant);
985 border: 0.0625em solid var(--border);
986 border-radius: 0.25em;
987 box-shadow: 0 0.5em 1.5em alpha(black, 0.25);
988 opacity: 0;
989 transition: opacity 110ms ease-out;
990}
991
992.floating-pane-container.floating-pane-visible {
993 opacity: 1;
994}
995
996.floating-pane-container.pane-active {
997 border-color: var(--accent);
998}
999
1000.floating-pane-omnibox {
1001 background-color: shade(var(--surface-variant), 1.08);
1002 border-bottom: 0.0625em solid var(--border);
1003 padding: 0.375em 0.5em;
1004}
1005
1006.floating-pane-content {
1007 background-color: var(--bg);
1008 min-height: 12em;
1009}
1010`
1011}