Ch3nyang's blog web_stories

post

assignment_ind

about

data_object

github

local_offer

tag

rss_feed

rss

现代 CSS 引入了许多新特性和功能,提升了网页设计的灵活性和表现力。本文将介绍一些现代 CSS 的重要特性,包括容器查询、:has() 伪类、选择器嵌套、滚动驱动动画、CSS 锚点定位、作用域样式、级联层、颜色函数、文本优化等。

容器查询

传统的 @media 查询基于视口大小,而容器查询允许组件根据其父容器的大小自适应样式,实现真正的组件级响应式设计。

基础用法

.card-container {
  container-name: card;
  container-type: inline-size;
}

@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 1fr 2fr;
  }
}

容器类型

类型 说明
size 可查询容器的块级和内联尺寸
inline-size 仅可查询内联尺寸(推荐,性能更好)
normal 不是查询容器

查询条件

尺寸查询支持以下条件:

条件 说明
width / height 宽度 / 高度
inline-size / block-size 内联大小 / 块级大小
aspect-ratio 宽高比
orientation 方向(landscape / portrait

样式查询(实验性)可以查询容器的 CSS 自定义属性:

@container style(--theme: dark) {
  .element {
    background: #1a1a1a;
    color: white;
  }
}
<div class="card-container">
  <div class="card">
    <div class="card-image">🖼️</div>
    <div class="card-content">
      <h3>响应式卡片</h3>
      <p>拖拽右下角调整容器大小,观察布局变化</p>
    </div>
  </div>
</div>
.card-container {
  container-name: card;
  container-type: inline-size;
  border: 2px dashed #999;
  padding: 20px;
  resize: horizontal;
  overflow: auto;
  min-width: 200px;
  max-width: 100%;
}

.card {
  background: white;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.card-image {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3rem;
}

.card-content {
  padding: 20px;
}

.card-content h3 {
  margin: 0 0 10px 0;
  color: #333;
}

.card-content p {
  margin: 0;
  color: #666;
  font-size: 0.9rem;
}

/* 当容器宽度 >= 400px 时,切换为横向布局 */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 150px 1fr;
  }
  
  .card-image {
    height: auto;
  }
}
容器查询示例

:has() 关系伪类

:has() 被称为”父选择器”,可以根据元素的后代兄弟元素来选择该元素。

/* 选择包含图片的卡片 */
.card:has(img) {
  padding: 0;
}

/* 选择没有子元素的段落 */
p:not(:has(*)) {
  text-indent: 2em;
}

/* 选择后面紧跟特定元素的元素 */
h2:has(+ p) {
  margin-bottom: 0.5em;
}
<div class="demo-container">
  <div class="card">
    <h3>普通卡片</h3>
    <p>这是一个没有特殊内容的卡片</p>
  </div>
  
  <div class="card">
    <h3>包含图标的卡片</h3>
    <span class="icon"></span>
    <p>这个卡片包含星标图标</p>
  </div>
  
  <div class="card">
    <h3>包含按钮的卡片</h3>
    <p>这个卡片包含操作按钮</p>
    <button>点击</button>
  </div>
