Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
fd94e82
refactor(web): align dashboard tokens with Charter palette
fadesany Aug 14, 2026
20a15bb
refactor(web): adopt Charter typography in app shell
fadesany Aug 14, 2026
cd968c6
refactor(web): switch app root to light paper theme
fadesany Aug 14, 2026
91b9525
refactor(web): remove gradient from loading shimmer
fadesany Aug 14, 2026
4c7cf90
feat(web): add accessible dashboard content frame
fadesany Aug 14, 2026
4581189
refactor(web): restyle dashboard header surface
fadesany Aug 14, 2026
1f32f10
refactor(web): give app header a Charter wordmark
fadesany Aug 14, 2026
73a52a4
feat(web): add dashboard and docs header links
fadesany Aug 14, 2026
b68df53
refactor(web): restyle wallet connect action
fadesany Aug 14, 2026
48635dd
refactor(web): restyle connected wallet control
fadesany Aug 14, 2026
2b62dcd
refactor(web): rebuild wallet account popover
fadesany Aug 14, 2026
37f1bab
refactor(web): restyle shared text inputs
fadesany Aug 14, 2026
e8d63da
refactor(web): restyle shared select controls
fadesany Aug 14, 2026
ff12a3f
refactor(web): restyle shared action buttons
fadesany Aug 14, 2026
2655d7a
refactor(web): rebuild loading and empty state surfaces
fadesany Aug 14, 2026
0ed33c1
refactor(web): rebuild modal dialog surface
fadesany Aug 14, 2026
f35f1ab
refactor(web): align request badges with semantic palette
fadesany Aug 14, 2026
b3256a5
refactor(web): promote threshold meter in request progress
fadesany Aug 14, 2026
6c3f826
refactor(web): turn addresses into tactile copy controls
fadesany Aug 14, 2026
2582175
refactor(web): add treasury orientation banner
fadesany Aug 14, 2026
0317669
refactor(web): strengthen organization directory heading
fadesany Aug 14, 2026
43fc02e
refactor(web): frame organization list as ledger surface
fadesany Aug 14, 2026
52260c5
refactor(web): redesign organization ledger rows
fadesany Aug 14, 2026
7355621
refactor(web): match empty directory action styling
fadesany Aug 14, 2026
c662d72
refactor(web): rebuild treasury identity header
fadesany Aug 14, 2026
edb9a68
refactor(web): make treasury balance the primary ledger signal
fadesany Aug 14, 2026
cd7902a
refactor(web): restyle treasury summary chips
fadesany Aug 14, 2026
0ba01fe
refactor(web): align allocation meter with ledger motif
fadesany Aug 14, 2026
08a8a10
refactor(web): frame budget categories as policy ledger
fadesany Aug 14, 2026
b2fbb6d
refactor(web): redesign category usage meters
fadesany Aug 14, 2026
6cb80aa
refactor(web): frame request queue as approval ledger
fadesany Aug 14, 2026
14c49b3
refactor(web): improve request row hierarchy and mobile flow
fadesany Aug 14, 2026
f39e85f
refactor(web): rebuild organization creation header
fadesany Aug 14, 2026
54da556
refactor(web): frame organization deployment form
fadesany Aug 14, 2026
16c0f93
refactor(web): redesign approver controls
fadesany Aug 14, 2026
f433254
refactor(web): make request decisions tactile
fadesany Aug 14, 2026
4e0926b
refactor(web): restyle category administration controls
fadesany Aug 14, 2026
4633ce6
refactor(web): rebuild request detail header
fadesany Aug 14, 2026
f1396eb
refactor(web): rebuild request detail ledger panels
fadesany Aug 14, 2026
4038884
refactor(web): redesign approver roster ledger
fadesany Aug 14, 2026
abc3555
refactor(web): finish deployment form controls
fadesany Aug 14, 2026
f775caa
refactor(web): align modal notice surfaces
fadesany Aug 14, 2026
b19ddfb
refactor(web): align request detail skeletons
fadesany Aug 14, 2026
aa3f9ad
refactor(web): restyle treasury retry action
fadesany Aug 14, 2026
7e9d298
refactor(web): standardize transaction error callouts
fadesany Aug 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions apps/web/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Metadata } from 'next';
import { Inter, JetBrains_Mono } from 'next/font/google';
import { IBM_Plex_Mono, Inter, Space_Grotesk } from 'next/font/google';
import type { ReactNode } from 'react';

