news 2026/9/11 23:55:42

Sim 项目 UI 打磨指南:让界面「感觉更好」的 16 条设计工程原则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sim 项目 UI 打磨指南:让界面「感觉更好」的 16 条设计工程原则

Sim 项目 UI 打磨指南:让界面「感觉更好」的 16 条设计工程原则

【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim

大型界面很少因为某一个亮点而显得精致,通常是一系列小细节叠加,最终复合出完整的高级体验。这份指南以仓库中.agents/skills/make-interfaces-feel-better/SKILL.md及其四份子文档(typography / surfaces / animations / performance)为骨架,结合 Sim 当前仓库的样式体系(.claude/rules/sim-styling.mdapps/sim/app/_styles/globals.csspackages/emcn)展开,面向所有在 Sim 中构建 UI 组件、review 前端代码、实现动画、悬浮态、阴影、边框、字号、微交互、进出场动画的开发者。读完你可以直接照单执行:每一条原则都配有可复制的 CSS / Tailwind / Motion 代码、仓库内落点以及 review 时的检查清单。

总览:什么时候该用哪套原则

SKILL.md 开头给出一张快速参考表,把细节打磨分成四大类,对应的深入文档分别是:

分类覆盖内容深入文档
Typography文本换行、字体平滑、tabular 数字.agents/skills/make-interfaces-feel-better/typography.md
Surfaces圆角、光学对齐、阴影、图片描边、点击区域.agents/skills/make-interfaces-feel-better/surfaces.md
Animations可中断动画、进出场过渡、图标动画、按压缩放.agents/skills/make-interfaces-feel-better/animations.md
Performance过渡属性收敛、will-change用法.agents/skills/make-interfaces-feel-better/performance.md

Sim 前端使用 Tailwind +framer-motionapps/sim/package.json中为"framer-motion": "^12.5.0"),组件库集中在packages/emcn。因此文中 Motion 示例使用motion/react(Framer Motion 新包名)语法,同时给出无 Motion 依赖时的纯 CSS 替代方案。

一、Surfaces:圆角、对齐、阴影与图片描边

1. 同心圆角(Concentric Border Radius)

嵌套圆角元素时,外半径必须等于内半径加上两者之间的 padding

outerRadius = innerRadius + padding

内外半径不匹配是让界面「感觉不对劲」最常见的单一原因。这条规则在嵌套表面距离较近时最有效;如果 padding 超过24px,就把各层当作独立表面,各自选半径,不必强行套同心数学。

/* 好——同心半径 */ .card { border-radius: 20px; /* 12 + 8 */ padding: 8px; } .card-inner { border-radius: 12px; } /* 坏——内外半径相同 */ .card { border-radius: 12px; padding: 8px; } .card-inner { border-radius: 12px; }

Tailwind 写法:

{/* 好——外层半径已计入 padding */} <div className="rounded-2xl p-2"> {/* 16px 半径,8px padding */} <div className="rounded-lg"> {/* 8px = 16 - 8 ✓ */} ... </div> </div> {/* 坏——同样半径 */} <div className="rounded-xl p-2"> <div className="rounded-xl"> {/* 同样半径,看起来不对 */} ... </div> </div>

2. 光学对齐优先于几何对齐(Optical Alignment)

当几何居中看起来偏时,要按光学对齐。按钮带图标、播放三角、不对称图标都需要手工调整。

文字 + 图标按钮:图标一侧 padding 比文字一侧少 2px,经验法则是icon-side padding = text-side padding - 2px

/* 好——图标侧更少的 padding */ .button-with-icon { padding-left: 16px; padding-right: 14px; /* 图标侧 = 文字侧 - 2px */ } /* 坏——等距 padding 让图标看起来被推得太靠右 */ .button-with-icon { padding: 0 16px; }
<button className="pl-4 pr-3.5 flex items-center gap-2"> <span>Continue</span> <ArrowRightIcon /> </button>

播放三角:三角形图标的几何中心不等于视觉中心,需向右微移:

.play-button svg { margin-left: 2px; /* 向右偏移以补偿三角形重心 */ }

