Skip to main content

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โ€‹

  1. The description text of the "no close button" / controlled examples was indented one level more than the title (visually not left-aligned).
  2. The buttons inside the Dialog ("Confirm" / "Got it") showed a border and shadow, inconsistent with Buttons elsewhere on the docs site.

Root Causeโ€‹

  1. dialog.mdx wrote <div style={{ padding: '0 24px' }}>...</div> on content, but the Dialog design layer already wraps a px-(--Spacing_24); stacked together this becomes a 48px inset.
  2. 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โ€‹

  1. BUTTON_BASE_CLASS adds border-0 and shadow-none, making the Button self-contained for the user-agent reset and no longer dependent on the design-site global reset; the border border-solid border-(...) explicitly declared by the secondary / destructive-outline variants naturally overrides via tailwind-merge.
  2. 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 internal px-(--Spacing_24).
  3. Dialog adds cancelText / okText / onCancel / onOk config 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 no footer is passed; if a footer is passed, the close logic is implemented by the caller. DialogClose is not exported externally โ€” callers should use the "config" path.
  4. 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โ€‹

  1. DROPDOWN_SEPARATOR_TOKEN_CLASS adds mx-(--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)

  1. The icon in Toast is visually too large: the StatusIcon SVG's own viewBox = '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.
  2. Wrong vertical alignment strategy: previously items-start! + mt-px was used to force the icon close to the top, inconsistent with the Message mode of Figma's counterAxisAlignItems: CENTER (i.e. items-center).

Figma measurements (319:30904 Alert component set):

ModeOuter frameIcon areaTitleDescription
Message (Description=False)420ร—44, py=8, items-centericon 20ร—20 container (vector 13.333)22px line-heightโ€”
Alert (Description=True)420ร—70, py=8, items-startIcon Warp 20ร—28 (py=4) โ†’ embedded 20ร—20 iconH3 28pxBody 22px

Changed Filesโ€‹

src/components/Toast/styles.ts

Changesโ€‹

  1. 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 uses size-full! to fit the remaining 14ร—14 area โ‰ˆ the Figma vector 13.33. src/Icons/index.tsx is untouched โ€” the Icons are correct in other components.
  2. The Toast outer items-start! โ†’ items-center! (the default Message mode is Figma counterAxisAlignItems: CENTER).
  3. The Alert mode (:has([data-description])) is switched separately to items-start!, and the icon gets my-1 (4px top and bottom, aligning with the py=4 of Figma's Icon Warp).
  4. 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.
  5. action button: wraps the Sonner toast function, automatically converting the config-style action: { 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.
  6. 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
  7. Also merge action + cancel: Sonner renders action and cancel as 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 write w-full, otherwise in the Message + Action single-line scenario it would grab all the width, shrinking content to 0px and stacking text vertically.
  8. 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. wrapToastFn uses a closure to lazily read the toast id (the id returned by sonnerToast.success(...) is only determined after the call), and sonnerToast.dismiss(toastId) inside onClick closes it correctly.
  9. 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 line icon, content, close lined up
      • action wrapper basis-full! โ†’ occupies the second line alone
    • 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.

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:

VariantDefaultHoverClicked
primarybg #000bg #333bg #333 + 1px inside stroke #000
secondarybg #fff + stroke Gray-4bg Gray-1 + stroke Gray-4bg Gray-2 + stroke Gray-4
tertiarytransparentbg Gray-1bg Gray-2
quaternarytransparentbg #000 op=0.04 (Tinted-Default)bg #000 op=0.08 (Tinted-Emphasized)
destructivebg Errorbg Error + #fefefe op=0.1 overlaybg Error + #000 op=0.1 overlay
destructive-outlinestroke Errorstroke Error + Background Error op=0.1stroke Error + Background Error op=0.2
link-colortext Linktext Link + full-character UNDERLINEtext Link + full-character UNDERLINE
link-graytext Tertiarytext Primary + full-character UNDERLINEtext Primary + full-character UNDERLINE

Difference fixes:

  1. 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 base shadow-none โ€” ring uses the --tw-ring-shadow variable).
  2. 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 current hover:underline / active:underline implementation 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โ€‹

  1. POPOVER_SIDE_OFFSET = 0 is the same as the Radix default โ†’ remove the constant, remove the sideOffset = POPOVER_SIDE_OFFSET prop default, and just use the Radix default.
  2. Combined with Arrow height=12 rendering, 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โ€‹

  1. 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).
  2. (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โ€‹

  1. MENU_ITEM_ICON_CLASS: text-(--Labels-Placeholder) โ†’ text-(--Labels-Tertiary)
  2. MENU_GROUP_ARROW_CLASS: text-(--Labels-Placeholder) โ†’ text-(--Labels-Tertiary)
  3. 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โ€‹

  1. TableColumnHeaderProps adds align?: 'left' | 'center' | 'right'.
  2. Internally maps align to justify-start / justify-center / justify-end and merges it into TABLE_HEADER_CELL_INNER_CLASS.
  3. table-data-mode.tsx passes col.align through when rendering TableColumnHeader.