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

Claude UI Motion: Physics, Shaders & Ambient Light

Claude UI animation spec: full @keyframes set, 4 Framer Motion spring presets, reasoning glow at 1.8s/2.2s, shimmer curve and the 7-stop scroll fade mask.

中文版Last updated:

⬅️ Previous: Chapter 3: Conversation Flow & Command Input Engine · 📚 Back to the 1.x overview · Next: Chapter 5: Audio, Media & Artifact Interaction Physics ➡️


This chapter documents the motion system of the Claude Desktop 1.x UI: the CSS keyframes, the variable-font icon animation engine, Framer Motion spring presets, the reasoning glow and shimmer, and the scroll-driven fade masks. It is a translation of the Chinese handbook chapter; every value is copied from it unchanged, and code blocks are reproduced as-is.

Chapter 4: Motion Physics, Shaders & Ambient Light

Technical domain: Motion Physics, Shaders & Ambient Systems Source of the analysis: extracted with REA (Reverse Engineer Anything) v6.5.0 from the macOS production build /Applications/Claude.app (@ant/desktop v2.37337.1, build timestamp 2026-10-13T22:16:06Z), with an in-depth look at the core style assets c6a992d55-sXiDGMFQ.css and frame-window-BEI3vR8Q.css and the runtime core shared-frame-BfjMqbuN.js.


4.1 Motion engine and keyframe specification

Technical domain: Motion Engine, CSS Houdini & Spring Presets

4.1.1 The full set of core CSS keyframes (@keyframes)

The following keyframes were extracted in full by REA from c6a992d55-sXiDGMFQ.css, with no guessed parameters:

/* 1. 字符流式晕染输出 (Grapheme Blur Reveal) */
@keyframes _grapheme-reveal_1plog_1 {
  0%   { opacity: 0; filter: blur(6px); }
  100% { opacity: 1; filter: blur(0); }
}

/* 2. 思考三点跳跃循环 (Quiet Dots Wave - 周期 1.9s) */
@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); }
}

