You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(skill): update app-shell-patterns for new components (1.7.0) (#359)
* docs(skill): update app-shell-patterns for new components (1.7.0)
Sync the app-shell-patterns skill with components shipped since 0.36.0:
- Bump documented AppShell version 0.36.0 -> 1.7.0
- Add Grid entry + make it the canonical wrapper for metric/KPI strips
- Add DocumentProgressCard entry (lifecycle/fulfilment progress card)
- Document Table Head/Cell `align` (left|center|right) and DataTable
Column `align` (numeric auto-right); switch examples off ad-hoc classes
- Expand Badge to the full 15-variant set (filled / subtle / outline-*)
with a primary/secondary status split
- Document standalone accessible-name props on Select/Combobox/Autocomplete
- Add cross-cutting UX + composition & emphasis rules (one primary action,
no duplicate actions, action placement, metric tiles in Grid, forms
default to modal, handle loading/empty/error states)
- Refine detail/hero-with-actions layout variants and list/dense-scan
status-variant guidance to match the primary/secondary badge split
Reference images are intentionally left out of this PR and will follow separately.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(skill): add Alert/Tabs entries and fix component interlinking
Complete the component-coverage pass on the app-shell-patterns skill:
- Add missing component entries: Alert (variant/action/dismissible) and
Tabs (Root/List/Tab/Panel, variant default|line|capsule)
- Fix doc bug: Layout example used Tabs.Trigger; real subcomponent is Tabs.Tab
- Wire up broken bidirectional links — Grid, MetricCard, DocumentProgressCard
were documented as "Used in patterns: detail/*" but never referenced back.
Add them to detail/hero-with-actions requiredImports + a new "Optional
cards" section (metric strip, progress card, inline Alert)
- Demonstrate the new alignment props in pattern examples: Table.Head/Cell
align="right" on numeric columns (detail), DataTable Column.align on the
Amount column (list-dense-scan)
Reference images remain excluded (separate PR).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(skill): apply oxfmt formatting to skill docs
Run oxfmt on the docs touched in this branch (the earlier commits skipped
the pre-commit hook while local Node was on the wrong major). No content
changes — emphasis-marker normalization and requiredImports array wrapping.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(skill): sharpen app-shell-patterns description for agent discovery
Adopt the repo's "<does X>. Use when: …" convention so the skill triggers
on UI-building intent (list/table/form/modal/wizard/interaction) and states
the app-shell-installed precondition, instead of the passive "catalog" phrasing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: interacsean <seanhasselback@gmail.com>
Copy file name to clipboardExpand all lines: packages/core/skills/app-shell-patterns/SKILL.md
+13-1Lines changed: 13 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
---
2
2
name: app-shell-patterns
3
-
description: UI pattern catalog for building pages with @tailor-platform/app-shell components
3
+
description: "Best-practice UI patterns and correct component usage for building pages in apps that use @tailor-platform/app-shell. Use when: building or editing any screen, page, list, table, detail view, form, modal, dialog, wizard, or bulk/confirm/toast interaction in an app with @tailor-platform/app-shell installed — or when choosing the right AppShell component, layout, or design token for a UI."
4
4
---
5
5
6
6
# App-Shell Patterns
@@ -65,3 +65,15 @@ These are the foundational rules that underpin all patterns. All patterns build
65
65
- NEVER mix patterns in a single page component
66
66
- ALWAYS use AppShell components — do NOT use raw HTML or third-party UI libraries
67
67
- If no entry matches, compose directly from fundamental references
68
+
69
+
### Cross-cutting UX rules (apply to every screen)
70
+
71
+
Full rationale in [`design-system.md`](references/fundamental/design-system.md) → Composition & emphasis rules.
72
+
73
+
-**One primary action per view:** at most one primary/filled `Button`; everything else is `outline`/`secondary`/`ghost`.
74
+
-**Status badges by semantic color:** a **filled** semantic variant for the record's primary/lifecycle status, **`outline-*`** for secondary statuses, **`subtle-*`** for tags; reserve brand `default` for non-status emphasis.
75
+
-**No duplicate actions:** an action lives in exactly one place — never repeat the same action in both `Layout.Header` and `ActionPanel`.
76
+
-**Action placement:** primary CTA + status in `Layout.Header`; workflow actions in `ActionPanel`; back/navigation in the breadcrumb — never in `ActionPanel`.
77
+
-**Metric tiles always go in a `Grid`** (`columns={{ initial: 1, md: 2, xl: 4 }}`) — never one-per-row.
78
+
-**Forms default to `form/modal`** — only build a routed full-page form when the design explicitly calls for one.
79
+
-**Handle every state:** loading (skeleton), empty (labelled empty state), and error (inline + retry) — never ship only the happy path.
**Used in patterns:** every page pattern (`list/*`, `detail/*`, `form/*`).
109
109
110
+
### `Grid`
111
+
112
+
**Import:**`import { Grid } from '@tailor-platform/app-shell'`
113
+
**Purpose:** Presentational CSS-Grid container for laying tiles/cards into equal or custom columns with responsive reflow — the canonical wrapper for **metric / KPI strips** and card galleries. Purely layout; makes no data assumptions.
114
+
**API:**`GridProps` — `columns` (number | CSS track string like `"280px 1fr"` | responsive object `{ initial, sm, md, lg, xl }`), `gap` (spacing step, default `3`) plus `gapX` / `gapY`, `minChildWidth` (auto-fit: as many ≥Npx columns as fit, no breakpoints needed), `rows`, `flow`, `align`, `justify`. Sub-component **`Grid.Item`** for spanning/placement — `colSpan`, `rowSpan`, `colStart`, `colEnd` (all responsive). Root carries `data-slot="grid"`.
**Used in patterns:** metric / KPI strips on `detail/*` and dashboards; any equal-column card layout.
129
+
130
+
**Notes:** Metric tiles and KPI cards **always** go in a `Grid` — never stacked one-per-row or in a single column. Use a responsive `columns` object (e.g. `{ initial: 1, md: 2, xl: 4 }`) so tiles reflow at smaller widths; reach for `minChildWidth` when the tile count is dynamic.
131
+
110
132
### `SidebarLayout`
111
133
112
134
**Import:**`import { SidebarLayout } from '@tailor-platform/app-shell'`
@@ -250,6 +272,28 @@ import { Button, Link } from '@tailor-platform/app-shell';
**Used in patterns:** any pattern with icon-only buttons (must have `aria-label` AND a tooltip).
252
274
275
+
### `Tabs`
276
+
277
+
**Import:**`import { Tabs } from '@tailor-platform/app-shell'`
278
+
**Purpose:** In-page tab navigation — split one record's sections (Overview / Line items / Activity) or bucket a list (All / Open / …) into switchable panels. Presentational; owns only the active-tab state.
279
+
**API:** Compound — `Tabs.Root` (`variant`: `default | line | capsule`; controlled `value` + `onValueChange`, or uncontrolled `defaultValue`), `Tabs.List`, `Tabs.Tab` (`value`), `Tabs.Panel` (`value`). Note: the sub-component is **`Tabs.Tab`**, not `Tabs.Trigger`.
**Used in patterns:**`list-dense-scan` (bucket tabs composed **above**`DataTable.Root`, synced to `useCollectionVariables` — see the `DataTable` "Bucket tabs" note), `detail/*` (sectioned record content).
294
+
295
+
**Notes:** For lists, AppShell's own filtering surface is **toolbar chips** (`DataTable.Filters`), not tabs — reach for `Tabs` only when the business genuinely thinks in a small set of named buckets. Don't render a `Tab` per enum value where a filter chip belongs.
296
+
253
297
---
254
298
255
299
## Display
@@ -260,22 +304,35 @@ import { Button, Link } from '@tailor-platform/app-shell';
260
304
261
305
**Import:**`import { Badge } from '@tailor-platform/app-shell'`
262
306
**Purpose:** Status labels and small categorical chips.
263
-
**API:**`BadgeProps` — `variant`: `default | success | warning | neutral | error | destructive`. Plus `badgeVariants` CVA for custom-styled siblings.
**Used in patterns:**`list/*` (status column), `detail/*` (header status).
325
+
**Used in patterns:**`list/*` (status column → `outline-*`), `detail/*` (header status).
326
+
327
+
**Notes:** Encode status by **semantic color** with a primary/secondary split: a record's **primary/lifecycle status** uses a **filled** semantic variant (one per row / one in a detail header); **secondary statuses** (delivery, billing) use **`outline-*`** (status dot); tags use **`subtle-*`**. Reserve **`default`** (brand) for non-status emphasis. Full rule: **`design-system.md`** → Composition & emphasis rules.
271
328
272
329
### `Table`
273
330
274
331
> Full API: [https://raw.githubusercontent.com/tailor-platform/app-shell/refs/heads/main/docs/components/table.md](https://raw.githubusercontent.com/tailor-platform/app-shell/refs/heads/main/docs/components/table.md)
275
332
276
333
**Import:**`import { Table } from '@tailor-platform/app-shell'`
277
334
**Purpose:** Semantic data table with scrollable container.
278
-
**API:** Compound — `Table.Root`, `Table.Header`, `Table.Body`, `Table.Footer`, `Table.Row`, `Table.Head`, `Table.Cell`, `Table.Caption`. `Table.Root` accepts `containerClassName` for the outer wrapper, `className` for the inner `<table>`.
335
+
**API:** Compound — `Table.Root`, `Table.Header`, `Table.Body`, `Table.Footer`, `Table.Row`, `Table.Head`, `Table.Cell`, `Table.Caption`. `Table.Root` accepts `containerClassName` for the outer wrapper, `className` for the inner `<table>`.`Table.Head` and `Table.Cell` accept **`align`** (`"left" | "center" | "right"`, default `"left"`) — prefer this over ad-hoc `className="astw:text-right"` and pair the same alignment on the head and its column's cells (right for numeric/money, center for compact status/icon columns).
279
336
**Example:**
280
337
281
338
```tsx
@@ -284,7 +341,7 @@ import { Button, Link } from '@tailor-platform/app-shell';
**Column alignment:** each `Column` accepts **`align`** (`"left" | "right"`) applied to both header and body cell. Numeric `type` columns (`"number"`, `"money"`) default to `"right"` automatically so digits align on the decimal place — pass `align="left"` to opt out; everything else defaults to `"left"`.
407
+
349
408
**Metadata path:** Prefer `createColumnHelper` + `inferColumns(tableMetadata.order)` (`@tailor-platform/app-shell-sdk-plugin` codegen) when available so enum/datetime/string filters bind to the right editors.
350
409
351
410
**Bucket tabs / segmented UX:** AppShell defines **toolbar chips**, not lifecycle tabs. When design places **`Tabs`** (All / Draft / …) inside the card, compose them **above**`DataTable.Root` and synchronize tab-driven bucket state with **`useCollectionVariables`** (`variables.query` / filters)—see **`patterns/list/dense-scan.md`**.
**Used in patterns:** KPI tiles, dashboards, **`detail/*`** metric strips where specs call for them.
489
+
490
+
**Notes:** Always wrap metric tiles in **`Grid`** (see the `Grid` entry) — a column of single-width `MetricCard`s stacked one-per-row is an anti-pattern.
491
+
492
+
### `DocumentProgressCard`
493
+
494
+
**Import:**`import { DocumentProgressCard } from '@tailor-platform/app-shell'`
495
+
**Purpose:** Presentational card for a document's lifecycle / fulfilment state — an optional headline percentage, a stacked progress bar, and a status legend. Domain-agnostic and view-only: pass an arbitrary set of `segments` (e.g. shipped / returned / pending) plus an explicit `percent`; derive both in the consumer.
496
+
**API:**`DocumentProgressCardProps` — `segments` (required; array of `{ label, value, color }`), `percent?` (0–100, headline shown top-right), `title?`, `legend?` (defaults to `segments`; supply only when the legend should differ from the bar, e.g. overlapping buckets), `total?` (bar denominator; defaults to the sum of segment values — a larger value leaves an unfilled remainder), `className?`. Segment `color`: `indigo | pink | green | amber | red | blue | neutral`.
**Used in patterns:**KPI tiles, dashboards, **`detail/*`** metric strips where specs call for them.
511
+
**Used in patterns:**`detail/*` — fulfilment / lifecycle summary cards (e.g. a purchase-order receipt progress) in the main column or right rail.
426
512
427
513
### `Avatar`
428
514
@@ -483,6 +569,24 @@ const table = useDataTable({
483
569
**API:** Compound — `ActivityCard.Root`, `ActivityCard.Items` (generic over item type), plus `ActivityCardProps`, `ActivityCardItem`, `ActivityCardItemProps`. Items render with timestamp + actor + description.
484
570
**Used in patterns:**`detail/hero-with-actions` (right column or bottom section).
485
571
572
+
### `Alert`
573
+
574
+
**Import:**`import { Alert } from '@tailor-platform/app-shell'`
575
+
**Purpose:** Inline, in-page banner for a persistent status message — a form/record error, a warning, or a success/info notice attached to a section. (Transient notifications use `useToast`; blocking confirmations use `Dialog` — see `interaction/confirm`.)
<Alert.Title>Couldn't load line items</Alert.Title>
582
+
<Alert.Description>Check your connection and try again.</Alert.Description>
583
+
</Alert.Root>
584
+
```
585
+
586
+
**Used in patterns:** any data-backed screen's **error** state (inline error + retry) and record-level warnings — the error affordance the composition rules require (**`design-system.md`** → Composition & emphasis rules → States).
587
+
588
+
**Notes:** Encode severity by `variant` (semantic color), same discipline as `Badge` — don't use `error` for a routine notice. Pair a retry/next-step control via `action` rather than a separate stray button.
589
+
486
590
---
487
591
488
592
## Forms
@@ -523,6 +627,7 @@ const table = useDataTable({
523
627
**Import:**`import { Select } from '@tailor-platform/app-shell'`
524
628
**Purpose:** Dropdown for fixed enumerations.
525
629
**API:** Compound. Sync version (small option lists). For async/typeahead, see `Combobox` and `Autocomplete`. Type: `SelectAsyncFetcher<T>` for the async variant.
630
+
**Accessible name:** inside a `Form` the field label provides it automatically. When used **standalone** (outside a `Form`), pass `aria-label` (or `aria-labelledby` / `id`) — these now forward to the underlying combobox element, so it isn't left named only by its current value. Same applies to `Combobox` and `Autocomplete`.
These are visual-composition rules every screen must follow, regardless of pattern. They exist because emphasis only works when it is scarce.
360
+
361
+
**Emphasis budget.** Attention is a budget you spend once per scan region.
362
+
363
+
-**One primary action per view.** A screen (or a card/section) has at most one filled/primary `Button`; everything else is `outline`, `secondary`, or `ghost`. If two things look equally important, neither reads as important.
364
+
-**Badges** encode status by semantic color, with a clear primary/secondary split:
365
+
- A record's **primary / lifecycle status** (PO status, SO status) → a **filled semantic** variant (`success` / `warning` / `error` / `info` / `neutral`) — one per row in a list, one in a detail header.
366
+
-**Secondary statuses** (delivery, billing, fulfilment) and dense supporting columns → **`outline-*`** (with status dot).
- Reserve **`default`** (brand fill) for non-status emphasis — never the brand color as a routine status. The defect to avoid: making _every_ chip a loud fill, or giving secondary statuses the same weight as the primary one. (Variants: **`components.md`** → `Badge`.)
369
+
-**Color:** status colors (`success`/`warning`/`error`/`info`) signal meaning, not decoration — don't tint neutral content.
370
+
371
+
**Hierarchy.** One `h1` per page (the `Layout.Header` title). Section headings step down (`h2` → `h3`); never skip levels for size — pick the role token (`design-system.md` §4 Typography), not the pixel size.
372
+
373
+
**States — never ship only the happy path.** Every data-backed screen handles:
374
+
375
+
-**Loading** — skeleton/placeholder, not a blank flash.
376
+
-**Empty** — a labelled empty state (what it is, how to add the first record), not a bare empty table.
377
+
-**Error** — an inline error with a retry affordance, not a silent failure.
378
+
379
+
**Spacing rhythm.** Use the spacing scale (§4) consistently — equal gaps between sibling sections, consistent card padding. A one-off `gap` or `padding` that doesn't match its siblings reads as a mistake.
0 commit comments