visual upgrade
ci / build-and-design (push) Failing after 13s

This commit is contained in:
Jason Stedwell
2026-07-23 04:23:19 -05:00
parent 9d7f593307
commit 822d8e41e6
53 changed files with 7757 additions and 285 deletions
Executable → Regular
+109 -26
View File
@@ -1,4 +1,9 @@
"use client";
import * as React from "react";
import { Menu } from "lucide-react";
import { Button } from "../components/button";
import { Dialog, DialogContent, DialogTitle } from "../components/dialog";
import { cn } from "../lib/cn";
export interface NavItem {
@@ -9,50 +14,128 @@ export interface NavItem {
onClick?: () => void;
}
/** Dashboard chrome: fixed sidebar + sticky topbar + scrolling content. */
function ShellNav({
items,
label,
onNavigate,
}: {
items: NavItem[];
label: string;
onNavigate?: () => void;
}) {
return (
<nav aria-label={label} className="flex-1 space-y-0.5 px-3 py-3">
{items.map((item) => {
const classes = cn(
"flex w-full items-center gap-3 rounded px-3 py-2 text-sm font-medium transition-colors duration-fast",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60 [&_svg]:size-4 [&_svg]:shrink-0",
item.active ? "bg-brand/15 text-brand" : "text-muted hover:bg-surface-2/60 hover:text-text"
);
const content = <>{item.icon}<span className="truncate">{item.label}</span></>;
if (item.href) {
return (
<a
key={item.label}
href={item.href}
aria-current={item.active ? "page" : undefined}
onClick={() => {
item.onClick?.();
onNavigate?.();
}}
className={classes}
>
{content}
</a>
);
}
return (
<button
key={item.label}
type="button"
aria-current={item.active ? "page" : undefined}
onClick={() => {
item.onClick?.();
onNavigate?.();
}}
className={classes}
>
{content}
</button>
);
})}
</nav>
);
}
/** Responsive dashboard chrome with desktop navigation and a mobile drawer. */
export function AppShell({
brand,
nav,
topbar,
children,
footer,
navLabel = "Primary navigation",
className,
mainClassName,
}: {
brand: React.ReactNode;
nav: NavItem[];
topbar?: React.ReactNode;
children: React.ReactNode;
footer?: React.ReactNode;
navLabel?: string;
className?: string;
mainClassName?: string;
}) {
const [mobileOpen, setMobileOpen] = React.useState(false);
const sidebar = (mobile = false) => (
<>
<div className={cn("flex h-14 items-center px-5 font-display text-lg font-bold", mobile && "pr-12")}>
{brand}
</div>
<ShellNav items={nav} label={navLabel} onNavigate={mobile ? () => setMobileOpen(false) : undefined} />
{footer && <div className="border-t border-border p-3 text-sm text-muted">{footer}</div>}
</>
);
return (
<div className="flex min-h-screen bg-bg text-text">
<div className={cn("flex min-h-dvh bg-bg text-text", className)}>
<a
href="#main-content"
className="fixed left-3 top-3 z-[70] -translate-y-20 rounded bg-brand px-3 py-2 text-sm font-semibold text-brand-foreground shadow-lg focus:translate-y-0"
>
Skip to content
</a>
<aside className="hidden w-60 shrink-0 flex-col border-r border-border bg-surface md:flex">
<div className="flex h-14 items-center px-5 font-display text-lg font-bold">{brand}</div>
<nav className="flex-1 space-y-0.5 px-3 py-3">
{nav.map((item) => {
const Comp = item.href ? "a" : "button";
return (
<Comp
key={item.label}
href={item.href}
onClick={item.onClick}
className={cn(
"flex w-full items-center gap-3 rounded px-3 py-2 text-sm font-medium transition-colors duration-fast [&_svg]:size-4",
item.active ? "bg-brand/15 text-brand" : "text-muted hover:bg-surface-2/60 hover:text-text"
)}
>
{item.icon}
{item.label}
</Comp>
);
})}
</nav>
{footer && <div className="border-t border-border p-3 text-sm text-muted">{footer}</div>}
{sidebar()}
</aside>
<Dialog open={mobileOpen} onOpenChange={setMobileOpen}>
<DialogContent className="inset-y-0 left-0 top-0 flex h-dvh w-[min(20rem,88vw)] max-w-none translate-x-0 translate-y-0 flex-col rounded-none border-y-0 border-l-0 p-0 md:hidden">
<DialogTitle className="sr-only">{navLabel}</DialogTitle>
{sidebar(true)}
</DialogContent>
</Dialog>
<div className="flex min-w-0 flex-1 flex-col">
<header className="sticky top-0 z-30 flex h-14 items-center justify-between gap-4 border-b border-border bg-bg/80 px-5 backdrop-blur">
{topbar}
<header className="sticky top-0 z-30 flex min-h-14 items-center gap-3 border-b border-border bg-bg/85 px-4 py-2 backdrop-blur md:px-5">
<Button
type="button"
variant="ghost"
size="icon"
className="shrink-0 md:hidden"
aria-label="Open navigation"
onClick={() => setMobileOpen(true)}
>
<Menu />
</Button>
<div className="min-w-0 flex-1">{topbar ?? <div className="font-display font-semibold md:hidden">{brand}</div>}</div>
</header>
<main className="flex-1 p-6">{children}</main>
<main id="main-content" tabIndex={-1} className={cn("flex-1 p-4 outline-none sm:p-6", mainClassName)}>
{children}
</main>
</div>
</div>
);
+4 -2
View File
@@ -45,9 +45,11 @@ export function CommandPalette({
open={open}
onOpenChange={setOpen}
label="Command palette"
overlayClassName="fixed inset-0 z-50 bg-black/60 backdrop-blur-sm"
contentClassName="fixed left-1/2 top-[18vh] z-50 w-[calc(100%-2rem)] max-w-xl -translate-x-1/2"
className={cn(
"fixed left-1/2 top-1/4 z-50 w-full max-w-xl -translate-x-1/2 overflow-hidden",
"rounded-lg border border-border bg-surface shadow-lg data-[state=open]:animate-scale-in"
"overflow-hidden rounded-lg border border-border bg-surface shadow-lg",
"data-[state=open]:animate-scale-in"
)}
>
<div className="flex items-center gap-2 border-b border-border px-4">
+1 -1
View File
@@ -44,7 +44,7 @@ export function Hero({
)}
{(primaryCta || secondaryCta) && (
<FadeIn delay={0.15}>
<div className="mt-10 flex items-center justify-center gap-3">
<div className="mt-10 flex flex-col items-stretch justify-center gap-3 sm:flex-row sm:items-center">
{primaryCta && (
<Button size="lg" asChild={!!primaryCta.href} onClick={primaryCta.onClick}>
{primaryCta.href ? <a href={primaryCta.href}>{primaryCta.label}</a> : primaryCta.label}