HiHuo
首页
博客
手册
工具
首页
博客
手册
工具
  • Vue & CSS 进阶

    • 第0章:工具链与开发体验
    • 第1章:TypeScript 核心
    • 第2章:CSS 现代能力
    • 第3章:Vue3 核心与原理
    • 第4章:路由与状态管理
    • 第5章:工程化与性能
    • 第6章:测试与质量
    • 第7章:CSS 进阶专题
    • 第8章:项目实战A - SaaS仪表盘
    • 第9章:项目实战B - 内容社区
    • 第10章:Vue 内核深入
    • 第11章:微前端与部署
    • CSS 变量体系与主题系统深度指南
    • 前端安全与防护专题
    • CSS 专题:动效优化与微交互
    • CSS 专题:图片与图形处理
    • 实时通信与WebSocket专题
    • 开发工具与调试技巧专题
    • 新兴技术与未来趋势专题
    • 移动端开发与PWA专题
    • 附录A:代码片段库
    • 附录B:练习题集

第2章:CSS 现代能力

从会用到会设计:掌握现代 CSS 的核心能力,不仅能写样式,还能架构样式系统。本章将深入讲解布局、现代特性、架构模式,为构建可维护的样式系统打下基础。

📋 本章内容

  • 基石概念
  • 布局三板斧
  • 现代 CSS 特性
  • 动画与过渡
  • CSS 架构
  • 实战练习

🎯 学习目标

  • 理解 CSS 层叠、优先级、盒模型等基础概念
  • 掌握 Flexbox 和 Grid 布局的核心用法
  • 学会使用现代 CSS 特性(变量、容器查询、:has())
  • 建立 CSS 架构思维,设计可维护的样式系统

🏗 基石概念

层叠与优先级

CSS 的层叠规则决定了当多个规则应用到同一个元素时,哪个规则会生效:

优先级计算(从高到低):
1. 来源:内联样式 > 作者样式 > 用户样式 > 浏览器默认样式
2. 重要性:!important
3. 指定度:ID > 类 > 元素
4. 顺序:后声明的覆盖先声明的
/* 指定度计算示例 */
#header .nav a { color: blue; }     /* 指定度:0,1,0,1 = 101 */
.nav a { color: red; }              /* 指定度:0,0,1,1 = 11 */
a { color: green; }                 /* 指定度:0,0,0,1 = 1 */

/* 结果:第一个规则生效,链接显示蓝色 */

盒模型

理解盒模型是 CSS 布局的基础:

/* 标准盒模型 */
.box {
  width: 200px;
  height: 100px;
  padding: 20px;
  border: 5px solid #333;
  margin: 10px;
  /* 总宽度 = 200 + 40 + 10 + 20 = 270px */
}

/* 推荐:使用 border-box */
* {
  box-sizing: border-box;
}

.box {
  width: 200px;
  height: 100px;
  padding: 20px;
  border: 5px solid #333;
  /* 总宽度 = 200px(包含 padding 和 border) */
}

格式化上下文

格式化上下文决定了元素如何布局和定位:

/* BFC(块级格式化上下文)特性 */
.bfc-container {
  overflow: hidden; /* 触发 BFC */
  /* 或者使用 */
  display: flow-root; /* 更语义化的方式 */
}

/* BFC 解决的问题 */
.clearfix::after {
  content: '';
  display: table;
  clear: both;
}

/* 现代解决方案 */
.clearfix {
  display: flow-root;
}

🎨 布局三板斧

Flexbox(轴向分布)

Flexbox 是一维布局方法,适合处理组件内部元素的排列:

基础概念

.flex-container {
  display: flex;
  flex-direction: row; /* 主轴方向:row | column | row-reverse | column-reverse */
  flex-wrap: nowrap;   /* 换行:nowrap | wrap | wrap-reverse */
  justify-content: flex-start; /* 主轴对齐:flex-start | center | flex-end | space-between | space-around */
  align-items: stretch; /* 交叉轴对齐:stretch | flex-start | center | flex-end | baseline */
  gap: 12px; /* 子元素间距 */
}

实战示例

