3Q工具箱
首页 / 教程中心 / 开发辅助

从布局到配色的可视化调参,Flex、Grid、颜色互转与对比度达标

开发辅助发布于 2026-09-12

布局和配色这两件事有个共同特点:靠脑内推演效率极低,靠改一行刷一次页面也慢。属性之间互相影响,参数取值又是连续的,最快的方式是把参数摆在面板上直接拖,看着结果反推规则。

这篇讲的是「拖之前需要先明白的那几条规则」,避免调出一个看着对但换个内容就塌的布局。所有面板都在浏览器里实时渲染,不需要建项目、不需要跑构建,改动只发生在你自己的页面上。

一、Flex 解决一维排列,Grid 解决二维网格

选错这一步,后面所有属性都会用得别扭。

  • Flex 是一维的。它只管一个方向上的排列:主轴方向排开,交叉轴方向对齐。子项尺寸由内容和 flex 属性协商决定,容器不预先规定格子。适合导航条、按钮组、卡片内部的图文排列、以及任何「按内容多少自然撑开」的场景。
  • Grid 是二维的。先声明行和列构成的网格,再把子项放进格子。行列尺寸由容器统一规定,子项可以跨行跨列。适合整页框架、仪表盘、商品列表这类「要求横竖都对齐」的场景。

一个判断口诀:如果你发现自己在用嵌套的 Flex 容器去模拟对齐的行列,就该换 Grid;如果你在用 Grid 却只声明了一行或一列,Flex 会更简单。

Flex与Grid布局生成可以两种模式来回切,容器属性调完点击预览区任意方块就能选中该子项单独设置 flex-growflex-shrinkflex-basisalign-selforder,Grid 模式下还能设置跨行跨列。子项数量可随时增减,专门用来验证换行与自动填充行为。

三个必须知道的细节,它们是「预览里好看、真实内容一进来就变形」的主要来源:

  • **1fr 的最小值默认是 auto**,所以一个包含长单词或长 URL 的子项会把轨道撑宽,看起来就不等分了。真正的等分要写 minmax(0, 1fr)。这是 Grid 最常见的一个坑。
  • auto-fillauto-fit 的区别只在没填满时显现auto-fill 保留空轨道,剩余空间被空格子占着;auto-fit 会把空轨道折叠掉,让现有子项撑满。只有三张卡片却想让它们铺满一行,用 auto-fit
  • flex: 1flex-grow: 1 不等价。前者是简写,同时把 flex-basis 设成了 0,意味着完全忽略内容宽度按比例分配;后者保留 flex-basis: auto,内容多的子项起点就更宽。两栏「等宽」调不出来,多半是这里。

二、居中与自适应的标准写法

居中的写法太多,实际值得记住的就这几条。

单个元素在容器内水平垂直居中,Flex 三行搞定,display: flexjustify-content: centeralign-items: center。Grid 更短:

display: grid;
place-items: center;

两端对齐、中间自动留白,用 justify-content: space-between。注意 space-aroundspace-evenly 的差别:前者每个子项左右各分一半间距,因此首尾的外侧间距是中间间距的一半;后者所有间隙完全相等。设计稿要求首尾贴边就用 space-between,要求间距视觉均匀就用 space-evenly

间距不要再用子项的 margin 拼gap 同时被 Flex 和 Grid 支持,它只作用于子项之间,不会在容器边缘多出一份间距,也不用写「最后一项去掉 margin」这种补丁。

一行放不下就换行的自适应卡片列表,Grid 一行声明即可:grid-template-columns: repeat(auto-fit, minmax(240px, 1fr))。它的含义是「每列至少 240px,能放几列放几列,剩余空间平分」,不需要任何媒体查询就能从手机排到宽屏。这是最值得记住的一条 Grid 写法。

垂直方向的「主内容撑满、页脚吸底」,容器 display: flexflex-direction: columnmin-height: 100vh,主内容区加 flex: 1。比用绝对定位或 calc 减去页脚高度可靠得多。

视觉细节则交给CSS渐变阴影圆角生成。渐变支持 linearradialconic 三种类型及其 repeating 形式,色标最多八个、各自可调颜色位置与透明度;阴影可叠加多层,box-shadowtext-shadow 都支持,还能切 inset 内阴影或改用 filterdrop-shadow 给不规则图形加影。两条经验:多层阴影是层次感的来源,通常「一层小模糊贴边 + 一层大模糊扩散」最自然;单层阴影的模糊半径超过 24px 通常只会让元素显得发灰而不是立体。预览区带棋盘格底纹,判断半透明叠加效果时靠它。conic-gradient 在较老浏览器上不支持,用于关键视觉时准备一个回退纯色。

三、Hex、RGB、HSL 互转的精度问题

三种表示法不是同一个空间的不同写法,转换过程存在真实的信息损失。

  • Hex 与 RGB 之间是无损的#FF5733 就是 rgb(255, 87, 51),两者一一对应,来回转多少次都不变。三位缩写 #F53 等价于 #FF5533,注意它是每位重复而不是补零。
  • RGB 与 HSL 之间会丢精度。RGB 每通道是 0 到 255 的整数,HSL 的色相是 0 到 360 度、饱和度和明度是百分比,转换涉及浮点运算与四舍五入。往返一次后 #3498DB 可能变成 #3498DA,肉眼看不出但代码里比对字符串会不相等。所以设计规范里请固定一种表示法作为唯一真值,另一种只在临时调色时用。
  • 灰色的色相是不确定的。饱和度为 0 时色相没有意义,转换实现通常返回 0,于是「灰色的色相是红色」这种看起来荒谬的结果是正常的。给灰色调整色相不会有任何视觉变化,除非同时给一点饱和度。
  • 透明度是独立的第四个通道rgbahsla 的 alpha 不参与色相饱和度明度的换算,八位 Hex 的后两位同样是 alpha。半透明色叠在不同背景上呈现完全不同的实际颜色,这一点在算对比度时尤其要注意。

