CC 咖啡猫的工作空间 Coding Space

CSS

选择器

优先级计算

权重体系(四位数值,从左到右权重递减):

!important          → 无限大(打破级联规则)
内联样式 style=""   → 1,0,0,0
ID 选择器 (#id)     → 0,1,0,0
类/属性/伪类        → 0,0,1,0
元素/伪元素         → 0,0,0,1
通配符/组合器       → 0,0,0,0
继承样式            → 无权重
/* 权重示例 */
#nav .item a:hover {}     /* 0,1,2,1 */
body #content .post {}    /* 0,1,1,1 */
li.active {}              /* 0,0,1,1 */
ul li {}                  /* 0,0,0,2 */

!important 处理规则

  • 多个 !important 按权重比较
  • 只能在 custom property 降级或覆盖第三方样式时使用
  • 正常项目中应避免,它破坏级联,让覆盖变得困难

CSS 选择器性能(从右向左匹配)

浏览器解析选择器时从右向左匹配(RLT, Right-to-Left):

/* 浏览器先找到所有 a 元素,再向上过滤是否在 .nav 内 */
.nav a {}
/* 比下面更快(因为先筛掉大量非 .highlight 元素) */
div.highlight {}

性能排序(从快到慢):

  • #id > 通过 id 直接定位
  • .class > 通过类名快速过滤
  • tag > 遍历所有元素
  • [attr] > 属性匹配
  • 伪类/伪元素 > 需额外计算

实际意义:在多数应用中,选择器性能差异可忽略(除非有上万元素),优先选择语义清晰的写法。

伪类与伪元素

类型 语法 作用 示例
伪类 : 元素特定状态 :hover, :focus, :nth-child(2n)
伪元素 :: 元素的特定部分 ::before, ::after, ::first-line

关键伪类

/* 结构伪类 */
:first-child / :last-child
:nth-child(2n+1)  /* 奇数项 */
:nth-child(-n+3)  /* 前 3 项 */
:nth-last-child(1) /* 倒数第 1 项 */
:first-of-type / :last-of-type
:only-child

/* 状态伪类 */
:enabled / :disabled
:checked
:valid / :invalid
:required / :optional
:focus-visible   /* 键盘聚焦时(推荐替代 :focus) */
:focus-within    /* 自身或后代聚焦 */
:empty           /* 无子节点(含文本节点则不为空) */
:not(selector)   /* 否定(可传多个选择器 :not(.a, .b)) */
:is(selector)    /* 匹配任一(比逗号选择器权重更低 :is(h1, h2, h3)) */
:where(selector) /* 同 :is,但权重始终为 0 */

伪元素

/* ::before / ::after 必须有 content */
.element::before {
  content: '';
  display: block;
  /* 用于图标、装饰、清除浮动 */
}

.element::after {
  content: attr(data-tip); /* 动态属性值 */
}

/* ::first-letter / ::first-line —— 排版用 */
p::first-letter { font-size: 2em; float: left; }
p::first-line   { font-weight: bold; }

/* ::selection —— 选中文本样式 */
::selection { background: #b3d4fc; color: #000; }

/* ::placeholder —— 输入框占位文字 */
input::placeholder { color: #999; }

盒模型

content-box vs border-box

/* 默认值:width 只包含内容区 */
box-sizing: content-box;
/* width = content,实际占宽 = content + padding + border */

/* 推荐:width 包含内容+内边距+边框 */
box-sizing: border-box;
/* width = content + padding + border */

对比

/* content-box */
.element {
  width: 200px;
  padding: 20px;
  border: 2px solid;
  /* 实际宽度 = 200 + 20*2 + 2*2 = 244px */
}

/* border-box */
.element {
  box-sizing: border-box;
  width: 200px;
  padding: 20px;
  border: 2px solid;
  /* 实际宽度 = 200px,内容宽度 = 200 - 40 - 4 = 156px */
}

全局设置(推荐):

*,
*::before,
*::after {
  box-sizing: border-box;
}

margin 塌陷

规则:垂直相邻的块级元素 margin 取最大值,不叠加。

<div style="margin-bottom: 30px;">A</div>
<div style="margin-top: 20px;">B</div>
<!-- A 和 B 之间的间距 = 30px,不是 50px -->

父子塌陷:父元素没有 border/padding/inline-content/overflow:hidden/BFC 时,子元素的 margin-top 会穿透到父元素外部。

解决

  • 父元素设置 overflow: hidden(触发 BFC)
  • 父元素设置 padding: 1pxborder: 1px solid transparent
  • 使用 display: flow-root(最干净的方式)

BFC(Block Formatting Context)

触发条件(任一):

  • overflowvisiblehidden/auto/scroll
  • display: flow-root(最语义化)
  • display: inline-block / table-cell / flex / grid
  • floatnone
  • position: absolute / fixed
/* 创建 BFC 的推荐方式 */
.container {
  display: flow-root; /* 2021+ 所有现代浏览器支持 */
}

应用场景

  1. 清除浮动:父容器创建 BFC 包含浮动子元素
  2. 防止 margin 塌陷:父元素创建 BFC 隔离内外 margin
  3. 两栏自适应布局overflow: hidden 触发 BFC 避免浮动覆盖
/* BFC 两栏自适应布局 */
.floated {
  float: left;
  width: 200px;
}
.main {
  overflow: hidden; /* 触发 BFC,不与浮动重叠 */
}

布局体系

正常流(Normal Flow)

块级元素垂直排列(display: block),行内元素水平排列(display: inline)。

/* display 常见值 */
block       /* 块级,独占一行 */
inline      /* 行内,宽高无效,水平排列 */
inline-block /* 行内但可设宽高 */
none        /* 隐藏(不占空间,与 visibility: hidden 不同) */

Flexbox

容器属性

.flex-container {
  display: flex;
  /* 或 display: inline-flex; */

  /* 主轴方向 */
  flex-direction: row | row-reverse | column | column-reverse;

  /* 换行 */
  flex-wrap: nowrap | wrap | wrap-reverse;

  /* 主轴对齐 */
  justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;

  /* 交叉轴对齐(单行) */
  align-items: stretch | flex-start | flex-end | center | baseline;

  /* 交叉轴对齐(多行) */
  align-content: stretch | flex-start | flex-end | center | space-between | space-around;

  /* gap 间距 */
  gap: 16px;          /* 行和列 */
  row-gap: 16px;
  column-gap: 12px;
}

子项属性

.flex-item {
  /* 增长比例(默认为 0,即不增长) */
  flex-grow: 1;

  /* 收缩比例(默认为 1) */
  flex-shrink: 1;

  /* 初始大小(默认为 auto,即内容大小) */
  flex-basis: auto | 200px | 50%;

  /* 简写:flex: grow shrink basis */
  flex: 1;             /* 1 1 0% */
  flex: auto;          /* 1 1 auto */
  flex: none;          /* 0 0 auto */
  flex: 0 0 200px;     /* 固定 200px */

  /* 覆盖容器 align-items */
  align-self: auto | stretch | center | flex-end;

  /* 顺序(默认为 0,值越小越靠前) */
  order: 1;
}

经典布局示例

/* 圣杯布局:中间自适应,两侧固定 */
.layout {
  display: flex;
  min-height: 100vh;
}
.sidebar {
  flex: 0 0 250px;
}
.main {
  flex: 1;
}

/* 等分布局 */
.row {
  display: flex;
  gap: 16px;
}
.col {
  flex: 1; /* 每项等宽 */
}

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

/* 粘性 footer */
.page { display: flex; flex-direction: column; min-height: 100vh; }
.content { flex: 1; }
footer { flex-shrink: 0; }

Grid

容器属性

.grid {
  display: grid;

  /* 列定义 */
  grid-template-columns: 200px 1fr 2fr;               /* 三列:固定+比例 */
  grid-template-columns: repeat(3, 1fr);               /* 三等分布局 */
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); /* 响应式列 */
  grid-template-columns: 200px auto 200px;             /* 中间自适应 */

  /* 行定义 */
  grid-template-rows: 100px auto 80px;

  /* 区域命名 */
  grid-template-areas:
    "header header header"
    "sidebar main main"
    "footer footer footer";

  /* 间距 */
  gap: 16px;
  row-gap: 16px;
  column-gap: 16px;

  /* 隐式网格行高 */
  grid-auto-rows: minmax(100px, auto);

  /* 对齐方式 */
  justify-items: start | end | center | stretch;
  align-items: start | end | center | stretch;
  place-items: center center;  /* justify-items align-items 简写 */

  justify-content: start | end | center | stretch | space-between | space-around | space-evenly;
  align-content: start | end | center | stretch | space-between | space-around | space-evenly;
}

子项属性

.grid-item {
  /* 单元格跨列 */
  grid-column: 1 / 3;            /* 从列线 1 到列线 3 */
  grid-column: 1 / span 2;      /* 跨 2 列 */
  grid-column: 1 / -1;          /* 从第一列到最后一列 */

  /* 单元格跨行 */
  grid-row: 1 / 3;

  /* 简写 */
  grid-area: header;            /* 对应 grid-template-areas 命名 */
  grid-area: 1 / 1 / 3 / 4;    /* row-start / col-start / row-end / col-end */

  /* 单个单元格内对齐 */
  justify-self: start;
  align-self: end;
  place-self: center;
}

fr 单位fr 是 Grid 的弹性单位,表示剩余空间的比例分配。只在 Grid 布局中有效。

/* 第一列固定 200px,剩余空间第二列占 1/3,第三列占 2/3 */
grid-template-columns: 200px 1fr 2fr;

Grid vs Flexbox 选择

场景 推荐
一维布局(行或列) Flexbox
二维布局(行列同时控制) Grid
内容驱动大小 Flexbox
布局驱动大小 Grid
等分布局 Grid (repeat)
对齐与分布 两者均可

定位

/* static —— 默认,正常流 */
position: static;

/* relative —— 相对自身原本位置偏移,不脱离文档流 */
position: relative;
top: 10px; left: 20px; /* 原本位置占据空间,视觉偏移 */

/* absolute —— 脱离文档流,相对于最近的 non-static 定位祖先 */
position: absolute;
top: 0; right: 0; /* 相对于父定位 */

/* fixed —— 脱离文档流,相对于视口 */
position: fixed;
bottom: 0; /* 粘性底部操作栏 */

/* sticky —— 相对与 fixed 混合(兼容性需注意) */
position: sticky;
top: 0; /* 滚动到顶部后粘住 */
/* 条件:必须有 top/bottom/left/right 之一 */
/* 生效需父容器未 overflow: hidden */

sticky 常见场景

/* 粘性导航:滚动到顶部后固定 */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: white;
}

/* sticky 失效排查 */
/* 1. 父元素有 overflow: hidden/scroll/auto */
/* 2. 未设置 top/bottom/left/right */
/* 3. 父元素高度小于 sticky 元素本身 */

多列布局

.multi-column {
  column-count: 3;           /* 固定列数 */
  column-width: 300px;       /* 最小列宽(与 column-count 结合) */
  columns: 300px 3;          /* 简写:宽度 数量 */
  column-gap: 2em;
  column-rule: 1px solid #ccc; /* 列分割线 */
  column-span: all;          /* 标题跨列 */
}

响应式设计

媒体查询

/* 移动优先(推荐)—— 基础样式为手机,然后增强 */
/* 基础样式:手机 */
body { font-size: 14px; }

/* >= 768px 平板 */
@media (min-width: 768px) {
  body { font-size: 16px; }
  .sidebar { display: block; }
}

/* >= 1024px 桌面 */
@media (min-width: 1024px) {
  .layout { display: grid; grid-template-columns: 250px 1fr; }
}

/* 桌面优先 —— 基础样式为桌面,然后降级 */
body { font-size: 18px; }
@media (max-width: 767px) {
  body { font-size: 14px; }
}

/* 更多查询条件 */
@media (prefers-color-scheme: dark) { /* 暗色模式 */}
@media (prefers-reduced-motion: reduce) { /* 减少动画 */}
@media (hover: hover) { /* 支持悬停设备 */}
@media (pointer: coarse) { /* 触摸设备 */}
@media (orientation: portrait) { /* 竖屏 */}

rem vs em vs vw/vh

/* em —— 相对于父元素字体大小(容易嵌套失控) */
.parent  { font-size: 20px; }
.child   { font-size: 1.5em; }   /* 30px */
.grandchild { font-size: 0.8em; } /* 24px(相对于 30px) */

/* rem —— 相对于根元素(html)字体大小(推荐) */
html { font-size: 16px; }
h1   { font-size: 2rem; }  /* 32px */
p    { font-size: 1rem; }  /* 16px */

/* vw/vh —— 相对于视口 */
.fullscreen { height: 100vh; }    /* 全屏高度 */
.fullwidth  { width: 100vw; }     /* 全宽 */
.hero       { height: 50vh; }     /* 半屏 */
.sidebar    { width: 20vw; }
/* 注意:100vw 包含滚动条宽度,可能导致水平溢出 */

/* dvh/svh/lvh —— 动态视口单位(解决移动端地址栏问题) */
.hero { height: 100dvh; } /* 动态视口高度 */

clamp / min / max

/* clamp(MIN, PREFERRED, MAX) —— 流体排版核心 */
h1 {
  font-size: clamp(1.5rem, 3vw + 1rem, 3rem);
  /* 最小 1.5rem,首选 3vw+1rem,最大 3rem */
}

/* min() / max() */
.sidebar {
  width: min(300px, 30%);  /* 取两者中较小值 */
}
.card {
  width: max(250px, 50%);  /* 取两者中较大值 */
}

容器查询(@container)

/* 定义容器 */
.card-container {
  container-type: inline-size; /* 基于内联轴尺寸 */
  container-name: card;        /* 可选命名 */
}

/* 容器查询(类似媒体查询但基于容器尺寸) */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
  }
}

