@tailor-platform/app-shell 1.14.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/dist/app-shell-core.css +1 -1
- package/dist/app-shell.d.ts +1078 -285
- package/dist/app-shell.js +3912 -2660
- package/dist/{appshell-CtS8Y3LW.js → appshell-DLVBFo5J.js} +729 -541
- package/dist/style-entry.d.ts +111 -111
- package/dist/testing.d.ts +111 -111
- package/dist/testing.js +1 -1
- package/dist/themes/_template.css +1 -1
- package/dist/vite-plugin.d.ts +111 -111
- package/package.json +7 -7
- package/skills/app-shell-patterns/SKILL.md +11 -14
- package/skills/app-shell-patterns/references/fundamental/components.md +48 -20
- package/skills/app-shell-patterns/references/fundamental/design-system.md +24 -17
- package/skills/app-shell-patterns/references/pages/detail.md +1148 -0
- package/skills/app-shell-patterns/references/patterns/form-composer.md +2 -2
- package/skills/app-shell-patterns/references/patterns/interaction-multi-select.md +1 -1
- package/skills/app-shell-patterns/references/patterns/detail-hero-with-actions.md +0 -201
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tailor-platform/app-shell",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "An opinionated React application framework for building ERP applications on Tailor Platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app-shell",
|
|
@@ -47,21 +47,21 @@
|
|
|
47
47
|
"access": "public"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@base-ui/react": "1.
|
|
50
|
+
"@base-ui/react": "1.8.0",
|
|
51
51
|
"@internationalized/date": "3.12.2",
|
|
52
52
|
"@standard-schema/spec": "^1.1.0",
|
|
53
53
|
"@tailor-platform/auth-public-client": "^0.6.1",
|
|
54
|
+
"@tailor-platform/vite-plugin-app-shell": "0.3.0",
|
|
54
55
|
"change-case": "^5.4.4",
|
|
55
56
|
"class-variance-authority": "^0.7.1",
|
|
56
57
|
"clsx": "^2.1.1",
|
|
57
58
|
"encoding-japanese": "^2.2.0",
|
|
58
|
-
"lucide-react": "
|
|
59
|
+
"lucide-react": "1.8.0",
|
|
59
60
|
"openai": "6.45.0",
|
|
60
61
|
"papaparse": "^5.5.3",
|
|
61
62
|
"react-router": "^8.3.0",
|
|
62
63
|
"sonner": "^2.0.7",
|
|
63
|
-
"tailwind-merge": "^3.6.0"
|
|
64
|
-
"@tailor-platform/vite-plugin-app-shell": "0.3.0"
|
|
64
|
+
"tailwind-merge": "^3.6.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@fontsource-variable/noto-sans-jp": "5.3.0",
|
|
@@ -70,14 +70,14 @@
|
|
|
70
70
|
"@testing-library/react": "16.3.3",
|
|
71
71
|
"@testing-library/user-event": "14.6.7",
|
|
72
72
|
"@types/encoding-japanese": "2.2.1",
|
|
73
|
-
"@types/node": "^25.9.
|
|
73
|
+
"@types/node": "^25.9.6",
|
|
74
74
|
"@types/papaparse": "5.5.2",
|
|
75
75
|
"@types/react": "^19",
|
|
76
76
|
"@types/react-dom": "^19",
|
|
77
77
|
"@vitejs/plugin-react": "^6.1.0",
|
|
78
78
|
"@vitest/coverage-v8": "4.1.10",
|
|
79
79
|
"happy-dom": "20.9.0",
|
|
80
|
-
"postcss": "8.5.
|
|
80
|
+
"postcss": "8.5.28",
|
|
81
81
|
"react": "^19.2.7",
|
|
82
82
|
"react-dom": "^19.2.7",
|
|
83
83
|
"react-hook-form": "7.71.2",
|
|
@@ -27,18 +27,14 @@ Read it when upgrading `@tailor-platform/app-shell`, and whenever styling, themi
|
|
|
27
27
|
|
|
28
28
|
## Available Pages
|
|
29
29
|
|
|
30
|
-
A page is the shape of a whole screen — the outer choice, made before picking patterns for the parts inside it. Where a pattern is one recipe, a page
|
|
31
|
-
|
|
30
|
+
A page is the shape of a whole screen — the outer choice, made before picking patterns for the parts inside it. Where a pattern is one recipe, a page fixes the layout of a whole screen and says what belongs in each part of it. Match the screen first, then use the patterns the entry cites for the pieces within it.
|
|
32
31
|
|
|
32
|
+
| Slug | Name | Description |
|
|
33
|
+
| -------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
34
|
+
| [`detail`](references/pages/detail.md) | Detail Page | The screen for a single record — its summary, its contents, the records around it, and the actions available on it |
|
|
33
35
|
|
|
34
36
|
## Available Patterns
|
|
35
37
|
|
|
36
|
-
### detail
|
|
37
|
-
|
|
38
|
-
| Slug | Name | Description |
|
|
39
|
-
| ----------------------------------------------------------------------------- | ------------------------ | --------------------------------------------------------------------- |
|
|
40
|
-
| [`detail/hero-with-actions`](references/patterns/detail-hero-with-actions.md) | Hero With Actions Detail | Single-record detail view with workflow actions and activity timeline |
|
|
41
|
-
|
|
42
38
|
### form
|
|
43
39
|
|
|
44
40
|
| Slug | Name | Description |
|
|
@@ -66,16 +62,17 @@ A page is the shape of a whole screen — the outer choice, made before picking
|
|
|
66
62
|
## How to Use
|
|
67
63
|
|
|
68
64
|
1. Identify the user's intent (list, detail, form, interaction, screen composition, recipe)
|
|
69
|
-
2. Match
|
|
70
|
-
3.
|
|
71
|
-
4. Read
|
|
72
|
-
5.
|
|
65
|
+
2. Building a whole screen? Match it to a **page** entry first and read that. A page fixes the screen's shape — its layout, what goes where, and in what order — before any of the parts inside it are chosen.
|
|
66
|
+
3. Match each part you are building to a **pattern** entry slug from the tables above. A one-off recipe rather than a screen skips straight to this step.
|
|
67
|
+
4. Read the entry's detailed spec: `references/<category>/<slug>.md` (relative to this file)
|
|
68
|
+
5. Read fundamental references for component APIs, design tokens, and GraphQL conventions: `references/fundamental/`
|
|
69
|
+
6. Implement using ONLY the imports listed in the entry's `requiredImports` — for a page, the `requiredImports` of the pattern it sent you to
|
|
73
70
|
|
|
74
71
|
## Rules
|
|
75
72
|
|
|
76
73
|
- ALWAYS cite the entry slug in a comment at the top of the file:
|
|
77
|
-
`/* pattern: list/dense-scan
|
|
78
|
-
- NEVER mix patterns in a
|
|
74
|
+
`/* pattern: list/dense-scan */`. Cite the pattern you implemented, even when a page entry sent you to it.
|
|
75
|
+
- NEVER mix two patterns that solve the same problem in one component — one part, one recipe. Composing patterns for _different_ parts of a screen is expected, and the screen's page entry says which.
|
|
79
76
|
- ALWAYS use AppShell components — do NOT use raw HTML or third-party UI libraries
|
|
80
77
|
- If no entry matches, compose directly from fundamental references
|
|
81
78
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| This file (`components.md`) | `design-system.md` |
|
|
10
10
|
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
11
11
|
| **Imports**, compound structure, hooks, canonical **composition** (`Card` + `Table`, `DataTable`, `Dialog`, etc.) | **Tokens**, theme imports, typography/spacing/radius/elevation **tables**, breakpoints **intent** |
|
|
12
|
-
| JSX examples tied to ERP patterns (`patterns/*`) |
|
|
12
|
+
| JSX examples tied to ERP patterns (`patterns/*`) | **AppShell styling boundary** — plain utilities and no internal `astw:` prefix |
|
|
13
13
|
| Prop summaries + links to upstream `docs/components/*.md` | **Visual conformance**: no magic colors/px on custom markup, motion, dark mode |
|
|
14
14
|
|
|
15
15
|
**Rule of thumb:** “Which component / prop?” → **here.** “Which token / spacing step / elevation?” → **`design-system.md`** §§4–6.
|
|
@@ -105,7 +105,7 @@ import { Button, Layout, Tabs } from "@tailor-platform/app-shell";
|
|
|
105
105
|
</Layout>
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
**Notes:** Children that aren't `Layout.Header` or `Layout.Column` are filtered out.
|
|
108
|
+
**Notes:** Children that aren't `Layout.Header` or `Layout.Column` are filtered out. Set column gaps with `<Layout gap={6} />` (supported steps: 4, 6, 8).
|
|
109
109
|
|
|
110
110
|
**Used in patterns:** every page pattern (`list/*`, `detail/*`, `form/*`).
|
|
111
111
|
|
|
@@ -257,7 +257,7 @@ import { Button, Link } from '@tailor-platform/app-shell';
|
|
|
257
257
|
|
|
258
258
|
**Used in patterns:** `list/*` (filter sheet variant).
|
|
259
259
|
|
|
260
|
-
**Notes:** Size the panel with `Sheet.Content`'s **`size`** prop (`"sm" | "md" | "lg" | "xl" | "full"`, default `"sm"`) — not a width utility.
|
|
260
|
+
**Notes:** Size the panel with `Sheet.Content`'s **`size`** prop (`"sm" | "md" | "lg" | "xl" | "full"`, default `"sm"`) — not a width utility.
|
|
261
261
|
|
|
262
262
|
### `Menu`
|
|
263
263
|
|
|
@@ -355,7 +355,7 @@ Plus `badgeVariants` CVA for custom-styled siblings.
|
|
|
355
355
|
|
|
356
356
|
**Import:** `import { Table } from '@tailor-platform/app-shell'`
|
|
357
357
|
**Purpose:** Semantic data table with scrollable container.
|
|
358
|
-
**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="
|
|
358
|
+
**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="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).
|
|
359
359
|
**Example:**
|
|
360
360
|
|
|
361
361
|
```tsx
|
|
@@ -385,8 +385,8 @@ Plus `badgeVariants` CVA for custom-styled siblings.
|
|
|
385
385
|
|
|
386
386
|
**Notes:**
|
|
387
387
|
|
|
388
|
-
- **Inside a card?**
|
|
389
|
-
- **Whole row is clickable.** Use `<Table.Row onClick={() => navigate(detailPath)} className="
|
|
388
|
+
- **Inside a card?** Zero the card's horizontal padding — drop `Card.Content` (bare list form) or pass `Card.Content className="px-0!"` (header+content form) — and leave the table container's padding alone. `Table.Head` / `Table.Cell` carry their own `first:pl-6` / `last:pr-6`, so a `containerClassName="px-6"` double-pads and the first column lands 24px right of the card title. See the `Card` entry for the two canonical forms and a DON'T example. Dense cell typography (**`text-sm`**, **`tabular-nums`** for figures, **`font-mono`** for identifiers) → **`design-system.md`** §4 Typography.
|
|
389
|
+
- **Whole row is clickable.** Use `<Table.Row onClick={() => navigate(detailPath)} className="cursor-pointer">`. For keyboard and screen-reader users, also wrap the primary identifier cell content in `<Link>` (so the row is reachable via Tab; `Table.Row` is a `<tr>` and cannot itself be a Link — wrapping a `<tr>` in `<a>` is invalid HTML). **No per-row "View" / "Open" / "→" buttons.** Per-row `Menu` (overflow `…`) is the only allowed per-row action surface and is reserved for non-navigation actions like Archive, Duplicate.
|
|
390
390
|
|
|
391
391
|
### `DataTable`
|
|
392
392
|
|
|
@@ -461,13 +461,13 @@ const table = useDataTable({
|
|
|
461
461
|
|
|
462
462
|
**Notes:**
|
|
463
463
|
|
|
464
|
-
- **
|
|
464
|
+
- **A table inside a card needs ONE geometry change, not two** (token-backed spacing rationale → **`design-system.md`** §4 Spacing): the card stops imposing horizontal padding. Drop `Card.Content` for the bare form, or pass `Card.Content className="px-0!"` for the header+content form. That is all. `Table.Head` and `Table.Cell` already carry an intrinsic `first:pl-6` / `last:pr-6`, which supplies the inset on its own — so the table container must add **none**. Padding on the container stacks on top of the cell's own and pushes the first column 24px past the card title.
|
|
465
465
|
|
|
466
466
|
**Bare table-in-card (list pages, no card-level title):**
|
|
467
467
|
|
|
468
468
|
```tsx
|
|
469
469
|
<Card.Root>
|
|
470
|
-
<Table.Root
|
|
470
|
+
<Table.Root>{/* … */}</Table.Root>
|
|
471
471
|
</Card.Root>
|
|
472
472
|
```
|
|
473
473
|
|
|
@@ -476,18 +476,19 @@ const table = useDataTable({
|
|
|
476
476
|
```tsx
|
|
477
477
|
<Card.Root>
|
|
478
478
|
<Card.Header title="Line items" />
|
|
479
|
-
<Card.Content className="
|
|
480
|
-
<Table.Root
|
|
479
|
+
<Card.Content className="px-0!">
|
|
480
|
+
<Table.Root>{/* … */}</Table.Root>
|
|
481
481
|
</Card.Content>
|
|
482
482
|
</Card.Root>
|
|
483
483
|
```
|
|
484
484
|
|
|
485
|
-
**DON'T — first column
|
|
485
|
+
**DON'T — the first column sits 24px right of the card title:**
|
|
486
486
|
|
|
487
487
|
```tsx
|
|
488
488
|
<Card.Root>
|
|
489
|
-
<Card.Content className="
|
|
490
|
-
|
|
489
|
+
<Card.Content className="px-0!">
|
|
490
|
+
{/* double-pads: the container's 24px adds to the cell's own first:pl-6 */}
|
|
491
|
+
<Table.Root containerClassName="px-6">{/* … */}</Table.Root>
|
|
491
492
|
</Card.Content>
|
|
492
493
|
</Card.Root>
|
|
493
494
|
```
|
|
@@ -565,26 +566,37 @@ const table = useDataTable({
|
|
|
565
566
|
/>
|
|
566
567
|
```
|
|
567
568
|
|
|
568
|
-
**Used in
|
|
569
|
+
**Used in:** `page/detail` (body sections).
|
|
569
570
|
|
|
570
571
|
### `ActionPanel`
|
|
571
572
|
|
|
572
573
|
**Import:** `import { ActionPanel } from '@tailor-platform/app-shell'`
|
|
573
574
|
**Purpose:** Right-rail panel listing workflow actions for a detail page (approve, reject, archive, etc.).
|
|
574
|
-
**API:** `ActionPanelProps` — `title`, `actions`
|
|
575
|
+
**API:** `ActionPanelProps` — `title`, `actions`, `className`. Each action is `{ key, label, icon, onClick?, disabled?, loading?, variant? }`; `key`, `label` and `icon` are all **required**, the handler is `onClick` (not `onSelect`), and `variant` is `"default" | "destructive"`. There is no `hidden` — omit a row by not spreading it in. The row type is not exported on its own; annotate an array as `ActionPanelProps["actions"]`.
|
|
575
576
|
**Example:**
|
|
576
577
|
|
|
577
578
|
```tsx
|
|
578
579
|
<ActionPanel
|
|
579
580
|
title="Actions"
|
|
580
581
|
actions={[
|
|
581
|
-
{
|
|
582
|
-
|
|
582
|
+
{ key: "approve", label: "Approve", icon: <Check />, onClick: handleApprove },
|
|
583
|
+
...(canReject
|
|
584
|
+
? [
|
|
585
|
+
{
|
|
586
|
+
key: "reject",
|
|
587
|
+
label: "Reject",
|
|
588
|
+
icon: <X />,
|
|
589
|
+
onClick: handleReject,
|
|
590
|
+
variant: "destructive" as const,
|
|
591
|
+
loading: rejecting,
|
|
592
|
+
},
|
|
593
|
+
]
|
|
594
|
+
: []),
|
|
583
595
|
]}
|
|
584
596
|
/>
|
|
585
597
|
```
|
|
586
598
|
|
|
587
|
-
**Used in
|
|
599
|
+
**Used in:** `page/detail`.
|
|
588
600
|
|
|
589
601
|
**Notes:**
|
|
590
602
|
|
|
@@ -595,7 +607,7 @@ const table = useDataTable({
|
|
|
595
607
|
**Import:** `import { ActivityCard } from '@tailor-platform/app-shell'`
|
|
596
608
|
**Purpose:** Timeline of events on a record (audit log, status changes, comments).
|
|
597
609
|
**API:** Compound — `ActivityCard.Root`, `ActivityCard.Items` (generic over item type), plus `ActivityCardProps`, `ActivityCardItem`, `ActivityCardItemProps`. Items render with timestamp + actor + description.
|
|
598
|
-
**Used in
|
|
610
|
+
**Used in:** `page/detail` (right column, for revision history / audit trail).
|
|
599
611
|
|
|
600
612
|
### `Alert`
|
|
601
613
|
|
|
@@ -747,7 +759,7 @@ Multi-select submits an array.
|
|
|
747
759
|
**Import:** `import { Textarea } from '@tailor-platform/app-shell'`
|
|
748
760
|
**Purpose:** Multi-line text input. Maps to spec field types: `text`, long-form `string`. Use instead of `Input` whenever the value is prose (notes, descriptions, replies, reasons).
|
|
749
761
|
**API:** `TextareaProps` — Base UI's field control (`value`, `defaultValue`, `onChange`, `onValueChange`, `disabled`, `required`, `readOnly`, `name`, aria-\*) rendered as a `<textarea>`, plus `rows`. `cols` / `wrap` are not accepted — the control is `w-full`, so neither can take effect.
|
|
750
|
-
**Sizing:** no fixed height — `rows` sets the visible line count and the user can drag it taller. `min-h-16` is a hard floor, so `rows` only takes effect from 3 up. Width is the parent's job: the control is `w-full`, and `cols` is not accepted because it could never win against that. Do **not** reach for `Input` and a height override; `Input` is locked to `h-9`, and an
|
|
762
|
+
**Sizing:** no fixed height — `rows` sets the visible line count and the user can drag it taller. `min-h-16` is a hard floor, so `rows` only takes effect from 3 up. Width is the parent's job: the control is `w-full`, and `cols` is not accepted because it could never win against that. Do **not** reach for `Input` and a height override; `Input` is locked to `h-9`, and an application must not use AppShell's internal `astw:` classes.
|
|
751
763
|
**Field integration:** drops straight into `Field.Root` for label/description/error wiring exactly like `Input` and `Checkbox` — no bespoke `error` prop.
|
|
752
764
|
**Example:**
|
|
753
765
|
|
|
@@ -792,6 +804,22 @@ Multi-select submits an array.
|
|
|
792
804
|
|
|
793
805
|
---
|
|
794
806
|
|
|
807
|
+
## AI
|
|
808
|
+
|
|
809
|
+
### `AIChat`
|
|
810
|
+
|
|
811
|
+
> Full API: [https://raw.githubusercontent.com/tailor-platform/app-shell/refs/heads/main/docs/components/ai-chat.md](https://raw.githubusercontent.com/tailor-platform/app-shell/refs/heads/main/docs/components/ai-chat.md)
|
|
812
|
+
|
|
813
|
+
**Import:** `import { AIChat } from '@tailor-platform/app-shell'`
|
|
814
|
+
**Purpose:** Building blocks for an LLM assistant UI — a streaming conversation view over a composer, plus reasoning, tool-call, and citation parts. The root places three regions in a fixed order, `Layout`-style: `AIChat.Header` (optional) over `AIChat.Conversation` over `AIChat.Composer` (optional). Each region carries its own props; the root carries `status`.
|
|
815
|
+
**API:** Root (`status`) + regions — `AIChat.Header` (`title`, `icon`, `actions`), `AIChat.Conversation` (`autoScroll`, transcript as children), `AIChat.Composer` (`onSubmit`, `onStop`, `value`/`defaultValue`/`onValueChange`, `placeholder`, `disabled`, `submitOnEnter`, `actions`) — + transcript parts: `AIChat.Message`, `.Response`, `.EmptyState`, `.Suggestions`/`.Suggestion`, `.Actions`/`.Action`, `.Reasoning`/`.ReasoningTrigger`/`.ReasoningContent`, `.ChainOfThought*`, `.Tool*`, `.Sources*`, `.History`.
|
|
816
|
+
**Composer:** Body is `Textarea` over one action row per `form/composer`'s layout. Enter submits (IME-safe), Shift+Enter newlines, and `status` from `useAIChat()` drives the busy/Stop state.
|
|
817
|
+
**Header:** a 48px strip (graphic + `text-sm font-semibold` title + right action slot) closed by a full-width rule; renders only when `title` or `actions` is set.
|
|
818
|
+
**Renders no chrome:** no border or background of its own — wrap in `Card`, `Sheet`, or a `Layout.Column`, with `overflow-hidden` so the header rule stays inside rounded corners.
|
|
819
|
+
**Used in patterns:** none yet — a component, not a catalogue pattern (platform-planning#1748).
|
|
820
|
+
|
|
821
|
+
---
|
|
822
|
+
|
|
795
823
|
## Auth & access
|
|
796
824
|
|
|
797
825
|
### `AuthProvider`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Design System
|
|
2
2
|
|
|
3
|
-
Authority for **visual-only** decisions — tokens, theme imports, breakpoints intent,
|
|
3
|
+
Authority for **visual-only** decisions — tokens, theme imports, breakpoints intent, and custom-component conformance. For **React component APIs** (imports, props, JSX composition), pair this file with `components.md`; that split avoids duplicating tables and lengthy examples across both docs.
|
|
4
4
|
|
|
5
5
|
`@tailor-platform/app-shell` ships an opinionated design system as CSS variables, bridged into Tailwind v4's token namespace. Use it whether you are consuming AppShell components (most cases) or building a custom component to fill a gap.
|
|
6
6
|
|
|
@@ -302,23 +302,30 @@ Stock Tailwind breakpoints — AppShell does not change them.
|
|
|
302
302
|
|
|
303
303
|
Two-column **behavior** (right rail stacks under `lg`): respect AppShell defaults — do not force side-by-side grids on narrow viewports. The `Layout` column width table lives in `components.md` → Layout; reuse those numbers instead of guessing rem values here.
|
|
304
304
|
|
|
305
|
-
##
|
|
305
|
+
## 4b. Links
|
|
306
306
|
|
|
307
|
-
|
|
307
|
+
One treatment for every text link — inside `DescriptionCard`, in table rows,
|
|
308
|
+
in cards:
|
|
308
309
|
|
|
309
|
-
|
|
310
|
+
- **`text-primary` at rest, `hover:underline` only.** Colour carries the affordance; a resting underline is noise. Pair with `underline-offset-4`.
|
|
311
|
+
- **Internal routes** use app-shell's `Link` (`to`), never a bare `<a href>`.
|
|
312
|
+
- **External destinations** use `<a target="_blank" rel="noopener noreferrer">` with a lucide `ExternalLink` at `size-3` after the label. There is no dedicated component for this; that markup is the convention.
|
|
313
|
+
- **Identifiers** (document numbers, SKUs) keep `font-mono` on top of the link style.
|
|
310
314
|
|
|
311
|
-
|
|
315
|
+
This is the treatment `DescriptionCard` already applies to its `type: "link"`
|
|
316
|
+
fields — match it rather than inventing a second one.
|
|
317
|
+
|
|
318
|
+
## 5. AppShell styling boundary
|
|
319
|
+
|
|
320
|
+
`astw:` is AppShell's internal compiled CSS prefix. **Never write it in an application.** It is not a consumer customization API and arbitrary prefixed utilities may not exist in the shipped CSS.
|
|
321
|
+
|
|
322
|
+
Use ordinary Tailwind utilities on your own markup. An explicitly documented layout hook, such as `Table.Root`'s `containerClassName`, also accepts ordinary utilities from the application's Tailwind build:
|
|
312
323
|
|
|
313
324
|
```tsx
|
|
314
|
-
<Table.Root containerClassName="
|
|
325
|
+
<Table.Root containerClassName="px-6 overflow-y-auto" />
|
|
315
326
|
```
|
|
316
327
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
- `astw:` only on AppShell `*ClassName` / root `className` hooks each component exposes. Use **plain** Tailwind (`flex`, `gap-4`, `bg-background`, …) on **your** markup.
|
|
320
|
-
- Stick to **layout** utilities (`flex`, `grid`, `max-h-*`, `min-h-0`, `overflow-*`, widths). Avoid painting over internal AppShell padding or colors via `astw:` — prefer an upstream prop or composition change.
|
|
321
|
-
- Steps like `astw:p-4` still resolve through the scale — never arbitrary `astw:p-[13px]`.
|
|
328
|
+
For an AppShell component's appearance, use its documented props, variants, or composition instead of styling over its internals.
|
|
322
329
|
|
|
323
330
|
## 6. When AppShell doesn't have a component you need
|
|
324
331
|
|
|
@@ -380,13 +387,13 @@ When a custom component proves reusable across 2+ apps, promote it upstream into
|
|
|
380
387
|
|
|
381
388
|
### Where to look
|
|
382
389
|
|
|
383
|
-
| Concern
|
|
384
|
-
|
|
|
385
|
-
| Component imports, props, JSX composition
|
|
386
|
-
| Page / screen layout patterns
|
|
387
|
-
| Design tokens,
|
|
390
|
+
| Concern | File |
|
|
391
|
+
| --------------------------------------------------------------- | ---------------------------------- |
|
|
392
|
+
| Component imports, props, JSX composition | **`components.md`** |
|
|
393
|
+
| Page / screen layout patterns | **`patterns/<type>/<slug>.md`** |
|
|
394
|
+
| Design tokens, AppShell styling boundary, custom UI conformance | **this file** (`design-system.md`) |
|
|
388
395
|
|
|
389
|
-
**`components.md`** holds long JSX compositions (e.g. **`DataTable`**, **card + `Table`**). **Do not copy those trees into this file** — link back here only for tokens and
|
|
396
|
+
**`components.md`** holds long JSX compositions (e.g. **`DataTable`**, **card + `Table`**). **Do not copy those trees into this file** — link back here only for tokens and the AppShell styling boundary.
|
|
390
397
|
|
|
391
398
|
`patterns/` is organised by page type: `patterns/list/`, `patterns/detail/`, `patterns/form/`, `patterns/interaction/`. Pick the slug matching the screen you're building.
|
|
392
399
|
|