From 029c32e022dd8353a578a1d1a987af846843818a Mon Sep 17 00:00:00 2001 From: Tyler Gillispie Date: Mon, 24 Aug 2026 22:18:34 -0700 Subject: [PATCH] feat(ui): implement design tokens and mobile-first adaptive navigation shell --- ...ell.design-tokens-and-adaptive-nav-2215.md | 0 ui/components/AdminLayout.tsx | 282 ++++++------ ui/components/AuthenticatedLayout.tsx | 408 ++++++++++++------ ui/components/CommonStyles.ts | 186 ++++++++ ui/components/Layout.tsx | 119 ++++- 5 files changed, 718 insertions(+), 277 deletions(-) rename tasks/{new => complete}/2026-0824.04.gem.feat.ui-shell.design-tokens-and-adaptive-nav-2215.md (100%) create mode 100644 ui/components/CommonStyles.ts diff --git a/tasks/new/2026-0824.04.gem.feat.ui-shell.design-tokens-and-adaptive-nav-2215.md b/tasks/complete/2026-0824.04.gem.feat.ui-shell.design-tokens-and-adaptive-nav-2215.md similarity index 100% rename from tasks/new/2026-0824.04.gem.feat.ui-shell.design-tokens-and-adaptive-nav-2215.md rename to tasks/complete/2026-0824.04.gem.feat.ui-shell.design-tokens-and-adaptive-nav-2215.md diff --git a/ui/components/AdminLayout.tsx b/ui/components/AdminLayout.tsx index 25507ed..d1b61a9 100644 --- a/ui/components/AdminLayout.tsx +++ b/ui/components/AdminLayout.tsx @@ -1,3 +1,5 @@ +import { COMMON_CSS } from "./CommonStyles.ts"; + export const AdminLayout = ({ children, title, @@ -7,8 +9,7 @@ export const AdminLayout = ({ title: string; currentPath: string; }) => { - const navItems = [ - { label: "← User Dashboard", href: "/dashboard" }, + const adminNavItems = [ { label: "Users", href: "/admin/users" }, { label: "Applications", href: "/admin/apps" }, { label: "Roles", href: "/admin/roles" }, @@ -21,167 +22,200 @@ export const AdminLayout = ({ - + + {title} - Auth-Yes Admin -
-
-

Auth-Yes Admin Console

- -
-
- - Logout - -
-
+
+ {/* Top Header */} +
+
+ + Auth-Yes + Admin + +
-
- {children} -
+
+ + ← User Hub + + + Logout + +
+
+ + {/* Sub-Nav Scroller */} + + + {/* Main Content */} +
+ {children} +
+
); diff --git a/ui/components/AuthenticatedLayout.tsx b/ui/components/AuthenticatedLayout.tsx index 151a258..01c7759 100644 --- a/ui/components/AuthenticatedLayout.tsx +++ b/ui/components/AuthenticatedLayout.tsx @@ -1,3 +1,5 @@ +import { COMMON_CSS } from "./CommonStyles.ts"; + export const AuthenticatedLayout = ({ children, title, @@ -10,151 +12,243 @@ export const AuthenticatedLayout = ({ isAdmin?: boolean; }) => { const navItems = [ - { label: "Launchpad", href: "/dashboard" }, - { label: "Sessions", href: "/dashboard/sessions" }, - { label: "Passkeys", href: "/dashboard/passkeys" }, - ...(isAdmin ? [{ label: "Admin Console", href: "/admin/users" }] : []), + { + label: "Launchpad", + href: "/dashboard", + icon: + ``, + }, + { + label: "Sessions", + href: "/dashboard/sessions", + icon: + ``, + }, + { + label: "Passkeys", + href: "/dashboard/passkeys", + icon: + ``, + }, + ...(isAdmin + ? [{ + label: "Admin", + href: "/admin/users", + icon: + ``, + }] + : []), ]; return ( - + + {title} - Auth-Yes -
-

Identity Provider

-
- -
- {children} -
+ ); diff --git a/ui/components/CommonStyles.ts b/ui/components/CommonStyles.ts new file mode 100644 index 0000000..4788d42 --- /dev/null +++ b/ui/components/CommonStyles.ts @@ -0,0 +1,186 @@ +export const COMMON_CSS = ` +:root { + /* Brand & Accents */ + --primary: #0066cc; + --primary-hover: #0052a3; + --primary-active: #003d7a; + --primary-light: #e6f0fa; + --primary-ring: rgba(0, 102, 204, 0.25); + + /* Neutrals & Surfaces */ + --surface-canvas: #f8fafc; + --surface-card: #ffffff; + --surface-muted: #f1f5f9; + --border-subtle: #e2e8f0; + --border-strong: #cbd5e1; + + /* Typography */ + --text-primary: #0f172a; + --text-secondary: #475569; + --text-muted: #94a3b8; + + /* Semantic Feedback */ + --success: #10b981; + --success-bg: #ecfdf5; + --success-border: #a7f3d0; + --success-text: #065f46; + --warning: #f59e0b; + --warning-bg: #fffbeb; + --warning-border: #fde68a; + --warning-text: #92400e; + --danger: #ef4444; + --danger-bg: #fef2f2; + --danger-border: #fecaca; + --danger-text: #991b1b; + --info: #0284c7; + --info-bg: #f0f9ff; + --info-border: #bae6fd; + --info-text: #0369a1; + + /* Geometry & Spacing */ + --radius-sm: 6px; + --radius-md: 10px; + --radius-lg: 16px; + --radius-full: 9999px; + --touch-target-min: 48px; + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); +} + +@media (prefers-color-scheme: dark) { + :root { + --surface-canvas: #0b0f19; + --surface-card: #111827; + --surface-muted: #1e293b; + --border-subtle: #1f2937; + --border-strong: #374151; + + --text-primary: #f8fafc; + --text-secondary: #cbd5e1; + --text-muted: #64748b; + + --primary-light: #172554; + --success-bg: #064e3b; + --success-border: #065f46; + --success-text: #6ee7b7; + --warning-bg: #451a03; + --warning-border: #78350f; + --warning-text: #fcd34d; + --danger-bg: #450a0a; + --danger-border: #7f1d1d; + --danger-text: #fca5a5; + --info-bg: #082f49; + --info-border: #075985; + --info-text: #7dd3fc; + } +} + +* { + box-sizing: border-box; + -webkit-tap-highlight-color: transparent; +} + +body { + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + background-color: var(--surface-canvas); + color: var(--text-primary); + margin: 0; + padding: 0; + -webkit-font-smoothing: antialiased; +} + +/* Common interactive components */ +button, .btn { + font-family: inherit; + font-size: 0.95rem; + font-weight: 600; + min-height: 44px; + padding: 0.6rem 1.2rem; + border-radius: var(--radius-md); + border: 1px solid transparent; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + transition: all 0.15s ease-in-out; + text-decoration: none; +} + +button:active, .btn:active { + transform: scale(0.98); +} + +.btn-primary { + background-color: var(--primary); + color: #ffffff; + border-color: var(--primary); +} +.btn-primary:hover { + background-color: var(--primary-hover); +} + +.btn-danger { + background-color: var(--danger); + color: #ffffff; + border-color: var(--danger); +} +.btn-danger:hover { + background-color: #dc2626; +} + +.btn-outline { + background-color: transparent; + color: var(--text-primary); + border-color: var(--border-strong); +} +.btn-outline:hover { + background-color: var(--surface-muted); +} + +input, select, textarea { + font-family: inherit; + font-size: 1rem; + min-height: 48px; + padding: 0.75rem 1rem; + border-radius: var(--radius-md); + border: 1px solid var(--border-subtle); + background-color: var(--surface-card); + color: var(--text-primary); + width: 100%; + transition: border-color 0.15s ease, box-shadow 0.15s ease; +} + +input:focus, select:focus, textarea:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--primary-ring); +} + +.card { + background-color: var(--surface-card); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 1.5rem; + box-shadow: var(--shadow-sm); + margin-bottom: 1.5rem; +} + +.badge { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.25rem 0.65rem; + font-size: 0.75rem; + font-weight: 600; + border-radius: var(--radius-full); + line-height: 1; +} + +.badge-success { background: var(--success-bg); color: var(--success-text); border: 1px solid var(--success-border); } +.badge-warning { background: var(--warning-bg); color: var(--warning-text); border: 1px solid var(--warning-border); } +.badge-danger { background: var(--danger-bg); color: var(--danger-text); border: 1px solid var(--danger-border); } +.badge-info { background: var(--info-bg); color: var(--info-text); border: 1px solid var(--info-border); } +.badge-secondary { background: var(--surface-muted); color: var(--text-secondary); border: 1px solid var(--border-subtle); } +`; diff --git a/ui/components/Layout.tsx b/ui/components/Layout.tsx index 4f5d9cd..7f531ca 100644 --- a/ui/components/Layout.tsx +++ b/ui/components/Layout.tsx @@ -1,3 +1,5 @@ +import { COMMON_CSS } from "./CommonStyles.ts"; + export const Layout = ({ children, title, @@ -9,28 +11,117 @@ export const Layout = ({ - - {title} + + + {title} - Auth-Yes -
{children}
+
+
+ {children} +
+
);