不对称图标(星、箭头、caret):最佳方案是直接改 SVG(调整 viewBox 或 path),让组件代码里不再需要额外 margin;兜底方案才用ml-px之类的微调类。

3. 阴影优先于边框(Shadows Over Borders)

注意:在 Sim 仓库中,不要把中性边框换成0 0 0 1px的阴影环。Sim 有自己完整的 token 体系:抬升效果用shadow-subtle/shadow-medium/shadow-overlay/shadow-card阴影 token,中性边线与分隔线保持--border边框(见 .claude/rules/sim-styling.md 的 "Line weight" 一节)。下面的--shadow-border模式是给没有这套 token 系统的其他项目用的。

在 apps/sim/app/_styles/globals.css 中可以看到 Sim 实际的阴影 token 定义:

--shadow-subtle: 0 2px 4px 0 rgba(0, 0, 0, 0.08); --shadow-medium: 0 4px 12px rgba(0, 0, 0, 0.1); --shadow-overlay: 0 10px 30px rgba(0, 0, 0, 0.11); --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.04);

--border的行宽来自--border-width:默认1px,在@media (min-resolution: 2dppx)下降到0.5px,让高密度屏得到真正的 hairline。注意sim-styling.md特别强调:线条的重量要调--border的颜色,不要调--border-width——浏览器会把边框取整到整设备像素,(0, 1px)区间内的值在 2dppx 屏幕上都会塌缩成同一根单像素线;而且永远不要用shadow-[inset_0_-1px_0_…]手工画线,box-shadow 有自己的宽度、无法跟随 token,会以双倍重量渲染。

没有 token 体系的项目,用阴影代替边框实现卡片/容器的深度感:

/* 浅色模式:三层阴影——1px 边框环 + 轻微抬升 + 环境深度 */ :root { --shadow-border: 0px 0px 0px 1px rgba(0, 0, 0, 0.06), 0px 1px 2px -1px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(0, 0, 0, 0.04); --shadow-border-hover: 0px 0px 0px 1px rgba(0, 0, 0, 0.08), 0px 1px 2px -1px rgba(0, 0, 0, 0.08), 0px 2px 4px 0px rgba(0, 0, 0, 0.06); } /* 深色模式:简化为单层白色环,深色背景上看不到分层深度阴影 */ --shadow-border: 0 0 0 1px rgba(255, 255, 255, 0.08); --shadow-border-hover: 0 0 0 1px rgba(255, 255, 255, 0.13);

配合 hover 过渡平滑切换:

.card { box-shadow: var(--shadow-border); transition-property: box-shadow; transition-duration: 150ms; transition-timing-function: ease-out; } .card:hover { box-shadow: var(--shadow-border-hover); }

什么时候用阴影、什么时候保留边框:

用阴影用边框
卡片、有深度的容器列表项之间的分隔线
带边框样式的按钮表格单元格边界
抬升元素(下拉、弹窗)表单输入框轮廓(可访问性考虑)
多变背景上的元素密集 UI 中的 hairline 分隔
hover/focus 抬升效果

不要把这条规则套到分隔线上border-bborder-t、侧边线),凡是职责是「布局分隔」而非「元素深度」的边框都应保持边框。

4. 图片描边(Image Outlines)

