diff --git a/apps/web/app/_components/relationship-list.tsx b/apps/web/app/_components/relationship-list.tsx
index 818f4f8..0246cdd 100644
--- a/apps/web/app/_components/relationship-list.tsx
+++ b/apps/web/app/_components/relationship-list.tsx
@@ -1,9 +1,9 @@
import Link from "next/link";
import type { RelatedNode } from "@govgraph/domain";
-import { Badge, Card } from "@govgraph/ui";
+import { Badge, EmptyState, NodeCard, NodeTypeBadge } from "@govgraph/ui";
-import { describeRelationship, humanizeNodeType } from "../_lib/presenters";
+import { describeRelationship } from "../_lib/presenters";
type RelationshipListProps = {
relatedNodes: RelatedNode[];
@@ -12,37 +12,34 @@ type RelationshipListProps = {
export function RelationshipList({ relatedNodes }: RelationshipListProps) {
if (relatedNodes.length === 0) {
return (
-
-
- No connected records are available for this node yet.
-
-
+
);
}
return (
-
+
{relatedNodes.map((entry) => (
-
-
- {describeRelationship(entry)}
- {humanizeNodeType(entry.node.nodeType)}
-
-
- {entry.node.canonicalName}
-
- {entry.node.description ? (
-
- {entry.node.description}
-
- ) : null}
-
+ badges={
+ <>
+
{describeRelationship(entry)}
+
+ >
+ }
+ description={entry.node.description}
+ title={
+
+ {entry.node.canonicalName}
+
+ }
+ />
))}
);
diff --git a/apps/web/app/_components/search-form.tsx b/apps/web/app/_components/search-form.tsx
index 5b4826b..af357a3 100644
--- a/apps/web/app/_components/search-form.tsx
+++ b/apps/web/app/_components/search-form.tsx
@@ -4,12 +4,14 @@ type SearchFormProps = {
defaultValue?: string;
action?: string;
placeholder?: string;
+ size?: "md" | "lg";
};
export function SearchForm({
defaultValue = "",
action = "/",
placeholder = "Search ministers, portfolios, and departments",
+ size = "lg",
}: SearchFormProps) {
return (
diff --git a/apps/web/app/design/page.tsx b/apps/web/app/design/page.tsx
new file mode 100644
index 0000000..3fe46dd
--- /dev/null
+++ b/apps/web/app/design/page.tsx
@@ -0,0 +1,279 @@
+import Link from "next/link";
+
+import { nodeTypes } from "@govgraph/domain";
+import {
+ Badge,
+ Button,
+ Card,
+ DataTable,
+ EmptyState,
+ KeyValue,
+ MetricBar,
+ NodeCard,
+ NodeTypeBadge,
+ PageShell,
+ SectionHeader,
+ Skeleton,
+ SourceCard,
+ StatCard,
+ Textarea,
+ TimelineEntry,
+ Tooltip,
+ Input,
+} from "@govgraph/ui";
+
+const budgetRows = [
+ { agency: "Treasury and Finance", budget: 8300000000, workforce: 1450 },
+ { agency: "Transport and Planning", budget: 16700000000, workforce: 3940 },
+ { agency: "Education", budget: 23200000000, workforce: 2180 },
+] as const;
+
+const budgetColumns = [
+ { key: "agency", header: "Agency", sortable: true },
+ {
+ key: "budget",
+ header: "Budget",
+ sortable: true,
+ align: "right" as const,
+ format: "currency" as const,
+ },
+ {
+ key: "workforce",
+ header: "Workforce",
+ sortable: true,
+ align: "right" as const,
+ format: "number" as const,
+ },
+] as const;
+
+export default function DesignPage() {
+ return (
+
+
+
+
+ GovGraph Design System
+ Live preview route
+
+
+
+ Register aesthetic
+
+
+ Typed tokens, shared components, and a calmer way to build the
+ atlas.
+
+
+ This page previews the shared system that now drives the live
+ pages. It is where new patterns should be checked before they land
+ in the atlas proper.
+
+
+
+
+
+
+
+
+
+
+
+ Search
+
+ Secondary
+
+
+ Remove
+
+
+
+ Neutral
+ Accent
+ Success
+ Warning
+
+
+
+
+
+
+
+
+
+
+
+ {nodeTypes.map((nodeType) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ Lead delivery agency
+ >
+ }
+ description="Supports the education portfolio, child policy, and statewide school governance."
+ footer={
+
+ Open record
+
+ }
+ title="Department of Education"
+ />
+
+ Open source
+
+ }
+ badges={
+ <>
+ VPSC
+ HTML
+ >
+ }
+ description="Published by Victorian Public Sector Commission • effective 2025-06-05"
+ metadata="Portfolio mapping"
+ title="Working with Ministers and Ministerial Officers"
+ />
+
+
+
+
+
+
+
+
+
+ Table help
+
+
+ }
+ />
+
+
+
+
+
+
+ Clear filters}
+ description="Try a broader search phrase or remove a filter to bring records back into view."
+ title="No records match this combination"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css
index 0918ae8..42a9fd6 100644
--- a/apps/web/app/globals.css
+++ b/apps/web/app/globals.css
@@ -1,17 +1,8 @@
@import "tailwindcss";
-
-:root {
- --govgraph-paper: #f6f1e6;
- --govgraph-ink: #0e2c24;
- --govgraph-deep: #163e35;
- --govgraph-accent: #b94a37;
- --govgraph-border: rgba(14, 44, 36, 0.14);
- --govgraph-muted: #5a675f;
- --govgraph-wash: #edf1eb;
-}
+@import "@govgraph/ui/tokens.css";
html {
- background: var(--govgraph-paper);
+ background: var(--gg-color-paper);
}
body {
@@ -20,9 +11,9 @@ body {
linear-gradient(180deg, rgba(255, 255, 255, 0.65), rgba(246, 241, 230, 1)),
radial-gradient(circle at top left, rgba(32, 96, 79, 0.18), transparent 28%),
radial-gradient(circle at bottom right, rgba(185, 74, 55, 0.12), transparent 24%),
- var(--govgraph-paper);
- color: var(--govgraph-ink);
- font-family: var(--font-body), sans-serif;
+ var(--gg-color-paper);
+ color: var(--gg-color-ink);
+ font-family: var(--gg-font-family-body);
}
body::before {
@@ -40,21 +31,28 @@ body::before {
h1,
h2,
h3 {
- font-family: var(--font-display), serif;
+ font-family: var(--gg-font-family-display);
}
code,
pre {
- font-family: var(--font-mono), monospace;
+ font-family: var(--gg-font-family-mono);
}
a,
button,
-input {
+input,
+textarea {
transition:
- color 160ms ease,
- border-color 160ms ease,
- background-color 160ms ease,
- transform 160ms ease,
- box-shadow 160ms ease;
+ color var(--gg-animation-duration-normal) var(--gg-animation-easing-standard),
+ border-color var(--gg-animation-duration-normal) var(--gg-animation-easing-standard),
+ background-color var(--gg-animation-duration-normal)
+ var(--gg-animation-easing-standard),
+ transform var(--gg-animation-duration-normal) var(--gg-animation-easing-standard),
+ box-shadow var(--gg-animation-duration-normal) var(--gg-animation-easing-standard);
+}
+
+::selection {
+ background: rgba(185, 74, 55, 0.18);
+ color: var(--gg-color-ink);
}
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
index 8cd532c..f71d0fa 100644
--- a/apps/web/app/layout.tsx
+++ b/apps/web/app/layout.tsx
@@ -1,5 +1,8 @@
import type { Metadata } from "next";
import { IBM_Plex_Mono, IBM_Plex_Sans, Newsreader } from "next/font/google";
+import Link from "next/link";
+import { Badge, Footer, SiteHeader } from "@govgraph/ui";
+
import "./globals.css";
const display = Newsreader({
@@ -34,7 +37,66 @@ export default function RootLayout({
- {children}
+
+
+
+ GovGraph
+
+
+ Official register interface
+
+
+ }
+ navigation={
+ <>
+
+ Atlas
+
+
+ Design
+
+ >
+ }
+ utility={
Prototype }
+ />
+
{children}
+
+ }
+ meta={
+
+ Built on official-source-first parsing with register-style UI
+ primitives shared across the app.
+
+ }
+ summary={
+
+ GovGraph turns Victorian Government structures into a readable
+ public register, with warm paper surfaces, deep-ink typography,
+ and every visible record anchored to source material.
+
+ }
+ />
+