Skip to content

feat(apollo-wind): compact density for the chat primitives - #1403

Open
BenGSchulz wants to merge 1 commit into
feat/apollo-wind-chat-primitive-gapsfrom
feat/apollo-wind-chat-compact-density
Open

BenGSchulz wants to merge 1 commit into
feat/apollo-wind-chat-primitive-gapsfrom
feat/apollo-wind-chat-compact-density

Conversation

@BenGSchulz

@BenGSchulz BenGSchulz commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

MST-16573, part of the Wind chat epic MST-16172. Stacked on #1402 (chat primitive gaps), which is stacked on #1400 (math fences). This is the last PR of the apollo-wind stack.

Material's spacing.compactMode shrinks the chat's type and spacing. The Wind chat marks compact mode with data-density="compact" on its root. The apollo-wind chat primitives now follow that attribute on any ancestor, with no prop plumbing.

Mechanism and contract

  • Tailwind's in-data-[density=compact]: variant, which compiles to :where(*[data-density="compact"]) & (checked with Tailwind 4.1.17, the build's version, and 4.3.0, the test runner's).
  • A host className on the root wins in both densities. Root-level sizes read CSS variables whose fallbacks are today's values, for example gap-(--markdown-gap,0.75rem) text-(length:--markdown-font-size,0.875rem) leading-(--markdown-leading,1.625), and the compact variant only sets the variables (in-data-[density=compact]:[--markdown-gap:0.5rem]). A host class such as gap-1 replaces the variable-reading class through cn() (tailwind-merge groups them; tested), so the compact value has nothing to change. The Wind chat's MessageContent className="gap-0" and Markdown className="gap-1 text-xs leading-4" keep working.
  • Default computed styles are unchanged (checked in Storybook: Markdown 14px on 22.75px with a 12px gap, code 13px on 20.8px, bubble 14px on 22.75px, message content gap 10px).
  • Nested elements a className cannot reach (Markdown headings and lists, the thinking icon, the avatar offsets) use the variant directly.
  • ChatComposer and EmptyState: without a density prop or size they are compact under any compact ancestor, like the CSS variant. They share an internal hook (src/lib/use-ancestor-density.ts, not exported) that finds the nearest compact ancestor after each render and watches only that ancestor's attribute; an ancestor that gains the attribute later is picked up on the next render. The prop or size wins.
  • ThinkingIndicator, Citation and ToolCall take their sm look in compact only when no size is given.

Why the composer and EmptyState use JS when everything else uses classes: density there changes props or markup, not just classes. The composer switches the textarea's rows/minRows (1 or 2; the autosizing Textarea writes its height inline from them), the attach and submit Button size (3xs or 2xs), the attachment chip size (xs or sm), and it exposes useChatComposer().density to host parts; EmptyState swaps its heading for a paragraph. A pure CSS version would repeat Button's and Attachment's size scales as compact overrides (about ten class strings that would drift), make the shared Textarea read its minimum rows from CSS, and leave the context value stale for host parts, so the roughly 30-line hook stays.

Material to Wind mapping