<!-- 经典三栏布局 -->
<div class="row">
  <div class="sidebar">Sidebar</div>
  <div class="main">Main Content</div>
  <div class="aside">Aside</div>
</div>
.row {
  display: flex;
  gap: 12px;
  min-height: 100vh;
}

.sidebar {
  flex: 0 0 200px; /* flex-grow: 0, flex-shrink: 0, flex-basis: 200px */
  background: #f5f5f5;
}

.main {
  flex: 1; /* flex-grow: 1, flex-shrink: 1, flex-basis: 0 */
  background: #fff;
}

.aside {
  flex: 0 0 300px;
  background: #f0f0f0;
}

/* 响应式:小屏幕时变为垂直布局 */
@media (max-width: 768px) {
  .row {
    flex-direction: column;
  }
  
  .sidebar, .aside {
    flex: none;
  }
}

高级技巧

/* 1. 垂直居中 */
.center {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

/* 2. 等分布局 */
.equal-columns {
  display: flex;
}

.equal-columns > * {
  flex: 1; /* 所有子元素等分空间 */
}

/* 3. 自适应布局 */
.adaptive {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.adaptive > * {
  flex: 1 1 300px; /* 最小宽度 300px,可伸缩 */
}

Grid(二维网格)

Grid 是二维布局方法,适合处理复杂的页面布局:

基础概念

.grid-container {
  display: grid;
  grid-template-columns: 200px 1fr 200px; /* 列定义 */
  grid-template-rows: 60px 1fr 60px;      /* 行定义 */
  gap: 1rem;                              /* 网格间距 */
  grid-template-areas:                     /* 区域命名 */
    "header header header"
    "sidebar main aside"
    "footer footer footer";
}

实战示例

<!-- 经典页面布局 -->
<div class="page">
  <header>Header</header>
  <aside>Sidebar</aside>
  <main>Main Content</main>
  <aside class="aside">Aside</aside>
  <footer>Footer</footer>
</div>
.page {
  display: grid;
  grid-template:
    "header header header" 60px
    "sidebar main aside"   1fr
    "footer footer footer" 60px
    / 200px 1fr 200px;
  height: 100vh;
  gap: 1rem;
}

header { grid-area: header; }
aside { grid-area: sidebar; }
main { grid-area: main; }
.aside { grid-area: aside; }
footer { grid-area: footer; }

/* 响应式布局 */
@media (max-width: 768px) {
  .page {
    grid-template:
      "header" 60px
      "main"   1fr
      "sidebar" auto
      "aside"  auto
      "footer" 60px
      / 1fr;
  }
}

高级技巧

/* 1. 自适应网格 */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1rem;
}

/* 2. 复杂网格布局 */
.complex-grid {
  display: grid;
  grid-template-columns: 
    [sidebar-start] 200px 
    [sidebar-end main-start] 1fr 
    [main-end aside-start] 200px 
    [aside-end];
  grid-template-rows: 
    [header-start] 60px 
    [header-end content-start] 1fr 
    [content-end footer-start] 60px 
    [footer-end];
}

/* 3. 子网格(CSS Grid Level 2) */
.subgrid {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
}

现代流式技巧

/* 1. minmax() 函数 */
.responsive-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/* 2. clamp() 函数 - 响应式字体 */
.responsive-text {
  font-size: clamp(1rem, 2.5vw, 2rem);
}

/* 3. auto-fit vs auto-fill */
.auto-fit {
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  /* 空列会被压缩 */
}

.auto-fill {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  /* 保持列数,空列保持最小宽度 */
}

🚀 现代 CSS 特性

自定义属性(CSS 变量)

CSS 变量是构建主题系统和设计令牌的基础:

/* 定义变量 */
:root {
  /* 颜色系统 */
  --color-primary: #42b883;
  --color-secondary: #35495e;
  --color-success: #4caf50;
  --color-warning: #ff9800;
  --color-error: #f44336;
  
  /* 间距系统 */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  
  /* 字体系统 */
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  
  /* 圆角系统 */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
}

/* 使用变量 */
.button {
  background-color: var(--color-primary);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  font-size: var(--font-size-base);
}

/* 动态主题切换 */
:root[data-theme="dark"] {
  --color-primary: #64b5f6;
  --color-secondary: #e0e0e0;
  --color-bg: #121212;
  --color-text: #ffffff;
}

/* 局部变量覆盖 */
.card {
  --card-padding: var(--space-lg);
  padding: var(--card-padding);
}

.card--compact {
  --card-padding: var(--space-sm);
}

容器查询

容器查询允许组件根据自身尺寸而不是视口尺寸来调整样式:

/* 定义容器上下文 */
.card {
  container-type: inline-size;
  /* 或者使用 container: inline-size; */
}

/* 基于容器宽度的样式 */
@container (min-width: 300px) {
  .card__content {
    display: flex;
    gap: 1rem;
  }
  
  .card__image {
    flex: 0 0 120px;
  }
}

@container (min-width: 500px) {
  .card__content {
    flex-direction: row;
  }
  
  .card__title {
    font-size: 1.25rem;
  }
}

/* 容器查询单位 */
.responsive-text {
  font-size: 10cqw; /* 容器宽度的 10% */
  padding: 5cqh;    /* 容器高度的 5% */
}

:has() 选择器

:has() 选择器允许根据子元素状态来设置父元素样式:

/* 父级感知选择器 */
.field:has(input:focus) {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px rgba(66, 184, 131, 0.2);
}

.field:has(input:invalid) {
  border-color: var(--color-error);
}

/* 导航高亮 */
.nav li:has(> a[aria-current="page"]) {
  background-color: var(--color-primary);
  color: white;
}

/* 卡片交互 */
.card:has(img:hover) {
  transform: translateY(-4px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}

/* 表单验证状态 */
.form-group:has(.error-message) {
  margin-bottom: 1.5rem;
}

.form-group:has(.error-message) input {
  border-color: var(--color-error);
}

嵌套(CSS 原生支持)

现代浏览器开始支持原生 CSS 嵌套:

/* 原生 CSS 嵌套 */
.card {
  padding: 1rem;
  border-radius: 0.5rem;
  background: white;
  
  /* 嵌套选择器 */
  &__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1rem;
    
    &-title {
      font-size: 1.25rem;
      font-weight: 600;
    }
  }
  
  &__content {
    line-height: 1.6;
    
    p {
      margin-bottom: 0.5rem;
      
      &:last-child {
        margin-bottom: 0;
      }
    }
  }
  
  /* 嵌套媒体查询 */
  @media (max-width: 768px) {
    padding: 0.75rem;
    
    &__header {
      flex-direction: column;
      align-items: flex-start;
      gap: 0.5rem;
    }
  }
}

🎭 动画与过渡

基础过渡

/* 简单过渡 */
.button {
  background-color: var(--color-primary);
  transition: background-color 0.3s ease;
}

.button:hover {
  background-color: var(--color-secondary);
}

/* 多属性过渡 */
.card {
  transform: translateY(0);
  opacity: 1;
  transition: 
    transform 0.3s ease,
    opacity 0.3s ease,
    box-shadow 0.3s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}

关键帧动画

/* 弹跳动画 */
@keyframes bounce {
  0%, 20%, 53%, 80%, 100% {
    transform: translate3d(0, 0, 0);
  }
  40%, 43% {
    transform: translate3d(0, -30px, 0);
  }
  70% {
    transform: translate3d(0, -15px, 0);
  }
  90% {
    transform: translate3d(0, -4px, 0);
  }
}

.bounce {
  animation: bounce 1s ease-in-out;
}

/* 淡入动画 */
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.fade-in {
  animation: fadeIn 0.5s ease-out;
}

/* 脉冲动画 */
@keyframes pulse {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.05);
  }
  100% {
    transform: scale(1);
  }
}

