fix(web): polish dashboard sidebar chrome and model card menus

Use momentum easing for sidebar transitions, switch sidebar typography to
sans-serif, replace the profile native select with the DS Select, and stop
clipping the Models page Use-as dropdown inside model cards.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Austin Pickett 2026-06-29 09:09:50 -04:00 committed by Teknium
parent 10374bb7a2
commit 1abf0c6cbf
7 changed files with 52 additions and 39 deletions

View file

@ -544,13 +544,13 @@ export default function App() {
id="app-sidebar"
aria-label={t.app.navigation}
className={cn(
"fixed top-0 left-0 z-50 flex h-dvh max-h-dvh w-64 min-h-0 flex-col",
"fixed top-0 left-0 z-50 flex h-dvh max-h-dvh w-64 min-h-0 flex-col font-sans",
"border-r border-current/20",
"bg-background-base",
"transition-[transform] duration-200 ease-out",
"transition-[transform] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]",
mobileOpen ? "translate-x-0" : "-translate-x-full",
"lg:sticky lg:top-0 lg:translate-x-0 lg:shrink-0 lg:overflow-hidden",
"lg:transition-[width] lg:duration-[600ms] lg:ease-[cubic-bezier(0.33,1.35,0.62,1)]",
"lg:transition-[width] lg:duration-300 lg:ease-[cubic-bezier(0.23,1,0.32,1)]",
collapsed && "lg:w-14",
)}
style={{
@ -636,7 +636,7 @@ export default function App() {
<span
className={cn(
"px-5 pt-2.5 pb-1",
"font-mondwest text-display text-xs tracking-[0.12em] text-text-tertiary",
"font-sans text-display text-xs tracking-[0.12em] text-text-tertiary",
isDesktopCollapsed && "lg:hidden",
)}
id="hermes-sidebar-plugin-nav-heading"
@ -843,7 +843,7 @@ function SidebarNavLink({
cn(
"group/nav relative flex items-center gap-3",
"px-5 py-2.5",
"font-mondwest text-display uppercase text-sm tracking-[0.12em]",
"font-sans text-display uppercase text-sm tracking-[0.12em]",
"whitespace-nowrap transition-colors cursor-pointer",
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground",
isActive
@ -939,7 +939,7 @@ function SidebarSystemActions({
<span
className={cn(
"px-5 pt-0.5 pb-0.5",
"font-mondwest text-display text-xs tracking-[0.12em] text-text-tertiary",
"font-sans text-display text-xs tracking-[0.12em] text-text-tertiary",
collapsed && "lg:hidden",
)}
>
@ -1009,7 +1009,7 @@ function SystemActionButton({
className={cn(
"group/action relative flex w-full items-center gap-3",
"px-5 py-2.5",
"font-mondwest text-display text-xs tracking-[0.1em]",
"font-sans text-display text-xs tracking-[0.1em]",
"whitespace-nowrap transition-colors cursor-pointer",
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground",
busy
@ -1183,7 +1183,7 @@ function SidebarTooltip({ anchor, label, warmRef }: SidebarTooltipProps) {
"fixed z-[100] pointer-events-none",
"px-2 py-1",
"bg-background-base border border-current/20 shadow-lg",
"font-mondwest text-display text-xs tracking-[0.1em] text-midground uppercase",
"font-sans text-display text-xs tracking-[0.1em] text-midground uppercase",
)}
style={{
top: rect.top + rect.height / 2,

View file

@ -78,7 +78,6 @@ export function LanguageSwitcher({ collapsed = false, dropUp = false }: Language
>
<span className="inline-flex items-center gap-1.5">
<Typography
mondwest
className="hidden sm:inline text-display tracking-wide text-xs"
>
{locale === "en" ? "EN" : current.name}
@ -151,7 +150,7 @@ function LanguageSwitcherOptions({
aria-selected={selected}
className={cn(
"w-full text-left px-3 py-1.5 flex items-center gap-2 cursor-pointer",
"font-mondwest text-display text-xs tracking-[0.08em]",
"font-sans text-display text-xs tracking-[0.08em]",
"hover:bg-accent hover:text-accent-foreground transition-colors",
selected ? "font-semibold text-foreground" : "text-muted-foreground",
)}

View file

@ -1,4 +1,9 @@
import { useMemo } from "react";
import { Users } from "lucide-react";
import {
Select,
SelectOption,
} from "@nous-research/ui/ui/components/select";
import { useProfileScope } from "@/contexts/useProfileScope";
import { useI18n } from "@/i18n";
import { cn } from "@/lib/utils";
@ -10,14 +15,24 @@ import { cn } from "@/lib/utils";
* Keys, Skills, MCP, Models) reads/writes the selected profile via the
* fetchJSON ?profile= injection. Hidden when only one profile exists.
*/
export function ProfileSwitcher({ collapsed }: { collapsed?: boolean }) {
export function ProfileSwitcher({ collapsed }: ProfileSwitcherProps) {
const { profile, currentProfile, profiles, setProfile } = useProfileScope();
const { t } = useI18n();
const currentDashboardLabel = useMemo(
() =>
(t.app.currentProfileOption ?? "this dashboard ({name})").replace(
"{name}",
currentProfile || "default",
),
[currentProfile, t.app.currentProfileOption],
);
if (profiles.length < 2) return null;
const managed = profile || currentProfile || "default";
const isOther = !!profile && profile !== currentProfile;
const managingLabel = t.app.managingProfile ?? "Managing profile";
return (
<div
@ -25,7 +40,7 @@ export function ProfileSwitcher({ collapsed }: { collapsed?: boolean }) {
"flex items-center gap-2 border-b border-current/10 px-3 py-2",
collapsed && "lg:justify-center lg:px-0",
)}
title={t.app.managingProfile ?? "Managing profile"}
title={managingLabel}
>
<Users
className={cn(
@ -33,35 +48,38 @@ export function ProfileSwitcher({ collapsed }: { collapsed?: boolean }) {
isOther ? "text-amber-300" : "text-text-tertiary",
)}
/>
<select
aria-label={t.app.managingProfile ?? "Managing profile"}
<Select
className={cn(
"h-7 w-full min-w-0 rounded-none border bg-background px-1 text-xs",
isOther
? "border-amber-500/50 text-amber-300"
: "border-border text-text-secondary",
"min-w-0 flex-1",
collapsed && "lg:hidden",
"[&_button]:h-7 [&_button]:border-border [&_button]:bg-background [&_button]:px-2 [&_button]:text-xs",
"[&_button]:font-sans [&_button]:normal-case [&_button]:tracking-normal",
"[&_[role=listbox]>div]:font-sans [&_[role=listbox]>div]:text-xs",
"[&_[role=listbox]>div]:normal-case [&_[role=listbox]>div]:tracking-normal",
isOther &&
"[&_button]:border-amber-500/50 [&_button]:text-amber-300",
)}
id="hermes-profile-switcher"
onValueChange={setProfile}
value={profile}
onChange={(e) => setProfile(e.target.value)}
>
<option value="">
{(t.app.currentProfileOption ?? "this dashboard ({name})").replace(
"{name}",
currentProfile || "default",
)}
</option>
<SelectOption value="">{currentDashboardLabel}</SelectOption>
{profiles
.filter((name) => name !== currentProfile)
.map((name) => (
<option key={name} value={name}>
<SelectOption key={name} value={name}>
{name}
</option>
</SelectOption>
))}
</select>
{collapsed && (
<span className="sr-only">{managed}</span>
)}
</Select>
{collapsed && <span className="sr-only">{managed}</span>}
</div>
);
}
interface ProfileSwitcherProps {
collapsed?: boolean;
}

View file

@ -25,7 +25,7 @@ export function SidebarFooter({ status }: SidebarFooterProps) {
target="_blank"
rel="noopener noreferrer"
className={cn(
"font-mondwest text-display text-xs tracking-[0.12em] text-midground",
"font-sans text-display text-xs tracking-[0.12em] text-midground",
"transition-opacity hover:opacity-90",
"focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground/40",
)}

View file

@ -31,7 +31,7 @@ export function SidebarStatusStrip({ status }: SidebarStatusStripProps) {
"focus-visible:ring-inset",
)}
>
<div className="flex flex-col gap-1 font-mondwest text-xs leading-snug tracking-[0.08em]">
<div className="flex flex-col gap-1 font-sans text-xs leading-snug tracking-[0.08em]">
<p className="break-words">
<span className="text-text-tertiary">{gatewayStatusLabel}</span>{" "}
<span className={cn("font-medium", gw.tone)}>{gw.label}</span>

View file

@ -81,7 +81,6 @@ export function ThemeSwitcher({ collapsed = false, dropUp = false }: ThemeSwitch
{!collapsed && (
<Typography
mondwest
className="hidden sm:inline text-display tracking-wide text-xs"
>
{label}
@ -134,7 +133,6 @@ export function ThemeSwitcher({ collapsed = false, dropUp = false }: ThemeSwitch
>
<div className="border-b border-current/20 px-3 py-2">
<Typography
mondwest
className="text-display text-xs tracking-[0.12em] text-text-tertiary"
>
{sheetTitle}
@ -192,7 +190,6 @@ function ThemeSwitcherOptions({
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
<Typography
mondwest
className="truncate text-display text-xs tracking-wide"
>
{th.label}
@ -235,7 +232,6 @@ function FontSection({ fontChoices, fontId, setFont }: FontSectionProps) {
<span className="inline-flex items-center gap-1.5">
<Type className="h-3 w-3 text-text-tertiary" />
<Typography
mondwest
className="text-display text-xs tracking-[0.12em] text-text-tertiary"
>
{t.theme?.fontTitle ?? "Font"}

View file

@ -268,7 +268,7 @@ function UseAsMenu({
}, [open]);
return (
<div className="relative" data-use-as-menu>
<div className={cn("relative", open && "z-20")} data-use-as-menu>
<Button
size="sm"
outlined
@ -392,7 +392,7 @@ function ModelCard({
return (
<Card
className={`min-w-0 max-w-full overflow-hidden${isMain ? " ring-1 ring-primary/40" : ""}`}
className={cn("min-w-0 max-w-full", isMain && "ring-1 ring-primary/40")}
>
<CardHeader className="pb-3">
<div className="flex items-start justify-between gap-2">