@container (max-width: 399px) {
  .card {
    display: flex;
    flex-direction: column;
  }
}

动画

transition

.element {
  /* property duration timing-function delay */
  transition: transform 0.3s ease-in-out, opacity 0.3s;
  /* 简写全部 */
  transition: all 0.3s ease;

  /* 分写 */
  transition-property: transform, opacity;
  transition-duration: 0.3s, 0.2s;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-delay: 0s, 0.1s;

  /* 限制可过渡的属性:最好指定属性,避免 all */
  /* 仅对 opacity 和 transform 可过渡(GPU 加速友好) */
}

.element:hover {
  transform: scale(1.1);
  opacity: 0.8;
}

贝塞尔曲线

/* 内置 */
ease         /* cubic-bezier(0.25, 0.1, 0.25, 1) */
linear       /* cubic-bezier(0, 0, 1, 1) */
ease-in      /* cubic-bezier(0.42, 0, 1, 1) */
ease-out     /* cubic-bezier(0, 0, 0.58, 1) */
ease-in-out  /* cubic-bezier(0.42, 0, 0.58, 1) */

/* 自定义 */
.custom {
  transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
  /* 弹性效果:超出目标一点后回弹 */
}

animation / @keyframes

@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateX(-100%);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.05); }
}

.element {
  /* name duration timing-function delay iteration-count direction fill-mode play-state */
  animation: slideIn 0.3s ease-out 0s 1 normal both;

  /* 分写 */
  animation-name: slideIn;
  animation-duration: 0.3s;
  animation-timing-function: ease-out;
  animation-delay: 0s;
  animation-iteration-count: infinite; /* 无限循环 */
  animation-direction: alternate;      /* 交替方向 */
  animation-fill-mode: both;           /* 开始前/结束后保持首尾状态 */
  animation-play-state: paused;        /* 控制暂停/运行 */
}