import { SiteHeader } from '@/components/site-header';
Expand All @@ -9,14 +9,23 @@ import '../globals.css';
// One sans family, one mono. The mono carries addresses, hashes, and amounts —
// anything where character alignment aids scanning; the sans carries everything
// else. Both are wired to the CSS variables globals.css references.
const sans = Inter({
const display = Space_Grotesk({
subsets: ['latin'],
variable: '--font-sans',
weight: ['700'],
variable: '--font-display',
display: 'swap',
});

const mono = JetBrains_Mono({
const body = Inter({
subsets: ['latin'],
weight: ['400', '500'],
variable: '--font-body',
display: 'swap',
});

const mono = IBM_Plex_Mono({
subsets: ['latin'],
weight: ['500'],
variable: '--font-mono',
display: 'swap',
});
Expand All @@ -29,11 +38,22 @@ export const metadata: Metadata = {

export default function AppLayout({ children }: { children: ReactNode }) {
return (
<html lang="en" className={`${sans.variable} ${mono.variable}`}>
<body className="min-h-screen">
<html lang="en" className={`app-root ${display.variable} ${body.variable} ${mono.variable}`}>
<body className="min-h-screen font-body">
<Providers>
<a
href="#app-content"
className="sr-only rounded-card border-2 border-ink bg-ledger-gold px-4 py-2 font-medium text-ink shadow-brutal focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-modal"
>
Skip to dashboard content
</a>
<SiteHeader />
<main className="mx-auto w-full max-w-6xl px-6 py-10">{children}</main>
<main
id="app-content"
className="mx-auto w-full max-w-7xl px-4 py-8 sm:px-6 sm:py-10 lg:px-8 lg:py-12"
>
{children}
</main>
</Providers>
</body>
</html>
Expand Down
13 changes: 7 additions & 6 deletions apps/web/app/(app)/new/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { NewOrgForm } from '@/components/new-org-form';
*/
export default function NewOrgPage() {
return (
<div className="mx-auto max-w-xl">
<nav className="mb-6">
<div className="mx-auto max-w-2xl">
<nav className="mb-8">
<Link
href="/app"
className="inline-flex items-center gap-1.5 text-sm text-ink-muted transition-colors duration-150 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent rounded"
className="inline-flex items-center gap-1.5 rounded-badge border-2 border-ink bg-paper-raised px-3 py-2 text-sm font-medium text-ink shadow-[2px_2px_0_#14171F] transition-[background-color,box-shadow,transform] duration-150 hover:bg-canvas-overlay hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink"
>
<svg viewBox="0 0 16 16" className="h-3.5 w-3.5" fill="none" aria-hidden>
<path d="M10 4L6 8l4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
Expand All @@ -21,9 +21,10 @@ export default function NewOrgPage() {
</Link>
</nav>

<header className="mb-8">
<h1 className="text-xl font-semibold text-ink">New organization</h1>
<p className="mt-1 text-sm text-ink-muted">
<header className="mb-8 border-b-2 border-ink pb-8">
<p className="mb-2 font-mono text-xs uppercase tracking-[0.08em] text-ink-muted">Factory deployment</p>
<h1 className="font-display text-4xl leading-none text-ink sm:text-[40px]">New organization</h1>
<p className="mt-4 max-w-xl text-sm leading-relaxed text-ink-muted">
Deploy a treasury contract. You&rsquo;ll be its admin, and the approver set
you define governs every disbursement.
</p>
Expand Down
32 changes: 18 additions & 14 deletions apps/web/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@
@tailwind utilities;

:root {
--font-sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--font-body: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--font-display: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
}

html {
color-scheme: dark;
color-scheme: light;
}

body {
@apply bg-canvas text-ink antialiased;
}

.app-root {
--color-ink: 20 23 31;
--color-ink-muted: 76 78 82;
--color-ink-faint: 112 110 103;
}

/* The marketing (landing) group opts into the light neo-brutalist theme. It
owns its own <html>/<body> via the (marketing) route group, so scoping the
override to this class leaves the dashboard's dark defaults untouched.
Expand Down Expand Up @@ -44,19 +51,16 @@ body {
.shimmer::after {
content: '';
position: absolute;
inset: 0;
inset-block: 0;
left: 0;
width: 35%;
transform: translateX(-100%);
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.04) 50%,
transparent 100%
);
background: rgb(251 250 246 / 0.72);
animation: shimmer 1.6s ease-in-out infinite;
}
@keyframes shimmer {
100% {
transform: translateX(100%);
transform: translateX(390%);
}
}

Expand Down Expand Up @@ -94,20 +98,20 @@ dialog[open]::backdrop {
@media (prefers-reduced-transparency: reduce) {
dialog[open]::backdrop {
backdrop-filter: none;
background-color: rgb(11 15 20 / 0.92);
background-color: rgb(20 23 31 / 0.82);
}
}

/* Thin, unobtrusive scrollbars that match the dark canvas. */
/* Thin scrollbars that sit quietly against the warm paper canvas. */
* {
scrollbar-width: thin;
scrollbar-color: #1e2a3a transparent;
scrollbar-color: #14171f transparent;
}
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-thumb {
background-color: #1e2a3a;
background-color: #14171f;
border-radius: 4px;
}
10 changes: 5 additions & 5 deletions apps/web/components/approval-progress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ export function ApprovalProgress({

return (
<span className={cn('inline-flex items-center gap-2', className)}>
<span className="flex items-center gap-0.5" aria-hidden>
<span className="flex h-4 items-center gap-0.5 rounded-pill border-2 border-ink bg-paper-raised p-0.5" aria-hidden>
{Array.from({ length: threshold }).map((_, i) => (
<span
key={i}
className={cn(
'h-1.5 w-4 rounded-full transition-colors duration-150',
'h-full w-4 first:rounded-l-pill last:rounded-r-pill transition-colors duration-150',
i < filled
? met
? 'bg-ok shadow-[0_0_6px_rgba(52,211,153,0.45)]'
: 'bg-accent'
? met && i === threshold - 1
? 'bg-signal-green'
: 'bg-ledger-gold'
: 'bg-canvas-overlay',
)}
/>
Expand Down
8 changes: 4 additions & 4 deletions apps/web/components/category-admin-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function CategoryAdminActions({
<button
type="button"
onClick={() => setEditing(true)}
className="rounded-md px-2 py-1 text-xs font-medium text-ink-muted transition-colors duration-150 hover:bg-canvas-overlay hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent"
className="rounded-badge border-2 border-ink bg-paper-raised px-2.5 py-1 text-xs font-medium text-ink shadow-[2px_2px_0_#14171F] transition-[background-color,box-shadow,transform] duration-150 hover:bg-canvas-overlay hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink"
>
Edit
</button>
Expand All @@ -68,10 +68,10 @@ export function CategoryAdminActions({
onClick={() => toggle.mutate()}
disabled={toggle.isPending}
className={cn(
'rounded-md px-2 py-1 text-xs font-medium transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent disabled:opacity-50',
'rounded-badge border-2 border-ink bg-paper-raised px-2.5 py-1 text-xs font-medium shadow-[2px_2px_0_#14171F] transition-[background-color,box-shadow,transform] duration-150 hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink disabled:opacity-50',
category.active
? 'text-ink-muted hover:bg-canvas-overlay hover:text-warn'
: 'text-accent hover:bg-canvas-overlay hover:text-accent-hover',
? 'text-ink-muted hover:bg-ledger-gold/20 hover:text-ink'
: 'text-ink hover:bg-ledger-gold/30',
)}
>
{toggle.isPending ? '…' : category.active ? 'Pause' : 'Resume'}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/components/category-form-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function CategoryForm({
<div>
<Label htmlFor="category-name">Name</Label>
{isEdit ? (
<p className="mt-1.5 rounded-lg border border-line bg-canvas px-3 py-2 text-sm text-ink-muted">
<p className="mt-2 rounded-badge border-2 border-ink bg-canvas-overlay px-3.5 py-2.5 text-sm text-ink-muted">
{mode.category.name}
</p>
) : (
Expand Down Expand Up @@ -175,7 +175,7 @@ function CategoryForm({
</div>

{bannerError !== null && (
<p role="alert" className="rounded-lg border border-danger/30 bg-danger/10 px-4 py-3 text-sm text-danger">
<p role="alert" className="rounded-badge border-2 border-danger bg-danger/10 px-4 py-3 text-sm text-danger">
{bannerError}
</p>
)}
Expand Down
6 changes: 3 additions & 3 deletions apps/web/components/copy-address.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ export function CopyAddress({
title={address}
aria-label={copied ? 'Address copied' : `Copy address ${address}`}
className={cn(
'group inline-flex items-center gap-1.5 rounded-md font-mono text-ink-muted',
'transition-colors duration-150 hover:text-ink',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent',
'group inline-flex items-center gap-1.5 rounded-badge border-2 border-ink bg-paper-raised px-2 py-1 font-mono text-xs text-ink-muted',
'transition-[background-color,transform] duration-150 hover:bg-canvas-overlay hover:text-ink hover:translate-x-0.5 hover:translate-y-0.5',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink',
className,
)}
>
Expand Down
34 changes: 17 additions & 17 deletions apps/web/components/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ export function FieldError({ children }: { children: ReactNode }) {
/** The shared input treatment: dark surface, accent focus ring, mono opt-in. */
const inputClass = (mono?: boolean, invalid?: boolean) =>
cn(
'mt-1.5 block w-full rounded-lg border bg-canvas-raised px-3 py-2 text-sm text-ink placeholder:text-ink-faint',
'transition-colors duration-150',
'focus:outline-none focus:ring-1',
'mt-2 block min-h-11 w-full rounded-badge border-2 bg-paper-raised px-3.5 py-2.5 text-sm text-ink placeholder:text-ink-faint',
'transition-[background-color,box-shadow] duration-150',
'focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-paper',
invalid
? 'border-danger/60 focus:border-danger focus:ring-danger'
: 'border-line hover:border-accent-muted focus:border-accent focus:ring-accent',
? 'border-danger focus:border-danger focus:ring-danger'
: 'border-ink hover:bg-white focus:border-ink focus:ring-ledger-gold',
'disabled:cursor-not-allowed disabled:opacity-50',
mono && 'font-mono',
);
Expand Down Expand Up @@ -96,20 +96,20 @@ export function Select({
children: ReactNode;
}) {
return (
<div className="relative mt-1.5">
<div className="relative mt-2">
<select
id={id}
value={value}
onChange={(e) => onChange(e.target.value)}
disabled={disabled}
aria-invalid={invalid || undefined}
className={cn(
'block w-full appearance-none rounded-lg border bg-canvas-raised py-2 pl-3 pr-9 text-sm text-ink',
'transition-colors duration-150',
'focus:outline-none focus:ring-1',
'block min-h-11 w-full appearance-none rounded-badge border-2 bg-paper-raised py-2.5 pl-3.5 pr-10 text-sm text-ink',
'transition-[background-color,box-shadow] duration-150',
'focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-paper',
invalid
? 'border-danger/60 focus:border-danger focus:ring-danger'
: 'border-line hover:border-accent-muted focus:border-accent focus:ring-accent',
? 'border-danger focus:border-danger focus:ring-danger'
: 'border-ink hover:bg-white focus:border-ink focus:ring-ledger-gold',
'disabled:cursor-not-allowed disabled:opacity-50',
)}
>
Expand Down Expand Up @@ -147,9 +147,9 @@ export function PrimaryButton({
onClick={onClick}
disabled={disabled}
className={cn(
'inline-flex h-9 items-center justify-center gap-2 rounded-lg bg-accent px-5 text-sm font-medium text-canvas',
'transition-[background-color,transform] duration-150 hover:bg-accent-hover active:translate-y-[0.5px]',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas',
'inline-flex min-h-10 items-center justify-center gap-2 rounded-card border-2 border-ink bg-ledger-gold px-5 text-sm font-medium text-ink shadow-brutal',
'transition-[background-color,box-shadow,transform] duration-150 hover:bg-accent-hover hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-brutal-pressed active:translate-x-0.5 active:translate-y-0.5 active:shadow-brutal-pressed',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-2 focus-visible:ring-offset-paper',
'disabled:cursor-not-allowed disabled:opacity-60',
)}
>
Expand Down Expand Up @@ -179,9 +179,9 @@ export function SecondaryButton({
onClick={onClick}
disabled={disabled}
className={cn(
'inline-flex h-9 items-center justify-center rounded-lg border border-line px-4 text-sm font-medium text-ink',
'transition-[color,background-color,border-color,transform] duration-150 hover:border-accent-muted hover:bg-canvas-overlay active:translate-y-[0.5px]',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas',
'inline-flex min-h-10 items-center justify-center rounded-card border-2 border-ink bg-paper-raised px-4 text-sm font-medium text-ink shadow-brutal',
'transition-[background-color,box-shadow,transform] duration-150 hover:bg-canvas-overlay hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-brutal-pressed active:translate-x-0.5 active:translate-y-0.5 active:shadow-brutal-pressed',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-2 focus-visible:ring-offset-paper',
'disabled:cursor-not-allowed disabled:opacity-50',
)}
>
Expand Down
6 changes: 3 additions & 3 deletions apps/web/components/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ export function Modal({
}}
aria-labelledby="modal-title"
aria-describedby={description ? 'modal-description' : undefined}
className="z-modal m-auto w-full max-w-md rounded-2xl border border-line bg-canvas-raised p-0 text-ink shadow-2xl backdrop:bg-canvas/70 backdrop:backdrop-blur-sm"
className="z-modal m-auto w-[calc(100%-2rem)] max-w-md rounded-card border-2 border-ink bg-paper-raised p-0 text-ink shadow-brutal backdrop:bg-ink/55"
>
<div className="p-6" onClick={(e) => e.stopPropagation()}>
<div className="mb-5 flex items-start justify-between gap-4">
<div className="space-y-1">
<h2 id="modal-title" className="text-base font-semibold text-ink">
<h2 id="modal-title" className="font-display text-xl text-ink">
{title}
</h2>
{description && (
Expand All @@ -77,7 +77,7 @@ export function Modal({
type="button"
onClick={onClose}
aria-label="Close"
className="-mr-1 -mt-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-ink-faint transition-colors duration-150 hover:bg-canvas-overlay hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent"
className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-badge border-2 border-ink bg-paper-raised text-ink shadow-[2px_2px_0_#14171F] transition-[background-color,box-shadow,transform] duration-150 hover:bg-canvas-overlay hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink"
>
<svg viewBox="0 0 16 16" className="h-4 w-4" fill="none" aria-hidden>
<path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
Expand Down
Loading
Loading