给图片加一条低透明度的 1px 描边,在周围元素都用边框或阴影的设计系统里能统一深度感。

  • 浅色模式:纯黑rgba(0, 0, 0, 0.1)
  • 深色模式:纯白rgba(255, 255, 255, 0.1)
  • 带灰度的中性色(slate-900、zinc-900、#0a0a0a#f5f5f7)以及 accent/ink 色会吸收周围表面颜色,在图片边缘看起来像脏污——描边是中性分隔符,不是主题元素。
img { outline: 1px solid rgba(0, 0, 0, 0.1); outline-offset: -1px; /* 内嵌,不增加布局尺寸 */ }

Tailwind(含暗色模式):

<img className="outline outline-1 -outline-offset-1 outline-black/10 dark:outline-white/10" src={src} alt={alt} />

注意只用outline-black/10/outline-white/10,不要用outline-slate-*outline-zinc-*outline-neutral-*等任何灰度刻度。为什么用 outline 而不是 border?outline不参与布局(不增加宽高),outline-offset: -1px保持内嵌,图片尺寸不变。

5. 最小点击区域(Minimum Hit Area)

交互元素需要至少 40×40px(WCAG 建议 44×44px)的点击区域。如果可见元素更小(比如 20×20 的复选框),用伪元素扩展命中区:

.checkbox { position: relative; width: 20px; height: 20px; } .checkbox::after { content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 40px; height: 40px; }
<button className="relative size-5 after:absolute after:top-1/2 after:left-1/2 after:size-10 after:-translate-1/2"> <CheckIcon /> </button>

碰撞规则:扩展后的命中区如果与另一个交互元素重叠,就缩小伪元素——但要在不碰撞的前提下尽量大。两个交互元素的命中区永远不能重叠。

二、Typography:换行、平滑与数字

6. 文本换行:balance 与 pretty

text-wrap: balance让文本均匀分布到各行,避免标题和短文本出现孤词。注意它只在 6 行以内(Chromium)或 10 行以内(Firefox)生效——平衡算法计算代价高,浏览器限制其只作用于短文本。对长段落使用会被静默忽略,属于浪费意图。

/* 好——短文本均匀分行 */ h1, h2, h3 { text-wrap: balance; } /* 坏——长段落上用 balance(被静默忽略) */ .article-body p { text-wrap: balance; }

Tailwind:text-balance

text-wrap: pretty防止段落最后一行悬挂孤词,它不追求行等宽,只保证末行不会短得尴尬。对任何长度文本都没有行数限制,是短到中等文本(段落、描述、说明文字、列表项、卡片文字)的默认选择。对 10 行以上的超长文本,两者都不建议用,浏览器默认换行即可,避免不必要的布局开销。

<p className="text-pretty"> 一个不会在最后一行留下孤词的短段落。 </p>

Tailwind:text-pretty

场景使用
标题、需要均匀分布的场景text-wrap: balance
短到中等文本:段落、描述、说明、UI 文字text-wrap: pretty
长文本(10 行以上)、代码块、预格式化文本都不用,保持默认

7. 字体平滑(macOS)

macOS 上文本默认渲染偏重。在根布局上应用 antialiased 平滑,让所有文本更清晰、更细:

html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
<html className="antialiased">

在根上只做一次,不要逐个元素应用,否则会出现「标题平滑、正文不平滑」的不一致。该属性只影响 macOS 渲染,其他平台忽略它,可以安全地全局应用。

8. Tabular Numbers(等宽数字)

动态更新的数字(计数器、价格、计时器、表格列)用 tabular-nums 让所有数字等宽,防止数值变化时布局跳动:

.counter { font-variant-numeric: tabular-nums; }
<span className="tabular-nums">{count}</span>
用 tabular-nums不要用
计数器、计时器静态展示数字
会更新的价格装饰性大数字
含数字的表格列电话号码、邮编
数字转场动画版本号(v2.1.0)
计分板、仪表盘

注意事项:某些字体(如 Inter)开启该属性后会改变数字外观——尤其是数字1会变宽并居中。这是预期行为且通常有利于对齐,但要在具体字体上确认效果。

三、Animations:可中断、错峰与进出场

9. 可中断动画(Interruptible Animations)

用户会在交互中途改变意图,动画不可中断会让界面「像坏了一样」。

CSS TransitionsCSS Keyframe Animations
行为向最新状态插值按固定时间线运行
可中断是——中途重新定向否——从头重新开始
用途交互状态变化(hover、toggle、开合)只运行一次的分段序列(入场、loading)
时长随剩余距离自适应与状态无关的固定时长
/* 好——toggle 用可中断 transition */ .drawer { transform: translateX(-100%); transition: transform 200ms ease-out; } .drawer.open { transform: translateX(0); } /* 动画中途再次点击会平滑反向——无卡顿 */ /* 坏——交互元素用 keyframe 动画 */ .drawer.open { animation: slideIn 200ms ease-out forwards; } /* 中途关闭会跳变或重播——感觉坏了 */

规则:交互元素永远优先用 CSS transition;keyframes 只留给一次性序列。

10. 入场动画:拆分 + 错峰(Split and Stagger)

不要动画一个巨大的容器。把内容拆成语义块,每块单独动画,块间约100ms延迟。

步骤:1)拆分为逻辑组(标题、描述、按钮);2)错峰约 100ms;3) 标题可拆到单词级、约 80ms 错峰;4) 组合opacity+blur+translateY完成入场效果。