.pulse {
  animation: pulse 2s infinite;
}

性能优化

/* 只使用 transform 和 opacity 进行动画 */
.optimized-animation {
  will-change: transform, opacity;
  transform: translateZ(0); /* 强制硬件加速 */
}

/* 避免触发布局和绘制的属性 */
.good-animation {
  transition: transform 0.3s ease, opacity 0.3s ease;
}

.bad-animation {
  transition: width 0.3s ease, height 0.3s ease; /* 会触发布局 */
}

🏛 CSS 架构

BEM 命名规范

BEM(Block Element Modifier)是一种 CSS 命名方法论:

/* Block(块) */
.button { }

/* Element(元素) */
.button__text { }
.button__icon { }

/* Modifier(修饰符) */
.button--primary { }
.button--large { }
.button--disabled { }

/* 组合使用 */
.button--primary.button--large { }
<!-- BEM 在 HTML 中的应用 -->
<button class="button button--primary button--large">
  <span class="button__icon">→</span>
  <span class="button__text">Submit</span>
</button>

ITCSS 分层架构

ITCSS(Inverted Triangle CSS)是一种可扩展的 CSS 架构:

/* 1. Settings - 全局变量 */
@import 'settings/variables';
@import 'settings/colors';

/* 2. Tools - 混入和函数 */
@import 'tools/mixins';
@import 'tools/functions';