/* 3. 消息气泡微弹进入 (Message Fade Up) */
@keyframes message-fade-up {
  0%   { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* 4. 用户气泡发送微缩放进入 (User Bubble Enter) */
@keyframes code-user-bubble-enter {
  0%   { opacity: 0; transform: scale(0.92) translateY(6px); }
  100% { opacity: 1; transform: none; }
}

/* 5. 复制对勾衰减消退 (Check Decay - 2.5s 缓动) */
@keyframes saved-check-decay {
  0%, 75% { opacity: 1; transform: scale(1); }
  100%    { opacity: 0; transform: scale(0.92); }
}

/* 6. 骨架屏高级倾斜扫光 (Skeleton Sweep - 2.0s 线性) */
@keyframes cds-skeleton-sweep {
  0%   { transform: translate(-100%); }
  100% { transform: translate(100%); }
}

/* 7. 分段滑块加载中微呼吸 */
@keyframes df-pill-pending {
  to { opacity: 0.45; }
}

/* 8. 附件磁贴入场放大 (File Thumbnail Grow) */
@keyframes file-thumbnail-grow-in {
  0%   { opacity: 0; transform: scale(0.85); }
  100% { opacity: 1; transform: scale(1); }
}

4.1.2 Variable Font Kinetic Engine

In Claude Desktop, many icon micro-interactions are not traditional SVG morphs or PNG swaps. They run on a glyph engine built on the CSS Houdini Properties & Values API and variable font axes (Variable Font Axes ANIM / ANM2) (evidence from c6a992d55-sXiDGMFQ.css):

/* 1. 注册原生数值可动画自定义属性 */
@property --cds-anim {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
@property --cds-anim2 {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

/* 2. 字体变异轴驱动核心规则 */
[data-cds-anim] {
  --cds-anim: 0;
  --cds-anim2: 0;
  font-variation-settings: 
    "opsz" var(--cds-opsz, 20), 
    "wght" var(--cds-wght, 400), 
    "ANIM" var(--cds-anim), 
    "ANM2" var(--cds-anim2);
  transition: --cds-anim var(--cds-anim-dur-out, var(--cds-anim-dur, 0.3s)) 
              var(--cds-anim-ease, cubic-bezier(0.34, 1.3, 0.64, 1));
}

/* 3. 循环动效关键帧 */
@keyframes cds-anim-loop {
  0%, 49.99% { --cds-anim2: 0; }
  50%, 100%  { --cds-anim2: 100; }
}

/* 4. 交互触发绑定:Focus 与 Hover 阶梯驱动 */
@media (prefers-reduced-motion: no-preference) {
  .group:focus-visible [data-cds-anim],
  [data-cds-anim]:focus-visible,
  .group:has(:focus-visible) [data-cds-anim] {
    --cds-anim: 100;
    transition-duration: var(--cds-anim-dur, 0.3s);
  }
  .group:focus-visible [data-cds-anim-loop],
  [data-cds-anim-loop]:focus-visible,
  .group:has(:focus-visible) [data-cds-anim-loop] {
    animation: 1s linear 0.1s infinite cds-anim-loop;
  }
  @media (hover: hover) {
    .group:hover [data-cds-anim],
    [data-cds-anim]:hover {
      --cds-anim: 100;
      transition-duration: var(--cds-anim-dur, 0.3s);
    }
    .group:hover [data-cds-anim-loop],
    [data-cds-anim-loop]:hover {
      animation: 1s linear 0.1s infinite cds-anim-loop;
    }
  }
}

How it feels: --cds-anim-ease is bound strictly to --cds-ease-overshoot (that is, cubic-bezier(0.34, 1.3, 0.64, 1)), so when an icon animates, the glyph skeleton overshoots elastically by +30% and then settles, giving a distinctive mechanical micro-motion feel.


4.1.3 Production Framer Motion spring presets (Spring Physics Presets)

For complex gesture snapping, pop-out expansion and interaction feedback, Claude uses spring animation (Framer Motion Spring), modeled on the classic second-order damped oscillator: $$m \frac{d^2 x}{dt^2} + c \frac{dx}{dt} + k (x - x_{\text{target}}) = 0$$

The damping ratio $\zeta = \frac{c}{2\sqrt{m k}}$ determines the character of the motion:

PresetStiffness (stiffness)Damping (damping)Mass (mass)Damping ratio $\zeta$OvershootSettling timeMain use and feel
Button & Small Controls600301.00.612 (underdamped)+8.8%0.22sButton clicks, toggles, icon presses. A quick, high-frequency bounce that returns crisply without lag.
Card / Popover Reveal320281.00.783 (underdamped)+1.8%0.35sMessages rising in, cards expanding, bubble popovers. Steady and smooth, a slight overshoot then a soft landing.
Bouncy Light Feedback380 (or dur 0.32)18 (or bounce 0.15)1.00.462 (clearly underdamped)+19.4%0.58sLikes, copy-success checkmarks, new-message hints. A visible two-cycle spring bounce.
High Damping Panel Drag1200800.13.650 (overdamped)0% (zero overshoot)0.14sGesture drag and snap for the sidebar and overlays. Very high stiffness and damping; snaps into place the instant you let go.

Production motion constants and Hook implementation: code-examples/ClaudeMotionPresets.ts.


4.2 Dynamic light shaders and the reasoning ambience

Technical domain: Ambient Shimmer, Glow & Pulse

4.2.1 Production light-wave shaders (c6a992d55-sXiDGMFQ.css)

The core keyframes and light-field shading rules extracted from the official minified stylesheet c6a992d55-sXiDGMFQ.css:

/* 1. 官方文本渐变光波扫描 (Shimmer Text Shine) */
@keyframes cds-shimmer-text-shine {
  0%, 15% {
    background-position: 83.333% 0;
    animation-timing-function: cubic-bezier(0.714, 0.121, 0.211, 0.888);
  }
  85%, 100% {
    background-position: 16.667% 0;
  }
}

/* 官方高对比扫光增强版 (Shimmer Text Shine Strong) */
@keyframes cds-shimmer-text-shine-strong {
  0%, 5% {
    background-position: 83.333% 0;
    animation-timing-function: cubic-bezier(0.4, 0.15, 0.6, 0.85);
  }
  95%, 100% {
    background-position: 16.667% 0;
  }
}

/* 2. 官方深度思考光晕脉冲 (wl-glow) - 周期 1.8s ease-in-out */
@keyframes wl-glow {
  0%, 100% {
    box-shadow: 
      0 10px 15px -3px #0000001a, 
      0 4px 6px -4px #0000001a, 
      0 0 0 2px var(--cds-fill-brand), 
      0 0 14px 3px color-mix(in srgb, var(--cds-fill-brand) 60%, transparent);
  }
  50% {
    box-shadow: 
      0 10px 15px -3px #0000001a, 
      0 4px 6px -4px #0000001a, 
      0 0 0 2px var(--cds-fill-brand), 
      0 0 16px 5px color-mix(in srgb, var(--cds-fill-brand) 85%, transparent);
  }
}

/* 3. 官方光晕外圈脉冲 (wl-halo) - 周期 2.2s ease-in-out */
@keyframes wl-halo {
  0%, 100% {
    box-shadow: 
      0 0 0 2px var(--cds-fill-brand), 
      0 0 14px 3px color-mix(in srgb, var(--cds-fill-brand) 60%, transparent);
  }
  50% {
    box-shadow: 
      0 0 0 2px var(--cds-fill-brand), 
      0 0 16px 5px color-mix(in srgb, var(--cds-fill-brand) 85%, transparent);
  }
}

/* 4. 官方三点差分跳跃脉冲 (cds-dot-pulse - 周期 1.8s) */
@keyframes cds-dot-pulse {
  0%, 5% {
    opacity: 0.2857;
    animation-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
  }
  15%, 25% {
    opacity: 1;
    animation-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
  }
  65%, 100% {
    opacity: 0.2857;
  }
}

/* 5. 官方线性扫光条 (Epitaxy Linear Sweep - 严格 1.1s cubic-bezier(.45, 0, .55, 1)) */
@keyframes epitaxy-linear-sweep {
  0%   { transform: translate(-100%); }
  100% { transform: translate(350%); }
}

/* RTL 镜像扫光 */
@keyframes epitaxy-linear-sweep-rtl {
  0%   { transform: translate(100%); }
  100% { transform: translate(-350%); }
}

/* 6. 成功对勾矢量绘制动效 (Epitaxy Check Draw - 0.18s) */
@keyframes epitaxy-check-draw {
  0%   { clip-path: inset(0 100% 0 0); }
  100% { clip-path: inset(0); }
}

4.2.2 Warm clay reasoning glow and the color-mix() light field

During a chain of thought (Extended Thinking) or complex task processing, the Claude container activates the wl-glow dynamic light field:

  • Light ring: the inner layer is a hard hairline border 0 0 0 2px var(--cds-fill-brand), with ambient shadows 0 10px 15px -3px #0000001a and 0 4px 6px -4px #0000001a;
  • Dynamic diffusion: the outer glow no longer uses a fixed RGBA but the modern CSS standard color-mix(in srgb, var(--cds-fill-brand) X%, transparent):
    • Base state: 0 0 14px 3px, mixed at 60%;
    • Peak state (50%): 0 0 16px 5px, with the mix rising to 85%;
  • Breathing frequencies: wl-glow is 1.8s and wl-halo is 2.2s; layering two sine waves at different frequencies avoids the mechanical, tiring look of a single rhythm.

4.2.3 Text shimmer wave physics (cds-shimmer-text-shine)

  • Claude uses a highly asymmetric Bézier curve, cubic-bezier(0.714, 0.121, 0.211, 0.888): between 0% and 15% it builds up energy while holding at 83.333% 0, then sweeps across the text with a non-linear impulse in the middle, and finally eases back to 16.667% 0 between 85% and 100%, imitating the delayed wavefront and sudden brightening of light passing through a medium.

4.2.4 Three-phase dot pulse and the Epitaxy linear sweep (cds-dot-pulse, epitaxy-linear-sweep)

  • Three-dot pulse: the base opacity is anchored at exactly 0.2857 (the constant $\frac{2}{7}$), the highlight jumps to 1.0, and the timing curve is bound to --cds-ease-out (cubic-bezier(0.165, 0.84, 0.44, 1));
  • Linear sweep: the production cycle is 1.1s (not the 1.6s misreported in older docs), the speed curve is a symmetric S-curve cubic-bezier(0.45, 0, 0.55, 1), and the sweep runs from -100% to +350%.

4.2.5 Complete production React implementation

The full standalone source is at: code-examples/ReasoningAmbientGlow.tsx.


4.3 Native CSS scroll-driven fade mask system

Technical domain: CSS Scroll-Driven Animations & 7-Stop Non-Linear Fade Masks

In the message stream, horizontal segmented pills and all kinds of overflow scroll lists, Claude uses a feathered mask system driven purely by native CSS, with no JS computation or event listeners on the main thread (extracted from c6a992d55-sXiDGMFQ.css).

4.3.1 Native CSS scroll-driven animation declarations (@supports (animation-timeline: scroll()))

@supports (animation-timeline: scroll()) {
  .scroll-fade {
    /* 动态计算当前各方向生效遮罩深度 */
    --cds-scroll-fade-top: calc(var(--cds-scroll-fade-top-raw) * var(--cds-scroll-fade-y-active) * var(--cds-scroll-fade-top-on));
    --cds-scroll-fade-bottom: calc(var(--cds-scroll-fade-bottom-raw) * var(--cds-scroll-fade-y-active) * var(--cds-scroll-fade-bottom-on));
    --cds-scroll-fade-left: calc(var(--cds-scroll-fade-left-raw) * var(--cds-scroll-fade-x-active) * var(--cds-scroll-fade-left-on));
    --cds-scroll-fade-right: calc(var(--cds-scroll-fade-right-raw) * var(--cds-scroll-fade-x-active) * var(--cds-scroll-fade-right-on));

    /* 纵横双轴复合 7-Stop 非线性遮罩图层 */
    mask-image: 
      linear-gradient(to bottom, 
        transparent, 
        #0000000d calc(var(--cds-scroll-fade-top) * .15), 
        #00000026 calc(var(--cds-scroll-fade-top) * .3), 
        #0000004d calc(var(--cds-scroll-fade-top) * .45), 
        #00000080 calc(var(--cds-scroll-fade-top) * .6), 
        #000000b3 calc(var(--cds-scroll-fade-top) * .75), 
        #000000d9 calc(var(--cds-scroll-fade-top) * .87), 
        #000 var(--cds-scroll-fade-top), 
        #000 calc(100% - var(--cds-scroll-fade-bottom)), 
        #000000d9 calc(100% - var(--cds-scroll-fade-bottom) * .87), 
        #000000b3 calc(100% - var(--cds-scroll-fade-bottom) * .75), 
        #00000080 calc(100% - var(--cds-scroll-fade-bottom) * .6), 
        #0000004d calc(100% - var(--cds-scroll-fade-bottom) * .45), 
        #00000026 calc(100% - var(--cds-scroll-fade-bottom) * .3), 
        #0000000d calc(100% - var(--cds-scroll-fade-bottom) * .15), 
        transparent
      ), 
      linear-gradient(to right, 
        transparent, 
        #0000000d calc(var(--cds-scroll-fade-left) * .15), 
        #00000026 calc(var(--cds-scroll-fade-left) * .3), 
        #0000004d calc(var(--cds-scroll-fade-left) * .45), 
        #00000080 calc(var(--cds-scroll-fade-left) * .6), 
        #000000b3 calc(var(--cds-scroll-fade-left) * .75), 
        #000000d9 calc(var(--cds-scroll-fade-left) * .87), 
        #000 var(--cds-scroll-fade-left), 
        #000 calc(100% - var(--cds-scroll-fade-right)), 
        #000000d9 calc(100% - var(--cds-scroll-fade-right) * .87), 
        #000000b3 calc(100% - var(--cds-scroll-fade-right) * .75), 
        #00000080 calc(100% - var(--cds-scroll-fade-right) * .6), 
        #0000004d calc(100% - var(--cds-scroll-fade-right) * .45), 
        #00000026 calc(100% - var(--cds-scroll-fade-right) * .3), 
        #0000000d calc(100% - var(--cds-scroll-fade-right) * .15), 
        transparent
      );

    /* 6 路并行原生滚动驱动时间轴绑定 */
    animation-name: 
      cds-scroll-fade-top, 
      cds-scroll-fade-bottom, 
      cds-scroll-fade-left, 
      cds-scroll-fade-right, 
      cds-scroll-fade-y-active, 
      cds-scroll-fade-x-active;
    animation-timing-function: linear;
    animation-fill-mode: both, both, both, both, none, none;
    animation-duration: auto;
    animation-timeline: 
      scroll(self), 
      scroll(self), 
      scroll(self inline), 
      scroll(self inline), 
      scroll(self), 
      scroll(self inline);
    animation-range: 
      0px var(--cds-scroll-fade-size), 
      calc(100% - var(--cds-scroll-fade-size)) 100%, 
      0px var(--cds-scroll-fade-size), 
      calc(100% - var(--cds-scroll-fade-size)) 100%, 
      0px 101%, 
      0px 101%;
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
  }
}

4.3.2 The official 7-stop non-linear alpha ladder (7-Stop Non-Linear Alpha Sampling)

A plain two-stop linear gradient easily shows visible banding on high-DPI Retina screens because of the gamma response. Claude's 7 sample points closely follow the power-law curve of human visual perception:

  • Position Stops: [15%, 30%, 45%, 60%, 75%, 87%, 100%];
  • True Hex Alpha:
    • 15%: #0000000d (Alpha $\approx 5.1%$)
    • 30%: #00000026 (Alpha $\approx 14.9%$)
    • 45%: #0000004d (Alpha $\approx 30.2%$)
    • 60%: #00000080 (Alpha $\approx 50.2%$)
    • 75%: #000000b3 (Alpha $\approx 70.2%$)
    • 87%: #000000d9 (Alpha $\approx 85.1%$)
    • 100%: #000000 (Alpha $100%$)

4.3.3 Complete production React implementation

The component supports the modern native CSS scroll-driven mode and falls back smoothly to a reactive JS onScroll calculation in hosts that don't support the API: code-examples/ScrollFadeContainer.tsx.


⬅️ Chapter 3: Conversation Flow & Command Input Engine · 📚 1.x overview · Chapter 5: Audio, Media & Artifact Interaction Physics ➡️ · Live demos: motion & light

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