// Motion(Framer Motion)——错峰入场 function PageHeader() { return ( <motion.div initial="hidden" animate="visible" variants={{ visible: { transition: { staggerChildren: 0.1 } }, }} > <motion.h1 variants={{ hidden: { opacity: 0, y: 12, filter: "blur(4px)" }, visible: { opacity: 1, y: 0, filter: "blur(0px)" }, }} > Welcome </motion.h1> <motion.p variants={{ hidden: { opacity: 0, y: 12, filter: "blur(4px)" }, visible: { opacity: 1, y: 0, filter: "blur(0px)" }, }} > A description of the page. </motion.p> <motion.div variants={{ hidden: { opacity: 0, y: 12, filter: "blur(4px)" }, visible: { opacity: 1, y: 0, filter: "blur(0px)" }, }} > <Button>Get started</Button> </motion.div> </motion.div> ); }

纯 CSS 错峰:

.stagger-item { opacity: 0; transform: translateY(12px); filter: blur(4px); animation: fadeInUp 400ms ease-out forwards; } .stagger-item:nth-child(1) { animation-delay: 0ms; } .stagger-item:nth-child(2) { animation-delay: 100ms; } .stagger-item:nth-child(3) { animation-delay: 200ms; } @keyframes fadeInUp { to { opacity: 1; transform: translateY(0); filter: blur(0); } }

11. 离场动画要克制(Subtle Exit Animations)

离场应比入场更柔和、更不抢注意力——用户注意力正要移向下一件事。

推荐——小幅固定 translateY

<motion.div exit={{ opacity: 0, y: -12, filter: "blur(4px)", transition: { duration: 0.15, ease: "easeIn" }, }} > {content} </motion.div>

全尺寸离场(仅当空间上下文重要时)——例如卡片回到列表、抽屉关闭:

<motion.div exit={{ opacity: 0, x: "-100%", transition: { duration: 0.2, ease: "easeIn" }, }} > {content} </motion.div>
/* 好——克制离场 */ .item-exit { opacity: 0; transform: translateY(-12px); transition: opacity 150ms ease-in, transform 150ms ease-in; } /* 坏——夸张离场抢走注意力 */ .item-exit { opacity: 0; transform: translateY(-100%) scale(0.5); transition: all 400ms ease-in; } /* 坏——完全没有离场动画(元素直接消失) */ .item-exit { display: none; }

要点:用小而固定的translateY(如-12px)而非容器整高;保留一点方向性移动指示元素去向;离场时长应短于入场(150ms vs 300ms);不要完全去掉离场动画——克制的动效保留空间上下文。

12. 上下文图标动画(Contextual Icon Animations)

图标随上下文出现/消失(hover、状态切换)时,用opacityscaleblur动画,而不是简单切换可见性。默认值:scale0.25→1、opacity0→1、blur4px→0{ type: "spring", duration: 0.3, bounce: 0 }

Motion 方案

import { AnimatePresence, motion } from "motion/react"; function IconButton({ isActive, icon: Icon }) { return ( <button> <AnimatePresence mode="popLayout"> <motion.span key={isActive ? "active" : "inactive"} initial={{ opacity: 0, scale: 0.25, filter: "blur(4px)" }} animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }} exit={{ opacity: 0, scale: 0.25, filter: "blur(4px)" }} transition={{ type: "spring", duration: 0.3, bounce: 0 }} > <Icon /> </motion.span> </AnimatePresence> </button> ); }

