Open Claude UI
中文
v1 · Claude UI Design Handbook (1.x)

Claude UI Components: Atoms & Interactive Controls

Claude UI components spec: 5-step button height scale, 6 button variants, segmented control, 6px status dots, dialog sizes and 7.5rem attachment tiles.

中文版Last updated:

⬅️ Previous: Chapter 1: Design Philosophy & Design Tokens · 📚 Back to the 1.x overview · Next: Chapter 3: Conversation Flow & Command Input Engine ➡️


This chapter documents the atomic Claude UI components found in the Claude Desktop 1.x front-end build: buttons, the segmented control, status indicators, the confirmation dialog and attachment tiles. It is a translation of the Chinese handbook chapter; every value is copied from it unchanged, and code blocks are reproduced as-is.

Chapter 2: Atomic Components & Interactive Controls

Technical domain: Foundational Atoms & Controls

Based on an analysis with REA (Reverse Engineer Anything) v6.5.0 of the latest Claude Desktop build (/Applications/Claude.app, @ant/desktop v2.37337.1, ASAR evidence ev_e0bd89cee50a5b7d0dbf9e75cef5dfbca2bcb1699df4d4371449dd08452a8358), this chapter breaks down the production DOM structure, CSS utility-class combinations and native micro-interaction algorithms of Claude's basic interactive controls.


2.1 The full button control ladder

Technical domain: Buttons & Controls - CdsButton / [data-cds="Button"]

2.1.1 Control height scale and type size matrix

From REA's analysis of shared-frame-BfjMqbuN.js and c6a992d55-sXiDGMFQ.css, Claude does not use a rough 4-step size set. It defines a strict 5-step height scale and aligns it to sub-pixels with the desktop optical scale factor --cds-rem-scale: calc(16 / 17) (≈ 0.941176):

SizeCSS expression (Rem Scale)Actual desktop pixelsPadding (padding)Radius (radius)Type and role
xxs (data-step="1")calc(1.25rem * scale)18.82px (about 19px)px-1.5 py-0--radius-sm (4px)11px Caption; very compact inline retry, minor secondary actions
xscalc(1.5rem * scale)22.59px (about 23px / 24px)px-2 py-0.5--radius-md (6px)11.5px Footnote; tag removal, code block helper bar
smcalc(1.75rem * scale)26.35px (about 26px / 28px)px-2.5 py-1--radius-lg (8px)12.5px; dropdown menu items, action pills inside the composer
md (official default)calc(2rem * scale)30.12px (about 32px)px-3.5 py-1.5--radius-lg (8px)13.5px Body; standard actions, form confirmation
lgcalc(2.5rem * scale)37.65px (about 40px)px-5 py-2--radius-xl (12px)15px Heading; primary global calls to action, main sign-in action
icon (icon only)Fixed square aspect ratioaspect-square w-controlp-0--radius-lg (8px)Standalone toolbar icon actions (close, copy, more)

[!NOTE] Nested control height reduction (--cds-h-control-nested):
When a button is nested inside the Composer or a toolbar container, Claude switches to the nested height variables:
nested-xs: calc(1.125rem * scale) (17px) / nested-sm: calc(1.25rem * scale) (18.8px) / nested-default: calc(1.375rem * scale) (20.7px) / nested-lg: calc(1.75rem * scale) (26.35px).


2.1.2 The 6 core production variants (Variants Architecture)

REA's analysis shows that Claude's button is built from two orthogonal layers: an outer interaction reset shell + an inner, separate paint layer (data-cds-part="paint"). This fully decouples the outer focus ring, inner shadow and background gradient, and avoids the slight inner/outer size jitter that traditional CSS buttons show on Hover/Active.

graph LR
    Outer["Button 外层容器<br>[data-cds='Button'] .cds-btn-squish"] --> Paint["独立绘景图层<br>[data-cds-part='paint'] -z-10"]
    Outer --> Content["内容排版层<br>Icon + Children + Kbd"]
    Outer --> Indicator["状态微标插槽<br>[data-cds-part='indicator'] Pulse"]