Element Material default to compact Wind default Wind compact
Markdown p, li, th, td, em, strong, link fontSizeM 14/20 to fontSizeS 12/16 14px on 22.75px 12px on 18px
h1 H3Bold 24/32 to H4Bold 20/24 text-xl text-lg
h2 H3Bold to H4Bold text-lg text-base
h3 H3Bold to H4Bold text-base text-sm
h4, h5, h6 LBold 16/24 to MBold 14/20 text-sm text-xs
Markdown block, list, quote spacing unchanged (24px) gap 12px, lists 4px, quotes 8px, paragraphs in li 8px gap 8px, lists 2px, quotes 6px, paragraphs in li 6px (Wind's choice)
Code block source 14px to 12px, rendered on an 18px line 13px on 20.8px 12px on 18px
Code block header unchanged 36px row, 28px buttons, 16px icons 28px row, 24px buttons (host actions too), 14px icons (Wind's choice)
Message text (primaryFontToken) M 14 to S 12 14px on 20px 12px on 16px
BubbleContent text M to S 14px on 22.75px 12px on 18px
BubbleContent padding unchanged 12px by 8px 10px by 6px (Wind's choice)
Gap between messages in a group (messageSpacing) 32 to 8 MessageGroup 8px 4px
Gap between a message's parts (messageGroupGap) 16 to 8 MessageContent 10px 5px, avatar offsets follow
Citation M to Xs (10px) 11px pill the sm pill (10px), no size given
Loading text (ThinkingIndicator) M to S 14px, 16px icon 12px, 14px icon, no size given
Source items M to S; icon 24 to 18 14px; 16px icon 12px; icon unchanged (already below Material's compact 18px)
ChatComposer compact rows and paddings density prop follows the ancestor; prop wins
EmptyState (history empty text) size="default", heading title compact look with a paragraph title under a compact ancestor when no size is given
MessageActions controls unchanged 24px, 16px icons 20px, 14px icons (Wind's choice)
ToolCall, no size unchanged p 12px, 14px/20px text, 20px status icon, body indent 12px, body gap 8px sm look: p 8px, 12px/16px text, 16px icon, indent 8px, body gap 4px (Wind's choice)
ToolCall toggle rows, DisclosureTree rows unchanged 4px padding, 24px row 2px vertical padding, 20px row (Wind's choice)
Attachment media / padding / content py (default, sm, xs) unchanged 40/8/8px, 32/6/6px, 28/4/4px; xs chip 38px 32/6/6px, 28/4/4px, 24/2/2px; xs chip 30px, type unchanged (Wind's choice)

Ticket claims corrected

The brief asked for a shorter code block header and smaller copy button, reduced bubble padding, smaller MessageActions controls, and possibly smaller ToolCall, DisclosureTree and Attachment gaps. Material's compact mode does none of these:

  • Material's code block header and its buttons do not read compactMode; only the code font changes.
  • Material's message box padding does not depend on compactMode; only the type and the two message gaps change.
  • MessageActions defaults to 3xs, not md, and Material's action bar ignores compact mode.
  • Material's tool call, tree and attachment chips do not read compactMode.

So none of these is Material parity. At review they were adopted as Wind-only choices (Wind leads here; Material may follow), listed below.

Wind-only choices

  • Compact body leading is 18px, not Material's 16px: Wind's comfortable leading is already looser than Material's (22.75 against 20), and 18px keeps that ratio.
  • Markdown block, list and quote spacing tightens in compact; Material keeps its spacing.
  • Code blocks inside a ToolCall follow the compact ancestor like any CodeBlock, which Material's do not; documented in ToolCall's JSDoc (a compact debug panel gains from it).
  • Code block header 36px to 28px with 24px buttons (the header's own actions included), bubble padding 12px by 8px to 10px by 6px, MessageActions controls 24px to 20px, an unsized ToolCall takes its sm look with a 4px body gap, ToolCall and DisclosureTree rows lose 2px of vertical padding, and Attachment chips step padding and media down one size (an xs chip 38px to 30px) while their type stays legible. Root-level properties follow the variable contract, so a host className still wins; Attachment's padding classes were already has-* variants, so a host override needs the same variant, as before.

What the Wind chat needs to do

Nothing for compact beyond the data-density attribute it already sets on its root. It may drop the density prop it passes to the composer, render its empty history message with EmptyState (no size needed), and drop its own ! size overrides on attachment chips, since an xs chip is now 30px in compact.

Stories

  • Chat/Patterns Embedded has a compact arg (off by default, so the default canvas is unchanged) that sets data-density="compact" on the panel and lets the footer composer and the empty state take their density from the panel.
  • New Markdown/CompactDensity (default and compact side by side, with a wide table and a long code line) and CodeBlock/CompactDensity.

Tests

  • Markdown and CodeBlock render inside a compact ancestor and compile their classes with Tailwind: the base classes read the variables with the comfortable fallbacks, and the compact variant sets them under :where(*[data-density="compact"]).
  • A host gap-1 text-xs leading-4 on Markdown and gap-0 on MessageContent replace the variable-reading classes inside a compact ancestor.
  • tailwind-merge groups gap-(--x,…), text-(length:--x,…), leading-(--x,…) and h-(--x,…) with their plain counterparts.
  • Message, Bubble, Citation and ThinkingIndicator compact classes inside a compact ancestor, with an explicit size opting out; the avatar's compact offsets order after the comfortable ones.
  • ChatComposer: compact from an ancestor, followed when the attribute changes, picked up on rerender when an ancestor gains it, compact even with a nearer comfortable, and the prop wins.
  • EmptyState: "turns compact under a compact ancestor unless a size is set".
  • Wind-only set: "shortens the header and its buttons in a compact ancestor" (CodeBlock), bubble padding variables (Message density test), "shrinks its controls to 20px with 14px icons in a compact ancestor" (MessageActions), "takes the sm look in a compact ancestor unless a size is set, with a tighter body" and "lets a host className on the root win in compact too" (ToolCall), "tightens its rows in a compact ancestor" (DisclosureTree), "steps padding and media down one size in a compact ancestor" (Attachment, with the compact rule ordered after the base one).
  • Storybook at 360px: Embedded compact (bubble padding 6px by 10px, sm chip 38px, compact empty state with a paragraph title) and Markdown/CompactDensity do not overflow (tables and code scroll inside their blocks), axe clean; CodeBlock/CompactDensity header 28px plus border, 24px buttons; also checked in dark, light-hc, dark-hc and future-light (the legacy dark contrast and Prism token contrast findings exist without compact too).
  • Biome on touched files, tsc --noEmit, the full apollo-wind suite and pnpm --filter @uipath/apollo-wind build.

🤖 Generated with Claude Code

@github-actions
github-actions Bot temporarily deployed to preview/apollo-design October 17, 2026 02:21 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-landing October 17, 2026 02:21 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-docs October 17, 2026 02:21 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-vertex October 17, 2026 02:21 Destroyed
@github-actions

Copy link
Copy Markdown
Contributor

Dependency License Review

  • ✅ 1947 package(s) scanned
  • ✅ No license issues found
  • ⚠️ 2 package(s) excluded (see details below)
License distribution
License Packages
MIT 1713
ISC 91
Apache-2.0 56
BSD-3-Clause 28
BSD-2-Clause 23
BlueOak-1.0.0 8
MPL-2.0 4
MIT-0 3
CC0-1.0 3
MIT OR Apache-2.0 2
(MIT OR Apache-2.0) 2
Unlicense 2
LGPL-3.0-or-later 1
Python-2.0 1
CC-BY-4.0 1
(MPL-2.0 OR Apache-2.0) 1
Unknown 1
Artistic-2.0 1
(WTFPL OR MIT) 1
(BSD-2-Clause OR MIT OR Apache-2.0) 1
CC-BY-3.0 1
0BSD 1
(MIT OR CC0-1.0) 1
MIT AND ISC 1
Excluded packages
Package Version License Reason
@img/sharp-libvips-linux-x64 1.3.4 LGPL-3.0-or-later LGPL pre-built binary, not linked
khroma 2.1.0 Unknown MIT per GitHub repo, missing license field in package.json

@BenGSchulz
BenGSchulz added this pull request to stack #1414 October 19, 2026 17:13
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

📊 Coverage + size by package

Per-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.

Package Coverage New-line coverage Packed (gzip) Unpacked vs main
@uipath/apollo-core — — 42.02 MB 50.17 MB ±0
@uipath/apollo-react 51.7% — 8.01 MB 31.63 MB +4.0 KB
@uipath/apollo-ui-icons — — 2.86 MB 6.97 MB ±0
@uipath/apollo-vertex — — — — —
@uipath/apollo-wind 79.5% 92.7% (38/41) 805.9 KB 4.66 MB +18.4 KB
@uipath/ap-chat 85.8% — 40.37 MB 45.58 MB ±0

"Coverage" is each package's own coverage.include scope (e.g. apollo-core instruments only scripts/). "Packed"/"Unpacked" come from npm pack --dry-run and only cover built packages — "—" means not measured this run (package not affected / not built). "vs main" is the packed (gzipped) delta against the last successful main build (the package-sizes artifact from the Release workflow); "—" there means no main baseline was available this run. The baseline is main's latest build, not this PR's exact merge-base, so it includes any drift since the branch diverged. Packages with no vitest config are omitted.

@BenGSchulz
BenGSchulz force-pushed the feat/apollo-wind-chat-compact-density branch from 018ee7c to 4cccb1d Compare October 19, 2026 19:03
Copilot AI balanced review requested due to automatic review settings October 19, 2026 19:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions
github-actions Bot temporarily deployed to preview/apollo-vertex October 19, 2026 19:03 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-landing October 19, 2026 19:03 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-design October 19, 2026 19:03 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-docs October 19, 2026 19:04 Destroyed
@github-actions github-actions Bot added the size:XL 500-999 changed lines. label Oct 19, 2026
Copilot AI balanced review requested due to automatic review settings October 19, 2026 19:19
@BenGSchulz
BenGSchulz force-pushed the feat/apollo-wind-chat-compact-density branch from 4cccb1d to 46f43a9 Compare October 19, 2026 19:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions
github-actions Bot temporarily deployed to preview/apollo-vertex October 19, 2026 19:19 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-landing October 19, 2026 19:19 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-docs October 19, 2026 19:19 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-design October 19, 2026 19:19 Destroyed
Copilot AI balanced review requested due to automatic review settings October 19, 2026 19:49
@BenGSchulz
BenGSchulz force-pushed the feat/apollo-wind-chat-compact-density branch from 46f43a9 to 3cc5745 Compare October 19, 2026 19:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions
github-actions Bot temporarily deployed to preview/apollo-vertex October 19, 2026 19:49 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-landing October 19, 2026 19:49 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-docs October 19, 2026 19:49 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-design October 19, 2026 19:49 Destroyed
Material's spacing.compactMode shrinks the chat's type and spacing, and
the Wind chat marks compact mode with data-density="compact" on its
root. The chat primitives now follow that attribute on any ancestor,
with no prop plumbing.

Root-level sizes read CSS variables whose fallbacks are today's values
(gap-(--markdown-gap,0.75rem) and so on), and an in-data-[density=compact]
variant sets the variables. A host className on the root replaces the
variable-reading class, so it wins in both densities, and the default
computed styles are unchanged. Nested elements (headings, lists, the
thinking icon, avatar offsets) use the variant directly.

- Markdown: body, lists and tables 14px to 12px, each heading one step
  down. Wind's own choices: an 18px line (Material uses 16px; this keeps
  the comfortable ratio) and tighter block, list and quote spacing
  (Material keeps 24px).
- CodeBlock: source 13px on 20.8px to 12px on 18px, as Material's
  compact code renders, through two variables since the Prism style is
  inline. Code blocks in a ToolCall shrink too, which Material's do
  not.
- Message and Bubble: text 14px to 12px, gaps between a group's messages
  and a message's parts halved, avatar offsets adjusted.
- ThinkingIndicator and Citation: the sm look when no size is given.
- Sources: items 14px to 12px.
- ChatComposer and EmptyState: compact under any compact ancestor when
  no density prop or size is given; the prop wins. They change props or
  markup (textarea rows, button and chip sizes, the heading element),
  which classes cannot, so a shared internal hook finds the ancestor and
  watches only its attribute.

Wind-only compact choices, where Material keeps its sizes: the code block
header shortens to 28px with 24px buttons, bubble padding tightens to
10px by 6px, message actions drop to 20px, an unsized ToolCall takes the
sm look with a 4px body gap, ToolCall and DisclosureTree rows lose 2px
of vertical padding, and attachment chips step their padding and media
down one size (an xs chip goes from 38px to 30px).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BenGSchulz
BenGSchulz force-pushed the feat/apollo-wind-chat-compact-density branch from 3cc5745 to d1d14ba Compare October 19, 2026 21:11
Copilot AI balanced review requested due to automatic review settings October 19, 2026 21:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions
github-actions Bot temporarily deployed to preview/apollo-landing October 19, 2026 21:12 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-design October 19, 2026 21:12 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-docs October 19, 2026 21:12 Destroyed
@github-actions
github-actions Bot temporarily deployed to preview/apollo-vertex October 19, 2026 21:12 Destroyed
@BenGSchulz
BenGSchulz marked this pull request as ready for review October 19, 2026 21:49
@github-actions
github-actions Bot had a problem deploying to preview/apollo-landing October 19, 2026 22:06 Failure

This branch had an error being deployed

2 failed and 2 active deployments
preview/apollo-vertex — d1d14ba8 Deployed Oct 19, 2026 by github-actions[bot]
preview/apollo-design — d1d14ba8 Deployed Oct 19, 2026 by github-actions[bot]
preview/apollo-docs — d1d14ba8 Deployed Oct 19, 2026 by github-actions[bot]
preview/apollo-landing — d1d14ba8 Deployed Oct 19, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg:apollo-wind size:XL 500-999 changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants