From 0c11332ac21caaf3b2829db234b03c55854c17da Mon Sep 17 00:00:00 2001 From: Vedh Krishnan Date: Sat, 20 Jun 2026 18:39:30 -0500 Subject: [PATCH 1/4] design(host39): adopt outshift-style design tokens, fonts, and key page restyle Restyles host39's frontend toward the ai-catalog.outshift.io visual language - cool blue Cisco-inspired palette, Inter sans throughout, soft blue-tinted card shadows on an 8px radius. * globals.css: replace warm-cream tokens with full Outshift token set (colors / fonts / radii / shadows), expose them to Tailwind v4 via @theme inline {}, keep legacy --page-bg/--panel/--border/--text /--muted/--soft as aliases so unswept components don't break. * layout.tsx: load Inter via next/font/google. * Header / PageShell / Footer: brand-tile + Inter wordmark + ink-weak subtitle, drop serif italic display, drop uppercase tracked nav. * Landing / list pages: shared Card pattern, new pill / button / input shapes, semantic accent mapping for status pills. No route, data, or behavior changes. --- web/next-env.d.ts | 6 + web/src/app/dashboard/cards/[id]/page.tsx | 102 ++++++++------- web/src/app/dashboard/cards/new/page.tsx | 134 ++++++++++++-------- web/src/app/dashboard/page.tsx | 77 ++++++----- web/src/app/globals.css | 148 +++++++++++++++++++--- web/src/app/layout.tsx | 12 +- web/src/app/login/page.tsx | 77 ++++++----- web/src/app/page.tsx | 68 +++++----- web/src/components/Footer.tsx | 4 +- web/src/components/Header.tsx | 41 +++--- web/src/components/PageShell.tsx | 12 +- 11 files changed, 452 insertions(+), 229 deletions(-) create mode 100644 web/next-env.d.ts diff --git a/web/next-env.d.ts b/web/next-env.d.ts new file mode 100644 index 0000000..9edff1c --- /dev/null +++ b/web/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/web/src/app/dashboard/cards/[id]/page.tsx b/web/src/app/dashboard/cards/[id]/page.tsx index 716c8b8..cae9cd0 100644 --- a/web/src/app/dashboard/cards/[id]/page.tsx +++ b/web/src/app/dashboard/cards/[id]/page.tsx @@ -8,9 +8,26 @@ import type { AgentCard, Me } from "@/lib/api"; import { clearAuthToken } from "@/lib/auth"; import { useRequireAuth } from "@/hooks/useRequireAuth"; +// ── Shared primitives (token-bound) ─────────────────────────────────────────── + +const cardClass = + "rounded-[var(--radius-card)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] shadow-[var(--shadow-card)]"; + +const inputClass = + "w-full h-10 rounded-[var(--radius-control)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] px-3 text-sm text-[color:var(--color-fg-default)] outline-none transition focus:border-[color:var(--color-primary)] focus:ring-2 focus:ring-[color:var(--color-primary-soft)]"; + +const textareaClass = + "w-full rounded-[var(--radius-control)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] px-3 py-2 text-sm text-[color:var(--color-fg-default)] outline-none transition focus:border-[color:var(--color-primary)] focus:ring-2 focus:ring-[color:var(--color-primary-soft)] resize-none"; + +const primaryBtnClass = + "inline-flex h-10 items-center rounded-[var(--radius-control)] bg-[color:var(--color-primary)] px-5 text-sm font-medium text-white transition hover:bg-[color:var(--color-primary-hover)] disabled:opacity-60"; + +const secondaryBtnClass = + "inline-flex h-10 items-center rounded-[var(--radius-control)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] px-4 text-sm font-medium text-[color:var(--color-fg-default)] transition hover:bg-[color:var(--color-surface-2)]"; + function FieldLabel({ children }: { children: React.ReactNode }) { return ( -

+