/* 3. Generic - 重置和标准化 */
@import 'generic/reset';
@import 'generic/normalize';

/* 4. Elements - 基础元素样式 */
@import 'elements/headings';
@import 'elements/forms';

/* 5. Objects - 布局对象 */
@import 'objects/container';
@import 'objects/grid';

/* 6. Components - UI 组件 */
@import 'components/button';
@import 'components/card';

/* 7. Utilities - 工具类 */
@import 'utilities/spacing';
@import 'utilities/display';

现代 CSS 架构实践

/* 1. 设计令牌层 */
:root {
  /* 颜色令牌 */
  --color-primary-50: #e8f5e8;
  --color-primary-500: #42b883;
  --color-primary-900: #1a5f3f;
  
  /* 间距令牌 */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  
  /* 字体令牌 */
  --font-family-sans: system-ui, -apple-system, sans-serif;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
}

/* 2. 组件层 */
.btn {
  /* 基础样式 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2) var(--space-4);
  border: 1px solid transparent;
  border-radius: 0.375rem;
  font-family: var(--font-family-sans);
  font-size: var(--font-size-base);
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: all 0.2s ease;
  
  /* 变体 */
  &--primary {
    background-color: var(--color-primary-500);
    color: white;
    
    &:hover {
      background-color: var(--color-primary-600);
    }
  }
  
  &--secondary {
    background-color: transparent;
    color: var(--color-primary-500);
    border-color: var(--color-primary-500);
    
    &:hover {
      background-color: var(--color-primary-50);
    }
  }
  
  /* 尺寸 */
  &--sm {
    padding: var(--space-1) var(--space-3);
    font-size: var(--font-size-sm);
  }
  
  &--lg {
    padding: var(--space-3) var(--space-6);
    font-size: var(--font-size-lg);
  }
}

/* 3. 工具层 */
.u-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.u-text-center { text-align: center; }
.u-text-left { text-align: left; }
.u-text-right { text-align: right; }

🧪 实战练习

练习1:响应式卡片组件

创建一个使用现代 CSS 特性的响应式卡片组件:

<!-- Card.vue -->
<template>
  <article class="card" :class="cardClass">
    <div class="card__image">
      <img :src="image" :alt="title" />
    </div>
    <div class="card__content">
      <h3 class="card__title">{{ title }}</h3>
      <p class="card__description">{{ description }}</p>
      <div class="card__actions">
        <button class="btn btn--primary">Read More</button>
      </div>
    </div>
  </article>
</template>

<script setup lang="ts">
interface Props {
  title: string
  description: string
  image: string
  variant?: 'default' | 'featured'
}

const props = withDefaults(defineProps<Props>(), {
  variant: 'default'
})

const cardClass = computed(() => ({
  'card--featured': props.variant === 'featured'
}))
</script>

<style scoped>
.card {
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  background: white;
  border-radius: var(--radius-lg);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  overflow: hidden;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}

.card__image {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
}

.card__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.card:has(.card__image:hover) img {
  transform: scale(1.05);
}

.card__content {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1;
}

.card__title {
  font-size: var(--font-size-lg);
  font-weight: 600;
  color: var(--color-gray-900);
  margin: 0;
}

.card__description {
  color: var(--color-gray-600);
  line-height: 1.6;
  margin: 0;
  flex: 1;
}

.card__actions {
  margin-top: auto;
}

