meowerse_
docs menu

AppHeader

The header of meowerse accounts: brand, navigation that follows the session, the theme toggle, and a menu button on phones.

import { AppHeader } from "@meowerse/ui"; · source packages/ui/src/components/AppHeader.tsx

variants and states

checking the session — session-dependent links wait
signed out
signed in
session check failed — no false "log in"

try it

session
demo source
tsx
import { useState } from "react";
import { AppHeader, RadioGroup, type Session } from "@meowerse/ui";

const SESSIONS: Record<string, Session> = {
  loading: { loading: true, authenticated: false },
  guest: { loading: false, authenticated: false },
  "signed-in": { loading: false, authenticated: true, username: "alxnko", verified: true },
  error: { loading: false, authenticated: false, error: "timeout" },
};

// AppHeader's default links point at the auth app (/login, /signup, /account, …), which 404 on
// meow.alxnko.dev: the strict link check has no exemptions, so this demo passes links that resolve
// on this site instead (same pattern as the static preview on this component's own page).
const DEMO_LINKS = {
  guest: [
    { label: "about", href: "/ui/" },
    { label: "developers", href: "/ui/components/" },
    { label: "docs", href: "/ui/" },
  ],
  guestActions: [
    { label: "log in", href: "/#projects" },
    { label: "sign up", href: "/#projects" },
  ],
  signedIn: [
    { label: "account", href: "/" },
    { label: "developers", href: "/ui/components/" },
    { label: "docs", href: "/ui/" },
  ],
};

export default function Demo() {
  const [k, setK] = useState("guest");
  return (
    <div className="demo">
      <RadioGroup name="demo-session" legend="session" value={k} onChange={setK} options={[
        { label: "checking", value: "loading" }, { label: "signed out", value: "guest" },
        { label: "signed in", value: "signed-in" }, { label: "check failed", value: "error" },
      ]} />
      <div className="demo__frame"><AppHeader session={SESSIONS[k]!} links={DEMO_LINKS} /></div>
    </div>
  );
}

props

proptypedefaultrequired
sessionSession the current session, from useSession(); its loading/authenticated state decides which links show.—yes
classNamestring extra class names to append.—no
linksAppHeaderLinks the nav links per session state; defaults to the auth app's set. A docs preview passes links that resolve on its own site.DEFAULT_LINKSno

css custom properties

variabledarklight
--c-accent#00ff82#0a6e3c
--c-bg#0a0a0b#e9e8e4
--c-fg#ededeb#1a1a1c
--c-fg-muted#b3b3af#46464a
--c-line#232326#cfcec8
--c-surface#151517#dfded9
--c-surface-raised#1a1a1d#f7f6f3
--control-height44pxsame
--d-fast120mssame
--easecubic-bezier(.16,1,.3,1)same
--font-mark"VT323 Mark", var(--font-mono)same
--fs-20.8125remsame
--fs-41remsame
--r-m4pxsame
--shadow-md0 8px 24px rgb(0 0 0 / 0.25) (legacy alias)—
--sp-14pxsame
--sp-28pxsame
--sp-312pxsame
--sp-416pxsame
--z-chrome10same

Classes: .mw-brand-auth, .mw-brand-meow, .mw-header. Set any of these variables on an ancestor (or wrap a subtree in .mw-theme--dark / .mw-theme--light) to restyle just that part of a page.

usage

checking the session

tsx
import { AppHeader } from "@meowerse/ui";

<AppHeader session={{"loading":true,"authenticated":false}} links={{"guest":[{"label":"about","href":"/ui/"},{"label":"developers","href":"/ui/components/"},{"label":"docs","href":"/ui/"}],"guestActions":[{"label":"log in","href":"/#projects"},{"label":"sign up","href":"/#projects"}],"signedIn":[{"label":"account","href":"/"},{"label":"developers","href":"/ui/components/"},{"label":"docs","href":"/ui/"}]}} />

signed out

tsx
import { AppHeader } from "@meowerse/ui";

<AppHeader session={{"loading":false,"authenticated":false}} links={{"guest":[{"label":"about","href":"/ui/"},{"label":"developers","href":"/ui/components/"},{"label":"docs","href":"/ui/"}],"guestActions":[{"label":"log in","href":"/#projects"},{"label":"sign up","href":"/#projects"}],"signedIn":[{"label":"account","href":"/"},{"label":"developers","href":"/ui/components/"},{"label":"docs","href":"/ui/"}]}} />

signed in

tsx
import { AppHeader } from "@meowerse/ui";

<AppHeader session={{"loading":false,"authenticated":true,"username":"alxnko","verified":true}} links={{"guest":[{"label":"about","href":"/ui/"},{"label":"developers","href":"/ui/components/"},{"label":"docs","href":"/ui/"}],"guestActions":[{"label":"log in","href":"/#projects"},{"label":"sign up","href":"/#projects"}],"signedIn":[{"label":"account","href":"/"},{"label":"developers","href":"/ui/components/"},{"label":"docs","href":"/ui/"}]}} />

session check failed

tsx
import { AppHeader } from "@meowerse/ui";

<AppHeader session={{"loading":false,"authenticated":false,"error":"network"}} links={{"guest":[{"label":"about","href":"/ui/"},{"label":"developers","href":"/ui/components/"},{"label":"docs","href":"/ui/"}],"guestActions":[{"label":"log in","href":"/#projects"},{"label":"sign up","href":"/#projects"}],"signedIn":[{"label":"account","href":"/"},{"label":"developers","href":"/ui/components/"},{"label":"docs","href":"/ui/"}]}} />

accessibility

  • The brand link is named "meowerse auth — home".
  • The phone menu button reports aria-expanded; the navigation opens below the header.
  • While the session is loading or failed, the header never offers "log in" to someone who may already be signed in (B9).
keydoes
Tabmoves through the links, the theme toggle and the menu button
Enter or Spaceopens or closes the phone menu

do and don't

do

  • Pass the session from useSession(base), so every island on the page shares one request.
  • Pass your own links prop; the live preview above points at this docs site's own pages, not the auth app's real destinations (see the props table's default column for those).

don't

  • Don't use it outside meowerse accounts yet: its brand is fixed. A configurable header comes with the accounts redesign (sub-project 3).