Studio: rounded rectangle hover states for menu items instead of pills (#6210)

* Studio: use rounded rectangles for menu item hover states instead of pills

Dropdown, select, and model picker items previously used fully rounded
pill highlights. Switch them to an 11px rounded rectangle so hover and
selected states match across the plus menu, profile menu, run settings,
selects, and the model picker. Also add a small side gutter to the plus
menu so item highlights sit slightly inset from the menu edge.

* Studio: concentric menu corners, wider gutters, single-item pill menus

Container radius now equals the item hover radius plus the side gutter
(12px + 10px = 22px) so the curves run parallel. Menus with a single
item render as fully rounded pills. The profile menu gets the same
gutter and hover radius. Model picker rows go back to their original
fully rounded hover.
This commit is contained in:
Michael Han 2026-06-12 03:03:24 -07:00 committed by GitHub
parent e0d6674ff6
commit 068b2c120f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 34 additions and 18 deletions

View file

@ -1099,7 +1099,7 @@ export function AppSidebar() {
side="top"
align="center"
sideOffset={8}
className="app-user-menu menu-soft-surface-up ring-0 w-[16rem] px-1.5 py-2.5 font-heading rounded-[20px] border-0"
className="app-user-menu menu-soft-surface-up ring-0 w-[16rem] px-2.5 py-2.5 font-heading rounded-[20px] border-0"
>
<DropdownMenuGroup>
<DropdownMenuItem

View file

@ -81,7 +81,7 @@ function DropdownMenuItem({
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-full px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-[11px] px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
{...props}
@ -99,7 +99,7 @@ function DropdownMenuCheckboxItem({
<DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-full py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-[11px] py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
checked={checked}
@ -138,7 +138,7 @@ function DropdownMenuRadioItem({
<DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-full py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-[11px] py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
{...props}
@ -224,7 +224,7 @@ function DropdownMenuSubTrigger({
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-full px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-[11px] px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
{...props}

View file

@ -173,7 +173,7 @@ function SelectItem({
<SelectPrimitive.Item
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-full py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-[11px] py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
{...props}

View file

@ -779,7 +779,7 @@
height: 36px;
padding: 0 0.75rem !important;
gap: 9.5px !important;
border-radius: 9999px;
border-radius: 12px;
font-weight: 500;
font-size: 15px;
line-height: 20px;
@ -1294,18 +1294,33 @@
/* Soft elevation; [data-slot] outranks the component ring-1, dropping the border. */
.unsloth-plus-menu[data-slot] {
/* Pin radius so dark matches light (rounded-lg resolves smaller in dark).
/* Concentric with the item hover boxes, like Gemini: container radius =
item radius (12px) + side gutter (10px), so the curves run parallel.
!important beats the global 14px dropdown radius. */
border-radius: 18px !important;
border-radius: 22px !important;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
/* Side gutter so item hover boxes sit inset from the menu edge, like
Gemini's menus (more breathing room than a hairline gutter). */
padding-left: 10px;
padding-right: 10px;
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
}
/* MCP menu is wider (232px), so nudge the radius up so it reads as round as
the narrower + menu (18 * 232/212 ~= 20px). */
/* Width does not change the concentric radius; the MCP menu uses the same
item radius + gutter, so it keeps the same container radius. */
.unsloth-plus-menu.mcp-menu[data-slot] {
border-radius: 20px !important;
border-radius: 22px !important;
}
/* One-line menus (a single item, e.g. "All saved prompts…" with nothing
pinned, or a lone "Export as Markdown") read as fully rounded pills. */
.unsloth-plus-menu[data-slot]:not(:has(> :nth-child(2))) {
border-radius: 9999px !important;
}
.unsloth-plus-menu[data-slot]:not(:has(> :nth-child(2)))
:is([data-slot="dropdown-menu-item"], [data-slot="dropdown-menu-sub-trigger"]) {
border-radius: 9999px;
}
.dark .unsloth-plus-menu[data-slot] {
@ -1319,14 +1334,15 @@
[data-slot="dropdown-menu-item"],
[data-slot="dropdown-menu-sub-trigger"]
) {
@apply gap-3 pl-4 pr-3 py-2 text-[14px];
@apply gap-3 pl-3 pr-3 py-2 text-[14px];
cursor: pointer;
/* Pin hover-box radius so dark matches light (same as the container). */
border-radius: 1.1rem;
/* Pin hover-box radius so dark matches light (container radius minus the
side gutter keeps the curves concentric). */
border-radius: 12px;
}
.unsloth-plus-menu [data-slot="dropdown-menu-label"] {
@apply pl-4 pr-3 py-1.5 text-[12px];
@apply pl-3 pr-3 py-1.5 text-[12px];
}
/* Active (green) items keep their primary text and icon color on hover. */
@ -1370,8 +1386,8 @@
[data-slot="dropdown-menu-sub-trigger"]
)
svg {
width: 1.05rem;
height: 1.05rem;
width: 1.15rem;
height: 1.15rem;
}
/* Destructive items keep red text and a red-tinted hover, not the grey one. */