/* 容器查询 */
@container (min-width: 300px) {
  .card {
    flex-direction: row;
  }
  
  .card__image {
    flex: 0 0 200px;
    aspect-ratio: 1;
  }
}

@container (min-width: 500px) {
  .card__content {
    padding: var(--space-6);
  }
  
  .card__title {
    font-size: var(--font-size-xl);
  }
}

/* 特色卡片 */
.card--featured {
  border: 2px solid var(--color-primary-500);
}

.card--featured .card__title {
  color: var(--color-primary-500);
}
</style>

练习2:现代表单组件

创建一个使用现代 CSS 特性的表单组件:

<!-- FormField.vue -->
<template>
  <div class="field" :class="fieldClass">
    <label v-if="label" :for="id" class="field__label">
      {{ label }}
      <span v-if="required" class="field__required">*</span>
    </label>
    
    <div class="field__input-wrapper">
      <input
        :id="id"
        :type="type"
        :value="modelValue"
        :placeholder="placeholder"
        :disabled="disabled"
        :required="required"
        class="field__input"
        @input="handleInput"
        @blur="handleBlur"
        @focus="handleFocus"
      />
      
      <div v-if="icon" class="field__icon">
        <component :is="icon" />
      </div>
    </div>
    
    <div v-if="error" class="field__error">
      {{ error }}
    </div>
    
    <div v-if="hint" class="field__hint">
      {{ hint }}
    </div>
  </div>
</template>

<script setup lang="ts">
interface Props {
  modelValue: string
  type?: 'text' | 'email' | 'password' | 'number'
  label?: string
  placeholder?: string
  hint?: string
  error?: string
  icon?: any
  required?: boolean
  disabled?: boolean
}

interface Emits {
  (e: 'update:modelValue', value: string): void
  (e: 'blur'): void
  (e: 'focus'): void
}

const props = withDefaults(defineProps<Props>(), {
  type: 'text'
})

const emit = defineEmits<Emits>()

const id = `field-${Math.random().toString(36).substr(2, 9)}`

const fieldClass = computed(() => ({
  'field--error': !!props.error,
  'field--disabled': props.disabled,
  'field--required': props.required
}))

const handleInput = (event: Event) => {
  const target = event.target as HTMLInputElement
  emit('update:modelValue', target.value)
}

const handleBlur = () => emit('blur')
const handleFocus = () => emit('focus')
</script>

<style scoped>
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field__label {
  font-weight: 500;
  color: var(--color-gray-700);
  font-size: var(--font-size-sm);
}

.field__required {
  color: var(--color-error-500);
}

.field__input-wrapper {
  position: relative;
  display: flex;
  align-items: center;
}

.field__input {
  width: 100%;
  padding: var(--space-3);
  padding-right: var(--space-10);
  border: 1px solid var(--color-gray-300);
  border-radius: var(--radius-md);
  font-size: var(--font-size-base);
  transition: all 0.2s ease;
  background: white;
}

.field__input:focus {
  outline: none;
  border-color: var(--color-primary-500);
  box-shadow: 0 0 0 3px rgba(66, 184, 131, 0.1);
}

.field__input::placeholder {
  color: var(--color-gray-400);
}

.field__icon {
  position: absolute;
  right: var(--space-3);
  color: var(--color-gray-400);
  pointer-events: none;
}

.field__error {
  color: var(--color-error-500);
  font-size: var(--font-size-sm);
}

.field__hint {
  color: var(--color-gray-500);
  font-size: var(--font-size-sm);
}

/* 状态样式 */
.field--error .field__input {
  border-color: var(--color-error-500);
}

.field--error .field__input:focus {
  border-color: var(--color-error-500);
  box-shadow: 0 0 0 3px rgba(244, 67, 54, 0.1);
}

.field--disabled .field__input {
  background-color: var(--color-gray-50);
  color: var(--color-gray-500);
  cursor: not-allowed;
}

/* 使用 :has() 选择器 */
.field:has(.field__input:focus) .field__label {
  color: var(--color-primary-500);
}

.field:has(.field__input:invalid) .field__input {
  border-color: var(--color-error-500);
}
</style>

练习3:响应式网格系统