transform

/* 2D 变换 */
transform: translateX(100px);        /* 水平移动 */
transform: translateY(-50%);         /* 垂直移动(百分比相对于自身尺寸) */
transform: translate(100px, -50%);
transform: scale(1.5);              /* 缩放 */
transform: scaleX(1.2);
transform: rotate(45deg);
transform: rotate(0.5turn);         /* 半圈 */
transform: skew(10deg, 5deg);       /* 倾斜 */

/* 3D 变换 */
transform: perspective(1000px) rotateY(45deg);
transform: translate3d(0, 0, 100px);
transform: rotateX(60deg);

/* transform-origin */
transform-origin: center;     /* 默认 */
transform-origin: top left;
transform-origin: 50% 100%;

/* 多个变换组合(顺序影响结果) */
transform: translateX(100px) rotate(45deg) scale(1.5);

will-change

/* 告知浏览器哪些属性会变化,让浏览器提前优化 */
.element {
  will-change: transform, opacity;
}
/* 
 * 注意事项:
 * - 不要给大量元素设置
 * - 在变化前设置,变化后移除
 * - 过度使用会浪费内存
 */

GPU 加速与合成层

触发 GPU 合成的 CSS 属性

  • transform: translateZ(0)translate3d(0,0,0) —— 强制提升为合成层
  • will-change: transform / opacity
  • opacity
  • filter
  • <video> / <canvas> / <iframe>

