首页
/ freeCodeCamp 响应式 Web 设计:用 CSS hsl() 调整颜色色调(Tone)的完整实战

freeCodeCamp 响应式 Web 设计:用 CSS hsl() 调整颜色色调(Tone)的完整实战

2026-09-06 13:33:17作者:裘晴惠Vivianne

本篇基于 freeCodeCamp 官方课程中的挑战文档 Adjust the Tone of a Color 展开。你将学到 CSS hsl() 函数中饱和度(saturation)与明度(lightness)两个参数如何决定颜色的色调变化(tint / shade / tone),并完整复现该课程中“给 nav 元素设置一个更暗青色背景”的实战题目:从初始代码、解题思路,到测试断言 rgb(13,115,115) 的数值验证过程。

该挑战在 freeCodeCamp 课程中的位置

这道题属于 freeCodeCamp 课程结构中的 Responsive Web Design 超模块(superblock)下的 Applied Visual Design 模块:

  • 超模块定义见 responsive-web-design.json,其中 applied-visual-design 排在 basic-html-and-html5basic-css 之后,即学习者已经掌握 HTML 与 CSS 基础后进入的“视觉设计应用”阶段;
  • 模块内的挑战顺序定义在 applied-visual-design.json,本挑战 587d78a4367417b2b2512ad5 位于“Learn about Complementary Colors”“Adjust the Hue of a Color”等颜色理论题之后、“Create a Gradual CSS Linear Gradient”之前,课程编排上正是“先懂色相 → 再调色调 → 最后做渐变”的递进路线;
  • 前序挑战 Adjust the Hue of a Color 已经介绍了 hsl() 的三个参数与常用色相取值表,本文在“参数速查”一节中会引用该背景知识。

背景知识:hsl() 的三个参数

颜色具有色相(hue)、饱和度(saturation)、明度(lightness)三个特征。CSS3 引入 hsl() 函数,允许直接通过这三个特征指定颜色:

  • 色相(h):即通常所说的“颜色”本身,取值 0–360,表示颜色在色轮上的角度。
  • 饱和度(s):颜色中灰色分量的多少,以百分比表示,100% 为完全饱和。
  • 明度(l):颜色中白色或黑色分量的多少,取值 0%(黑)到 100%(白),50% 为正常颜色。

前面色相挑战中给出的常见颜色对照表可作为速查:

颜色 HSL
red hsl(0, 100%, 50%)
yellow hsl(60, 100%, 50%)
green hsl(120, 100%, 50%)
cyan hsl(180, 100%, 50%)
blue hsl(240, 100%, 50%)
magenta hsl(300, 100%, 50%)

核心概念:用 hsl() 调整色调(Tone)

hsl() 让调整颜色色调变得很直观,其混色规则可以归纳为三条:

  1. Tint(浅色):给纯色(pure hue)混入白色,得到该颜色的浅色版本;
  2. Shade(暗色):给纯色混入黑色,得到该颜色的暗色版本;
  3. Tone(色调):给纯色混入灰色,或者同时做 tinting 和 shading,即得到该颜色的一个色调变化。

映射到 hsl() 的参数上(原文强调,hsl() 中的 “s” 和 “l” 分别代表 saturation 和 lightness):

  • 饱和度百分比(s) 改变颜色中的灰色量——越低越灰、越不鲜艳;
  • 明度百分比(l) 决定颜色中白色或黑色的比例——低于 50% 相当于掺黑(趋向 shade),高于 50% 相当于掺白(趋向 tint)。

这套机制的实际用途是:当你看中某个基础色相(base hue)、又需要它的一系列深浅变化时(例如同一青色做页头、导航栏、按钮的不同层级),无需另外找色值,只要固定色相、调整 s 和 l 即可得到成套配色。

实战题目:让 nav 用同一青色相,但变成深色调

题目场景(完整继承自原文档)

所有元素的默认 background-colortransparent。示例中 nav 元素看起来有 cyan 背景,其实只是因为它后面的元素background-color 设成了 cyan(透明背景下透出底层颜色)。题目要求:给 nav 元素添加 background-color,使其与页头使用相同的 cyan 色相(180),但饱和度为 80%、明度为 25%,从而改变它的色调并做成暗色(shade)。

初始代码(seed)

课程提供的初始代码如下(来自文档 --seed-contents-- 部分):