/* 提取自官方生产包 shared-frame-BfjMqbuN.js 原生变体映射 */
const Il = "cds-reset group/btn relative isolate inline-flex shrink-0 items-center justify-center gap-1.5 whitespace-nowrap select-none cursor-[var(--cds-cursor-interactive)] aria-disabled:cursor-default data-[disabled]:cursor-default border-0 outline-none focus-visible:outline-hidden rounded h-control font-sans text-body transition-shadow duration-fast";
VariantVisual roleText and foregroundPaint layer styles (data-cds-part="paint")Interaction states and physical traits
secondary(Claude's official default)Secondary actions inside cards; never has a hard border, strictly follows the single closed border principletext-primary font-normal font-control aria-pressed:text-accentbg-fill-secondary group-hover/btn:bg-fill-secondary-hover dark:bg-white/10 dark:hover:bg-white/15shadow-field group-aria-pressed/btn:shadow-field-pressed
primaryKey positive submits, sending a prompttext-on-primary font-medium font-controlbg-fill-primary group-hover/btn:bg-fill-primary-hover (solid clay)shadow-2xs active:brightness-95
ghostToolbar icons, inline micro-actions, secondary collapse barstext-primary font-normal font-control aria-pressed:text-accentbg-transparent group-hover/btn:bg-fill-ghost-hoverFades in 5% gray on hover; active state uses bg-fill-ghost-selected
dangerDestructive actions such as confirming a delete or clearing a projecttext-on-danger font-medium font-controlbg-fill-danger group-hover/btn:bg-fill-danger-hover (warning dark red)Dedicated red glow focus-visible:shadow-focus-danger
brandOfficial brand-highlight actions on desktoptext-on-brand font-medium font-controlbg-fill-brand group-hover/btn:bg-fill-brand-hover (--brand: #c6613f)The official deep-clay desktop brand color
onContentOverlay actions floating above complex code, images or full-screen videotext-primary font-medium font-controlbg-[var(--cds-button-on-content)] backdrop-blur-md shadow-[inset_0_0_0_1px_var(--cds-button-on-content-ring)]4px frosted-glass feathering; dark translucency resists background noise

2.1.3 Button damping physics and the 15-stop pure-CSS spring (.cds-btn-squish)

While analyzing c6a992d55-sXiDGMFQ.css, REA found Claude's striking real spring deformation system. It is not the hard-coded scale(0.98) common in front-end code: it computes a damping factor from the button's runtime height and, on release, triggers a 15-stop CSS linear() spring curve:

/* 提取自 c6a992d55-sXiDGMFQ.css 真实生产定义 */
:root {
  --cds-btn-spring: linear(
    0, .2459, .6526, .9468, 1.0764, 1.0915, 1.0585, 1.0219, 
    .9993, .9914, .9921, .9957, .9988, 1.0004, 1
  );
}

.cds-btn-squish {
  --cds-btn-press-sx: 0.975;
  --cds-btn-press-sy: 0.975;
  transform-origin: 50%;
  transition: transform var(--cds-dur-slow) var(--cds-btn-spring);
}

.cds-btn-squish:active, :active > .cds-btn-squish {
  transform: scale(var(--cds-btn-press-sx), var(--cds-btn-press-sy));
  transition: transform var(--cds-dur-fast) ease-out;
}

[!TIP] Spring Physics Analysis:
In the --cds-btn-spring curve, the release first shoots out to 1.0915 at the 40% mark (+9.15% physical overshoot), then rebounds slightly to 0.9914 and settles precisely at 1.0004. Combined with a dynamic JS adjustment:
Bl(h) = Math.min(1.5, Math.max(0.5, h * (1 / 32))), small buttons scale delicately and large buttons rebound solidly.


2.1.4 Joined button group protocol (Joined Buttons)

When several buttons sit side by side as a combined bar (view switchers, undo/redo groups), the outer wrapper injects joined topology control, which removes the inner corner radii and the doubled middle border automatically:

  • First item (start): rounded-e-none (square right side)
  • Middle items (middle): rounded-none (square on both sides)
  • Last item (end): rounded-s-none (square left side)

Standalone production component: code-examples/CdsButton.tsx (fully synced: 5 sizes, 6 variants, separate paint layer and the .cds-btn-squish 15-stop spring algorithm).


2.2 Segmented sliding pill selector

Technical domain: Segmented Control & Pills - .df-pills[data-segmented]

The sliding tabs in Claude Desktop's top navigation and settings use a 999px fully rounded track + purely geometric, equal-width thumb + adaptive collapse with two container-query breakpoints.

2.2.1 Exact geometry of the track and sliding indicator

/* 提取自 shared-styles-Bl9zqrv-.css 与 frame-window-BEI3vR8Q.css */
.df-pills[data-segmented] {
  padding: 1px; /* 严格 1px 环绕间距,杜绝 2px 的笨拙感 */
  container-type: inline-size;
  border-radius: 999px;
  background: var(--cds-segmented-control-track, rgba(0, 0, 0, 0.06));
}

.df-pills[data-segmented] .df-pill-indicator {
  position: absolute;
  top: 1px;
  bottom: 1px;
  border-radius: calc(var(--cds-radius) - 1px);
  will-change: transform;
  transition: transform var(--cds-dur-snap) var(--cds-ease-snap), 
              background-color var(--df-dur-slow) var(--df-ease-out);
}
  • Pure Geometric Allocation formula: The thumb's horizontal position and width match the 1px gap inside the track exactly:
    left: calc(1px + var(--active-index) * (100% - 2px) / var(--total-tabs));
    width: calc((100% - 2px) / var(--total-tabs));
  • Dual-Layer Optical Shadow:
    • Light mode: background: var(--cds-neutral-20); box-shadow: inset 0 0 0 0.5px var(--cds-border), 0 1px 2px #0000000f, 0 2px 6px -1px #0000000d;
    • Dark mode: background: var(--cds-segmented-control-thumb); box-shadow: inset 0 0 0 0.5px #ffffff14, 0 1px 2px #0000004d, 0 2px 6px -1px #00000040;

2.2.2 Adaptive collapse with two container-query breakpoints (@container)

When the container is squeezed by a narrow sidebar or window resizing, Claude never wraps awkwardly. It uses @container to shrink the labels of inactive items step by step:

/* 提取自 c6a992d55-sXiDGMFQ.css 真实生产容器断点 */

/* 1. 两项分段控制器:容器宽度 <= 180px 时折叠 */
@container (width <= 180px) {
  .df-pills[data-segmented][data-pills="2"] .df-pill:not([data-active]) .df-pill-label {
    display: none;
  }
  .df-pills[data-segmented][data-pills="2"] .df-pill:not([data-active]) {
    padding: 0;
  }
  .df-pills[data-segmented][data-pills="2"] .df-pill[data-active] {
    flex: none;
    padding: 0 16px;
  }
}

/* 2. 三项分段控制器:容器宽度 <= 254px 时折叠 */
@container (width <= 254px) {
  .df-pills[data-segmented][data-pills="3"] .df-pill:not([data-active]) .df-pill-label {
    display: none;
  }
  .df-pills[data-segmented][data-pills="3"] .df-pill:not([data-active]) {
    padding: 0;
  }
  .df-pills[data-segmented][data-pills="3"] .df-pill[data-active] {
    flex: none;
    padding: 0 16px;
  }
}

Standalone production component: code-examples/SegmentedControl.tsx (supports strict 1px track positioning, adaptive collapse at two container breakpoints, and a dual mode with Framer Motion springs).


2.3 Status indicators and micro-motion kit

Technical domain: Status Indicators & Shimmer

In the Claude interface, tiny indicator dots and shimmer bars carry the key job of communicating live system state (running, thinking, idle, ready):

2.3.1 The official 6px status dot matrix (.status-dot)

REA extracted the official base status class from shared-styles-Bl9zqrv-.css: .status-dot (a strict 6px-diameter circle):

.status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  display: block;
  position: relative;
}
StateColorAnimation timing and keyframesWhere it is used
readyvar(--cds-fill-accent)Static, always on, no extra costDesktop ready, conversation sync complete, model standing by
runningvar(--cds-fill-accent)1.2s ease-in-out infinite dframe-dot-blink (opacity 0.3 $\leftrightarrow$ 1.0)Background activity in progress, artifact code building
errorvar(--cds-fill-danger)Static deep red highlightConnection timeout, MCP tool execution failed
clayvar(--cds-clay)3.0s ease-in-out infinite dframe-clay-breatheExtended Thinking deep reasoning and the model's core compute pulse
/* 陶土呼吸关键帧提取自 shared-styles-Bl9zqrv-.css */
@keyframes dframe-clay-breathe {
  0%, 100% { filter: brightness(0.96); transform: scale(0.86); }
  50%      { filter: brightness(1.06); transform: scale(1); }
}

2.3.2 Thinking wave: three bouncing dots (.timeline-quiet-dots)

While a chain of thought or message output is pending, Claude renders its own three-dot wave pulse:

  • Container geometry: gap: 3.5px; display: inline-flex; position: relative; top: 0.26em;
  • Dot geometry: width: 3px; height: 3px; border-radius: 50%; background: var(--cds-clay, #d97757);
  • Three-step delay and motion path:
    • Cycle: 1.9s cubic-bezier(0.4, 0, 0.2, 1) infinite
    • Phase offsets: dot 1 at 0s, dot 2 at 0.15s, dot 3 at 0.3s
    • Keyframes:
      @keyframes timeline-quiet-dot {
        0%, 8%   { opacity: 0; transform: translateY(2px); }
        22%      { opacity: 1; transform: translateY(-2.5px); }
        36%, 70% { opacity: 1; transform: translateY(0); }
        90%, 100%{ opacity: 0; transform: translateY(0); }
      }

2.3.3 Skeleton 105° angled sweep (cds-skeleton-sweep)

While card and body skeletons are loading, Claude uses a textured, asymmetric angled sweep:

  • Cycle and easing: animation: cds-skeleton-sweep 2s linear infinite; (a strict 2.0s constant speed, not 1.6s).
  • Band angle:
    • LTR mode: linear-gradient(105deg, transparent 35%, var(--cds-skeleton-sheen) 50%, transparent 65%)
    • RTL mirrored mode: linear-gradient(255deg, transparent 35%, var(--cds-skeleton-sheen) 50%, transparent 65%)
  • Keyframes:
    @keyframes cds-skeleton-sweep {
      0%   { transform: translate(-100%); }
      100% { transform: translate(100%); }
    }

Standalone production component: code-examples/StatusIndicators.tsx.


2.4 Modal confirmation dialog

Technical domain: Confirmation Dialog - [data-cds="Dialog"]

The modal confirmation dialog appears when clearing a conversation, deleting a project, resetting a key, or before a high-risk MCP tool call:

2.4.1 The 6-step dialog scale and the viewport safety cap

From REA's extraction of the production Dialog controller in shared-frame-BfjMqbuN.js, Claude defines a system-wide 6-step dialog matrix:

/* 提取自官方生产包 shared-frame-BfjMqbuN.js 原生 Qg 尺寸表 */
const Qg = {
  sm: "max-w-[400px]",       // 极简确认、单项授权
  md: "max-w-[520px]",       // 标准确认模态框(默认)
  lg: "max-w-[720px]",       // 复杂表单、环境配置
  xl: "max-w-[880px]",       // 代码对比确认
  "2xl": "max-w-[1024px]",   // 宽屏工件预览
  "3xl": "max-w-[1152px]"    // 全功能工作区看板
};
  • Viewport-height safety cap formula (--environment-dialog-cap):
    --environment-dialog-cap: calc(
      100dvh - max(2rem, 2 * var(--desktop-titlebar-inset, 0px)) 
      - 2 * max(env(safe-area-inset-top, 0px), env(safe-area-inset-bottom, 0px))
    );
    This keeps dialogs from overflowing the viewport on every desktop setup (macOS traffic lights and notch, Windows title bar).

2.4.2 Nested dialog defense (Nested Dialog Blurring Protocol)

When a modal dialog opens a second-level child dialog (for example clicking "View details" inside a confirmation dialog), Claude uses an attribute selector to blur and dim the parent dialog underneath:

/* 提取自 shared-frame-BfjMqbuN.js */
const Xg = "after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-backdrop after:opacity-0 after:z-50 after:transition-opacity after:duration-base after:ease-out data-[nested-dialog-open]:after:opacity-100";
const Zg = "data-[nested-dialog-open]:blur-[2px]";

When the parent dialog detects data-[nested-dialog-open]:

  1. It immediately applies blur-[2px], removing high-frequency text detail;
  2. The after pseudo-element's opacity transitions smoothly from 0 to 100%, covering it with a light translucent mask;
  3. Visual focus goes entirely to the newly opened second-level dialog.

2.4.3 Native scroll timeline and double-ring diffuse shadow

  • Double-ring Popover shadow (--cds-shadow-popover): An outer Gaussian blur plus an inner bevel highlight; the outline has no hard 1px black line.
  • Scroll-timeline driven: The container includes [scroll-timeline:--cds-dialog-scroll_block] and .scroll-fade-y. When long content scrolls vertically, it automatically triggers the 28px relative-color fade mask extracted in Chapter 1.

Standalone production component: code-examples/ConfirmationDialog.tsx.


2.5 Attachment tiles and context pills

Technical domain: Attachment Cards & Inline Chips - [data-cds="MessageAttachmentsFile"]

The card control shown when a user sends a file in the conversation or stages an attachment in the composer:

2.5.1 Strict 7.5rem square tiles with adaptive concentric radii

REA's full extraction of the production bundle shared-2-Cb74JByF.js corrects the common community guess of a "128px × 96px wide rectangle". The real official classes are:

/* 提取自 shared-2-Cb74JByF.js 原生 Yf 磁贴容器类名 */
const Yf = "group/tile relative isolate size-[calc(7.5rem*var(--cds-rem-scale,1))] shrink-0 rounded-card outline-none focus-visible:shadow-focus";
  • Actual geometry: size-[calc(7.5rem * var(--cds-rem-scale, 1))]: a strict 120px square at base, 112.94px × 112.94px after the desktop scale factor.
  • Concentric radii: uses the rounded-card (--radius-card) defined in Chapter 1, so inner elements stay exactly concentric with the outer frame.
  • Diffuse depth: an outer shadow-field-drop that lifts slightly to hover:shadow-xs on Hover.

2.5.2 Floating round remove button (data-cds-attachment-remove)

The remove button takes no space in the inner grid; it floats precisely at the top-right corner:

/* 提取自 shared-2-Cb74JByF.js 原生 _p 移除按钮 */
const removeBtnClasses = "cds-reset absolute -end-2 -top-2 z-[1] inline-flex size-5 items-center justify-center rounded-full bg-surface-3 text-secondary shadow-card-ring hover:text-primary outline-none focus-visible:shadow-focus";
  • Absolute positioning: absolute -end-2 -top-2 z-[1] (floats 8px beyond the top-right).
  • Tiny circle: size-5 (a strict 20px diameter), background on the highest layer, Surface 3.
  • Mistake-proofing: a shadow-card-ring hairline protection ring, with active:scale-90 tactile feedback on press.

2.5.3 Bi-Directional Mid-Truncation

For long file names, traditional one-sided truncation (truncate / text-overflow: ellipsis) cuts off the extension at the end, giving long_filename_final..., so the user cannot tell the file type. Claude uses a precise bi-directional mid-truncation algorithm:

<!-- 提取自 shared-2-Cb74JByF.js Np 组件 DOM 结构 -->
<span class="flex min-w-0 flex-col text-footnote font-normal text-primary">
  <!-- 1. 头部文件名截取 -->
  <bdi class="overflow-hidden break-words [overflow-wrap:anywhere]">{head}</bdi>
  <!-- 2. 中间省略号 + 尾部词干 + 完整保留的扩展名 -->
  <span class="flex min-w-0 before:shrink-0 before:content-['…']">
    <bdi class="truncate py-[0.125em] -my-[0.125em]">{tailStem}</bdi>
    <span class="shrink-0 font-medium">{tailExt}</span>
  </span>
</span>

However long the file name is (for example deep_reinforcement_learning_policy_gradient_v3.tsx), the extension .tsx is always locked in view with shrink-0, and the middle is joined dynamically by before:content-['…'].


2.5.4 Dynamic uppercase type badge (Uppercase Badge)

The top-left of the tile parses the file extension and turns it into an uppercase sans-serif badge (such as TS, PY, PDF, CSV, SQL), shown in monospace glyphs on a subtle high-contrast pill background so the file type is clear at a glance.

Standalone production component: code-examples/AttachmentCard.tsx (fully synced: 7.5rem square, bi-directional mid-truncation and the floating remove button protocol).


⬅️ Chapter 1: Design Philosophy & Design Tokens · 📚 1.x overview · Chapter 3: Conversation Flow & Command Input Engine ➡️ · Live demos: atomic components

Open Claude UI is an unofficial community analysis, MIT licensed. Claude is a trademark of Anthropic; this project is not affiliated with Anthropic.

On this page