Page title variants
One fixed hierarchy, identity then title then actions, so a reader parses any page the same way.
What this solves
Gestalt hierarchy and self-explaining action container. Every page opens with the same fixed order, identity, title, subtitle, then actions, so a reader parses a brand-new page the same way they parsed the last one, and the actions bar states up front what this page lets them do.
Use it when
- A list page's header, with an icon, a subtitle, and a primary action.Speedway's Jobs page (jobs.tsx:186-195): a plain icon plus a New job button.
- A module or workspace whose identity carries a theme color.Speedway's Modules page (modules.tsx:680-688): the module's own accent rides the avatar slot.
- A status that doubles as a live filter toggle riding beside the title.Walmart's Jobs page (Jobs.tsx:833-852): the in-review StatusPill both reports and filters.
Rendered
Fixture data; check both themes.Plain icon + toolbar
Jobs
Each time you run a module, it shows up here.
Themed identity chip
Workflow
Chain modules into one run.
Status pill beside the title
Jobs
in review3
Runs across every module.
Loading skeleton
Jobs
Each time you run a module, it shows up here.
Back-linked detail header
patterns/page-title-variants/example.tsx
Copy this into a fresh route and it renders as above.
"use client";import { useState } from "react";import { Button, PageTitle, RenderIcon, StatusPill } from "@versable-git/ui";// Five shipping shapes of one header row. The identity disc and the loading// state are the variant axes: a plain Icon, a theme-colored chip riding the// avatar slot (a custom title span would skip the kit's h1 styling and drift// off the house size, so the tile rides the identity slot instead), the// skeleton mirroring whichever slots the loaded row will use, and a// back-linked detail header.export function PageTitleVariants() { const [loaded, setLoaded] = useState(true); return ( <div className="flex flex-col gap-8"> <div className="flex flex-col gap-2"> <span className="text-base-content/65 text-xs font-medium tracking-wide uppercase">Plain icon + toolbar</span> <PageTitle Icon="Table" title="Jobs" subtitle="Each time you run a module, it shows up here." actions={<Button color="primary" size="sm" Icon="Success" content="New job" />} /> </div> <div className="flex flex-col gap-2"> <span className="text-base-content/65 text-xs font-medium tracking-wide uppercase">Themed identity chip</span> <PageTitle avatar={<RenderIcon Icon="Refresh" size={18} />} iconWrapClassName="bg-success/10 text-success rounded-lg" title="Workflow" subtitle="Chain modules into one run." /> </div> <div className="flex flex-col gap-2"> <span className="text-base-content/65 text-xs font-medium tracking-wide uppercase">Status pill beside the title</span> <PageTitle Icon="Table" title="Jobs" subtitle="Runs across every module." titleExtra={ <StatusPill kind="review" size="sm" count={3}> in review </StatusPill> } /> </div> <div className="flex flex-col gap-2"> <div className="flex items-center justify-between"> <span className="text-base-content/65 text-xs font-medium tracking-wide uppercase">Loading skeleton</span> <Button size="xs" variant="outline" content={loaded ? "Reload" : "Loaded"} onClick={() => setLoaded((v) => !v)} /> </div> <PageTitle skeleton={!loaded} Icon="Table" title="Jobs" subtitle="Each time you run a module, it shows up here." actions={<Button color="primary" size="sm" Icon="Success" content="New job" />} /> </div> <div className="flex flex-col gap-2"> <span className="text-base-content/65 text-xs font-medium tracking-wide uppercase">Back-linked detail header</span> <PageTitle Icon="Refresh" back={ <a href="#jobs" onClick={(e) => e.preventDefault()}> Back to Jobs </a> } title="Run 4" subtitle="Brake pads Q3 · started 9m ago" actions={<Button variant="outline" size="sm" content="View log" />} /> </div> </div> );}Where it ships
speedway/app/routes/workspaces/jobs/jobs.tsx:186-195plain Icon plus an actions toolbarspeedway/app/routes/workspaces/modules.tsx:680-688the themed chip rides the avatar slot; each module gets its identity color from shared metawalmart-mvp/frontend/src/pages/Jobs.tsx:833-852titleExtra carries a live StatusPill that doubles as a review filter toggle
App-specific: Identity colors come from the apps' module meta maps, the pill in variant three toggles a real filter, and back-links ride the back slot. The apps never restyle the title span itself; anything custom rides avatar, titleExtra, or actions.