diff --git a/apps/web/app/(app)/layout.tsx b/apps/web/app/(app)/layout.tsx index b443933..4915a10 100644 --- a/apps/web/app/(app)/layout.tsx +++ b/apps/web/app/(app)/layout.tsx @@ -4,7 +4,7 @@ import { AppShellHeader } from "@/components/app-shell-header"; export default function AppShellLayout({ children }: { children: ReactNode }) { return ( -
+
{children}
diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css new file mode 100644 index 0000000..f817248 --- /dev/null +++ b/apps/web/app/globals.css @@ -0,0 +1,67 @@ +/* Studiqo authenticated app — tokens aligned with docs/frontend/studiqo-style-guide.md */ + +@import "tailwindcss"; + +@theme inline { + /* Semantic palette (warm education) */ + --color-canvas: #f5f1ea; + --color-surface: #fffcf7; + --color-raised: #f0ebe3; + --color-line: #e4ddd3; + --color-line-strong: #c4b8a8; + --color-ink: #1c1917; + --color-ink-muted: #57534e; + --color-ink-faint: #78716c; + --color-accent: #2a5f5a; + --color-accent-hover: #1f4744; + --color-accent-soft: rgb(42 95 90 / 0.14); + --color-danger: #b91c1c; + --color-success: #15803d; + + /* next/font variables are set on */ + --font-sans: var(--font-ui), "Source Sans 3", ui-sans-serif, system-ui, sans-serif; + --font-serif-display: var(--font-display), Georgia, "Times New Roman", serif; +} + +@layer base { + *, + *::before, + *::after { + box-sizing: border-box; + } + + html { + color-scheme: light; + } + + body { + @apply m-0 min-h-screen bg-canvas font-sans text-base leading-normal text-ink antialiased; + } +} + +@layer components { + .app-nav-link { + @apply inline-block rounded-lg px-3.5 py-2 text-sm leading-snug text-ink-muted transition-colors duration-200 ease-in-out hover:bg-raised hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-2 focus-visible:ring-offset-canvas motion-reduce:transition-none; + } + + .app-nav-link[aria-current="page"] { + @apply bg-accent-soft font-semibold text-accent; + } + + .app-brand-link { + @apply text-lg font-semibold leading-snug tracking-tight text-ink no-underline transition-colors duration-200 hover:text-accent focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-2 focus-visible:ring-offset-surface motion-reduce:transition-none; + } + + .app-btn { + @apply inline-flex min-h-11 cursor-pointer items-center justify-center rounded-lg border border-line-strong bg-surface px-4 text-sm font-semibold leading-snug text-ink transition-colors duration-200 ease-in-out hover:bg-raised hover:border-ink-faint focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-2 focus-visible:ring-offset-surface motion-reduce:transition-none; + } + + .app-btn-primary { + @apply border-accent bg-accent text-white hover:border-accent-hover hover:bg-accent-hover hover:text-white; + } + + /* Focus ring offset matches header surface (nav row uses default canvas offset). */ + .app-nav-link--on-surface { + @apply focus-visible:ring-offset-surface; + } +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index dedba19..5b6816d 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,10 +1,25 @@ +import { Fraunces, Source_Sans_3 } from "next/font/google"; import type { ReactNode } from "react"; import { Providers } from "./providers"; +import "./globals.css"; + +const fraunces = Fraunces({ + subsets: ["latin"], + variable: "--font-display", + display: "swap", +}); + +const sourceSans = Source_Sans_3({ + subsets: ["latin"], + variable: "--font-ui", + display: "swap", +}); + export default function RootLayout({ children }: { children: ReactNode }) { return ( - + {children} diff --git a/apps/web/app/t/[tenantSlug]/(public)/layout.tsx b/apps/web/app/t/[tenantSlug]/(public)/layout.tsx index 2613295..37986b7 100644 --- a/apps/web/app/t/[tenantSlug]/(public)/layout.tsx +++ b/apps/web/app/t/[tenantSlug]/(public)/layout.tsx @@ -2,7 +2,5 @@ import type { ReactNode } from "react"; /** Invitation flows are public (no session required). */ export default function TenantPublicLayout({ children }: { children: ReactNode }) { - return ( -
{children}
- ); + return
{children}
; } diff --git a/apps/web/app/t/[tenantSlug]/tenant-chrome.tsx b/apps/web/app/t/[tenantSlug]/tenant-chrome.tsx index 7ef5528..0c300e9 100644 --- a/apps/web/app/t/[tenantSlug]/tenant-chrome.tsx +++ b/apps/web/app/t/[tenantSlug]/tenant-chrome.tsx @@ -22,38 +22,33 @@ export function TenantChrome({ ); return ( -
-
-
- +
+
+
+

{activeOrg?.name ?? tenantSlug} - +

{activeOrg ? ( - {activeOrg.slug} + + {activeOrg.slug} + ) : null} - +

{user?.email ?? "—"} {user?.role ? ` · ${user.role}` : null} {user?.isSuperadmin ? " · superadmin" : null} - +

-
- +
+ All organizations
-
- -
-
{children}
+ +
{children}
); } diff --git a/apps/web/components/app-shell-header.tsx b/apps/web/components/app-shell-header.tsx index a7541a2..9839894 100644 --- a/apps/web/components/app-shell-header.tsx +++ b/apps/web/components/app-shell-header.tsx @@ -1,64 +1,75 @@ "use client"; import Link from "next/link"; +import { usePathname } from "next/navigation"; import { useSession } from "@/lib/auth/session"; import { appShellUrl } from "@/lib/urls"; export function AppShellHeader() { + const pathname = usePathname() ?? ""; const { user, authStatus, logout } = useSession(); const authed = authStatus === "authenticated"; const loading = authStatus === "loading"; return ( -
-
- +
+
+ Studiqo -
{loading ? ( - Session… + Session… ) : authed ? ( -
- +
+ {user?.email ?? "—"} {user?.role ? ` · ${user.role}` : null} {user?.isSuperadmin ? " · superadmin" : null}