136 lines
5.1 KiB
TypeScript
136 lines
5.1 KiB
TypeScript
import {
|
|
Badge,
|
|
Button,
|
|
PageHeader,
|
|
PageHeaderActions,
|
|
PageHeaderDescription,
|
|
PageHeaderEyebrow,
|
|
PageHeaderLeading,
|
|
PageHeaderMeta,
|
|
PageHeaderTitle
|
|
} from "@ai-ui/ui";
|
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
|
|
function PageHeaderExample({
|
|
align = "start",
|
|
density = "comfortable",
|
|
variant = "default"
|
|
}: {
|
|
align?: "start" | "end";
|
|
density?: "comfortable" | "compact";
|
|
variant?: "hero" | "default" | "compact";
|
|
}) {
|
|
return (
|
|
<div className="w-full max-w-[1100px]">
|
|
<PageHeader align={align} density={density} variant={variant}>
|
|
<PageHeaderLeading>
|
|
<PageHeaderMeta>
|
|
<PageHeaderEyebrow>Friday, Dec 5, 2025</PageHeaderEyebrow>
|
|
<Badge tone="primary" variant="subtle">
|
|
Calm pulse
|
|
</Badge>
|
|
</PageHeaderMeta>
|
|
<PageHeaderTitle>Welcome back, Nathan.</PageHeaderTitle>
|
|
<PageHeaderDescription>
|
|
Revenue operations is holding a calm pulse today. Growth is still being led by
|
|
AI-assisted follow-up, while cost pressure stays within the board target window.
|
|
</PageHeaderDescription>
|
|
</PageHeaderLeading>
|
|
|
|
<PageHeaderActions>
|
|
<Button>Morning brief</Button>
|
|
<Button variant="secondary">Watch anomalies</Button>
|
|
</PageHeaderActions>
|
|
</PageHeader>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const meta = {
|
|
title: "Patterns/PageHeader",
|
|
component: PageHeaderExample,
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
component:
|
|
"PageHeader is the shared top-of-page pattern for workspace and dashboard views. Use it when a screen needs one primary title stack with supporting context and a separate action cluster, instead of reassembling the same flex and spacing rules in each scene. Its motion should stay structural and calm: the header itself carries only a light ornamental wake-up, while badges and actions inherit a restrained lift that keeps the top rail feeling alive without turning it into a hero animation."
|
|
}
|
|
},
|
|
layout: "centered"
|
|
},
|
|
tags: ["autodocs"]
|
|
} satisfies Meta<typeof PageHeaderExample>;
|
|
|
|
export default meta;
|
|
|
|
type Story = StoryObj<typeof meta>;
|
|
|
|
export const Playground: Story = {};
|
|
|
|
export const Variants: Story = {
|
|
render: () => (
|
|
<div className="mx-auto grid w-full max-w-[1100px] gap-8">
|
|
<PageHeaderExample align="end" variant="hero" />
|
|
<PageHeaderExample variant="default" />
|
|
<PageHeaderExample density="compact" variant="compact" />
|
|
</div>
|
|
)
|
|
};
|
|
|
|
export const Anatomy: Story = {
|
|
render: () => (
|
|
<div className="mx-auto grid w-full max-w-[1100px] gap-5 rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
|
<div className="grid gap-2">
|
|
<p className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
|
Page header anatomy
|
|
</p>
|
|
<p className="text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
Keep the title stack in the leading slot and keep controls in the actions slot. The
|
|
pattern stays flexible enough for badges, dates, or status markers through the meta slot
|
|
without turning every page header into custom layout glue. Use `variant`, `density`, and
|
|
`align` on the root when a page needs hero weight, a quieter default header, or a tighter
|
|
compact treatment.
|
|
</p>
|
|
</div>
|
|
|
|
<PageHeaderExample />
|
|
|
|
<div className="grid gap-3 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
<p>
|
|
<code className="text-[var(--color-foreground)]">data-slot="leading"</code> owns the
|
|
meta, title, and description stack.
|
|
</p>
|
|
<p>
|
|
<code className="text-[var(--color-foreground)]">data-slot="actions"</code> is reserved
|
|
for the page-level control cluster so page scenes stop improvising this split every time.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
};
|
|
|
|
export const Motion: Story = {
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
story:
|
|
"Hover the badges and action cluster. The pattern itself should feel gently staged through ornament and shared transition polish, but the main title stack should stay anchored and readable."
|
|
}
|
|
}
|
|
},
|
|
render: () => (
|
|
<div className="mx-auto grid w-full max-w-[1100px] gap-5 rounded-[var(--radius-xl)] border border-[color-mix(in_oklch,var(--color-border)_72%,transparent)] bg-[linear-gradient(180deg,color-mix(in_oklch,var(--color-surface)_78%,white_22%),color-mix(in_oklch,var(--color-surface-container-low)_86%,white_14%))] p-6 shadow-[var(--shadow-sm)]">
|
|
<div className="grid gap-2">
|
|
<p className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-primary)]">
|
|
Motion review
|
|
</p>
|
|
<p className="max-w-2xl text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
PageHeader should not animate like a card. It should feel like polished infrastructure:
|
|
the slab wakes up through a light glow and the attached controls carry the interaction lift.
|
|
</p>
|
|
</div>
|
|
<PageHeaderExample align="end" variant="hero" />
|
|
</div>
|
|
)
|
|
};
|