纯 CSS 交叉淡入(无 Motion 依赖):两个图标都留在 DOM 里,一个绝对定位叠在另一个上面。切换状态即交叉淡入——进入的图标从0.25放大、退出的图标缩到0.25,都带 opacity 和 blur。非绝对定位的图标决定布局尺寸,绝对定位的图标叠放其上、不参与文档流。

function IconButton({ isActive, ActiveIcon, InactiveIcon }) { return ( <button> <div className="relative"> <div className={cn( "absolute inset-0 flex items-center justify-center", "transition-[opacity,filter,scale] duration-300", "cubic-bezier(0.2, 0, 0, 1)", isActive ? "scale-100 opacity-100 blur-0" : "scale-[0.25] opacity-0 blur-[4px]" )} > <ActiveIcon /> </div> <div className={cn( "transition-[opacity,filter,scale] duration-300", "cubic-bezier(0.2, 0, 0, 1)", isActive ? "scale-[0.25] opacity-0 blur-[4px]" : "scale-100 opacity-100 blur-0" )} > <InactiveIcon /> </div> </div> </button> ); }

怎么选:检查项目package.json里有没有motionframer-motion。有就用 Motion 方案(Sim 有framer-motion ^12.5.0);没有就用 CSS 交叉淡入——不要只为图标过渡引入新依赖

Motion (Framer Motion)CSS transitions(双图标常驻 DOM)
入场动画
离场动画有(经AnimatePresence有(交叉淡入——图标永不移除)
弹簧物理无——用cubic-bezier(0.2, 0, 0, 1)近似
适用场景项目已用motion/react无 Motion 依赖,或想控制包体积
该动画不该动画
hover 才出现的图标(操作按钮)静态导航图标
状态切换图标(播放→暂停、喜欢→已喜欢)装饰性图标
上下文工具栏里的图标始终可见的图标
加载/成功状态指示器图标标签(图标旁的文字)

13. 按压缩放(Scale on Press)

点击时轻微缩小(约 0.96–0.97)给按钮触觉反馈。用 CSS transition 保证可中断——用户在按压中途松手也能平滑复位。不是每个按钮都需要;给按钮组件加staticprop 在动效会分散注意力时禁用缩放。

.button { transition-property: scale; transition-duration: 150ms; transition-timing-function: ease-out; } .button:active { scale: 0.96; }
<button className="transition-transform duration-150 ease-out active:scale-[0.96]"> Click me </button>
<motion.button whileTap={{ scale: 0.96 }}> Click me </motion.button>

Static Prop 模式

const tapScale = "active:not-disabled:scale-[0.96]"; function Button({ static: isStatic, className, children, ...props }) { return ( <button className={cn( "transition-transform duration-150 ease-out", !isStatic && tapScale, className, )} {...props} > {children} </button> ); } <Button>Click me</Button> {/* 按压缩放 */} <Button static>Submit</Button> {/* 无缩放 */}

Sim 仓库落点:SKILL.md 明确指出,按压反馈应归属 emcn 的Button/Chipchrome(packages/emcn),而不是消费方类。查 packages/emcn/src/components/button/button.tsx 可以看到当前buttonVariants只有transition-colors、hover 态与 focus 处理,目前两个组件都没有实现按压缩放——正确的做法是去packages/emcn里提这个能力,而不是在每个调用点手写 transform 类。

14. 页面加载跳过入场动画

AnimatePresence上用initial={false}防止首次渲染触发入场动画。已经处于默认状态的元素不应在页面加载时动起来,只在后续状态变化时动画。

{/* 好——图标挂载时不动画,只在状态切换时动画 */} <AnimatePresence initial={false} mode="popLayout"> <motion.span key={isActive ? "active" : "inactive"} initial={{ opacity: 0, scale: 0.25, filter: "blur(4px)" }} animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }} exit={{ opacity: 0, scale: 0.25, filter: "blur(4px)" }} > <Icon /> </motion.span> </AnimatePresence>

适用于:图标切换、toggle、标签页、分段控件——任何页面加载时有默认状态的元素。

