Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions apps/design-system/__registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,28 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"PageBreadcrumbs": {
name: "PageBreadcrumbs",
type: "components:fragment",
registryDependencies: undefined,
component: React.lazy(() => import("@/../../packages/ui-patterns/src/PageBreadcrumbs")),
source: "",
files: ["registry/default//PageBreadcrumbs/index.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"PageNav": {
name: "PageNav",
type: "components:fragment",
registryDependencies: undefined,
component: React.lazy(() => import("@/../../packages/ui-patterns/src/PageNav")),
source: "",
files: ["registry/default//PageNav/index.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"PageSection": {
name: "PageSection",
type: "components:fragment",
Expand Down Expand Up @@ -2315,6 +2337,28 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"page-breadcrumbs-demo": {
name: "page-breadcrumbs-demo",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/page-breadcrumbs-demo")),
source: "",
files: ["registry/default/example/page-breadcrumbs-demo.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"page-nav-demo": {
name: "page-nav-demo",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/page-nav-demo")),
source: "",
files: ["registry/default/example/page-nav-demo.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"page-layout-detail": {
name: "page-layout-detail",
type: "components:example",
Expand All @@ -2326,6 +2370,28 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"page-layout-edge-function": {
name: "page-layout-edge-function",
type: "components:example",
registryDependencies: ["filter-bar"],
component: React.lazy(() => import("@/registry/default/example/page-layout-edge-function")),
source: "",
files: ["registry/default/example/page-layout-edge-function.tsx","registry/default/example/page-layout-logs-content.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"page-layout-full-width": {
name: "page-layout-full-width",
type: "components:example",
registryDependencies: ["filter-bar"],
component: React.lazy(() => import("@/registry/default/example/page-layout-full-width")),
source: "",
files: ["registry/default/example/page-layout-full-width.tsx","registry/default/example/page-layout-logs-content.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"page-layout-list": {
name: "page-layout-list",
type: "components:example",
Expand Down Expand Up @@ -2359,6 +2425,17 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"page-layout-auth-emails": {
name: "page-layout-auth-emails",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/page-layout-auth-emails")),
source: "",
files: ["registry/default/example/page-layout-auth-emails.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"page-header-demo": {
name: "page-header-demo",
type: "components:example",
Expand Down
20 changes: 14 additions & 6 deletions apps/design-system/components/component-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface ComponentPreviewProps extends React.HTMLAttributes<HTMLDivElement> {
showDottedGrid?: boolean
wide?: boolean
hideCode?: boolean
padded?: boolean
}

export function ComponentPreview({
Expand All @@ -32,6 +33,7 @@ export function ComponentPreview({
showDottedGrid = true,
wide = false,
hideCode = false,
padded = true,
...props
}: ComponentPreviewProps) {
const [config] = useConfig()
Expand All @@ -41,6 +43,7 @@ export function ComponentPreview({
const Code = Codes[index]

const [expand, setExpandState] = React.useState(false)
const previewClassName = className

const Preview = React.useMemo(() => {
const Component = Index[config.style][name]?.component
Expand All @@ -64,11 +67,16 @@ export function ComponentPreview({
return (
<>
<div
className={cn('preview flex min-h-[256px] w-full justify-center p-10', {
'items-center': align === 'center',
'items-start': align === 'start',
'items-end': align === 'end',
})}
className={cn(
'preview flex min-h-[256px] w-full justify-center',
padded && 'p-10',
{
'items-center': align === 'center',
'items-start': align === 'start',
'items-end': align === 'end',
},
previewClassName
)}
>
<React.Suspense
fallback={
Expand All @@ -80,7 +88,7 @@ export function ComponentPreview({
</div>
</>
)
}, [Preview, align])
}, [Preview, align, padded, previewClassName])

const wideClasses = wide ? '2xl:-ml-20 2xl:-mr-20' : ''

Expand Down
10 changes: 10 additions & 0 deletions apps/design-system/config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ export const docsConfig: DocsConfig = {
href: '/docs/fragments/page-container',
items: [],
},
{
title: 'Page Breadcrumbs',
href: '/docs/fragments/page-breadcrumbs',
items: [],
},
{
title: 'Page Nav',
href: '/docs/fragments/page-nav',
items: [],
},
{
title: 'Page Header',
href: '/docs/fragments/page-header',
Expand Down
44 changes: 44 additions & 0 deletions apps/design-system/content/docs/fragments/page-breadcrumbs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Page Breadcrumbs
description: Full-width breadcrumb row for page chrome, placed above PageHeader and page content.
fragment: true
---

<ComponentPreview
name="page-breadcrumbs-demo"
description="Breadcrumb row with optional actions"
align="start"
className="p-0"
padded={false}
peekCode
wide
/>

## Usage

```tsx
import { PageBreadcrumbs, PageBreadcrumbsActions } from 'ui-patterns/PageBreadcrumbs'
```

```tsx
<PageBreadcrumbs
actions={
<PageBreadcrumbsActions>
<Button type="primary" size="tiny">
Create
</Button>
</PageBreadcrumbsActions>
}
>
<BreadcrumbList>{/* … */}</BreadcrumbList>
</PageBreadcrumbs>
```

## Sub-components

- `PageBreadcrumbs` — bordered, full-width breadcrumb bar (wraps shadcn `Breadcrumb`)
- `PageBreadcrumbsActions` — actions on the right of the breadcrumb row (pass via `actions` prop)

`PageBreadcrumbs` uses `PageContainer` with `size="full"` for width and padding. It does not depend on `PageHeader`.

Place it as a sibling above `PageHeader` and page content, not inside `PageHeader`. For legacy pages that still nest breadcrumbs inside `PageHeader`, use `PageHeaderBreadcrumb` instead.
4 changes: 3 additions & 1 deletion apps/design-system/content/docs/fragments/page-container.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fragment: true

<ComponentPreview
name="page-container-demo"
description="Container with default size (1200px max-width)"
description="Container with small, default, large, and full sizes"
peekCode
wide
/>
Expand All @@ -29,3 +29,5 @@ The `PageContainer` supports four size variants:
- `default` - max-width: 1200px
- `large` - max-width: 1600px
- `full` - no max-width constraint

Use `small` or `default` for most pages. Use `full` for dense page experiences such as logs, code, editors, charts, or tables that need the viewport width.
63 changes: 52 additions & 11 deletions apps/design-system/content/docs/fragments/page-header.mdx
Original file line number Diff line number Diff line change
@@ -1,24 +1,65 @@
---
title: Page Header
description: A compound component for building page headers with consistent structure.
description: A compound component for page title, description, and actions.
fragment: true
---

Compound header for page context: optional icon, title, description, and aside actions. Breadcrumbs and sub navigation use [Page Breadcrumbs](./page-breadcrumbs) and [Page Nav](./page-nav) as siblings above this component — see [Layout patterns](../ui-patterns/layout).

<ComponentPreview
name="page-header-demo"
description="Basic page header with title, description, and actions"
description="Title, description, icon, and aside actions"
align="start"
className="p-0"
padded={false}
peekCode
wide
/>

## Quick reference

| Piece | Role |
| ----------------------- | ---------------------------------------------------------------------------- |
| `PageHeaderMeta` | Wrapper for icon, summary, and aside — use when the page needs a title block |
| `PageHeaderIcon` | Optional icon to the left of the summary |
| `PageHeaderSummary` | Groups `PageHeaderTitle` and `PageHeaderDescription` |
| `PageHeaderTitle` | Page heading (`h1`) |
| `PageHeaderDescription` | Supporting text below the title |
| `PageHeaderAside` | Page-level actions when there is no filter row above a table |

**Sizes:** `small`, `default`, `large`, `full` — passed to `PageHeader` and applied to the meta container width.

## Example

```tsx
<PageHeader size="default">
<PageHeaderMeta>
<PageHeaderIcon>
<Card className="flex h-14 w-14 shrink-0 items-center justify-center">
<Database className="h-5 w-5" />
</Card>
</PageHeaderIcon>
<PageHeaderSummary>
<PageHeaderTitle>Templates</PageHeaderTitle>
<PageHeaderDescription>Manage email templates for your project.</PageHeaderDescription>
</PageHeaderSummary>
<PageHeaderAside>
<Button type="primary" size="tiny">
Create template
</Button>
</PageHeaderAside>
</PageHeaderMeta>
</PageHeader>
```

## Sub-components

- `PageHeader` - Root container with size variants (`default`, `small`, `large`, `full`)
- `PageHeaderBreadcrumb` - Breadcrumb navigation wrapper (should be first child)
- `PageHeaderMeta` - Meta wrapper for icon, summary, and aside (groups icon and summary together, places aside on the right)
- `PageHeaderIcon` - Icon container positioned left of title (should be inside PageHeaderMeta, has `shrink-0`)
- `PageHeaderSummary` - Container for title and description (should be inside PageHeaderMeta, has `flex-1`)
- `PageHeaderTitle` - Primary page heading (h1)
- `PageHeaderDescription` - Supporting text below title
- `PageHeaderAside` - Container for action buttons (should be inside PageHeaderMeta, has `shrink-0`)
- `PageHeaderNavigationTabs` - Container for tab navigation (NavMenu, should be last child)
- `PageHeader` — root; `size` variants: `small`, `default`, `large`, `full`
- `PageHeaderMeta` — groups icon, summary, and aside
- `PageHeaderIcon` — optional icon (inside `PageHeaderMeta`)
- `PageHeaderSummary` — title and description (inside `PageHeaderMeta`)
- `PageHeaderTitle` — page heading (`h1`)
- `PageHeaderDescription` — supporting text
- `PageHeaderAside` — actions when meta is shown

Legacy sub-components `PageHeaderBreadcrumb` and `PageHeaderNavigationTabs` remain for existing pages; prefer [Page Breadcrumbs](./page-breadcrumbs) and [Page Nav](./page-nav) for new work.
39 changes: 39 additions & 0 deletions apps/design-system/content/docs/fragments/page-nav.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: Page Nav
description: Full-width sub-navigation row for page chrome, placed below breadcrumbs and outside PageHeader.
fragment: true
---

<ComponentPreview
name="page-nav-demo"
description="Sub-navigation with NavMenu tabs"
align="start"
className="p-0"
padded={false}
peekCode
wide
/>

## Usage

```tsx
import { NavMenu, NavMenuItem } from 'ui'
import { PageNav } from 'ui-patterns/PageNav'
```

```tsx
<PageNav>
<NavMenu>
<NavMenuItem active>Overview</NavMenuItem>
<NavMenuItem>Logs</NavMenuItem>
</NavMenu>
</PageNav>
```

## Sub-components

- `PageNav` — bordered, full-width container for `NavMenu` tab navigation

`PageNav` uses `PageContainer` with `size="full"` for width and padding. It does not depend on `PageHeader`.

Place it directly below `PageBreadcrumbs` and outside `PageHeader`. For legacy pages, `PageHeaderNavigationTabs` remains available on `PageHeader`.
4 changes: 4 additions & 0 deletions apps/design-system/content/docs/fragments/page-section.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ fragment: true
- `vertical` - Content stacks vertically (default)
- `horizontal` - Summary and content arranged horizontally on larger screens

## Layout Guidance

Use `PageSectionTitle` and `PageSectionDescription` when a page has multiple sections and no `PageHeader` title—each section gets its own heading. Header titles describe the page; section titles describe a distinct block of content on that page.

## Examples

### Horizontal Orientation
Expand Down
Loading
Loading