fix(frontend): responsive layout across all dashboard pages
- SectionHeader: action (filters/legends) now wraps below the title on narrow screens instead of overflowing beside it (flex-wrap, gap-2 sm:gap-3). - GuildChannelPicker: selects go full-width and stack on mobile (w-full sm:w-44 / sm:w-52) instead of fixed widths that exceeded a 375px viewport. - Messages search: w-full sm:w-64 so it doesn't crowd the picker on mobile. - TopBar: tighter padding (px-4 sm:px-5), smaller title on mobile, connection status uses compact (dot only) on mobile, ambient pill hidden < sm. - Shell main + dashboard channel label: responsive padding / shrink-0 widths. Verified tsc --noEmit + next build clean; targets breakpoints 375/768/1024/1440.
This commit is contained in:
@@ -155,7 +155,7 @@ export function DashboardView({
|
|||||||
100;
|
100;
|
||||||
return (
|
return (
|
||||||
<div key={c.channel_id} className="flex items-center gap-3">
|
<div key={c.channel_id} className="flex items-center gap-3">
|
||||||
<span className="w-40 truncate text-sm text-ink-soft">
|
<span className="w-28 shrink-0 truncate text-sm text-ink-soft sm:w-40">
|
||||||
{c.channel_name ?? c.channel_id.slice(0, 8)}
|
{c.channel_name ?? c.channel_id.slice(0, 8)}
|
||||||
</span>
|
</span>
|
||||||
<div className="h-2 flex-1 overflow-hidden rounded-full bg-white/8">
|
<div className="h-2 flex-1 overflow-hidden rounded-full bg-white/8">
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export function MessagesView({
|
|||||||
setLoadedPages(0);
|
setLoadedPages(0);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="relative ml-auto w-64">
|
<div className="relative ml-auto w-full sm:w-64">
|
||||||
<Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-ink-faint" />
|
<Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-ink-faint" />
|
||||||
<Input
|
<Input
|
||||||
className="pl-9"
|
className="pl-9"
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export function GuildChannelPicker({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-col gap-2 sm:flex-row sm:flex-wrap sm:items-center">
|
||||||
<Select
|
<Select
|
||||||
value={g}
|
value={g}
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
@@ -52,7 +52,7 @@ export function GuildChannelPicker({
|
|||||||
options={guildOpts}
|
options={guildOpts}
|
||||||
placeholder="Guild"
|
placeholder="Guild"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="w-44"
|
className="w-full sm:w-44"
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
value={c}
|
value={c}
|
||||||
@@ -63,7 +63,7 @@ export function GuildChannelPicker({
|
|||||||
options={channelOpts}
|
options={channelOpts}
|
||||||
placeholder={mode === "voice" ? "Voice channel" : "Text channel"}
|
placeholder={mode === "voice" ? "Voice channel" : "Text channel"}
|
||||||
size="sm"
|
size="sm"
|
||||||
className="w-52"
|
className="w-full sm:w-52"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ export function SectionHeader({
|
|||||||
className?: string;
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={cn("mb-3 flex items-end justify-between gap-3", className)}>
|
<div className={cn("mb-3 flex flex-wrap items-start justify-between gap-2 sm:gap-3", className)}>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
{eyebrow && <div className="eyebrow mb-1">{eyebrow}</div>}
|
{eyebrow && <div className="eyebrow mb-1">{eyebrow}</div>}
|
||||||
<h2 className="display text-xl text-ink">{title}</h2>
|
<h2 className="display text-xl text-ink">{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
{action}
|
{action && <div className="flex flex-wrap items-center gap-2">{action}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export function AppFrame({ children }: { children: React.ReactNode }) {
|
|||||||
<NavRail />
|
<NavRail />
|
||||||
<div className="flex min-w-0 flex-1 flex-col">
|
<div className="flex min-w-0 flex-1 flex-col">
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<main className="min-h-0 flex-1 overflow-y-auto px-5 pb-8">
|
<main className="min-h-0 flex-1 overflow-y-auto px-4 pb-8 sm:px-5">
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,20 +32,20 @@ export function TopBar() {
|
|||||||
: "text-signal";
|
: "text-signal";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 z-40 flex items-center gap-4 px-5 py-3.5">
|
<header className="sticky top-0 z-40 flex items-center gap-3 px-4 py-3 sm:gap-4 sm:px-5 sm:py-3.5">
|
||||||
<div className="flex min-w-0 items-baseline gap-3">
|
<div className="flex min-w-0 items-baseline gap-2 sm:gap-3">
|
||||||
<span className="eyebrow">GMW</span>
|
<span className="eyebrow hidden sm:inline">GMW</span>
|
||||||
<h1 className="display truncate text-[1.5rem] text-ink">{label}</h1>
|
<h1 className="display truncate text-[1.25rem] text-ink sm:text-[1.5rem]">{label}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="ml-auto flex items-center gap-3">
|
<div className="ml-auto flex items-center gap-2 sm:gap-3">
|
||||||
<span className={cn("pill", signalTone)}>
|
<span className={cn("pill hidden sm:flex", signalTone)}>
|
||||||
<span
|
<span
|
||||||
className={cn("size-1.5 rounded-full bg-current animate-breathe")}
|
className={cn("size-1.5 rounded-full bg-current animate-breathe")}
|
||||||
/>
|
/>
|
||||||
{state.label ?? "nominal"}
|
{state.label ?? "nominal"}
|
||||||
</span>
|
</span>
|
||||||
<ConnectionStatus />
|
<ConnectionStatus compact />
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Open command palette"
|
aria-label="Open command palette"
|
||||||
|
|||||||
Reference in New Issue
Block a user