feat(desktop): icon mode selector, wrapping grid, light-on-top SVG

- Replace preview thumbnails in mode selector with Lucide icons
  (SunMoon for System, Sun for Light, Moon for Dark)
- Switch from horizontal scroll to wrapping grid for theme list
  (since mode selector already filters the themes)
- Remove HorizontalScrollWithFade component (no longer needed)
- Remove section subtitles — just the mode buttons
- Swap SystemPreferencePreviewSvg: light on top, dark on bottom
- Remove unused expanded state and Show all toggle
This commit is contained in:
klopez4212
2026-07-06 13:50:01 +01:00
parent d15ef62c6b
commit d2a9eda9d7
2 changed files with 42 additions and 107 deletions

View File

@@ -1,4 +1,4 @@
import { type ReactNode, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import {
BellRing,
Bot,
@@ -10,9 +10,12 @@ import {
LayoutTemplate,
LockKeyhole,
MonitorCog,
Moon,
Smartphone,
Smile,
Stethoscope,
Sun,
SunMoon,
UserRound,
type LucideIcon,
} from "lucide-react";
@@ -330,35 +333,6 @@ function SingleThemeTile({
);
}
function HorizontalScrollWithFade({
children,
expanded,
}: {
children: ReactNode;
expanded: boolean;
}) {
if (expanded) {
return <div className="flex flex-wrap gap-3 p-1">{children}</div>;
}
return (
<div className="relative">
<div className="flex gap-3 overflow-x-auto p-1 pb-2 [scrollbar-width:thin]">
{children}
</div>
{/* Right fade */}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-y-0 right-0 w-8"
style={{
background:
"linear-gradient(to left, hsl(var(--background)), hsl(var(--background) / 0))",
}}
/>
</div>
);
}
type AppearanceMode = "system" | "light" | "dark";
function ThemeSettingsCard() {
@@ -383,7 +357,6 @@ function ThemeSettingsCard() {
: "light";
const [selectedMode, setSelectedMode] = useState<AppearanceMode>(activeMode);
const [expanded, setExpanded] = useState(false);
const getVars = (name: SyntaxThemeName) =>
withAccentPreviewVars(
@@ -407,7 +380,6 @@ function ThemeSettingsCard() {
const handleModeSelect = (mode: AppearanceMode) => {
setSelectedMode(mode);
setExpanded(false);
};
const handleSelectTheme = (name: SyntaxThemeName) => {
@@ -436,18 +408,18 @@ function ThemeSettingsCard() {
/>
{/* Mode selector: System / Light / Dark */}
<div className="mb-6 flex gap-3">
<div className="mb-8 flex gap-2">
{(
[
{ mode: "system" as const, label: "System" },
{ mode: "light" as const, label: "Light" },
{ mode: "dark" as const, label: "Dark" },
{ mode: "system" as const, label: "System", Icon: SunMoon },
{ mode: "light" as const, label: "Light", Icon: Sun },
{ mode: "dark" as const, label: "Dark", Icon: Moon },
] as const
).map(({ mode, label }) => (
).map(({ mode, label, Icon }) => (
<button
aria-pressed={selectedMode === mode}
className={cn(
"flex flex-col items-center gap-1.5 rounded-2xl border px-5 py-3 text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
"flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
selectedMode === mode
? "border-primary bg-primary/10 text-foreground"
: "border-border/70 text-muted-foreground hover:border-border hover:text-foreground",
@@ -457,52 +429,15 @@ function ThemeSettingsCard() {
onClick={() => handleModeSelect(mode)}
type="button"
>
{mode === "system" && (
<SystemPreferencePreviewFrame
className="h-12 w-16"
darkVars={getVars(
pairedLight[0]
? (getThemePair(pairedLight[0]) ?? pairedLight[0])
: pairedLight[0],
)}
lightVars={getVars(pairedLight[0])}
/>
)}
{mode === "light" && (
<ThemePreviewFrame
className="h-12 w-16"
vars={getVars(allLightThemes[0])}
/>
)}
{mode === "dark" && (
<ThemePreviewFrame
className="h-12 w-16"
vars={getVars(allDarkThemes[0])}
/>
)}
<Icon className="h-4 w-4" />
{label}
</button>
))}
</div>
{/* Theme list based on selected mode */}
{/* Theme grid based on selected mode */}
<div className="mb-6">
<div className="mb-3 flex items-center justify-between">
<h3 className="text-sm font-medium text-foreground">Themes</h3>
{((selectedMode === "system" && pairedLight.length > 4) ||
(selectedMode === "light" && allLightThemes.length > 4) ||
(selectedMode === "dark" && allDarkThemes.length > 4)) && (
<button
className="text-2xs text-muted-foreground hover:text-foreground"
onClick={() => setExpanded((v) => !v)}
type="button"
>
{expanded ? "Collapse" : "Show all"}
</button>
)}
</div>
<HorizontalScrollWithFade expanded={expanded}>
<div className="flex flex-wrap gap-3 p-1">
{selectedMode === "system" &&
pairedLight.map((lightName) => {
const darkName = getThemePair(lightName);
@@ -538,7 +473,7 @@ function ThemeSettingsCard() {
vars={getVars(name)}
/>
))}
</HorizontalScrollWithFade>
</div>
</div>
{/* Accent color picker */}

View File

@@ -233,12 +233,12 @@ function SystemPreferencePreviewSvg({
viewBox="0 0 118 80"
xmlns="http://www.w3.org/2000/svg"
>
{/* Dark half (top) */}
<g clipPath={`url(#${clipDark})`}>
<rect fill={darkBg} height="180" rx="3.6" width="288" />
<rect fill={darkSidebar} height="180" width="57.375" />
{/* Light half (top) */}
<g clipPath={`url(#${clipLight})`}>
<rect fill={lightBg} height="180" rx="3.6" width="288" />
<rect fill={lightSidebar} height="180" width="57.375" />
<rect
fill={darkSidebarFg}
fill={lightSidebarFg}
height="3.6"
rx="0.9"
width="3.6"
@@ -246,7 +246,7 @@ function SystemPreferencePreviewSvg({
y="15.9751"
/>
<rect
fill={darkSidebarFg}
fill={lightSidebarFg}
height="3.6"
rx="0.9"
width="3.6"
@@ -254,7 +254,7 @@ function SystemPreferencePreviewSvg({
y="21.375"
/>
<rect
fill={darkSidebarFg}
fill={lightSidebarFg}
height="3.6"
rx="0.9"
width="3.6"
@@ -262,7 +262,7 @@ function SystemPreferencePreviewSvg({
y="26.7749"
/>
<rect
fill={darkSidebarFg}
fill={lightSidebarFg}
height="3.6"
rx="0.9"
width="3.6"
@@ -324,7 +324,7 @@ function SystemPreferencePreviewSvg({
y="4.7811"
/>
<rect
fill={darkSidebarFg}
fill={lightSidebarFg}
height="1.8"
rx="0.225"
width="45.225"
@@ -332,7 +332,7 @@ function SystemPreferencePreviewSvg({
y="16.875"
/>
<rect
fill={darkSidebarFg}
fill={lightSidebarFg}
height="1.8"
rx="0.225"
width="45.225"
@@ -340,7 +340,7 @@ function SystemPreferencePreviewSvg({
y="22.2749"
/>
<rect
fill={darkSidebarFg}
fill={lightSidebarFg}
height="1.8"
rx="0.225"
width="45.225"
@@ -348,7 +348,7 @@ function SystemPreferencePreviewSvg({
y="27.675"
/>
<rect
fill={darkSidebarFg}
fill={lightSidebarFg}
height="1.8"
rx="0.225"
width="45.225"
@@ -356,7 +356,7 @@ function SystemPreferencePreviewSvg({
y="33.075"
/>
<rect
fill={darkMutedFg}
fill={lightMutedFg}
height="1.8"
rx="0.225"
width="26.775"
@@ -365,13 +365,13 @@ function SystemPreferencePreviewSvg({
/>
</g>
{/* Light half (bottom) — clipped to bottom portion */}
<g clipPath={`url(#${clipLight})`}>
{/* Dark half (bottom) — clipped to bottom portion */}
<g clipPath={`url(#${clipDark})`}>
<g clipPath={`url(#${clipOuter})`}>
<rect fill={lightBg} height="180" rx="3.6" width="288" y="22" />
<rect fill={lightSidebar} height="180" width="57.375" y="22" />
<rect fill={darkBg} height="180" rx="3.6" width="288" y="22" />
<rect fill={darkSidebar} height="180" width="57.375" y="22" />
<rect
fill={lightSidebarFg}
fill={darkSidebarFg}
height="3.6"
rx="0.9"
width="3.6"
@@ -379,7 +379,7 @@ function SystemPreferencePreviewSvg({
y="37.9751"
/>
<rect
fill={lightSidebarFg}
fill={darkSidebarFg}
height="3.6"
rx="0.9"
width="3.6"
@@ -387,7 +387,7 @@ function SystemPreferencePreviewSvg({
y="43.375"
/>
<rect
fill={lightSidebarFg}
fill={darkSidebarFg}
height="3.6"
rx="0.9"
width="3.6"
@@ -395,7 +395,7 @@ function SystemPreferencePreviewSvg({
y="48.7749"
/>
<rect
fill={lightSidebarFg}
fill={darkSidebarFg}
height="3.6"
rx="0.9"
width="3.6"
@@ -403,7 +403,7 @@ function SystemPreferencePreviewSvg({
y="54.175"
/>
<rect
fill={lightSidebarFg}
fill={darkSidebarFg}
height="1.8"
rx="0.225"
width="45.225"
@@ -411,7 +411,7 @@ function SystemPreferencePreviewSvg({
y="38.875"
/>
<rect
fill={lightSidebarFg}
fill={darkSidebarFg}
height="1.8"
rx="0.225"
width="45.225"
@@ -419,7 +419,7 @@ function SystemPreferencePreviewSvg({
y="44.2749"
/>
<rect
fill={lightSidebarFg}
fill={darkSidebarFg}
height="1.8"
rx="0.225"
width="45.225"
@@ -427,7 +427,7 @@ function SystemPreferencePreviewSvg({
y="49.675"
/>
<rect
fill={lightSidebarFg}
fill={darkSidebarFg}
height="1.8"
rx="0.225"
width="45.225"
@@ -435,7 +435,7 @@ function SystemPreferencePreviewSvg({
y="55.075"
/>
<rect
fill={lightMutedFg}
fill={darkMutedFg}
height="1.8"
rx="0.225"
width="26.775"
@@ -446,10 +446,10 @@ function SystemPreferencePreviewSvg({
</g>
<defs>
<clipPath id={clipDark}>
<clipPath id={clipLight}>
<rect fill="white" height="180" rx="3.6" width="288" />
</clipPath>
<clipPath id={clipLight}>
<clipPath id={clipDark}>
<path d="M0 37H118V80H0V37Z" fill="white" />
</clipPath>
<clipPath id={clipOuter}>