创建一个现代的响应式网格系统:

/* Grid System */
.grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(12, 1fr);
}

/* 响应式断点 */
@media (max-width: 640px) {
  .grid {
    grid-template-columns: 1fr;
    gap: var(--space-3);
  }
}

@media (min-width: 641px) and (max-width: 1024px) {
  .grid {
    grid-template-columns: repeat(6, 1fr);
  }
}

/* 网格项 */
.col {
  grid-column: span 12;
}

.col-1 { grid-column: span 1; }
.col-2 { grid-column: span 2; }
.col-3 { grid-column: span 3; }
.col-4 { grid-column: span 4; }
.col-5 { grid-column: span 5; }
.col-6 { grid-column: span 6; }
.col-7 { grid-column: span 7; }
.col-8 { grid-column: span 8; }
.col-9 { grid-column: span 9; }
.col-10 { grid-column: span 10; }
.col-11 { grid-column: span 11; }
.col-12 { grid-column: span 12; }

/* 响应式列 */
@media (max-width: 640px) {
  .col-sm-1 { grid-column: span 1; }
  .col-sm-2 { grid-column: span 2; }
  .col-sm-3 { grid-column: span 3; }
  .col-sm-4 { grid-column: span 4; }
  .col-sm-6 { grid-column: span 6; }
  .col-sm-12 { grid-column: span 12; }
}

@media (min-width: 641px) and (max-width: 1024px) {
  .col-md-1 { grid-column: span 1; }
  .col-md-2 { grid-column: span 2; }
  .col-md-3 { grid-column: span 3; }
  .col-md-4 { grid-column: span 4; }
  .col-md-6 { grid-column: span 6; }
  .col-md-12 { grid-column: span 12; }
}

@media (min-width: 1025px) {
  .col-lg-1 { grid-column: span 1; }
  .col-lg-2 { grid-column: span 2; }
  .col-lg-3 { grid-column: span 3; }
  .col-lg-4 { grid-column: span 4; }
  .col-lg-6 { grid-column: span 6; }
  .col-lg-8 { grid-column: span 8; }
  .col-lg-9 { grid-column: span 9; }
  .col-lg-12 { grid-column: span 12; }
}

📚 最佳实践

1. 性能优化

/* 使用 transform 和 opacity 进行动画 */
.good-animation {
  transition: transform 0.3s ease, opacity 0.3s ease;
}

/* 避免触发布局的属性 */
.bad-animation {
  transition: width 0.3s ease, height 0.3s ease;
}

/* 使用 will-change 提示浏览器 */
.optimized {
  will-change: transform;
}

/* 动画结束后移除 will-change */
.optimized.animation-complete {
  will-change: auto;
}

2. 可维护性

/* 使用 CSS 变量统一管理 */
:root {
  --component-padding: var(--space-4);
  --component-radius: var(--radius-md);
  --component-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

/* 组件样式模块化 */
.card {
  padding: var(--component-padding);
  border-radius: var(--component-radius);
  box-shadow: var(--component-shadow);
}

3. 可访问性

/* 焦点样式 */
.focusable:focus {
  outline: 2px solid var(--color-primary-500);
  outline-offset: 2px;
}

/* 减少动画(用户偏好) */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* 高对比度支持 */
@media (prefers-contrast: high) {
  .button {
    border: 2px solid currentColor;
  }
}

🚨 常见问题

1. Flexbox 布局问题

问题:子元素不按预期排列 解决:检查 flex-direction、justify-content、align-items 设置

2. Grid 布局问题

问题:网格项位置不正确 解决:确认 grid-template-columns 和 grid-column 设置

3. CSS 变量不生效

问题:变量值没有应用 解决:检查变量名拼写和作用域

📖 延伸阅读

  • CSS Grid 完整指南
  • Flexbox 完整指南
  • CSS 自定义属性
  • 容器查询
  • :has() 选择器

下一章预告:我们将深入 Vue3 的核心原理,包括响应式系统的源码解析、模板编译机制、虚拟 DOM 的 diff 算法等,为理解 Vue 的内部工作原理打下坚实基础。

Prev
第1章:TypeScript 核心
Next
第3章:Vue3 核心与原理