diff --git a/app/design-system/page.tsx b/app/design-system/page.tsx new file mode 100644 index 0000000..17a453e --- /dev/null +++ b/app/design-system/page.tsx @@ -0,0 +1,97 @@ +'use client'; + +import { ErrorState } from '@/components/states/ErrorState'; +import { EmptyState } from '@/components/states/EmptyState'; +import { LoadingState, Skeleton } from '@/components/states/LoadingState'; +import { OfflineState } from '@/components/states/OfflineState'; +import { PermissionDenied } from '@/components/states/PermissionDenied'; + +export default function DesignSystemStates() { + return ( +
+
+

State Components

+

+ A showcase of all the shared state components used throughout the application. + These cover loading, errors, empty data, offline, and permission denied scenarios. +

+
+ +
+

+ 1 + Loading States +

+
+
+

Spinner

+
+ +
+
+
+

Skeleton

+
+
+
+ +
+ + +
+
+ +
+
+
+
+
+ +
+

+ 2 + Error & Offline +

+
+
+

General Error

+ console.log('retrying...')} + /> +
+
+

Offline

+ console.log('retrying...')} + /> +
+
+
+ +
+

+ 3 + Empty & Permission +

+
+
+

Empty Data

+ Create Issue} + /> +
+
+

Permission Denied

+ +
+
+
+
+ ); +} diff --git a/app/globals.css b/app/globals.css index fe05f0e..cafef2e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -3,6 +3,7 @@ @tailwind utilities; :root { + /* Legacy aliases remain while existing routes migrate to semantic tokens. */ --ink: #111111; --ink-soft: #555555; --ink-faint: #858585; @@ -10,6 +11,16 @@ --accent: #e53935; --accent-deep: #c62828; --cream: #f9f5f0; + --bg: #FFF8EB; + --card: #FFF0D2; + --primary: #FF5D68; + --teal: #00B4A5; + --gold: #FFBE1E; + --orange: #FF8C1E; + --purple: #B450DC; + --green: #3CC864; + --text-bright: #262630; + --muted: #A07864; } .dark { @@ -28,23 +39,25 @@ body { margin: 0; min-height: 100vh; overflow-x: hidden; - background: #fff; - color: var(--ink); + background: var(--bg); + color: var(--text-bright); font-family: var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } a { color: inherit; } -::selection { background: rgba(229, 57, 53, .18); } +::selection { background: rgba(255, 93, 104, .25); } @layer components { .shell { @apply mx-auto w-full max-w-[1240px] px-5 md:px-8; } - .eyebrow { @apply font-mono text-[11px] font-bold uppercase tracking-[0.12em] text-accent; } - .button-primary { @apply inline-flex items-center justify-center gap-2 rounded-full bg-ink px-5 py-3 text-sm font-bold text-white no-underline transition-all duration-200 hover:-translate-y-0.5 hover:bg-[#303030]; } - .button-secondary { @apply inline-flex items-center justify-center gap-2 rounded-full border border-[#d7d7d7] bg-white px-5 py-3 text-sm font-bold no-underline transition-all duration-200 hover:-translate-y-0.5 hover:border-[#aaa]; } - .surface { @apply rounded-2xl border border-[var(--line)] bg-white shadow-card; } + .eyebrow { @apply font-mono text-[11px] font-bold uppercase tracking-[0.12em] text-primary; } + .button-primary { @apply inline-flex items-center justify-center gap-2 rounded-full bg-text-bright px-5 py-3 text-sm font-bold text-bg no-underline transition-all duration-200 hover:-translate-y-0.5 hover:bg-[#1a1a24]; } + .button-secondary { @apply inline-flex items-center justify-center gap-2 rounded-full border border-muted/20 bg-card px-5 py-3 text-sm font-bold no-underline transition-all duration-200 hover:-translate-y-0.5 hover:border-muted/40 hover:bg-[#ffe3b0]; } + .surface { @apply rounded-2xl border border-muted/10 bg-card shadow-card; } } .hero-grid { - background-image: linear-gradient(rgba(17,17,17,.035) 1px, transparent 1px), linear-gradient(90deg, rgba(17,17,17,.035) 1px, transparent 1px); + background-image: linear-gradient(rgba(38,38,48,.035) 1px, transparent 1px), linear-gradient(90deg, rgba(38,38,48,.035) 1px, transparent 1px); background-size: 32px 32px; mask-image: linear-gradient(to bottom, black 35%, transparent 95%); } @@ -54,7 +67,12 @@ a { color: inherit; } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } - *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } } .dark body { diff --git a/app/host/error.tsx b/app/host/error.tsx new file mode 100644 index 0000000..ed9bbc4 --- /dev/null +++ b/app/host/error.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { useEffect } from 'react'; +import { ErrorState } from '@/components/states/ErrorState'; + +export default function HostError({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error(error); + }, [error]); + + return ( +
+ +
+ ); +} diff --git a/app/host/loading.tsx b/app/host/loading.tsx new file mode 100644 index 0000000..af876d8 --- /dev/null +++ b/app/host/loading.tsx @@ -0,0 +1,9 @@ +import { LoadingState } from '@/components/states/LoadingState'; + +export default function HostLoading() { + return ( +
+ +
+ ); +} diff --git a/app/host/not-found.tsx b/app/host/not-found.tsx new file mode 100644 index 0000000..cb847aa --- /dev/null +++ b/app/host/not-found.tsx @@ -0,0 +1,26 @@ +import Link from 'next/link'; +import { Icon } from '@/components/icons'; +import { EmptyState } from '@/components/states/EmptyState'; + +export default function HostNotFound() { + return ( +
+ + + + + + } + title="Page not found" + description="The host workspace page you are looking for does not exist." + action={ + + Return to Host Dashboard + + } + /> +
+ ); +} diff --git a/components/host-sidebar.tsx b/components/host-sidebar.tsx index d20ed68..ce68421 100644 --- a/components/host-sidebar.tsx +++ b/components/host-sidebar.tsx @@ -12,16 +12,16 @@ const navigation = [ export function HostSidebar() { return ( -