何时会坏:组件依赖initialprop 建立首次入场(如错峰 hero、加载态)时不要用initial={false}——那会跳过整个入场。应用前请做一次整页刷新验证。

四、Performance:过渡收敛与 GPU 提示

15. 永远不要用transition: all

transition: all迫使浏览器监听每一个属性的变化:会在你没打算动画的属性(颜色、padding、阴影)上产生意外过渡,并阻止浏览器优化。

/* 好——只过渡变化的部分 */ .button { transition-property: scale, background-color; transition-duration: 150ms; transition-timing-function: ease-out; } /* 坏——过渡一切 */ .button { transition: all 150ms ease-out; }
// 好——显式属性 <button className="transition-[scale,background-color] duration-150 ease-out"> // 坏——过渡一切 <button className="transition duration-150 ease-out">

Tailwindtransition-transform注意:它映射为transition-property: transform, translate, scale, rotate——覆盖所有 transform 相关属性,不只是transform。只动 transform 时用它;多个非 transform 属性用方括号语法transition-[scale,opacity,filter]

16.will-change要克制

will-change提示浏览器把元素预提升到独立 GPU 合成层。没有它,浏览器只在动画开始时才提升——那一次性的层提升会在第一帧造成微卡顿。特别适合元素在改变scalerotation或用transform移动的场景。其他属性帮助不大——浏览器本来就没法在 GPU 上合成它们。

/* 好——受益于 GPU 合成的具体属性 */ .animated-card { will-change: transform; } /* 坏——永远不要用 will-change: all */ .animated-card { will-change: all; } /* 坏——本来就不能 GPU 合成的属性 */ .animated-card { will-change: background-color, padding; }
属性可 GPU 合成值得用will-change
transform
opacity
filter(blur、brightness)
clip-path
topleftwidthheight
backgroundbordercolor

何时跳过:现代浏览器自身优化已经很好。只在观察到首帧卡顿时才加will-change——Safari 尤其受益。不要给每个动画元素都预先加上;每多一个合成层都占内存。

常见错误速查

错误修复
父子元素同样圆角计算outerRadius = innerRadius + padding
图标看起来不居中光学微调(padding 或直接改 SVG)
区块之间硬边框Sim 内用--borderhairline token;其他项目用带透明度的分层box-shadow
生硬的进出场动画拆分、错峰、离场保持克制
数字引起布局跳动应用tabular-nums
macOS 上文字偏重根元素应用antialiased
页面加载就播动画AnimatePresenceinitial={false}
元素上有transition: all指定精确属性
首帧动画卡顿will-change: transform(克制)
小控件点击区域太小用伪元素扩展到 40×40px

实践:UI Review 的输出格式与检查清单

Review 输出格式:以 Markdown 表格呈现改动,带Before / After两列;每条原则一个表格并加标题;每行一个 diff;当代码片段无法自解释时,引用文件和具体属性。如果某条原则 review 后无需改动,直接省略该表格——空表格只会增加噪音。

示例:

同心圆角
BeforeAfter
卡片rounded-xl+ 内按钮rounded-xlp-2卡片rounded-2xl(12 + 8)、内按钮rounded-lg
两个嵌套表面都是border-radius: 16px外层24px,内层16px8pxpadding
Tabular 数字
BeforeAfter
动画计数器<span>{count}</span><span className="tabular-nums">{count}</span>
计时器默认数字根元素加font-variant-numeric: tabular-nums
按压缩放
BeforeAfter
<button className="...">active:scale-[0.96] transition-transform
按压scale(0.9)提升到scale(0.96)——低于0.95感觉夸张

完整 Review 检查清单(逐项核对):

  • 嵌套圆角元素使用同心圆角
  • 图标是光学居中,不只是几何居中
  • 合适场景用阴影而非边框
  • 入场动画已拆分并错峰
  • 离场动画克制
  • 动态数字用 tabular-nums
  • 已应用字体平滑
  • 标题用 text-wrap: balance
  • 图片有细微描边
  • 按钮在合适场景使用按压缩放
  • 默认态元素上的AnimatePresence使用initial={false}
  • transition: all——只有精确属性
  • will-change只在 transform/opacity/filter 上,绝不用于all
  • 交互元素至少有 40×40px 点击区域