动画性能属性对比

属性 触发操作 性能
transform 合成 最佳(仅合成线程)
opacity 合成 最佳
left/top 布局+绘制+合成 差(触发回流)
width/height 布局+绘制+合成
color/background 绘制+合成 中等
box-shadow 绘制+合成 中等

合成层创建条件(硬件加速):

/* 强制合成 */
.optimized {
  transform: translateZ(0);
  /* 或 */
  will-change: transform;
}

FLIP 动画技巧

FLIP(First, Last, Invert, Play)—— 高性能动画技术,避免计算真实变化量。

function flipAnimation(el, callback) {
  // First:记录初始位置
  const first = el.getBoundingClientRect();

  // 执行变化
  callback();

  // Last:记录最终位置
  const last = el.getBoundingClientRect();

  // Invert:计算差异并反向设置
  const dx = first.left - last.left;
  const dy = first.top - last.top;
  const dw = first.width / last.width;
  const dh = first.height / last.height;

  el.style.transform = `translate(${dx}px, ${dy}px) scale(${dw}, ${dh})`;
  el.style.transition = 'none';

  // Play:触发动画回到最终状态
  requestAnimationFrame(() => {
    el.style.transition = 'transform 0.3s ease';
    el.style.transform = '';
  });
}

FLIP 核心:不计算中间状态,只做"记录-反向-播放"三步,动画性能极高。


