Second round of fixes (designer feedback) (WEB-377)
Pre-release ยท Type: ๐ Bug Fix
WEB-377 second round of fixes, based on new feedback from designer Vincent's screenshots.
WEB-610: Dialog description not left-aligned + button border ghostingโ
Status: Fixed
Problemโ
- The description text of the "no close button" / controlled examples was indented one level more than the title (visually not left-aligned).
- The buttons inside the Dialog ("Confirm" / "Got it") showed a border and shadow, inconsistent with Buttons elsewhere on the docs site.
Root Causeโ
dialog.mdxwrote<div style={{ padding: '0 24px' }}>...</div>oncontent, but the Dialog design layer already wraps apx-(--Spacing_24); stacked together this becomes a 48px inset.- The Dialog mounts to
<body>via Radix Portal, escaping the reset scope of design-site's[class*='playgroundPreview']. design-site deliberately does not include the Tailwind preflight, so the user-agent default<button>border/shadow leaks out from the portal.
Changed Filesโ
src/components/Button/styles.ts, src/components/Dialog/Dialog.tsx, packages/design-site/docs/components/patterns/dialog.mdx, packages/design-site/i18n/zh-CN/.../dialog.mdx
Changesโ
BUTTON_BASE_CLASSaddsborder-0andshadow-none, making the Button self-contained for the user-agent reset and no longer dependent on the design-site global reset; theborder border-solid border-(...)explicitly declared by thesecondary/destructive-outlinevariants naturally overrides via tailwind-merge.- The 4 redundant
content={<div style={{ padding: '0 24px' }}>...</div>}instances in dialog.mdx are changed to strings / simple content, avoiding stacking with the Dialog's internalpx-(--Spacing_24). - Dialog adds
cancelText/okText/onCancel/onOkconfig options: the default-rendered cancel/confirm buttons are wrapped with an internal<DialogClose asChild>, so the Dialog closes automatically on click. This only takes effect when nofooteris passed; if afooteris passed, the close logic is implemented by the caller.DialogCloseis not exported externally โ callers should use the "config" path. - The dialog.mdx demo switches to using
cancelText/okText, and adds a "custom footer" example to explain the close semantics in footer mode.
WEB-611: DropdownMenu separator flush to both endsโ
Status: Fixed
Problemโ
The DropdownMenu separator currently renders flush to the left and right of the Content; the design spec requires a 16px inset at both ends, displayed centered.
Changed Filesโ
src/components/DropdownMenu/styles.ts
Changesโ
DROPDOWN_SEPARATOR_TOKEN_CLASSaddsmx-(--Spacing_16), insetting the separator 16px on each side to align with the left and right edges of the item text.
WEB-612: Toast icon visually too large + not aligned with textโ
Status: Fixed
Root Causeโ
(dual problem)
- The icon in Toast is visually too large: the
StatusIconSVG's ownviewBox = '0 0 13.333 13.333', but IconSvg's default width/height = 20, which stretches the 13.333 path to render at 20ร20 โ about 50% denser visually than the Figma vector. But the Icons render fine in other components, and Icons itself cannot be changed โ so the SVG render size can only be limited within Toast. - Wrong vertical alignment strategy: previously
items-start!+mt-pxwas used to force the icon close to the top, inconsistent with the Message mode of Figma'scounterAxisAlignItems: CENTER(i.e. items-center).
Figma measurements (319:30904 Alert component set):
| Mode | Outer frame | Icon area | Title | Description |
|---|---|---|---|---|
| Message (Description=False) | 420ร44, py=8, items-center | icon 20ร20 container (vector 13.333) | 22px line-height | โ |
| Alert (Description=True) | 420ร70, py=8, items-start | Icon Warp 20ร28 (py=4) โ embedded 20ร20 icon | H3 28px | Body 22px |
Changed Filesโ
src/components/Toast/styles.ts
Changesโ
- status icon: the data-icon container is 20ร20 with
p-[3px]!(corresponding to the Figma vector's 3.33 padding on each side), and the inner SVG usessize-full!to fit the remaining 14ร14 area โ the Figma vector 13.33.src/Icons/index.tsxis untouched โ the Icons are correct in other components. - The Toast outer
items-start!โitems-center!(the default Message mode is FigmacounterAxisAlignItems: CENTER). - The Alert mode (
:has([data-description])) is switched separately toitems-start!, and the icon getsmy-1(4px top and bottom, aligning with the py=4 of Figma's Icon Warp). - Close button: keeps the Sonner SVG default 12px; the padding is changed from 4px to
p-(--Spacing_8)!(8px), so the total button size is 12 + 8*2 = 28ร28, consistent with Figma; the X is visually 12px, slightly larger than the Figma vector 8.96, but the visual padding is controlled via padding, avoiding introducing flex centering / svg size hard constraints. - action button: wraps the Sonner
toastfunction, automatically converting the config-styleaction: { label, onClick }into a<Button variant="quaternary" size="sm">. Callers continue to use the Sonner config API and don't need to write className or manually insert a Button. Figma measured node 21220:3476: 76ร32, transparent bg + Labels-Primary text, corresponding to Quaternary Small. - Distinguish Message + Action single-line / Alert + Action two-line (Figma measurements):
- Message + Action (21220:3470, 420ร60): single line
icon โ content โ action โ close - Alert + Action (21220:3504, 420ร114): two lines โ top line
icon โ content โ close, bottom line the action wrapper occupies its own line aligned right - Sonner defaults to horizontal nowrap, cramming all elements into one line + the close priority issue needs dedicated handling
- Message + Action (21220:3470, 420ร60): single line
- Also merge action + cancel: Sonner renders
actionandcancelas two separate toast child elements; previously only action was transformed, while cancel still went through Sonner's default button rendering (data-button data-cancel), so two Buttons appeared in the DOM. Fix: transformOptions now recognizes both action's and cancel's{ label, onClick }config and merges them into a single<div data-toast-action className="flex justify-end gap-(--Spacing_12)">(cancel first / action second, corresponding to Figma Action 2 / Action 1), and sets the original cancel field to undefined to prevent Sonner from re-rendering it. Note: the wrapper does not writew-full, otherwise in the Message + Action single-line scenario it would grab all the width, shrinking content to 0px and stacking text vertically. - dismiss closure: the default cancel behavior is to close the toast (dismiss on click). Sonner's default cancel button dismisses automatically, but after switching to a ReactNode we lose this ability.
wrapToastFnuses a closure to lazily read the toast id (the id returned bysonnerToast.success(...)is only determined after the call), andsonnerToast.dismiss(toastId)inside onClick closes it correctly. - flex order controlling the dual-shape layout (core idea):
- close button base:
static! order-99!(defaults to the end) +self-start! - action wrapper base:
order-3!(after icon/content, before close) - data-content base:
flex-1! min-w-0!(lets long text shrink, keeps close from being pushed to the next line) - only when description + action both exist (
:has([data-description]):has([data-toast-action])), rewrite to:- the toast switches to
flex-wrap!+gap-y-(--Spacing_12)! - close button
order-1!(pulled before action) โ first lineicon, content, closelined up - action wrapper
basis-full!โ occupies the second line alone
- the toast switches to
- this way Message + Action single-line, Alert + Action two-line, and Alert without action single-line (items-start makes the icon top-aligned) โ all three shapes share the same set of base classes.
- close button base:
WEB-725: Button missing click/pressed state (no code change needed)โ
Status: Answered, no change needed
Problemโ
The designer did not see a pressed state shown on the docs site.
Notesโ
The Click state (:active) only takes effect at the instant the user actually presses the mouse, and cannot be shown statically. This was explained to the designer in a Linear comment; no code change needed.
WEB-725 supplement: compare all Button variant states against Figmaโ
Status: Fixed
Compared the Default / Hover / Clicked states of all variants (primary / secondary / tertiary / quaternary / destructive / destructive-outline / link-color / link-gray) one by one against the Figma Medium nodes, listing the differences and fixing them.
Figma measurement results:
| Variant | Default | Hover | Clicked |
|---|---|---|---|
| primary | bg #000 | bg #333 | bg #333 + 1px inside stroke #000 |
| secondary | bg #fff + stroke Gray-4 | bg Gray-1 + stroke Gray-4 | bg Gray-2 + stroke Gray-4 |
| tertiary | transparent | bg Gray-1 | bg Gray-2 |
| quaternary | transparent | bg #000 op=0.04 (Tinted-Default) | bg #000 op=0.08 (Tinted-Emphasized) |
| destructive | bg Error | bg Error + #fefefe op=0.1 overlay | bg Error + #000 op=0.1 overlay |
| destructive-outline | stroke Error | stroke Error + Background Error op=0.1 | stroke Error + Background Error op=0.2 |
| link-color | text Link | text Link + full-character UNDERLINE | text Link + full-character UNDERLINE |
| link-gray | text Tertiary | text Primary + full-character UNDERLINE | text Primary + full-character UNDERLINE |
Difference fixes:
- primary Clicked missing inside stroke #000 โ added
active:ring-1 active:ring-inset active:ring-(--Labels-Primary), simulating the Figma 1px inside stroke (using ring inset does not affect box size, and does not conflict with the baseshadow-noneโ ring uses the--tw-ring-shadowvariable). - destructive-outline Clicked wrong opacity โ
bg-(--Labels-Error)/10โbg-(--Labels-Error)/20(Figma Background opacity 0.2).
Unchanged but verified consistent:
- secondary: bg-Gray-1 hover, bg-Gray-2 active consistent with Figma tokens.
- tertiary: bg-Gray-1 hover, bg-Gray-2 active consistent.
- quaternary: Grays-Tinted-Default hover, Grays-Tinted-Emphasized active consistent.
- destructive: the current
color-mix(in_srgb, var(--Labels-Error) 90%, white/black)is mathematically equivalent to Figma's 90% Error + 10% #fefefe/#000 fill overlay (under sRGB linear mixing, #fefefe and #ffffff differ by < 1 RGB unit, imperceptible). Keep the current implementation. - link-color / link-gray: Figma hover/clicked add UNDERLINE to the whole word via
styleOverrideTable+characterStyleOverrides; the currenthover:underline/active:underlineimplementation is consistent.
Changed Filesโ
src/components/Button/styles.ts
WEB-742: Popover spacing tightened furtherโ
Status: Fixed
Problemโ
The designer measured the current Popover-to-trigger spacing at 8px (including the arrow render area) and requested tightening it to a 4px visual effect.
Changed Filesโ
src/components/Popover/styles.ts, src/components/Popover/Popover.tsx
Changesโ
POPOVER_SIDE_OFFSET = 0is the same as the Radix default โ remove the constant, remove thesideOffset = POPOVER_SIDE_OFFSETprop default, and just use the Radix default.- Combined with
Arrow height=12rendering, the visual spacing is closer to the Figma 4px.
WEB-743: Tooltip spacing tightened further + arrow align padding failure fixโ
Status: Fixed
Problemโ
Problem 1: The designer reported that even after the first round changed it to 4px, it still looked too large, and requested that if it is already 4px, change it to 0px to bring the Tooltip closer to the trigger.
Problem 2: With align=start/end, the arrow should be near the corresponding edge (top:12px / bottom:12px / left:12px / right:12px), but it actually renders the arrow at the trigger centerline (Radix default behavior); the CSS did not take effect.
Root Causeโ
TOOLTIP_CONTENT_CLASS uses [&[data-side=...][data-align=...]>span:last-child] to select the arrow wrapper. In the actual Radix DOM, the order of tooltip children is: text โ arrow span (with svg) โ role="tooltip" sr-only span, so span:last-child selected the trailing sr-only span, applying the CSS to a hidden element, and the arrow was unaffected.
Changed Filesโ
src/components/Tooltip/styles.ts
Changesโ
- All selectors
>span:last-childโ>span:has(>svg), precisely targeting the arrow wrapper by the svg child element (none of the other spans contain svg). - (Optional) remove
TOOLTIP_SIDE_OFFSET = 0: same as the Radix default โ remove the constant and prop default, and rely on the Radix default.
WEB-745: Menu icon color too darkโ
Status: Fixed
Problemโ
The designer noted that the chevron and group-action + icon colors in the Menu should be Tertiary, while the current ones use Placeholder (too light).
Changed Filesโ
src/components/Menu/styles.ts
Changesโ
MENU_ITEM_ICON_CLASS:text-(--Labels-Placeholder)โtext-(--Labels-Tertiary)MENU_GROUP_ARROW_CLASS:text-(--Labels-Placeholder)โtext-(--Labels-Tertiary)MENU_GROUP_ACTION_CLASS:text-(--Labels-Placeholder)โtext-(--Labels-Tertiary)
WEB-746: Table column alignment not workingโ
Status: Fixed
Problemโ
The header of the Price column set to align: 'right' was still left-aligned; the body td works via the inline textAlign, but the header <TableColumnHeader> is a flex container (flex w-full items-center), defaulting to justify-start, and does not respond to the textAlign on the td.
Changed Filesโ
src/components/Table/table-column-header.tsx, src/components/Table/table-data-mode.tsx
Changesโ
TableColumnHeaderPropsaddsalign?: 'left' | 'center' | 'right'.- Internally maps align to
justify-start / justify-center / justify-endand merges it intoTABLE_HEADER_CELL_INNER_CLASS. table-data-mode.tsxpassescol.alignthrough when renderingTableColumnHeader.