History

Polish the app title bar: quiet Beta tag, no traffic-light overlap (#1207)

Replace the filled Beta pill next to the executor wordmark with quiet muted-mono metadata, matching the registry-minimal design language, and extract a shared Wordmark so the desktop and dashboard shells render the brand identically. Apply the macOS traffic-light offset to the mobile sidebar overlay and the collapsed top bar as well, so the native window controls never land on top of the wordmark when the window is narrow.

Rhys Sullivan committed 4 months ago

Commit c8d9b9d1 parent 5d23cee4 files · +45 −31Browse files

Unified · Split

4 files
  1. .changeset/desktop-titlebar-polish.md +8 −0
  2. packages/app/src/web/shell.tsx +14 −26
  3. packages/react/src/components/wordmark.tsx +20 −0
  4. packages/react/src/multiplayer/shell.tsx +3 −5
.changeset/desktop-titlebar-polish.md+8−0
@@ -0,0 +1,8 @@
1---
2"@executor-js/desktop": patch
3"@executor-js/host-selfhost": patch
4"@executor-js/host-cloudflare": patch
5"@executor-js/react": patch
6---
7
8Polish the app's title bar. The release tag beside the `executor` wordmark is now quiet muted-mono metadata instead of a filled pill, matching the registry-minimal design language, and the wordmark is shared across the desktop and dashboard shells so the brand reads identically everywhere. The macOS traffic-light offset is also applied to the mobile sidebar overlay and the collapsed top bar, so the native window controls never sit on top of the wordmark when the window is narrow.
packages/app/src/web/shell.tsx+14−26
@@ -14,6 +14,7 @@ import { IntegrationIconWithAccount } from "@executor-js/react/components/integr · 13 unmodified lines
14import { CommandPalette } from "@executor-js/react/components/command-palette";14import { CommandPalette } from "@executor-js/react/components/command-palette";
15import { useClientPlugins, useIntegrationPlugins } from "@executor-js/sdk/client";15import { useClientPlugins, useIntegrationPlugins } from "@executor-js/sdk/client";
16import { SidebarUpdateCard } from "@executor-js/react/components/update-card";16import { SidebarUpdateCard } from "@executor-js/react/components/update-card";
17import { Wordmark } from "@executor-js/react/components/wordmark";
17import { ServerConnectionMenu } from "./server-connection-menu";18import { ServerConnectionMenu } from "./server-connection-menu";
18 19
19// ── Env ─────────────────────────────────────────────────────────────────20// ── Env ─────────────────────────────────────────────────────────────────
@@ -161,16 +162,8 @@ function SidebarContent(props: { pathname: string; onNavigate?: () => void; show · 141 unmodified lines
161 <>162 <>
162 {props.showBrand !== false && (163 {props.showBrand !== false && (
163 <div className="desktop-macos-titlebar flex h-12 shrink-0 items-center gap-2 border-b border-sidebar-border px-4">164 <div className="desktop-macos-titlebar flex h-12 shrink-0 items-center gap-2 border-b border-sidebar-border px-4">
164 <Link165 <Link to="/{-$orgSlug}" className="desktop-macos-no-drag flex shrink-0 items-center">
165 to="/{-$orgSlug}"166 <Wordmark />
166 className="desktop-macos-no-drag flex shrink-0 items-center gap-1.5"
167 >
168 <span className="font-mono text-sm font-medium tracking-tight text-foreground">
169 executor
170 </span>
171 <span className="rounded bg-primary/15 px-1.5 py-px text-[10px] font-semibold uppercase tracking-wider text-primary">
172 Beta
173 </span>
174 </Link>167 </Link>
175 <div className="desktop-macos-no-drag ml-auto flex min-w-0 flex-1 justify-end pl-3">168 <div className="desktop-macos-no-drag ml-auto flex min-w-0 flex-1 justify-end pl-3">
176 <ServerConnectionMenu variant="header" />169 <ServerConnectionMenu variant="header" />
@@ -312,21 +305,16 @@ export function Shell() { · 135 unmodified lines
312 onClick={() => setMobileSidebarOpen(false)}305 onClick={() => setMobileSidebarOpen(false)}
313 />306 />
314 <div className="relative flex h-full w-[84vw] max-w-xs flex-col border-r border-sidebar-border bg-sidebar shadow-2xl">307 <div className="relative flex h-full w-[84vw] max-w-xs flex-col border-r border-sidebar-border bg-sidebar shadow-2xl">
315 <div className="flex h-12 shrink-0 items-center justify-between border-b border-sidebar-border px-4">308 <div className="desktop-macos-titlebar flex h-12 shrink-0 items-center justify-between border-b border-sidebar-border px-4">
316 <Link to="/{-$orgSlug}" className="flex items-center gap-1.5">309 <Link to="/{-$orgSlug}" className="desktop-macos-no-drag flex items-center">
317 <span className="font-mono text-sm font-medium tracking-tight text-foreground">310 <Wordmark />
318 executor
319 </span>
320 <span className="rounded bg-primary/15 px-1.5 py-px text-[10px] font-semibold uppercase tracking-wider text-primary">
321 Beta
322 </span>
323 </Link>311 </Link>
324 <Button312 <Button
325 variant="ghost"313 variant="ghost"
326 size="icon-sm"314 size="icon-sm"
327 aria-label="Close navigation"315 aria-label="Close navigation"
328 onClick={() => setMobileSidebarOpen(false)}316 onClick={() => setMobileSidebarOpen(false)}
329 className="text-sidebar-foreground hover:bg-sidebar-active hover:text-foreground"317 className="desktop-macos-no-drag text-sidebar-foreground hover:bg-sidebar-active hover:text-foreground"
330 >318 >
331 <svg viewBox="0 0 16 16" className="size-3.5">319 <svg viewBox="0 0 16 16" className="size-3.5">
332 <path320 <path
@@ -356,14 +344,16 @@ export function Shell() { · 23 unmodified lines
356 the top and their borders line up with the sidebar header. */}344 the top and their borders line up with the sidebar header. */}
357 <div className="desktop-macos-main-titlebar" />345 <div className="desktop-macos-main-titlebar" />
358 346
359 {/* Mobile top bar */}347 {/* Mobile top bar. The desktop-macos-titlebar offset keeps the
360 <div className="flex h-12 shrink-0 items-center justify-between border-b border-border bg-background px-4 md:hidden">348 far-left hamburger clear of the native traffic lights when the macOS
349 window is forced below the md breakpoint (issue #1125). */}
350 <div className="desktop-macos-titlebar flex h-12 shrink-0 items-center justify-between border-b border-border bg-background px-4 md:hidden">
361 <Button351 <Button
362 variant="outline"352 variant="outline"
363 size="icon-sm"353 size="icon-sm"
364 aria-label="Open navigation"354 aria-label="Open navigation"
365 onClick={() => setMobileSidebarOpen(true)}355 onClick={() => setMobileSidebarOpen(true)}
366 className="bg-card hover:bg-accent/50"356 className="desktop-macos-no-drag bg-card hover:bg-accent/50"
367 >357 >
368 <svg viewBox="0 0 16 16" className="size-4">358 <svg viewBox="0 0 16 16" className="size-4">
369 <path359 <path
@@ -374,10 +364,8 @@ export function Shell() { · 4 unmodified lines
374 />364 />
375 </svg>365 </svg>
376 </Button>366 </Button>
377 <Link to="/{-$orgSlug}" className="flex items-center gap-1.5">367 <Link to="/{-$orgSlug}" className="desktop-macos-no-drag flex items-center">
378 <span className="font-mono text-sm font-medium tracking-tight text-foreground">368 <Wordmark />
379 executor
380 </span>
381 </Link>369 </Link>
382 <div className="w-8 shrink-0" />370 <div className="w-8 shrink-0" />
383 </div>371 </div>
packages/react/src/components/wordmark.tsx+20−0
@@ -0,0 +1,20 @@
1import { cn } from "../lib/utils";
2
3/**
4 * The product wordmark: `executor` set in Geist Mono, with a quiet `beta` tag.
5 *
6 * Registry-minimal (see design.md): identity comes from restraint, not
7 * decoration. The release tag is muted mono metadata sitting next to the
8 * wordmark, NOT a filled pill or a brand-hued badge. Shared by the desktop/web
9 * shell and the multiplayer shell so the brand reads identically everywhere.
10 */
11export function Wordmark(props: { readonly className?: string }) {
12 return (
13 <span className={cn("inline-flex items-baseline gap-1.5", props.className)}>
14 <span className="font-mono text-sm font-medium tracking-tight text-foreground">executor</span>
15 <span className="font-mono text-[10px] font-medium uppercase tracking-[0.12em] text-muted-foreground">
16 beta
17 </span>
18 </span>
19 );
20}
packages/react/src/multiplayer/shell.tsx+3−5
@@ -22,6 +22,7 @@ import { · 21 unmodified lines
22 integrationPresetIconUrl,22 integrationPresetIconUrl,
23} from "../components/integration-favicon";23} from "../components/integration-favicon";
24import { CommandPalette } from "../components/command-palette";24import { CommandPalette } from "../components/command-palette";
25import { Wordmark } from "../components/wordmark";
25import { useClientPlugins, useIntegrationPlugins } from "@executor-js/sdk/client";26import { useClientPlugins, useIntegrationPlugins } from "@executor-js/sdk/client";
26import { useAuth } from "./auth-context";27import { useAuth } from "./auth-context";
27 28
@@ -113,11 +114,8 @@ export interface ShellProps { · 85 unmodified lines
113 114
114function Brand(props: { onNavigate?: () => void }) {115function Brand(props: { onNavigate?: () => void }) {
115 return (116 return (
116 <Link to="/{-$orgSlug}" onClick={props.onNavigate} className="flex items-center gap-1.5">117 <Link to="/{-$orgSlug}" onClick={props.onNavigate} className="flex items-center">
117 <span className="font-mono text-sm font-medium tracking-tight text-foreground">executor</span>118 <Wordmark />
118 <span className="rounded-sm bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-primary">
119 Beta
120 </span>
121 </Link>119 </Link>
122 );120 );
123}121}