结合 Sim 仓库落地的补充说明

上述原则在 Sim 代码库中有几处明确的「本地化」约定,落地时务必遵循:

  1. 阴影 token 优先:抬升用shadow-subtle/shadow-medium/shadow-overlay/shadow-card,定义见 apps/sim/app/_styles/globals.css;中性边线保持--border,见 .claude/rules/sim-styling.md 的 "Line weight" 一节。sim-styling.md同时规定过渡类用transition-colors(交互状态)、禁止内联样式、精确值优于近似值(如h-[26px]而非h-6,但字号必须用具名 token)。

  2. 按压缩放的归属:应放在packages/emcnButton/Chipchrome 中统一实现(当前 button.tsx 尚无该能力),不要在消费方逐个手写active:scale-*

  3. 图标渲染细节sim-styling.md规定图标默认size-[14px],等宽高用size-*而非h-N w-N;这与光学对齐原则直接呼应。

  4. Motion 依赖apps/sim/package.json已含framer-motion ^12.5.0,图标切换、进出场可直接使用motion/reactAnimatePresence+ 弹簧过渡;若目标代码路径不允许引入 Motion,则用上文「双图标常驻 DOM + CSS 交叉淡入」方案。

这 16 条原则是一套可以反复套用的「打磨清单」:构建新组件时逐条对照,review 代码时按表格输出 Before/After。细节不会单独成就一个界面,但一打正确的细节叠加,就是高级感本身

【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 23:54:01

基于Hadoop+Spark+Hive的空气质量预测系统设计与优化

1. 项目背景与核心价值空气质量预测系统是当前智慧城市建设的刚需场景。我在某环保科技公司参与过类似项目&#xff0c;发现传统单机算法在应对TB级气象和污染数据时存在明显瓶颈。这套基于HadoopSparkHive的技术栈&#xff0c;恰好解决了三个行业痛点&#xff1a;海量数据存储…

作者头像 李华
网站建设 2026/9/11 23:53:52

daisyUI Stat 组件完全指南:用 stats 块优雅展示数字与数据

daisyUI Stat 组件完全指南&#xff1a;用 stats 块优雅展示数字与数据 【免费下载链接】daisyui &#x1f33c; &#x1f33c; &#x1f33c; &#x1f33c; &#x1f33c;  The most popular, free and open-source Tailwind CSS component library 项目地址: https://git…

作者头像 李华
网站建设 2026/9/11 23:53:37

基于AD5293数字电位器的STM32 SPI校准系统设计实践

在仪器仪表和标定设备里&#xff0c;电阻从来不是配角。我去年接手一个传感器标定台改造项目&#xff0c;原方案用的是机械电位器&#xff0c;每块板子下线前都要工人拿螺丝刀反复调&#xff0c;调完还得点胶固定&#xff0c;不仅慢&#xff0c;而且温漂、震动之后阻值会慢慢跑…

作者头像 李华
网站建设 2026/9/11 23:53:23

长沙AI培训哪家好,25年老牌职业技能培训机构官方备案口碑靠谱

正文摘要本文从官方备案资质、办学积淀深度、权威认证背书、产业合作基础、办学成果口碑五个维度&#xff0c;拆解长沙 AI 培训的机构靠谱度差异&#xff0c;结合机构官方背景与办学资源&#xff0c;为学习 AI 技术的大学生、转行者筛选正规机构提供客观参考依据。信息来源&…

作者头像 李华
网站建设 2026/9/11 23:51:54

树莓派人脸识别系统实战:Haar锁定人脸、百度云识别与MySQL入库

简介&#xff1a;基于树莓派的人脸识别系统&#xff0c;是一套面向毕业设计、课程项目与入门级人工智能开发者的完整Python工程。工程围绕树莓派摄像头完成人脸采集、本地存储与云端识别闭环&#xff0c;包含五个可运行脚本、一个Markdown说明及一份Word教程。其中五个脚本分别…

作者头像 李华