CSS 工程化

CSS 模块化方案

BEM(Block Element Modifier)

/* Block:独立组件 */
.block { }

/* Element:组件内元素(双下划线) */
.block__element { }

/* Modifier:变体(双连字符) */
.block--modifier { }
.block__element--modifier { }
<div class="card card--featured">
  <h2 class="card__title">标题</h2>
  <p class="card__description">描述</p>
  <button class="card__button card__button--primary">点击</button>
</div>

BEM 优缺点

  • 优点:命名自解释、无层级嵌套、可预测
  • 缺点:类名冗长、不适合深层嵌套组件

CSS Modules

/* 文件:Button.module.css */
.button { color: red; }
.active { font-weight: bold; }
// React 中使用
import styles from './Button.module.css';
// styles.button → 生成唯一类名如 Button_button_1a2b3c
<button className={`${styles.button} ${isActive ? styles.active : ''}`} />

CSS-in-JS(styled-components / Emotion / linaria):

const Button = styled.button`
  background: ${props => props.variant === 'primary' ? 'blue' : 'gray'};
  color: white;
  padding: 8px 16px;
  border-radius: 4px;

  &:hover {
    opacity: 0.9;
  }
`;

方案对比

方案 作用域 动态样式 运行时开销 类型安全
BEM 手动 手动切换类
CSS Modules 自动(文件级) 类切换 部分(.module.css.d.ts)
CSS-in-JS 自动 直接 props
Utility-first 全局 组合类

预处理器

Sass(SCSS)核心功能

// 变量
$primary: #007bff;
$spacing: 16px;

// 嵌套(注意不要超过 3 层)
.card {
  padding: $spacing;
  &__title { font-size: 1.25rem; }
  &--featured { border-color: $primary; }
  &:hover { box-shadow: 0 2px 8px rgba(0,0,0,.15); }
}

// Mixin(可传参)
@mixin respond-to($breakpoint) {
  @if $breakpoint == 'md' {
    @media (min-width: 768px) { @content; }
  } @else if $breakpoint == 'lg' {
    @media (min-width: 1024px) { @content; }
  }
}
.sidebar { display: none; @include respond-to('md') { display: block; } }

