From ab873bbcaaf531c26fd9fb37ff43ee55073c329a Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sun, 9 Aug 2026 03:18:04 +0200 Subject: [PATCH 1/3] =?UTF-8?q?feat(theme):=20R10=20S2=20=E2=80=94=20re-cl?= =?UTF-8?q?ose=20the=20rem=20system=20(px-frozen=20labels/chrome=20->=20re?= =?UTF-8?q?m)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ~124 px-frozen text-[Npx] labels didn't track the Settings font-scale slider while their rem-sized neighbours did. Add text-2xs (0.6875rem) / text-3xs (0.625rem) font-size-ONLY tokens (no paired line-height -> matches the arbitrary they replace, zero vertical-rhythm shift) and codemod every text-[Npx] to a rem class (rare 8/9/12/13px -> rem arbitraries). Also: --hdr-h 68px -> 4.25rem so header content clears the rem-sized pills at scale 1.3 instead of sliding under; NavSidebar/SidePanel slot widths + their icon/badge px sizes go rem so the rails keep pace with their (now-scaling) labels; a w-24/h-[54px] thumb -> h-13.5 to preserve 16:9 at scale. Media thumbnails, page-width caps and fixed container widths deliberately stay px. Verified via getComputedStyle at scale 1.06/1.3: text-2xs 11.66->14.3px, header clearance 72->88px, nav slot 246->302px — all scale proportionally. --- frontend/src/components/ActiveFilterChips.tsx | 2 +- frontend/src/components/AdminUsers.tsx | 10 +++---- frontend/src/components/AuditLog.tsx | 2 +- frontend/src/components/ChannelPicker.tsx | 2 +- frontend/src/components/ChannelTags.tsx | 6 ++-- frontend/src/components/Channels.tsx | 4 +-- frontend/src/components/ChatThread.tsx | 2 +- frontend/src/components/ConfigPanel.tsx | 16 +++++------ frontend/src/components/DownloadCenter.tsx | 4 +-- frontend/src/components/GlassTuner.tsx | 14 +++++----- frontend/src/components/LanguageSwitcher.tsx | 2 +- frontend/src/components/Messages.tsx | 4 +-- frontend/src/components/NavSidebar.tsx | 24 ++++++++-------- .../src/components/NotificationsPanel.tsx | 8 +++--- frontend/src/components/PlayerModal.tsx | 4 +-- frontend/src/components/Playlists.tsx | 6 ++-- frontend/src/components/PlaylistsRail.tsx | 6 ++-- frontend/src/components/PlexBrowse.tsx | 26 ++++++++--------- frontend/src/components/PlexInfo.tsx | 6 ++-- frontend/src/components/PlexPlayer.tsx | 28 +++++++++---------- frontend/src/components/PlexPlaylistView.tsx | 10 +++---- frontend/src/components/PlexSidebar.tsx | 4 +-- frontend/src/components/ReleaseNotes.tsx | 2 +- frontend/src/components/SavedViewsWidget.tsx | 2 +- frontend/src/components/Scheduler.tsx | 16 +++++------ frontend/src/components/SettingsPanel.tsx | 4 +-- frontend/src/components/SidePanel.tsx | 12 ++++---- frontend/src/components/Sidebar.tsx | 14 +++++----- frontend/src/components/Tabs.tsx | 2 +- frontend/src/components/TagManager.tsx | 2 +- frontend/src/components/Toaster.tsx | 2 +- frontend/src/components/VideoCard.tsx | 6 ++-- frontend/src/components/VideoEditor.tsx | 2 +- frontend/src/components/Welcome.tsx | 4 +-- frontend/src/components/auditColumns.tsx | 2 +- frontend/src/components/playlistColumns.tsx | 8 +++--- frontend/src/index.css | 18 +++++++++--- 37 files changed, 144 insertions(+), 142 deletions(-) diff --git a/frontend/src/components/ActiveFilterChips.tsx b/frontend/src/components/ActiveFilterChips.tsx index a4fa8e61..625f4235 100644 --- a/frontend/src/components/ActiveFilterChips.tsx +++ b/frontend/src/components/ActiveFilterChips.tsx @@ -21,7 +21,7 @@ export default function ActiveFilterChips({ className="flex flex-wrap items-center gap-1.5 pb-3" aria-label={t("feed.chips.label")} > - + {t("feed.chips.label")} {filters.map((f) => ( diff --git a/frontend/src/components/AdminUsers.tsx b/frontend/src/components/AdminUsers.tsx index d96df65c..31e038cc 100644 --- a/frontend/src/components/AdminUsers.tsx +++ b/frontend/src/components/AdminUsers.tsx @@ -61,9 +61,7 @@ function Badge({ ? "border-warn/40 text-warn" : "border-border text-muted"; return ( - - {children} - + {children} ); } @@ -180,7 +178,7 @@ function UsersRoles({ me }: { me: Me }) { {u.display_name ?? u.email.split("@")[0]} {self && · {t("users.roles.you")}} -
{u.email}
+
{u.email}
{u.role === "admin" && {t("users.roles.admin")}} {u.is_demo && {t("users.roles.demo")}} @@ -436,7 +434,7 @@ function AdminDemo() {
{r.email}
{r.added_by && ( -
+
{t("settings.demo.addedBy", { who: r.added_by })}
)} @@ -521,7 +519,7 @@ function InviteRow({
{inv.email}
{inv.requested_at && ( -
+
{t("settings.invites.requested", { time: new Date(inv.requested_at).toLocaleString(), })} diff --git a/frontend/src/components/AuditLog.tsx b/frontend/src/components/AuditLog.tsx index 6f061e0c..f19b0457 100644 --- a/frontend/src/components/AuditLog.tsx +++ b/frontend/src/components/AuditLog.tsx @@ -84,7 +84,7 @@ export default function AuditLog() { emptyText={t("audit.empty")} /> {truncated && ( -

+

{t("audit.truncated", { returned: data!.returned, total: data!.total })}

)} diff --git a/frontend/src/components/ChannelPicker.tsx b/frontend/src/components/ChannelPicker.tsx index 25957150..e2fd5225 100644 --- a/frontend/src/components/ChannelPicker.tsx +++ b/frontend/src/components/ChannelPicker.tsx @@ -71,7 +71,7 @@ export default function ChannelPicker({ random to anyone who hasn't set a priority. Only while unfiltered: a search reorders nothing but does drop the rows that explain it. */} {!q && all.length > 0 && ( -
{t("sidebar.channelOrder")}
+
{t("sidebar.channelOrder")}
)}
{t("channels.tags.noneYet")}

+

{t("channels.tags.noneYet")}

)}
@@ -238,7 +238,7 @@ export function ChannelTagsControl({ }, [hidden, overflowOpen]); const pillClass = - "shrink-0 text-[10px] px-1.5 py-0.5 rounded-full bg-card text-muted border border-border"; + "shrink-0 text-3xs px-1.5 py-0.5 rounded-full bg-card text-muted border border-border"; return ( // When `bounded`, a FIXED width keeps the cell's footprint constant under `table-layout:auto`: a diff --git a/frontend/src/components/Channels.tsx b/frontend/src/components/Channels.tsx index 1c533ce9..d15b889e 100644 --- a/frontend/src/components/Channels.tsx +++ b/frontend/src/components/Channels.tsx @@ -748,7 +748,7 @@ export default function Channels() { ))}
-

{t("channels.blocked.hint")}

+

{t("channels.blocked.hint")}

)}
@@ -814,7 +814,7 @@ function SyncBadge({ ok, label, hint }: { ok: boolean; label: string; hint?: str return ( diff --git a/frontend/src/components/ChatThread.tsx b/frontend/src/components/ChatThread.tsx index 58f96a5d..8f74741d 100644 --- a/frontend/src/components/ChatThread.tsx +++ b/frontend/src/components/ChatThread.tsx @@ -183,7 +183,7 @@ export default function ChatThread({ }`} > {text} -
+
{relativeTime(m.created_at)}
diff --git a/frontend/src/components/ConfigPanel.tsx b/frontend/src/components/ConfigPanel.tsx index 9c1fe3d6..c51dd554 100644 --- a/frontend/src/components/ConfigPanel.tsx +++ b/frontend/src/components/ConfigPanel.tsx @@ -230,9 +230,7 @@ export default function ConfigPanel() { {t("config.plexTest")}
- {dirty && ( -

{t("config.plexTestDirty")}

- )} + {dirty &&

{t("config.plexTestDirty")}

} {plexResult && (

@@ -243,15 +241,15 @@ export default function ConfigPanel() {

{plexResult.media_check?.checked && (plexResult.media_check.ok ? ( -

{t("config.plexMediaOk")}

+

{t("config.plexMediaOk")}

) : ( -

+

{t("config.plexMediaMissing", { path: plexResult.media_check.local_path ?? "", })}

))} -

{t("config.plexPickLibraries")}

+

{t("config.plexPickLibraries")}

{plexResult.sections.map((s) => ( ))}
-

{t("config.plexPickHint")}

+

{t("config.plexPickHint")}

)}
@@ -348,7 +346,7 @@ function Field({

{t(`config.fields.${item.key}.hint`, "")}

-

{status}

+

{status}

diff --git a/frontend/src/components/DownloadCenter.tsx b/frontend/src/components/DownloadCenter.tsx index 1b4975cb..ed0cbefe 100644 --- a/frontend/src/components/DownloadCenter.tsx +++ b/frontend/src/components/DownloadCenter.tsx @@ -227,7 +227,7 @@ function DownloadRow({
{job.job_kind === "edit" && ( - + {t("downloads.clipBadge")} )} @@ -280,7 +280,7 @@ function DownloadRow({ style={{ width: `${job.progress}%` }} />
-
+
{job.progress}% {job.speed_bps ? {formatSpeed(job.speed_bps)} : null} {job.eta_s ? {formatEta(job.eta_s)} : null} diff --git a/frontend/src/components/GlassTuner.tsx b/frontend/src/components/GlassTuner.tsx index 5c0fb624..fed6ad06 100644 --- a/frontend/src/components/GlassTuner.tsx +++ b/frontend/src/components/GlassTuner.tsx @@ -288,7 +288,7 @@ export default function GlassTuner() { >
◐ Glass Tuner - lab + lab ))}
@@ -351,7 +351,7 @@ export default function GlassTuner() { {tab === "palette" && (
-

+

Overrides the live scheme (inline on <html>). Reset clears these back to the scheme.

@@ -364,7 +364,7 @@ export default function GlassTuner() { className="w-8 h-6 rounded-sm border border-border bg-transparent cursor-pointer" /> {p.label} - {swatches[p.k]} + {swatches[p.k]} ))}
@@ -372,7 +372,7 @@ export default function GlassTuner() { {/* Export */}
-
+
Export → bake in
@@ -176,7 +176,7 @@ function ConversationList({ {previewText(c)} {c.unread > 0 && ( - + {c.unread} )} diff --git a/frontend/src/components/NavSidebar.tsx b/frontend/src/components/NavSidebar.tsx index 1daeedfb..92b54547 100644 --- a/frontend/src/components/NavSidebar.tsx +++ b/frontend/src/components/NavSidebar.tsx @@ -198,12 +198,12 @@ export default function NavSidebar({ }`} > - + {/* Collapsed rail: a numeric badge centred on a circle at the icon corner; the ring matches the row background so it reads cleanly whether the row is active or not. */} {show && slim && ( @@ -214,7 +214,7 @@ export default function NavSidebar({ {!slim && {label}} {show && !slim && ( {badge > 99 ? "99+" : badge} @@ -226,14 +226,14 @@ export default function NavSidebar({ return ( // Layout slot: reserves only the slim footprint when unpinned, so the expanded rail (below) // overlays content instead of pushing it. Pinned reserves the full width (in-flow). -
+