关于 repeated-component-alignment
Repeated Component Alignment 是面向界面开发者的设计技能,适用于卡片、行和信息块等重复元素。它介绍基于固定槽位的布局方法,使内容长度变化时仍保持对齐,并确保可查看被截断的文本。
主要功能
- 01为重复元素定义固定槽位
- 02固定各实例中的锚点
- 03为可选槽位预留空间
- 04截断文本并提供完整内容
- 05处理图片与本地化
适用场景
- 01对齐含不同长度文本的卡片
- 02保持表格行与控件稳定
- 03避免翻译后的标签破坏布局
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dembrandt/ dembrandt-skills --skill repeated-component-alignment安装后在 Claude Code 中输入/repeated-component-alignment确认已加载。
详细介绍
重复组件对齐
当一个组件被反复渲染时——无论是卡片网格、列表、表格、导航菜单、一排 KPI 磁贴还是信息流——它就不再是单个盒子,而会成为一种模式。模式的价值在于节奏:眼睛只需学习一次布局,就能在每个实例中扫描同一个位置(标题行、价格行、操作行)。除非组件经过设计,能够吸收内容长度的差异,否则内容长度的变化就会打乱这种节奏。
这个原则具有普遍性。卡片是最常见的情况,但同样的规则也适用于列表行、表格单元格、导航项、磁贴、评论条目、仪表盘组件、搜索结果——任何重复出现的内容。把它们都视为固定槽位模型,而不是自由格式的容器。
目标:**无论内容长度如何,各个实例的外观都保持一致。**这既涉及内容制作(按目标长度撰写),也涉及布局工程(构建能够容纳差异的槽位)。本指南讲解布局这一部分,以及两者的交汇之处。
槽位模型
为槽位统一命名,并将其视为每个实例都必须遵守的约定。以下以产品卡片为例:
┌──────────────────────┐
│ [media] │ ← fixed aspect ratio
│ │
├──────────────────────┤
│ ● Badge │ ← optional slot, space reserved
│ Title │ ← clamp to N lines
│ Subtitle / meta │
│ │
│ Description text… │ ← flexible slot, grows
│ │
├──────────────────────┤
│ €19.99 Read more│ ← anchor, pinned to bottom
└──────────────────────┘
同样的三条规则也适用于列表行(头像 · 姓名 · 元信息 · 状态固定在右侧)、KPI 磁贴(标签 · 大号数字 · 趋势固定在底部)或搜索结果(标题 · URL · 截断的摘要):
- 每个槽位的位置都是固定的,无论某个实例是否包含该槽位的内容。
- 由一个槽位吸收差异(通常是描述或摘要)。其他槽位都固定或截断。
- 锚定元素固定在指定位置——主要操作或数值(CTA、价格、状态、趋势)在每个实例中的位置都相同,不受其上方或旁边内容多少的影响。
根据状态切换的控件
一个单元格可能会根据状态显示不同的控件——先是按钮,然后是输入框,再变成数值或链接——但它仍然是一个槽位。为每个位置设定固定宽度,并让其中的控件拉伸以填满该位置。每一行、每种状态下的同一位置都承担相同的作用。按内容调整尺寸的控件会随着标签改变宽度,导致行重新排布、相邻控件在光标下方移动,整列也不再像一组列表。
对齐锚点
最常见的问题是:文本长度不同,导致锚定元素(例如“阅读更多”链接、价格或状态标签)在每个实例中的位置不一致。解决方法是让可伸缩槽位扩展,并将锚点推到固定边缘。
纵向布局(卡片、磁贴)——将页脚固定在底部:
.item {
display: flex;
flex-direction: column;
height: 100%; /* fill the grid/flex track */
}
.item__media {
aspect-ratio: 4 / 3; /* never let media height vary */
object-fit: cover;
}
.item__description {
flex: 1; /* the flexible slot absorbs the slack */
}
.item__footer {
margin-top: auto; /* pin the anchor (price + CTA) to the bottom */
}
要让同级实例高度相等,容器轨道必须拉伸它们——CSS Grid 和 Flex 默认会这样做(align-items: stretch)。然后,height: 100% 会让每个实例填满其轨道,而 margin-top: auto 会将所有页脚对齐。
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: var(--space-4);
/* align-items: stretch is the default — do not override it */
}
横向布局(列表行、表格单元格)——将锚点固定在右侧:
.row {
display: flex;
align-items: center;
gap: var(--space-3);
}
.row__title {
flex: 1; /* title absorbs the width variance */
min-width: 0; /* REQUIRED for the title to be allowed to shrink/ellipsis */
}
.row__status {
margin-left: auto; /* status pinned to the right edge */
}
不要用硬编码的 height 强制统一尺寸——由自然高度最大的实例决定整体尺寸,超出的内容会被裁切或溢出。让轨道拉伸,并固定锚点位置。
为可选槽位预留空间
某个槽位只在部分实例中出现——例如徽章、折扣标签或“已验证”标记——会导致有该槽位的实例中,其后的所有内容发生偏移,从而破坏对齐。有两种修复方法:
- 预留槽位——始终以固定尺寸渲染容器;没有内容时也保留空容器:
.item__badge-row {
min-height: 24px; /* always occupies the row */
}
- 叠加槽位——使用绝对定位,使其不参与文档流:
.item__badge {
position: absolute;
top: var(--space-2);
left: var(--space-2);
}
当元素是行内元信息时,预留槽位;当元素是媒体上的标记(促销、新品)时,使用叠加。无论采用哪种方式,其后方的槽位都必须在每个实例中从相同位置开始。
限制溢出文本——并让用户能查看完整内容
当某个区域必须保持固定尺寸、但内容长度不一时,可将文本限制为指定行数,并用省略号标示截断位置。这样可以保持布局稳定。但截断会隐藏信息——务必确保用户能查看完整内容。
多行文本——限制为 N 行:
.clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
单行值(名称、SKU、路径)——使用省略号:
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0; /* needed inside a flex row to allow shrinking */
}
提供查看完整内容的方式。 如果无法查看完整文本,截断或省略后的字符串就会成为易用性陷阱。请提供以下方式:
- 原生工具提示,用于纯文本:
<span title="Full value here">…</span>。零成本,随处可用,但仅支持悬停(不支持触屏),且无法设置样式。 - 自定义工具提示,适用于需要支持触屏、设置样式或展示富内容的场景。
- 原地展开,适用于完整内容本身就是重点的场景——使用“阅读更多”切换按钮展开当前内容,或打开详情视图,而不是永久隐藏用户需要的文本。
只有在文本确实被截断时才添加工具提示——对完整显示的字符串设置
title会产生多余的悬停提示。检测溢出(scrollWidth > clientWidth),并有条件地设置title。
动态生成的列表最容易忽略这一点。 导航栏、目录、树形视图或侧边栏会将来自数据的标签渲染到固定宽度的列中,因此总会有一些行被截断。为每一行的完整标签设置 title(这是“有条件地设置”的例外:列表很长、宽度固定,而且逐行检测溢出的成本高于多余的悬停提示)。先解码文本——根据渲染后的 HTML 构建 title 会让 & 和 " 原样显示出来,观感比截断更差。
处理长度不定内容的优先顺序:
- 控制内容长度——最简洁的解决方法。为内容作者设定目标长度(例如标题不超过 60 个字符,摘要不超过 120 个字符),这样大多数值就不需要截断。布局技巧是安全网,而非首选方案。
- 限制长度并提供查看完整内容的方式——适用于无法保证内容长度的情况(用户生成内容、第三方信息流、i18n 文本扩展)。
- 让一个区域自由伸展——对于唯一允许长度变化的区域,用 flex 吸收长度变化,并固定其后的所有内容。
区域内的图像
只限制图像的一个维度,绝不要同时限制宽度和高度。设置宽度并使用 height: auto,或指定固定的 aspect-ratio 并使用 object-fit。同时设置两个约束会导致变形。
Grid 会将未包装的图像拉伸至列宽,flex 列则会沿交叉轴拉伸图像。请为图像添加包装元素,或声明 align-self/justify-self。如果同一个徽标在一张卡片中看起来被压扁、在另一张中却正常,几乎总是这个原因。
在将标记错位归咎于 CSS 之前,先检查素材尺寸。文件中自带的内边距会导致图案偏离边框中心,任何布局规则都无法修正这一点。
标签和值列表
重复的标签/值列表应为标签列设置固定宽度:可以使用首列固定的 Grid,也可以使用表格。这样每个值都会从同一位置开始。带间距并使用分隔符号(箭头、短横线、竖线)的 flex 行无法实现对齐,而且每行都多出一个需要读者扫过的符号。
国际化说明
翻译后的文本通常会变长——德语和芬兰语的文本通常比英语长 30–40%。在英语中对齐完美的组件,在其他语言环境中可能会错乱。设计时要考虑较长的情况:限制文本长度,为可选区域预留空间,为 flex 行设置 min-width: 0,绝不要因为某个标签在源语言中能显示为一行,就假设它在其他语言中也能。
控件区域在不同状态下保持同一高度
当画布与控件争夺纵向空间时,只显示当前状态下可用的控件;其他控件则在其他状态下占据相同位置。相反,显示或隐藏控件行会使画布在每次状态变化时调整大小,产生故障感,并移动用户正在查看的内容。处于某种状态时被禁用的控件应最先让出空间。
审查清单
- 重复组件是否采用已定义的插槽模型——每个实例都以相同顺序填充相同插槽?
- 同级实例是否通过拉伸网格或弹性布局轨道保持等高,而非使用硬编码高度?
- 锚点(CTA/价格/状态/数值)是否固定——列布局使用
margin-top: auto,行布局使用margin-left: auto——以便在各实例间对齐? - 是否只有一个插槽吸收长度差异,其余插槽固定或限制长度?
- 可选插槽(徽标、标签)是否预留空间或采用覆盖方式,以免推移后续插槽?
- 媒体是否使用固定的
aspect-ratio,确保尺寸始终不变? - 每张图片是否只在一个维度上受限,并且不会被网格或弹性布局拉伸?
- 标签/数值列表是否使用固定宽度的标签列,而非使用带分隔符的弹性行?
- 多行插槽是否限制行数,单行数值是否使用省略号?
- 会截断文本的弹性行是否设置了
min-width: 0,以允许文本收缩? - 文本被截断时,是否都能通过标题/工具提示/展开查看完整内容?
- 是否仅在文本实际溢出时才显示工具提示?
- 内容编写者是否明确目标长度,使截断成为安全措施而非常态?
- 是否根据译文最长的语言检查过插槽,而不只是根据源语言?
- 单元格根据状态切换控件时,每种状态是否都填充相同的固定宽度插槽?
- 控件区域在每种状态下是否保持相同高度,且特定状态的控件是否共用插槽?