// 继承(@extend)
.error { border: 1px solid red; color: red; }
.serious-error { @extend .error; font-weight: bold; }

// 函数
@function rem($px) {
  @return $px / 16 * 1rem;
}
body { font-size: rem(16); }

Less 核心功能(类似但不完全兼容):

@primary: #007bff;

.box-shadow(@x, @y, @blur, @color) {
  box-shadow: @x @y @blur @color;
}

.card {
  .box-shadow(0, 2px, 4px, rgba(0,0,0,.1));
}

PostCSS

PostCSS 是基于插件的 CSS 处理工具,不是预处理器。

常用插件

// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer'),         // 自动添加厂商前缀
    require('postcss-preset-env'),    // 使用未来 CSS 语法
    require('cssnano'),              // 压缩
    require('postcss-import'),       // 合并 @import 为单个文件
    require('postcss-nesting'),      // 原生 CSS 嵌套
  ]
};

CSS 变量(自定义属性)

:root {
  --primary: #007bff;
  --primary-hover: #0056b3;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --font-size-base: 1rem;
  --border-radius: 4px;
  --shadow: 0 2px 4px rgba(0,0,0,.1);
}

.button {
  background: var(--primary);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--border-radius);
  font-size: var(--font-size-base, 16px); /* 第二个参数是 fallback */
}

/* 作用域覆盖 */
.dark-theme {
  --primary: #66b0ff;
}

/* 可以用 JS 动态修改 */
document.documentElement.style.setProperty('--primary', '#ff6600');

/* calc 结合变量 */
.element {
  width: calc(var(--spacing-md) * 10);
}

CSS 变量 vs SCSS 变量

特性 CSS 变量 SCSS 变量
作用域 继承(级联) 编译时词法作用域
运行时修改 支持(JS 或媒体查询) 不支持
媒体查询 可覆盖 无法覆盖
类型 任意值 具体值
动效 transition 可过渡 不可

@layer 级联层

/* 定义层级顺序(先定义的优先级低) */
@layer reset, base, components, utilities;

/* 层内定义 */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; margin: 0; }
}

@layer base {
  body { font-family: sans-serif; line-height: 1.6; }
  a { color: blue; }
}

@layer components {
  .card { padding: 16px; border: 1px solid; }
}

@layer utilities {
  .mt-4 { margin-top: 16px; }
  .text-center { text-align: center; }
}

@layer 意义:解决 CSS 优先级冲突,让低优先级的 reset 不会覆盖组件样式,不再依赖选择器权重或 !important


新特性

:has() 选择器(CSS 选择器第 4 级)

/* 父选择器:选择包含某后代的元素 */
.card:has(.featured) { border-color: gold; }
figure:has(figcaption) { background: #f5f5f5; }

/* 兄弟选择 */
h2:has(+ p) { margin-bottom: 0; }

/* 条件选择 */
.form-group:has(:invalid) label { color: red; }

/* 数量查询 */
.row:has(> :nth-child(3):last-child) { /* 恰好 3 个子项 */ }

@container(容器查询)

@container (min-width: 400px) {
  /* 组件级响应式 */
}

View Transitions API

// SPA 页面过渡
document.startViewTransition(() => {
  updateDOMForNewPage();
});

/* CSS */
::view-transition-old(root) {
  animation: fadeOut 0.3s ease;
}
::view-transition-new(root) {
  animation: fadeIn 0.3s ease;
}

color-mix()

/* 颜色混合 */
.element {
  background: color-mix(in srgb, red, blue);
  /* 比例控制 */
  background: color-mix(in srgb, red 30%, blue);
  background: color-mix(in hsl, var(--primary), white 50%);
}

CSS Nesting(原生嵌套)

.card {
  padding: 16px;

  & .title { font-size: 1.25rem; }    /* 嵌套选择器 */
  & > .body { margin-top: 8px; }
  &:hover { box-shadow: 0 2px 4px; }  /* & 代表父选择器 */
  @media (width > 768px) {            /* 嵌套媒体查询 */
    padding: 24px;
  }
}