refactor(ui): editorial monochrome design refresh (#473)

* refactor(ui): editorial monochrome design refresh

Design-system layer only, no functional changes. Cascades app-wide
from 8 files in the primitives + tokens layer.

- Swap Fraunces → Hedvig Letters Serif for display typography
  (single-weight; drop font-medium from CardTitle and PageHeader)
- Token sweep: pure white background, warm beige secondary
  (40 11% 89%), achromatic primary, calibrated 45 5% 85% border,
  halved-opacity shadows
- Flatten Card: remove shadow, rounded-xl → rounded-lg, full-opacity
  border (was border-border/60)
- Flatten Button: remove shadow-sm, remove active:scale-[0.98],
  drop outline border morph, transition-all 300ms → transition-colors
  150ms
- Soften Dialog overlay (bg-black/80 → bg-black/40 with dark variant)
  and use halved --shadow-md token on DialogContent
- Sidebar: bg-card/90 → bg-background, full-opacity hairline border,
  warm-beige active state (bg-secondary), hover bg-secondary/60
- .hover-lift utility: replace translateY + box-shadow with flat
  background-color shift
- PageHeader title: text-2xl font-medium → text-3xl md:text-4xl
  (no font-medium)
- CLAUDE.md Brand & Aesthetic, Typography, and Forbidden Patterns
  sections rewritten to reflect new system

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(ui): address PR review

- Remove dead .hover-lift utility (no callers; CLAUDE.md now bans
  hover-lift patterns, so leaving the class would contradict the docs)
- Bump Dialog overlay bg-black/40 → bg-black/50 — gives a more
  perceptible separation layer over the new pure-white background
  while keeping the lighter editorial feel

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-05-13 23:01:53 +02:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 2ed8096150
commit ba45b8f661
8 changed files with 63 additions and 71 deletions
+11 -11
View File
@@ -188,7 +188,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
<>
{/* Desktop sidebar */}
<aside className="hidden md:fixed md:inset-y-0 md:flex md:w-64 md:flex-col">
<div className="flex min-h-0 flex-1 flex-col border-r border-border/30 bg-card/90">
<div className="flex min-h-0 flex-1 flex-col border-r border-border bg-background">
<div className="flex flex-1 flex-col overflow-y-auto pt-7 pb-4">
{/* Company switcher */}
<div className="px-5 mb-8">
@@ -235,8 +235,8 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
? cn(
'transition-colors duration-150',
active
? 'bg-primary/12 text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
? 'bg-secondary text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-secondary/60'
)
: 'text-muted-foreground/40 cursor-not-allowed'
)
@@ -306,8 +306,8 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
? cn(
'transition-colors duration-150',
active
? 'bg-primary/12 text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
? 'bg-secondary text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-secondary/60'
)
: 'text-muted-foreground/40 cursor-not-allowed'
)
@@ -364,8 +364,8 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
? cn(
'transition-colors duration-150',
active
? 'bg-primary/12 text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
? 'bg-secondary text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-secondary/60'
)
: 'text-muted-foreground/40 cursor-not-allowed'
)
@@ -403,8 +403,8 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
? cn(
'transition-colors duration-150',
active
? 'bg-primary/12 text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
? 'bg-secondary text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-secondary/60'
)
: 'text-muted-foreground/40 cursor-not-allowed'
)
@@ -430,7 +430,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
</div>
{/* Support + Logout */}
<div className="flex-shrink-0 px-3 py-3 border-t border-border/30 space-y-1">
<div className="flex-shrink-0 px-3 py-3 border-t border-border space-y-1">
<div className="px-3 py-1.5">
<SupportLink variant="muted" />
</div>
@@ -762,7 +762,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
</div>
{/* Support + Logout */}
<div className="px-2 py-2 mt-1 border-t border-border/30 space-y-1">
<div className="px-2 py-2 mt-1 border-t border-border space-y-1">
<div className="px-3 py-2">
<SupportLink variant="muted" />
</div>
+5 -5
View File
@@ -4,16 +4,16 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",
"inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-sm hover:bg-primary/80 active:scale-[0.98]",
"bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/80 active:scale-[0.98]",
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-transparent hover:bg-secondary hover:border-secondary-foreground/20",
"border border-input bg-transparent hover:bg-secondary",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/70",
ghost:
@@ -21,7 +21,7 @@ const buttonVariants = cva(
link:
"text-primary underline-offset-4 hover:underline",
success:
"bg-success text-success-foreground shadow-sm hover:bg-success/80 active:scale-[0.98]",
"bg-success text-success-foreground hover:bg-success/90",
},
size: {
default: "h-10 px-5 py-2 rounded-lg",
+2 -2
View File
@@ -8,7 +8,7 @@ const Card = React.forwardRef<
<div
ref={ref}
className={cn(
"rounded-xl border border-border/60 bg-card text-card-foreground shadow-[var(--shadow-sm)]",
"rounded-lg border border-border bg-card text-card-foreground",
className
)}
{...props}
@@ -35,7 +35,7 @@ const CardTitle = React.forwardRef<
<h3
ref={ref}
className={cn(
"font-display text-xl font-medium leading-none tracking-tight",
"font-display text-xl leading-none tracking-tight",
className
)}
{...props}
+2 -2
View File
@@ -20,7 +20,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"fixed inset-0 z-50 bg-black/50 dark:bg-black/60 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
@@ -37,7 +37,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 max-h-[calc(100dvh-2rem)] overflow-y-auto data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-[var(--shadow-md)] duration-200 max-h-[calc(100dvh-2rem)] overflow-y-auto data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
+1 -1
View File
@@ -10,7 +10,7 @@ export function PageHeader({ title, description, action }: PageHeaderProps) {
return (
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mb-8">
<div>
<h1 className="font-display text-2xl font-medium tracking-tight">{title}</h1>
<h1 className="font-display text-3xl md:text-4xl tracking-tight">{title}</h1>
{description && (
<p className="text-muted-foreground mt-1 text-balance">{description}</p>
)}