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
16 changes: 13 additions & 3 deletions .agents/references/component-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,16 +431,26 @@ Review whether component styling remains self-contained and consistent:

Text that a component renders takes its size, line height, weight, and letter spacing from a typography role, not from a raw size + weight combination. The roles are listed in the Typography section of `docs/concepts/styling-theming.md`.

- Use the role utility (`astw:text-heading-md`, `astw:text-label-md`, ...) on the component's own element, or render `Text` when the element itself is the text.
- Do not write new raw combinations such as `astw:text-lg astw:font-semibold` or `astw:text-sm astw:font-medium`. Existing ones are being moved to roles.
- Render content text with `Text`: titles, descriptions, captions, hints, messages, error text, and key and value display. Pass a Base UI part, or an element that differs from the role's default tag, with `render`, and keep the part's `data-slot` on the render element.
- Use the role utility (`astw:text-heading-md`, `astw:text-label-md`, ...) for text that is part of a control or sits inside one (buttons, inputs, menu and list items, badges, toolbars, table cells), in cva strings, and on containers that set the size for their children.
- Do not write new raw combinations such as `astw:text-lg astw:font-semibold` or `astw:text-sm astw:font-medium`. `tests/typography-usage.test.ts` fails on a raw size.
- Combine a role only with the four modifiers: `astw:leading-none` (single-line text), `astw:tabular-nums`, `astw:font-semibold` (emphasis on inline text), and `astw:uppercase`. Do not add other `leading-*`, `tracking-*`, or `text-[Npx]` values. 12px is the minimum size.
- A role carries no color. Set the color with a token such as `astw:text-muted-foreground`.
- Show a selected item (active navigation or list item) with `astw:font-semibold` and its background.
- A heading's level comes from the document structure, not from its size. Keep a title that is a heading in a heading element, and pick the role for its look. Do not change the element to get a different size, and do not turn a heading into a `div` to get a different line height. Under the `cream` and `bloom` palettes, `h1` to `h6` ignore `leading-none`; see the Typography section.

```tsx
// Good
<h3 data-slot="card-title" className="astw:text-heading-md astw:leading-none">{title}</h3>
<Text
variant="heading-md"
className="astw:leading-none"
// eslint-disable-next-line jsx-a11y/heading-has-content
render={<h3 data-slot="card-title" />}
>
{title}
</Text>
<Text variant="body-sm" className="astw:text-muted-foreground">{updatedAt}</Text>
<button className={cn("astw:rounded-md astw:px-3 astw:text-label-md", className)}>{label}</button>
```