<style>
  header {
    background-color: hsl(180, 90%, 35%);
    color: #FFFFFF;
  }

  nav {

  }

  h1 {
    text-indent: 10px;
    padding-top: 10px;
  }

  nav ul {
    margin: 0px;
    padding: 5px 0px 5px 30px;
  }

  nav li {
    display: inline;
    margin-right: 20px;
  }

  a {
    text-decoration: none;
    color: inherit;
  }
</style>

<header>
  <h1>Cooking with FCC!</h1>
  <nav>
    <ul>
      <li><a href="#">Home</a></li>
      <li><a href="#">Classes</a></li>
      <li><a href="#">Contact</a></li>
    </ul>
  </nav>
</header>

注意两点:页头已经是 hsl(180, 90%, 35%)——色相 180(cyan)、饱和度 90%、明度 35%;nav 选择器是空的,等待你填入规则。

标准答案

nav {
  background-color: hsl(180, 80%, 25%);
}

即保持色相 180 不变,把饱和度从 90% 降到 80%(掺入更多灰)、明度从 35% 降到 25%(掺入更多黑),得到一个比页头更深、更沉稳的同色系导航条。文档 --solutions-- 部分给出的完整解法就是仅在这一处补充该声明,其余样式与 seed 完全一致。

测试如何验证答案:computed style 与 rgb(13,115,115)

原档的提示(hints)部分是本题最硬核的验证逻辑。它并不检查你写的源代码文本,而是通过课程内置的样式辅助类 CSSHelp 读取浏览器计算后的样式(computed style)

// Computed style of hsl(180, 80%, 25%) results in rgb(13,115,115)
assert.equal(
  new __helpers.CSSHelp(document).getStyle('nav').getPropVal('background-color', true),
  'rgb(13,115,115)'
)

从源码结构看,freeCodeCamp 客户端的挑战框架会在运行环境中向测试脚本注入 __helpers(例如 frame.ts 中专门为 CSSHelper 类处理框架自身样式注入的注释),getStyle('nav') 取得目标元素的样式集合,getPropVal('background-color', true) 取计算后的背景色并归一化为 rgb() 字符串。这带来两个重要结论:

  1. 等价写法都能通过:只要浏览器把值解析为 hsl(180, 80%, 25%) 的计算结果,断言就能通过,因此用 hsla()、省略写法或直接写等价的 rgb(13,115,115) 在计算结果层面也是等价的(但课程意图是练习 hsl() 语法);
  2. rgb 值是浏览器把 HSL 转换后统一归一化的结果,与书写格式无关。

手动验证:hsl(180, 80%, 25%) 为什么等于 rgb(13,115,115)

按 HSL → RGB 标准算法可以手工核算这个断言值,确认题目数值无误:

  1. 归一化:L = 0.25,S = 0.8,色相角 H′ = 180/60 = 3;
  2. 计算色度 C = (1 − |2L − 1|) × S = (1 − 0.5) × 0.8 = 0.4;
  3. H′ = 3 落在 3~4 区间,预混合分量为 (0, X, C),其中 X = C = 0.4,得 (0, 0.4, 0.4);
  4. 加偏移 m = L − C/2 = 0.25 − 0.2 = 0.05,得 (0.05, 0.45, 0.45);
  5. 映射到 0–255:0.05×255 ≈ 12.75 → 13;0.45×255 ≈ 114.75 → 115。

结果正是 rgb(13,115,115),与测试断言完全一致。同理可用该方法核算页头的 hsl(180, 90%, 35%)rgb(9,170,170),可见导航条比页头明显更暗(明度 25% vs 35%)、更灰(饱和度 80% vs 90%),这正是“同色相下通过调 s/l 制造层级对比”的典型做法。

小结与延伸

  • hsl() 的三个参数中,色相决定“是什么颜色”,饱和度决定“掺了多少灰”,明度决定“掺了多少白/黑”;固定色相、只动 s 和 l,就能产出一套协调的色调序列(tints、shades、tones)。
  • 判断当前效果时,应以浏览器计算后的 rgb() 值为准,而不是源代码文本——这也是本课程测试断言 rgb(13,115,115) 的验证思路。
  • 学完本节后,课程在 applied-visual-design.json 中的下一题是 Create a Gradual CSS Linear Gradient,会把这些 hsl() 颜色值作为渐变的色标(color stop)组合起来,是本文配色思路的直接下游应用。
登录后查看全文
热门项目推荐
相关项目推荐