</div>
.demo-container {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

.card {
  background: #f5f5f5;
  padding: 20px;
  border-radius: 8px;
  border: 2px solid transparent;
  transition: all 0.3s;
}

.card h3 {
  margin: 0 0 10px 0;
  color: #333;
}

.card p {
  margin: 0;
  color: #666;
}

/* 包含图标的卡片显示金色边框 */
.card:has(.icon) {
  border-color: #ffd700;
  background: linear-gradient(135deg, #fff9e6 0%, #fff 100%);
}

/* 包含按钮的卡片显示蓝色边框 */
.card:has(button) {
  border-color: #4a90e2;
  background: linear-gradient(135deg, #e6f2ff 0%, #fff 100%);
}

.icon {
  font-size: 2rem;
  display: block;
  margin: 10px 0;
}

button {
  margin-top: 10px;
  padding: 8px 16px;
  background: #4a90e2;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
:has() 伪类示例

CSS 嵌套

CSS 原生支持选择器嵌套,无需预处理器即可编写更简洁的代码。

.parent {
  color: blue;
  
  /* 使用 & 引用父选择器 */
  &:hover {
    color: red;
  }
  
  /* 嵌套子选择器 */
  .child {
    font-size: 14px;
  }
  
  /* 复合选择器 */
  &.active {
    font-weight: bold;
  }
}
<nav class="menu">
  <ul>
    <li><a href="#" class="active">首页</a></li>
    <li><a href="#">关于</a></li>
    <li>
      <a href="#">服务</a>
      <ul class="submenu">
        <li><a href="#">设计</a></li>
        <li><a href="#">开发</a></li>
      </ul>
    </li>
  </ul>
</nav>
.menu {
  background: #2c3e50;
  padding: 10px;
  border-radius: 8px;
  
  ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    gap: 5px;
    
    li {
      position: relative;
      
      a {
        display: block;
        padding: 10px 20px;
        color: white;
        text-decoration: none;
        border-radius: 4px;
        transition: background 0.3s;
        
        &:hover {
          background: rgba(255,255,255,0.1);
        }
        
        &.active {
          background: #3498db;
        }
      }
      
      &:hover .submenu {
        display: block;
      }
    }
  }
  
  .submenu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    background: #34495e;
    min-width: 150px;
    border-radius: 4px;
    margin-top: 5px;
    flex-direction: column;
    
    li {
      width: 100%;
    }
  }
}
CSS 嵌套示例

滚动驱动动画

基于滚动位置触发和控制动画,无需 JavaScript。

animation-timeline

使用 scroll()view() 函数定义滚动时间线:

/* 基于滚动容器 */
.element {
  animation: fade-in linear;
  animation-timeline: scroll(root block);
}

/* 基于视口可见性 */
.element {
  animation: slide-in linear;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

animation-range

定义动画的触发范围:

  • entry:元素进入滚动容器时
  • exit:元素离开滚动容器时
  • contain:元素完全在滚动容器内时
  • cover:元素覆盖滚动容器时
<div class="scroll-container">
  <div class="scroll-spacer"></div>
  
  <div class="reveal-card" style="--delay: 0">
    <h3>卡片 1</h3>
    <p>向下滚动查看动画效果</p>
  </div>
  
  <div class="reveal-card" style="--delay: 0.1">
    <h3>卡片 2</h3>
    <p>每个卡片独立触发动画</p>
  </div>
  
  <div class="reveal-card" style="--delay: 0.2">
    <h3>卡片 3</h3>
    <p>基于视口位置自动播放</p>
  </div>
  
  <div class="scroll-spacer"></div>
</div>
.scroll-container {
  height: 400px;
  overflow-y: scroll;
  background: linear-gradient(to bottom, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
  padding: 40px 20px;
  border-radius: 8px;
}

.scroll-spacer {
  height: 200px;
}

.reveal-card {
  background: white;
  padding: 30px;
  margin: 30px 0;
  border-radius: 12px;
  box-shadow: 0 10px 40px rgba(0,0,0,0.3);
  
  animation: reveal-up linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

.reveal-card h3 {
  margin: 0 0 10px 0;
  color: #2c3e50;
}

.reveal-card p {
  margin: 0;
  color: #7f8c8d;
}

@keyframes reveal-up {
  from {
    opacity: 0;
    transform: translateY(100px) scale(0.9);
    filter: blur(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: blur(0);
  }
}
滚动驱动动画示例

CSS 锚点定位

将元素精确定位到另一个元素的位置,无需 JavaScript。

/* 定义锚点 */
.button {
  anchor-name: --my-button;
}

/* 相对锚点定位 */
.tooltip {
  position: absolute;
  position-anchor: --my-button;
  bottom: anchor(top);
  left: anchor(center);
  translate: -50% 0;
}
<div class="anchor-demo">
  <button class="anchor-button">悬停显示提示</button>
  <div class="tooltip">这是一个提示框</div>
</div>
.anchor-demo {
  padding: 100px 20px;
  text-align: center;
  min-height: 200px;
  position: relative;
}

.anchor-button {
  anchor-name: --my-button;
  padding: 12px 24px;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 16px;
  transition: background 0.3s;
}

.anchor-button:hover {
  background: #2980b9;
}

.tooltip {
  position: absolute;
  position-anchor: --my-button;
  bottom: anchor(top);
  left: anchor(center);
  translate: -50% -10px;
  
  background: #2c3e50;
  color: white;
  padding: 8px 12px;
  border-radius: 4px;
  font-size: 14px;
  white-space: nowrap;
  
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
}

.tooltip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  translate: -50% 0;
  border: 6px solid transparent;
  border-top-color: #2c3e50;
}

.anchor-button:hover + .tooltip {
  opacity: 1;
}
锚点定位示例

@scope 作用域规则

限制 CSS 规则的作用范围,避免样式污染。

@scope (.component) to (.boundary) {
  h2 {
    color: blue;
  }
  
  p {
    font-size: 14px;
  }
}
<article class="scoped-article">
  <h2>文章标题(蓝色)</h2>
  <p>这段文字在作用域内,字号为 14px</p>
  
  <div class="scope-boundary">
    <h2>内部标题(不受影响)</h2>
    <p>这段文字在边界外,使用默认样式</p>
  </div>
</article>

<h2>外部标题(不受影响)</h2>
@scope (.scoped-article) to (.scope-boundary) {
  h2 {
    color: #3498db;
    font-size: 24px;
  }
  
  p {
    font-size: 14px;
    color: #555;
    line-height: 1.6;
  }
}

.scoped-article {
  background: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 20px;
}

.scope-boundary {
  background: white;
  padding: 15px;
  margin-top: 15px;
  border-left: 3px solid #e74c3c;
}
@scope 示例

@layer 级联层

控制样式的优先级顺序,更好地管理复杂样式。

@layer reset, base, components, utilities;

@layer reset {
  * { margin: 0; padding: 0; }
}

@layer base {
  body { font-family: sans-serif; }
}

@layer components {
  .button { padding: 10px 20px; }
}
<div class="layer-demo">
  <button class="btn">基础按钮</button>
  <button class="btn btn-primary">主要按钮</button>
  <button class="btn btn-utility">工具按钮</button>
</div>
@layer base, theme, utilities;

@layer base {
  .btn {
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    font-size: 14px;
    cursor: pointer;
    background: #ecf0f1;
    color: #2c3e50;
  }
}

@layer theme {
  .btn-primary {
    background: #3498db;
    color: white;
  }
}

@layer utilities {
  .btn-utility {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
    color: white !important;
    font-weight: bold;
  }
}

.layer-demo {
  display: flex;
  gap: 10px;
  padding: 20px;
}
@layer 示例

color-mix() 颜色混合

混合两种颜色生成新颜色。

.element {
  background: color-mix(in srgb, #ff0000 60%, #0000ff 40%);
}
<div class="color-demo">
  <div class="color-box" style="background: #e74c3c">
    <span>红色</span>
  </div>
  <div class="color-box" style="background: #3498db">
    <span>蓝色</span>
  </div>
  <div class="color-box" style="background: color-mix(in srgb, #e74c3c 50%, #3498db 50%)">
    <span>混合 50/50</span>
  </div>
  <div class="color-box" style="background: color-mix(in srgb, #e74c3c 70%, #3498db 30%)">
    <span>混合 70/30</span>
  </div>
</div>
.color-demo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 15px;
  padding: 20px;
}

.color-box {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  color: white;
  font-weight: bold;
  text-align: center;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

.color-box span {
  padding: 10px;
  background: rgba(0,0,0,0.2);
  border-radius: 4px;
}
color-mix() 示例

相对颜色语法

基于现有颜色创建变体。

.element {
  --base: #3498db;
  background: rgb(from var(--base) r g b / 0.5);
  border-color: hsl(from var(--base) h s calc(l - 10%));
}
<div class="relative-color-demo">
  <div class="color-item original">原始颜色</div>
  <div class="color-item lighter">更亮 20%</div>
  <div class="color-item darker">更暗 20%</div>
  <div class="color-item transparent">半透明</div>
</div>
.relative-color-demo {
  --base-color: #3498db;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 15px;
  padding: 20px;
}

.color-item {
  padding: 30px 20px;
  text-align: center;
  color: white;
  font-weight: bold;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

.original {
  background: var(--base-color);
}

.lighter {
  background: hsl(from var(--base-color) h s calc(l + 20%));
}

.darker {
  background: hsl(from var(--base-color) h s calc(l - 20%));
}

.transparent {
  background: rgb(from var(--base-color) r g b / 0.5);
}
相对颜色示例

text-wrap 文本换行

优化文本布局,避免孤行等问题。

说明
wrap 正常换行
nowrap 不换行
balance 平衡行长度
pretty 优化排版,避免孤字
stable 编辑时保持稳定
<div class="text-wrap-demo">
  <div class="text-box">
    <h4>normal(默认)</h4>
    <p style="text-wrap: wrap">
      这是一段很长的文本内容,用于演示不同的文本换行效果。注意观察最后一行的单词分布。
    </p>
  </div>
  
  <div class="text-box">
    <h4>balance(平衡)</h4>
    <p style="text-wrap: balance">
      这是一段很长的文本内容,用于演示不同的文本换行效果。注意观察最后一行的单词分布。
    </p>
  </div>
  
  <div class="text-box">
    <h4>pretty(美化)</h4>
    <p style="text-wrap: pretty">
      这是一段很长的文本内容,用于演示不同的文本换行效果。注意观察最后一行的单词分布。
    </p>
  </div>
</div>
.text-wrap-demo {
  display: grid;
  gap: 20px;
  padding: 20px;
}

.text-box {
  background: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  max-width: 400px;
}

.text-box h4 {
  margin: 0 0 10px 0;
  color: #2c3e50;
  font-size: 16px;
}

.text-box p {
  margin: 0;
  line-height: 1.6;
  color: #555;
}
text-wrap 示例

subgrid 子网格

子网格继承父网格的轨道定义。

.parent {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.child {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: span 3;
}
<div class="grid-parent">
  <div class="grid-header">
    <div>列 1</div>
    <div>列 2</div>
    <div>列 3</div>
  </div>
  <div class="grid-item">A1</div>
  <div class="grid-item">A2</div>
  <div class="grid-item">A3</div>
</div>
.grid-parent {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  padding: 20px;
  background: #f8f9fa;
  border-radius: 8px;
}

.grid-header {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  background: #3498db;
  color: white;
  font-weight: bold;
  border-radius: 4px;
  overflow: hidden;
}

.grid-header > div {
  padding: 15px;
  text-align: center;
  border-right: 1px solid rgba(255,255,255,0.2);
}

.grid-header > div:last-child {
  border-right: none;
}

.grid-item {
  background: white;
  padding: 20px;
  text-align: center;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
subgrid 示例

field-sizing 字段大小

控制表单字段根据内容自动调整大小。

textarea {
  field-sizing: content;
  min-height: 3lh;
  max-height: 10lh;
}
<div class="form-demo">
  <label>自动调整高度的文本框:</label>
  <textarea 
    placeholder="输入内容会自动增长高度..."
    style="field-sizing: content"></textarea>
</div>
.form-demo {
  padding: 20px;
  max-width: 500px;
}

.form-demo label {
  display: block;
  margin-bottom: 10px;
  font-weight: bold;
  color: #2c3e50;
}

.form-demo textarea {
  width: 100%;
  padding: 12px;
  border: 2px solid #ddd;
  border-radius: 6px;
  font-size: 14px;
  font-family: inherit;
  resize: none;
  min-height: 60px;
  max-height: 200px;
  transition: border-color 0.3s;
}

.form-demo textarea:focus {
  outline: none;
  border-color: #3498db;
}
field-sizing 示例