颜色代码转换支持这三种格式互转,输入任意一种即可拿到其他表示。日常用途很明确:设计稿给的是 Hex,而调整明暗、生成同色系的深浅阶梯必须在 HSL 里做,因为只有 HSL 把「颜色本身」和「多亮」分成了两个可独立调节的维度。

四、WCAG 的 4.5:1 与 3:1 到底在说什么

对比度是一个比值,由两个颜色的相对亮度算出:(L1 + 0.05) / (L2 + 0.05),其中 L 是按 sRGB 分量做伽马校正后加权求和的相对亮度。取值范围从 1:1(两色相同)到 21:1(纯黑与纯白)。

它衡量的是明暗差异,不是色彩差异。这带来一个反直觉但重要的结论:红配绿的对比度可能很低。两个色相截然不同但亮度接近的颜色,在数值上不达标,在色觉障碍用户眼里也确实难以区分。所以「看起来很跳」不等于「可读」。

WCAG 2.x 的几档要求各有适用范围:

  • AA 级正文 4.5:1。这是绝大多数产品应当满足的基线,也是无障碍法规通常引用的档位。
  • AA 级大号文字 3:1。大号文字指 18pt 常规或 14pt 粗体以上,换算成 CSS 像素约为 24px 常规、18.5px 粗体。别把 16px 的正文按大号标准放宽,这是最常见的自我欺骗。
  • AAA 级正文 7:1、AAA 级大号文字 4.5:1。要求更严,通常用于面向老年用户或视力障碍用户的产品。
  • 非文本元素 3:1。图标、表单输入框边框、焦点指示环也在要求范围内。浅灰边框的输入框在白底上往往只有 1.5:1,用户根本看不出哪里可以输入——这是实际项目里最容易漏掉的一项。

颜色对比度检查会一次给出上面五行判定,每行明确通过或不通过,并用 12px、16px、24px 三种字号以及按钮、边框的真实样式做预览。只看数字容易忽略观感,只看观感容易高估自己的眼睛,两者都要看。计算在本地完成,配色方案不会被上传或统计。

五、改色的正确方向是只动明度

对比度不达标时,最省事也最错误的做法是把文字改成纯黑或纯白。这会让品牌色消失,一个页面改几处就变得毫无个性。

正确方向是在 HSL 空间里保持色相与饱和度不变,只沿明度方向调整,并且朝远离背景亮度的那一侧走:浅色背景上把文字压暗,深色背景上把文字提亮。这样调出来的颜色仍然是原设计的同一个色调,只是深浅不同,放在色板里是自然的一阶。工具的「自动修正」正是按这个策略实现的,它在 HSL 空间沿明度逼近 4.5:1,优先朝远离背景的方向调,所以结果不会退化成黑白。

配套的几条实践:

  • 先固定背景,再调前景。背景通常承担更多设计约束(品牌色块、图片),前景文字色是更廉价的调整对象。
  • 深色模式不是把颜色取反。用「交换」功能快速检查反色方案的对比度,通常需要单独一套明度更高、饱和度略低的前景色,直接反转会得到刺眼的高饱和色。
  • 文字压在图片或渐变上时按最不利区域取色。整张图的平均亮度没有意义,取文字实际覆盖到的最亮(或最暗)局部去测。稳妥做法是加一层半透明遮罩或文字阴影,把局部亮度拉到可控范围。
  • 不要只靠颜色传递信息。表单错误只标红、图表只用颜色区分系列,对色觉障碍用户等于没有信息。补一个图标、一段文字或不同的线型。
  • 禁用态是唯一的例外。WCAG 对禁用控件不作对比度要求,但也别做到完全看不见,否则用户会以为页面坏了。

常见问题

Grid 的列宽明明写了 1fr 却不等分,是浏览器 bug 吗? 不是。1fr 的最小尺寸默认为 auto,内容更宽的子项会把轨道撑开。改成 minmax(0, 1fr) 即可强制等分,代价是过长的内容会溢出,需要配合 overflowword-break 处理。

Flex 的子项为什么被压得比内容还窄? flex-shrink 默认为 1,空间不足时所有子项按比例收缩,而收缩的下限受 min-width 影响,默认 auto 但在某些嵌套情况下会失效。给不希望被压缩的子项设 flex-shrink: 0,或显式给一个 min-width

同一个颜色在设计稿和浏览器里看着不一样? 先排除三件事:设计稿的色彩配置文件是否为 sRGB、显示器是否开启了广色域或护眼模式、以及元素上是否叠了半透明层或混合模式。CSS 的颜色值本身没有歧义,差异来自渲染链路。

对比度算出来达标,为什么用户还是说看不清? 对比度只覆盖明暗一个维度。字号过小、字重过细、行高过密、使用了极细的西文字体,都会在数值达标的情况下依然难读。数值是底线不是目标,最终要在真实字号下用眼睛确认。

文中用到的工具

同类教程