```tsx
Expand Down
16 changes: 16 additions & 0 deletions .changeset/component-typography-roles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@tailor-platform/app-shell": minor
---

AppShell components now use the typography roles. Titles, descriptions, captions, messages, error text, and key and value display render with `Text`; controls such as buttons, inputs, menu items, badges, and table cells use the role classes. Most text looks the same. These values change:

- Text below 12px moves to 12px: chat history group titles, tool status and section labels, tool JSON, command palette paths and descriptions, chain-of-thought search result badges, and `xs` and `sm` Avatar initials.
- Line heights outside the roles move to the role: chat messages (`body-md-relaxed`, 22.75 to 24px), attachment file names and upload text, the ActivityCard activity line, Appearance switcher options, and paragraphs in `Alert.Description`.
- Section labels use weight 500: the filter rail section legend, the ActivityCard day label, and `Menu.GroupLabel` (600 to 500).
- Key and value pairs use a 500 key and a 400 value: DescriptionCard values (500 to 400), and the AppInfo, DocumentProgressCard, and CsvImporter summary keys (400 to 500).
- The chat empty state title and the default sidebar app title use `heading-sm` (600).
- `Alert.Title` drops its letter spacing (-0.025em to 0).
- A selected item uses weight 600 (500 before): sidebar items, chat history, and the DataTable filter panel.
- Inline code and code blocks use `code-sm`, with an 18px line height.

Element types do not change. Elements rendered with `Text` and no `data-slot` of their own get `data-slot="text"`.
5 changes: 5 additions & 0 deletions .changeset/text-children.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tailor-platform/app-shell": patch
---

`Text` passes `children` only when you give them. A `render` element that supplies its own content now keeps it, for example `<Text variant="label-md" render={<Field.Error />} />` shows the validation message.
18 changes: 15 additions & 3 deletions decisions/typography-roles.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Decision: typography role tokens

> Status: **Decided for the first two changes — add 10 typography role tokens as CSS variables with Tailwind `text-*` utilities, then a `Text` component on top of them. AppShell's own components do not use the roles yet.**
> Scope: role names and values, how they reach Tailwind, the rules for combining them, and the `Text` component (see "Second change" at the end). This does not cover applying roles to AppShell components or sizes below 12px.
> Status: **Decided — add 10 typography role tokens as CSS variables with Tailwind `text-*` utilities, a `Text` component on top of them, and use them in AppShell's own components (see "Third change" at the end).**
> Scope: role names and values, how they reach Tailwind, the rules for combining them, and the `Text` component (see "Second change" at the end). "Third change" covers applying the roles to AppShell components and moving sizes below 12px to 12px.

## Context

Expand Down Expand Up @@ -81,7 +81,7 @@ Limit: this does not let a role replace a size that core sets on its own element

## Consequences and open questions

- **No visual change.** No component uses the roles yet. The default, cream, and bloom palettes render as before.
- **No visual change in the first change.** No component used the roles yet. The default, cream, and bloom palettes render as before.
- **cream and bloom `h1` to `h6` rule.** Both palettes set `line-height: 1.2` and `letter-spacing: -0.03em` on `html :where(h1, h2, h3, h4, h5, h6)` outside any `@layer`. That rule wins over a role utility on those elements, so a heading role on an `h1` to `h6` gets the palette values for line height and letter spacing. It also wins over any `leading-*` or `tracking-*` class on those elements, including the `leading-none` in the card title pairing: an `h3` with `text-heading-md leading-none` has a line height of 1.2 under cream and bloom, and 1.0 under the default palette. The same classes on a `div` or `span` render as documented. This PR keeps the rule and documents the effect. Open question for the maintainers: what does this rule protect, and should it stay once heading roles exist?
- **Keyboard hint.** The earlier pairing table listed "keyboard hint" with `font-mono text-xs`. The only keyboard hint in the code (the sidebar ⌘K hint) uses `text-xs font-normal`. The new table has no keyboard hint row.
- **Follow-up changes** (not in this PR): apply the roles to core components; `overline`; migrate 10px and 11px text to 12px; a `Text` component (added in a second change, see below); numeric DataTable columns with `tabular-nums` and right alignment as the default; a MetricCard value role and a display role above 24px.
Expand All @@ -98,3 +98,15 @@ Limit: this does not let a role replace a size that core sets on its own element
- **No `textVariants` export.** tailor-inc/platform-planning#1580 asked for a variants export. It is descoped, not deferred. The `text-<role>` utilities already apply the same role mappings, so a cva export would be a third way to do the same thing, and it would put `astw:`-prefixed classes into consumer code. Unlike `Button` and `Badge`, `Text` does not export its cva function.
- **`code-sm` includes `font-mono`.** The utility cannot set the font family, but the component can.
- **cream and bloom.** The `h1` to `h6` rule is unchanged. The default `h2` takes the palette's line height and letter spacing under these palettes, the same as existing headings in AppShell components today.

## Third change: roles in AppShell components

> Status: **Decided.**

- **Text or the role utility.** Content text renders with `Text`: titles, descriptions, captions, hints, messages, error text, and key and value display. Text that is part of a control, or sits inside one, keeps the role utility: buttons, inputs, menu, listbox, palette and tab items and their group labels, badges, counters, toolbars, pagination, and table cells. cva strings, shared class strings, and containers that set the size for their children also keep the utility. Radix Themes, Primer, and the Atlassian Design System split their components the same way. Polaris uses its `Text` almost everywhere; Mantine and Chakra do not use theirs inside components.
- **`render` and `data-slot`.** A Base UI part, or an element whose tag differs from the role's default, is passed with `render`, for example `<Text variant="heading-md" render={<BaseDialog.Title data-slot="dialog-title" {...props} />} />`. The `data-slot` stays on the render element, because its props win over the `data-slot="text"` that `Text` sets.
- **Lint.** Where `render` gets an empty `h1`, `h3`, `h4`, `a`, or `label`, the matching `jsx-a11y` rule is disabled on that line, as the `Text` documentation shows for apps.
- **`Text` passes children only when given.** A render part that supplies its own content keeps it. Base UI's `Field.Error` shows the validation message this way.
- **Value changes.** Text below 12px moves to 12px. Line heights outside the allowed modifiers move to the role's line height, or to a relaxed role for chat messages. The uppercase section labels, the ActivityCard day label, and the menu group label use `label-sm` at weight 500. `code-sm` adds 2px of line height. In key and value pairs, the key is `label-md` with a muted color and the value is `body-md`, the same as numbers in DataTable. An `h1` to `h6` element takes a heading role (chat empty state title, default sidebar app title). `Alert.Title` drops `tracking-tight`. A selected item (sidebar, chat history, DataTable filter panel) uses `font-semibold` with its background, as Primer and Polaris do.
- **Element types do not change.** Under the cream and bloom palettes, headings render as before. The open question about their `h1` to `h6` rule stays open.
- **Guard.** `tests/typography-usage.test.ts` fails on a raw Tailwind text size in a component, on a role class with an unknown name, and on `text-code-sm` without `font-mono`.
56 changes: 28 additions & 28 deletions docs-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"hashes": {
"typeSurface": "7cb8a34c1c36eb02",
"outline": "797e012e34847b80",
"snapshot": "e43bea5b03d30595",
"snapshot": "a21f0e323ea4774e",
"outputMd": "824d1aa36bb7f194",
"examples": null
}
Expand All @@ -82,7 +82,7 @@
"hashes": {
"typeSurface": "f28bf34ae1466912",
"outline": "6138eac23571e177",
"snapshot": "202f4fa7806204a0",
"snapshot": "d795320d53bd9ddb",
"outputMd": "db2c4388ca783a75",
"examples": "65d5fc73550e395a"
}
Expand Down Expand Up @@ -140,7 +140,7 @@
"hashes": {
"typeSurface": "0a9f0c3f82e57e02",
"outline": "66aaf22c5067aef1",
"snapshot": "4cc22ecbb5ea4434",
"snapshot": "a5bfea8fbe1224ea",
"outputMd": "35bebce540d6ac28",
"examples": null
}
Expand Down Expand Up @@ -174,7 +174,7 @@
"hashes": {
"typeSurface": "81040ce8342602bc",
"outline": "b8d3f69b4adc4f13",
"snapshot": "70d538794b811717",
"snapshot": "714e29c5156e1f91",
"outputMd": "bd6ae01bf39d1ad6",
"examples": "63e2a53825a527a4"
}
Expand All @@ -191,7 +191,7 @@
"hashes": {
"typeSurface": "d6ee5e809e0f0590",
"outline": "e368c9ff0db15bb3",
"snapshot": "b70ad798aec94bc9",
"snapshot": "ef0699939dd47de5",
"outputMd": "a874b74c176bcb47",
"examples": "009005eeac70961d"
}
Expand Down Expand Up @@ -225,7 +225,7 @@
"hashes": {
"typeSurface": "16df9927f8394548",
"outline": "200f12eb4bec959a",
"snapshot": "aa2095c057e2cbee",
"snapshot": "f57af2cc49c8b4d8",
"outputMd": "bce0e2fada1d1638",
"examples": "1ee01b1e27764cd2"
}
Expand All @@ -242,7 +242,7 @@
"hashes": {
"typeSurface": "6ce6f0fdf2450cec",
"outline": "a8c217856c9c367a",
"snapshot": "31ad44b3faa1da06",
"snapshot": "e6783ef54e17e11e",
"outputMd": "675dc665e5082ff0",
"examples": "b20054836086ced0"
}
Expand All @@ -259,7 +259,7 @@
"hashes": {
"typeSurface": "3886bf7012f24283",
"outline": "916645fe8e5baf10",
"snapshot": "2d057b30b9401e12",
"snapshot": "8d4d6148a3df5d61",
"outputMd": "14f7ba23f441df38",
"examples": "e8f09fba5acb116f"
}
Expand All @@ -276,7 +276,7 @@
"hashes": {
"typeSurface": "74ec02220321c626",
"outline": "d56abfe9595252ba",
"snapshot": "dfb2af263b78cd70",
"snapshot": "b2d280c2ba21f456",
"outputMd": "dfb775c7c4307409",
"examples": "4c96b71b68808c66"
}
Expand Down Expand Up @@ -615,7 +615,7 @@
"hashes": {
"typeSurface": "b1614970f950ceb6",
"outline": "bda40376d6e3e008",
"snapshot": "ac70f5a7004d3bab",
"snapshot": "81a29fe2495df98d",
"outputMd": "ec6a0ac098d1d87a",
"examples": "934c52d4e8761404"
}
Expand Down Expand Up @@ -649,7 +649,7 @@
"hashes": {
"typeSurface": "306ea2ad2a256b8d",
"outline": "c1448e17cc81a142",
"snapshot": "57f1bbb52b30d78b",
"snapshot": "b2cfca0400f29ba0",
"outputMd": "d0fb940f685f8869",
"examples": "6962c6f28d09eb0a"
}
Expand Down Expand Up @@ -683,7 +683,7 @@
"hashes": {
"typeSurface": "b795ce4e10be0653",
"outline": "d9e8930edd2a191c",
"snapshot": "89321bc807b1eaa1",
"snapshot": "ddebbf9718587cff",
"outputMd": "c6a48c4d8e8b0078",
"examples": null
}
Expand All @@ -700,7 +700,7 @@
"hashes": {
"typeSurface": "7b374d45d53d4535",
"outline": "79c9b0f08ca38027",
"snapshot": "88a0c82baaaa2a08",
"snapshot": "5d7a7904c5256f29",
"outputMd": "91d901b17aed7781",
"examples": null
}
Expand Down Expand Up @@ -938,7 +938,7 @@
"hashes": {
"typeSurface": "7c5b64c579f508ee",
"outline": "9b273bc9e509f1fe",
"snapshot": "e9b20c2459b52b79",
"snapshot": "9e448d8e65418e9b",
"outputMd": "a3a6c9679af99d37",
"examples": "fc036f7af21b9dd4"
}
Expand Down Expand Up @@ -1023,7 +1023,7 @@
"hashes": {
"typeSurface": "c7e8fec79020d657",
"outline": "02ed5081ea465edf",
"snapshot": "2510afae99b97375",
"snapshot": "03dedb3cc14b1eaf",
"outputMd": "66696139a46fabb0",
"examples": "2a4aa694c4d48324"
}
Expand Down Expand Up @@ -1057,7 +1057,7 @@
"hashes": {
"typeSurface": "943616def28cafb0",
"outline": "8f06df28129676c8",
"snapshot": "6233cd9de511210f",
"snapshot": "44c485a38e8d9eb4",
"outputMd": "ceb4a05822538d20",
"examples": "cff0508bb5520ae6"
}
Expand Down Expand Up @@ -1210,7 +1210,7 @@
"hashes": {
"typeSurface": "429710973dfa912b",
"outline": "721e8995f53f0e8f",
"snapshot": "68fd61885280b114",
"snapshot": "2ef5b7324018ff8f",
"outputMd": "ae287e2bb5abc1c6",
"examples": "1ce89cec30045466"
}
Expand All @@ -1227,7 +1227,7 @@
"hashes": {
"typeSurface": "e760949efcbe4c38",
"outline": "464d91acc48f7c9d",
"snapshot": "2c9114c5a81198be",
"snapshot": "429bdb8c86bb2ad6",
"outputMd": "1f81ebd562c50371",
"examples": "b0eebb8c5e2219dc"
}
Expand Down Expand Up @@ -1345,9 +1345,9 @@
"symbols": [],
"hashes": {
"typeSurface": null,
"outline": "f7ca9d0f8db238e9",
"outline": "c76f789e50713de5",
"snapshot": null,
"outputMd": "5c55865c499890a8",
"outputMd": "bea2c2be5e2ba53c",
"examples": "4d19f4f688e0f0ac"
}
},
Expand All @@ -1363,7 +1363,7 @@
"hashes": {
"typeSurface": "bc60676f880b485b",
"outline": "9381eff2f7987a0e",
"snapshot": "54e27fa3789afbd2",
"snapshot": "18b98aa5db32996b",
"outputMd": "f85ec64ceb7f4acf",
"examples": "2b9d94065f2f77b7"
}
Expand All @@ -1380,7 +1380,7 @@
"hashes": {
"typeSurface": "f6795191b952e110",
"outline": "b73ba87ed529e09d",
"snapshot": "40acc0898ecb5e4f",
"snapshot": "655d96c1394a3ace",
"outputMd": "30bb32e57fc78bd9",
"examples": "b07f258088e81cac"
}
Expand All @@ -1396,9 +1396,9 @@
"symbols": ["Text", "TextProps"],
"hashes": {
"typeSurface": "ed54a16ca677dbe3",
"outline": "81e7ce4d7d73ba60",
"outline": "114c81c557b82a18",
"snapshot": "cb5f569f7dd2ecfc",
"outputMd": "0e872fb19aff887a",
"outputMd": "5b6cf767868d6558",
"examples": "4a832708dc79474b"
}
},
Expand Down Expand Up @@ -1465,7 +1465,7 @@
"hashes": {
"typeSurface": "2d88f0306efcaa77",
"outline": "1257a84ccd91d5c3",
"snapshot": "c85c431dcd58bc43",
"snapshot": "56d61f993125508c",
"outputMd": "91d70b140c7bb747",
"examples": null
}
Expand All @@ -1482,7 +1482,7 @@
"hashes": {
"typeSurface": "d652b740db9471c7",
"outline": "eea5aed5cf4c8f33",
"snapshot": "13ed55385962bf3a",
"snapshot": "22f8d6be85ad8f80",
"outputMd": "113c95b2995917ed",
"examples": "87b93974e65d3a07"
}
Expand Down Expand Up @@ -1734,7 +1734,7 @@
"packages/core/skills/app-shell-patterns/references/concepts/modules-and-resources.md": "bf15b5460f22d165",
"packages/core/skills/app-shell-patterns/references/concepts/routing-navigation.md": "8034c7f885994b29",
"packages/core/skills/app-shell-patterns/references/concepts/sidebar-navigation.md": "3670e37a3c78abe7",
"packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "5c55865c499890a8",
"packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "bea2c2be5e2ba53c",
"packages/core/skills/app-shell-patterns/references/components/action-panel.md": "71794d4b561e4d4b",
"packages/core/skills/app-shell-patterns/references/components/activity-card.md": "4f386e49380fb0b0",
"packages/core/skills/app-shell-patterns/references/components/ai-chat.md": "824d1aa36bb7f194",
Expand Down Expand Up @@ -1775,7 +1775,7 @@
"packages/core/skills/app-shell-patterns/references/components/spinner.md": "e05d94ba9c3d17da",
"packages/core/skills/app-shell-patterns/references/components/table.md": "f85ec64ceb7f4acf",
"packages/core/skills/app-shell-patterns/references/components/tabs.md": "30bb32e57fc78bd9",
"packages/core/skills/app-shell-patterns/references/components/text.md": "0e872fb19aff887a",
"packages/core/skills/app-shell-patterns/references/components/text.md": "5b6cf767868d6558",
"packages/core/skills/app-shell-patterns/references/components/textarea.md": "c940bc01cd573334",
"packages/core/skills/app-shell-patterns/references/components/timeline.md": "699249c9452485ab",
"packages/core/skills/app-shell-patterns/references/components/toolbar.md": "91d70b140c7bb747",
Expand Down
Loading
Loading