{children}

); @@ -167,7 +184,7 @@ export default function EditCardPage() { if (loading) { return ( -
+
Loading…
@@ -177,7 +194,7 @@ export default function EditCardPage() { if (!card || !me) { return ( -
+
{error ?? "Card not found."}
@@ -191,9 +208,6 @@ export default function EditCardPage() { me.handle ); - const inputClass = - "w-full rounded-xl border border-black/10 px-4 py-2.5 text-sm outline-none focus:ring-2 focus:ring-slate-300"; - return ( {/* Public URL banner */} {isPublic ? ( -
-

+

+

Public URL

- + {publicUrl} @@ -220,25 +234,25 @@ export default function EditCardPage() { href={publicUrl} target="_blank" rel="noopener noreferrer" - className="shrink-0 rounded-lg border border-black/10 px-3 py-1.5 text-xs text-slate-600 hover:bg-slate-50" + className="shrink-0 rounded-[var(--radius-control)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] px-3 py-1.5 text-xs text-[color:var(--color-fg-default)] hover:bg-[color:var(--color-surface-2)]" > Open
) : ( -
-

Private

-

This card is not publicly accessible. Enable “Public” below to publish it.

+
+

Private

+

This card is not publicly accessible. Enable “Public” below to publish it.

)} {/* Edit form */} -
+ {/* Basic */}
-

Basic info

+

Basic info

Slug * @@ -267,17 +281,17 @@ export default function EditCardPage() { value={description} onChange={(e) => setDescription(e.target.value)} rows={3} - className={`${inputClass} resize-none`} + className={textareaClass} />
-
+
{/* Runtime */}
-

Runtime

+

Runtime

Runtime URL @@ -319,23 +333,23 @@ export default function EditCardPage() {
-
+
{/* Capabilities */}
-

Capabilities & Auth

+

Capabilities & Auth

-
+
@@ -363,7 +377,7 @@ export default function EditCardPage() { setStatus(e.target.value as "active" | "inactive")} - className={`${inputClass} bg-white`} + className={inputClass} > @@ -398,12 +412,12 @@ export default function EditCardPage() {
{error && ( -

+

{error}

)} {success && ( -

+

{success}

)} @@ -412,14 +426,14 @@ export default function EditCardPage() { @@ -427,32 +441,32 @@ export default function EditCardPage() { {/* Delete zone */} -
-

Danger zone

-

+

+

Danger zone

+

Permanently delete this agent card. This action cannot be undone.

{!confirmDelete ? ( ) : ( -
-

Are you sure?

+
+

Are you sure?

diff --git a/web/src/app/dashboard/cards/new/page.tsx b/web/src/app/dashboard/cards/new/page.tsx index b283c82..f1132f1 100644 --- a/web/src/app/dashboard/cards/new/page.tsx +++ b/web/src/app/dashboard/cards/new/page.tsx @@ -26,6 +26,26 @@ interface FormState { is_public: boolean; } +// ── Shared primitives (token-bound) ─────────────────────────────────────────── + +const cardClass = + "rounded-[var(--radius-card)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] shadow-[var(--shadow-card)]"; + +const inputClass = + "w-full h-10 rounded-[var(--radius-control)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] px-3 text-sm text-[color:var(--color-fg-default)] outline-none transition focus:border-[color:var(--color-primary)] focus:ring-2 focus:ring-[color:var(--color-primary-soft)]"; + +const textareaClass = + "w-full rounded-[var(--radius-control)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] px-3 py-2 text-sm text-[color:var(--color-fg-default)] outline-none transition focus:border-[color:var(--color-primary)] focus:ring-2 focus:ring-[color:var(--color-primary-soft)] resize-none"; + +const primaryBtnClass = + "inline-flex h-10 items-center rounded-[var(--radius-control)] bg-[color:var(--color-primary)] px-5 text-sm font-medium text-white transition hover:bg-[color:var(--color-primary-hover)] disabled:opacity-60"; + +const secondaryBtnClass = + "inline-flex h-10 items-center rounded-[var(--radius-control)] border border-[color:var(--color-border)] bg-[color:var(--color-surface)] px-5 text-sm font-medium text-[color:var(--color-fg-default)] transition hover:bg-[color:var(--color-surface-2)]"; + +const microLabelClass = + "mb-1 block text-xs font-semibold uppercase tracking-wide text-[color:var(--color-fg-weak)]"; + // ── Defaults per identity type ──────────────────────────────────────────────── function defaultsForMe(me: Me): Partial { @@ -79,27 +99,27 @@ function Field({ mono?: boolean; textarea?: boolean; optional?: boolean; }) { const base = cn( - "w-full rounded-2xl border px-4 py-2.5 text-sm outline-none focus:ring-2 focus:ring-slate-300 bg-white transition", + textarea ? textareaClass : inputClass, mono && "font-mono", - error ? "border-rose-300 bg-rose-50/40" : "border-black/10", + error && "border-[color:var(--color-danger)] focus:border-[color:var(--color-danger)] focus:ring-[color:var(--color-danger-soft)]", ); return (