CSS 扩展与预处理器
本文在 CSS 基础之上,系统介绍 CSS Grid 布局、CSS 自定义属性、过渡与动画、SCSS/Sass 预处理器,以及 Tailwind CSS 原子化方案,帮助你构建可维护的样式体系。
CSS Grid 布局
Grid 是二维布局系统,与 Flexbox(一维)互补。适合整体页面框架、卡片网格等需要行列同时控制的场景。
/* 容器:声明 Grid */
.grid-container {
display: grid;
/* 列定义:3 列等宽 */
grid-template-columns: repeat(3, 1fr);
/* 行定义:第一行 80px,其余自动 */
grid-template-rows: 80px auto;
/* 间距 */
gap: 16px; /* 行列统一 */
column-gap: 24px; /* 仅列间距 */
row-gap: 12px; /* 仅行间距 */
}
/* 子项:定位到具体格子 */
.item-header {
grid-column: 1 / 4; /* 跨第 1~3 列(span 写法:span 3) */
grid-row: 1;
}
.item-sidebar {
grid-column: 1;
grid-row: 2 / 5; /* 跨多行 */
}
.item-main {
grid-column: 2 / 4;
grid-row: 2;
}命名模板区域
.layout {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 60px 1fr 50px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }常用函数与关键字
.responsive-grid {
display: grid;
/* auto-fill: 自动填充列数;minmax(200px, 1fr): 最小 200px,最大 1fr */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
/* fr 单位:剩余空间比例 */
/* 3 列:1:2:1 */
grid-template-columns: 1fr 2fr 1fr;
}
/* 对齐 */
.grid {
justify-items: center; /* 单元格内水平 */
align-items: center; /* 单元格内垂直 */
justify-content: space-between; /* 容器内列的对齐 */
align-content: start; /* 容器内行的对齐 */
}
.item {
justify-self: end; /* 单个项目水平对齐 */
align-self: center; /* 单个项目垂直对齐 */
}Grid vs Flexbox 选择
| 场景 | 推荐 |
|---|---|
| 单行工具栏、导航项 | Flexbox |
| 整体页面布局(行+列) | Grid |
| 卡片网格、图片墙 | Grid |
| 未知数量的列表项居中 | Flexbox |
| 表单字段标签对齐 | Grid |
CSS 自定义属性(变量)
CSS 变量(--variable-name)是运行时值,可被 JavaScript 读写,作用域遵循 CSS 层叠规则。
/* 全局变量:通常挂在 :root */
:root {
--color-primary: #3b82f6;
--color-bg: #f8fafc;
--spacing-base: 8px;
--font-body: "Inter", sans-serif;
--radius: 6px;
--shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
/* 使用变量 */
.button {
background-color: var(--color-primary);
border-radius: var(--radius);
padding: calc(var(--spacing-base) * 1.5) calc(var(--spacing-base) * 3);
font-family: var(--font-body);
box-shadow: var(--shadow);
}
/* 带默认值的 var() */
.card {
color: var(--card-text, var(--color-primary)); /* 回退值 */
}
/* 局部作用域:覆盖特定组件的变量 */
.dark-card {
--color-bg: #1e293b;
--card-text: #f1f5f9;
background-color: var(--color-bg);
color: var(--card-text);
}主题切换(暗色模式)
:root {
--bg: #ffffff;
--text: #1a1a1a;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #f0f0f0;
}
}
/* 或者用 data 属性手动切换 */
[data-theme="dark"] {
--bg: #1a1a1a;
--text: #f0f0f0;
}// JavaScript 读写 CSS 变量
const root = document.documentElement
root.style.setProperty("--color-primary", "#ef4444")
getComputedStyle(root).getPropertyValue("--color-primary").trim()
// 切换主题
document.documentElement.setAttribute("data-theme", "dark")CSS 过渡与动画
transition(过渡)
过渡让属性变化在指定时间内平滑完成,适合交互状态切换(hover、focus、active)。
.button {
background-color: #3b82f6;
transform: scale(1);
opacity: 1;
/* transition: 属性 持续时间 缓动函数 延迟 */
transition:
background-color 0.2s ease,
transform 0.15s ease-out,
opacity 0.2s ease;
}
.button:hover {
background-color: #2563eb;
transform: scale(1.03);
}
/* 常用缓动函数 */
/* ease(默认):慢快慢 ease-in:渐入 ease-out:渐出 linear:匀速 */
/* cubic-bezier(x1, y1, x2, y2):自定义曲线 */
/* 多属性简写 */
.card {
transition: all 0.3s ease; /* 所有变化属性(性能较差,不推荐生产) */
}@keyframes(关键帧动画)
关键帧动画适合循环动画、入场/离场效果等复杂序列。
/* 定义动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* 应用动画 */
.card {
/* animation: 名称 持续时间 缓动 延迟 次数 方向 填充模式 */
animation: fadeIn 0.4s ease-out 0.1s 1 normal forwards;
}
.loader {
animation: spin 1s linear infinite;
}
.skeleton {
animation: pulse 1.5s ease-in-out infinite;
}
/* 暂停/播放 */
.paused { animation-play-state: paused; }性能优化原则
过渡和动画只对 transform 和 opacity 是 GPU 加速的;修改 width、height、top、left 会触发重排(reflow),性能代价高。
/* ✅ GPU 加速:transform + opacity */
.modal { transform: translateX(-100%); opacity: 0; }
.modal.open { transform: translateX(0); opacity: 1; }
/* ❌ 触发重排 */
.modal { left: -300px; }
.modal.open { left: 0; }SCSS / Sass
Sass 是 CSS 的超集,支持变量、嵌套、Mixin、继承等功能。现代项目推荐使用 SCSS 语法(兼容标准 CSS 语法)。
安装与编译
# 安装 Dart Sass(官方推荐实现)
npm install -g sass
# 编译
sass styles.scss styles.css
# 监听变化
sass --watch styles.scss styles.css
# 项目中使用(Vite 内置支持,无需额外配置)
npm install -D sass变量
// 变量使用 $ 前缀
$color-primary: #3b82f6;
$color-danger: #ef4444;
$font-size-base: 16px;
$border-radius: 6px;
$spacing: 8px;
.button {
background-color: $color-primary;
font-size: $font-size-base;
padding: $spacing ($spacing * 2);
border-radius: $border-radius;
}嵌套
// 嵌套选择器(减少重复书写父选择器)
.navbar {
display: flex;
background: #1e293b;
.nav-item {
padding: 12px 16px;
color: #94a3b8;
&:hover { // & 代表父选择器(.navbar .nav-item:hover)
color: white;
}
&.active { // .navbar .nav-item.active
color: #60a5fa;
font-weight: 600;
}
&::after { // 伪元素
content: "";
}
}
// 嵌套媒体查询(Sass 4.0+ / 新标准原生支持)
@media (max-width: 768px) {
flex-direction: column;
}
}@mixin 与 @include
Mixin 是可复用的样式块,支持参数和默认值。
// 定义 mixin
@mixin flex-center($direction: row) {
display: flex;
flex-direction: $direction;
justify-content: center;
align-items: center;
}
@mixin respond-to($breakpoint) {
@if $breakpoint == "mobile" {
@media (max-width: 640px) { @content; }
} @else if $breakpoint == "tablet" {
@media (max-width: 1024px) { @content; }
}
}
@mixin button-variant($bg, $text: white) {
background-color: $bg;
color: $text;
border: 1px solid darken($bg, 10%);
&:hover {
background-color: darken($bg, 8%);
}
}
// 使用 mixin
.hero {
@include flex-center(column);
min-height: 100vh;
}
.card {
@include respond-to("mobile") {
padding: 12px;
}
}
.btn-primary { @include button-variant(#3b82f6); }
.btn-danger { @include button-variant(#ef4444); }@extend(继承)
// 占位符选择器(% 开头,不会生成 CSS,仅供继承)
%card-base {
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.product-card {
@extend %card-base;
border: 1px solid #e2e8f0;
}
.alert-card {
@extend %card-base;
border-left: 4px solid #ef4444;
}@extend vs @mixin:
@extend 合并选择器(输出更少 CSS),但不支持参数,且跨文件继承容易出问题。@mixin 更灵活安全,通常优先选 mixin。@use 与 @forward(模块化)
现代 Sass 用 @use 替代已废弃的 @import,避免全局命名污染。
// _variables.scss
$color-primary: #3b82f6 !default; // !default:允许调用方覆盖
$spacing-unit: 8px !default;
// _mixins.scss
@use "variables" as vars;
@mixin spacing($n: 1) {
padding: vars.$spacing-unit * $n;
}
// styles.scss(入口)
// 配置变量(必须在 @use 的 with 子句中)
@use "variables" as vars with (
$color-primary: #6366f1,
$spacing-unit: 10px,
);
@use "mixins";
.button {
color: vars.$color-primary;
@include mixins.spacing(2);
}// @forward:让 _index.scss 充当"桶文件",对外暴露多个分模块
// styles/_index.scss
@forward "variables";
@forward "mixins";
@forward "typography";
// 在其它文件只需一行引入
@use "styles";
.heading { font-size: styles.$font-size-lg; }内置模块
@use "sass:color";
@use "sass:math";
@use "sass:string";
@use "sass:list";
@use "sass:map";
$blue: #3b82f6;
.card {
background: color.scale($blue, $lightness: 80%); // 调亮
border: 1px solid color.adjust($blue, $lightness: -15%); // 调暗
}
.layout {
width: math.percentage(2 / 3); // 66.6667%
padding: math.round(12.7px); // 13px
}项目目录结构(7-1 模式简化版)
styles/
├── _variables.scss # 全局变量
├── _mixins.scss # Mixin
├── _base.scss # 重置 / 基础样式
├── _typography.scss # 字体
├── _layout.scss # 网格/布局
├── components/
│ ├── _button.scss
│ └── _card.scss
└── main.scss # 入口:@use 所有模块Tailwind CSS
Tailwind 是原子化 CSS 框架:每个 class 只做一件事,样式直接写在 HTML 中,由构建工具按需生成最小 CSS。
安装(Vite 项目)
npm install tailwindcss @tailwindcss/vite
# vite.config.ts 中注册插件
# import tailwindcss from '@tailwindcss/vite'
# plugins: [vue(), tailwindcss()]在 CSS 入口文件中引入:
/* src/style.css */
@import "tailwindcss";常用工具类速查
<!-- 布局 -->
<div class="flex items-center justify-between gap-4">...</div>
<div class="grid grid-cols-3 gap-6">...</div>
<!-- 间距 (1 = 4px) -->
<div class="p-4 m-2 px-6 py-3 mt-8 space-x-4">...</div>
<!-- 尺寸 -->
<div class="w-full h-screen max-w-4xl min-h-0">...</div>
<div class="w-1/2 w-64 h-auto">...</div>
<!-- 字体 -->
<p class="text-base text-lg text-xl text-2xl font-bold font-medium">...</p>
<p class="text-gray-700 leading-relaxed tracking-wide">...</p>
<!-- 颜色 / 背景 -->
<div class="bg-blue-500 text-white hover:bg-blue-600">...</div>
<div class="bg-gradient-to-r from-blue-500 to-purple-600">...</div>
<!-- 边框 / 圆角 / 阴影 -->
<div class="border border-gray-200 rounded-lg shadow-md">...</div>
<div class="rounded-full border-2 border-blue-500">...</div>
<!-- 响应式(移动优先,sm/md/lg/xl/2xl) -->
<div class="w-full md:w-1/2 lg:w-1/3">...</div>
<p class="text-sm md:text-base lg:text-lg">...</p>
<!-- 暗色模式 -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">...</div>
<!-- 状态变体 -->
<button class="bg-blue-500 hover:bg-blue-600 focus:ring-2 active:scale-95 disabled:opacity-50">
Click me
</button>配置主题(tailwind.config.js / CSS)
Tailwind v4 支持在 CSS 中直接配置主题:
@import "tailwindcss";
@theme {
/* 自定义颜色 */
--color-brand: #6366f1;
--color-brand-dark: #4f46e5;
/* 自定义字体 */
--font-sans: "Inter", sans-serif;
/* 自定义断点 */
--breakpoint-xs: 480px;
}// tailwind.config.js(v3 写法)
export default {
content: ["./src/**/*.{html,js,ts,vue,jsx,tsx}"],
darkMode: "class", // 或 "media"
theme: {
extend: {
colors: {
brand: { DEFAULT: "#6366f1", dark: "#4f46e5" },
},
fontFamily: {
sans: ["Inter", "sans-serif"],
},
animation: {
"fade-in": "fadeIn 0.3s ease-out",
},
},
},
}@apply(在组件中复用工具类)
/* 将常用工具类组合为语义化类(Vue SFC 中使用) */
.btn-primary {
@apply inline-flex items-center px-4 py-2 rounded-lg
bg-blue-500 text-white font-medium
hover:bg-blue-600 transition-colors duration-200
focus:outline-none focus:ring-2 focus:ring-blue-400;
}Tailwind vs 自定义 CSS 选择
| 场景 | 推荐 |
|---|---|
| 快速原型、管理后台 | Tailwind |
| 高度定制化品牌组件库 | SCSS / CSS Modules |
| 设计令牌(Design Token)管理 | CSS 自定义属性 + SCSS |
| 已有 SCSS 项目扩展功能 | SCSS |
| Vue/React 组件库 | CSS Modules 或 Tailwind |
最后更新于