mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
feat(titlebar): selectable window button styles + minimize toggle (#1083)
* feat(titlebar): selectable window button styles + minimize toggle Custom title bar (Linux) gains a window-button style picker, mirroring the seekbar style picker pattern. Six form-named styles: dots, dotsGlyph, flat, pill, outline, glyph. All buttons now carry minimize/maximize/close glyphs for clear, colour-blind-friendly iconography; dots reveal glyphs on hover, dotsGlyph always shows them. - New authStore state windowButtonStyle (default dots) + showMinimizeButton, with rehydrate validation falling back to dots on unknown values. - WindowButtonPreview reuses the real .titlebar-btn classes for WYSIWYG tiles. - Picker + minimize toggle render under the Custom title bar setting, gated on the toggle being on. - Monochrome styles use --text-primary glyphs and stronger borders for contrast on dark themes. - Dev-build grey marker scoped to the real title bar so previews show true colours. - i18n keys in all 9 locales; setter and rehydrate tests. * docs(changelog): window button styles (#1083)
This commit is contained in:
@@ -89,49 +89,117 @@
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
/* Shared base — each style below overrides shape, fill and glyph colour. */
|
||||
.titlebar-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18);
|
||||
transition: filter var(--transition-fast), box-shadow var(--transition-fast);
|
||||
transition: filter var(--transition-fast), box-shadow var(--transition-fast),
|
||||
background var(--transition-fast), color var(--transition-fast),
|
||||
border-color var(--transition-fast), opacity var(--transition-fast);
|
||||
}
|
||||
|
||||
.titlebar-btn-close {
|
||||
background: #ff5f57;
|
||||
.titlebar-btn svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.titlebar-btn-minimize {
|
||||
background: #febc2e;
|
||||
}
|
||||
|
||||
.titlebar-btn-maximize {
|
||||
background: #28c840;
|
||||
}
|
||||
|
||||
.titlebar-btn:hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.titlebar-btn-close:hover { box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18), 0 0 6px rgba(255, 95, 87, 0.75); }
|
||||
.titlebar-btn-minimize:hover { box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18), 0 0 6px rgba(254, 188, 46, 0.75); }
|
||||
.titlebar-btn-maximize:hover { box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18), 0 0 6px rgba(40, 200, 64, 0.75); }
|
||||
|
||||
.titlebar-btn:active {
|
||||
filter: brightness(0.92);
|
||||
}
|
||||
|
||||
.titlebar-btn:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18), 0 0 0 2px rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 0 0 2px var(--accent);
|
||||
}
|
||||
|
||||
/* ── dots / dotsGlyph — colour-circle window buttons ── */
|
||||
[data-btnstyle="dots"] .titlebar-btn,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
color: rgba(0, 0, 0, 0.55);
|
||||
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
[data-btnstyle="dots"] .titlebar-btn svg,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn svg {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
[data-btnstyle="dots"] .titlebar-btn-close,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn-close { background: #ff5f57; }
|
||||
[data-btnstyle="dots"] .titlebar-btn-minimize,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn-minimize { background: #febc2e; }
|
||||
[data-btnstyle="dots"] .titlebar-btn-maximize,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn-maximize { background: #28c840; }
|
||||
[data-btnstyle="dots"] .titlebar-btn:hover,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn:hover { filter: brightness(1.1); }
|
||||
[data-btnstyle="dots"] .titlebar-btn-close:hover,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn-close:hover { box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18), 0 0 6px rgba(255, 95, 87, 0.75); }
|
||||
[data-btnstyle="dots"] .titlebar-btn-minimize:hover,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn-minimize:hover { box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18), 0 0 6px rgba(254, 188, 46, 0.75); }
|
||||
[data-btnstyle="dots"] .titlebar-btn-maximize:hover,
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn-maximize:hover { box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18), 0 0 6px rgba(40, 200, 64, 0.75); }
|
||||
/* dots: glyphs reveal on hover (authentic); dotsGlyph: always shown */
|
||||
[data-btnstyle="dots"] .titlebar-btn svg { opacity: 0; }
|
||||
[data-btnstyle="dots"] .titlebar-controls:hover .titlebar-btn svg,
|
||||
[data-btnstyle="dots"]:hover .titlebar-btn svg { opacity: 1; }
|
||||
[data-btnstyle="dotsGlyph"] .titlebar-btn svg { opacity: 0.85; }
|
||||
|
||||
/* ── flat — full-height rectangular buttons, red close hover ── */
|
||||
.titlebar-controls[data-btnstyle="flat"] { gap: 0; padding: 0; margin-right: -6px; }
|
||||
[data-btnstyle="flat"] .titlebar-btn {
|
||||
width: 30px;
|
||||
height: var(--titlebar-height);
|
||||
border-radius: 0;
|
||||
}
|
||||
[data-btnstyle="flat"] .titlebar-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
|
||||
[data-btnstyle="flat"] .titlebar-btn-close:hover { background: #e81123; color: #fff; }
|
||||
|
||||
/* ── pill — soft circular monochrome buttons ── */
|
||||
[data-btnstyle="pill"] .titlebar-btn {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
[data-btnstyle="pill"] .titlebar-btn:hover { filter: brightness(1.18); color: var(--text-primary); }
|
||||
[data-btnstyle="pill"] .titlebar-btn-close:hover { background: #e01b24; color: #fff; filter: none; }
|
||||
|
||||
/* ── outline — square bordered buttons, accent hover ── */
|
||||
[data-btnstyle="outline"] .titlebar-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
[data-btnstyle="outline"] .titlebar-btn:hover {
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
||||
}
|
||||
[data-btnstyle="outline"] .titlebar-btn-close:hover {
|
||||
border-color: #e01b24;
|
||||
color: #e01b24;
|
||||
background: color-mix(in srgb, #e01b24 12%, transparent);
|
||||
}
|
||||
|
||||
/* ── glyph — themed monochrome, no fill, blends with the app ── */
|
||||
[data-btnstyle="glyph"] .titlebar-btn {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
[data-btnstyle="glyph"] .titlebar-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
|
||||
[data-btnstyle="glyph"] .titlebar-btn-close:hover { background: var(--bg-hover); color: #ff5f57; }
|
||||
|
||||
/* Resizer handles must start below the titlebar */
|
||||
.app-shell[data-titlebar] .resizer {
|
||||
top: var(--titlebar-height);
|
||||
|
||||
@@ -9,26 +9,23 @@ html[data-dev-build] .sidebar-brand svg {
|
||||
--logo-color-end: #fecaca;
|
||||
}
|
||||
|
||||
html[data-dev-build] .titlebar-btn-close,
|
||||
html[data-dev-build] .titlebar-btn-minimize,
|
||||
html[data-dev-build] .titlebar-btn-maximize {
|
||||
/* Scope the dev-grey to the real title bar only — the Settings window-button
|
||||
* style previews reuse `.titlebar-btn` classes and must show true colours. */
|
||||
html[data-dev-build] .titlebar .titlebar-btn-close,
|
||||
html[data-dev-build] .titlebar .titlebar-btn-minimize,
|
||||
html[data-dev-build] .titlebar .titlebar-btn-maximize {
|
||||
background: #8b8b8b;
|
||||
}
|
||||
|
||||
html[data-dev-build] .titlebar-btn-close:hover,
|
||||
html[data-dev-build] .titlebar-btn-minimize:hover,
|
||||
html[data-dev-build] .titlebar-btn-maximize:hover {
|
||||
html[data-dev-build] .titlebar .titlebar-btn-close:hover,
|
||||
html[data-dev-build] .titlebar .titlebar-btn-minimize:hover,
|
||||
html[data-dev-build] .titlebar .titlebar-btn-maximize:hover {
|
||||
background: #a3a3a3;
|
||||
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
html[data-dev-build] .titlebar-btn-close:hover,
|
||||
html[data-dev-build] .titlebar-btn-minimize:hover,
|
||||
html[data-dev-build] .titlebar-btn-maximize:hover {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
html[data-dev-build] .titlebar-btn:focus-visible {
|
||||
html[data-dev-build] .titlebar .titlebar-btn:focus-visible {
|
||||
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.18), 0 0 0 2px rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user