CSS 函数让样式计算和动���值成为可能。从基础的 calc() 到响应式的 clamp(),从变量的 var() 到环境的 env(),这些函数极大扩展了 CSS 的能力。
calc() 计算函数#
基本用法#
.element { /* 混合单位计算 */ width: calc(100% - 40px); height: calc(100vh - 80px); padding: calc(1rem + 10px);}支持的运算#
.math { /* 加法 */ width: calc(100px + 50px);
/* 减法 */ width: calc(100% - 20px);
/* 乘法(至少一个无单位数) */ width: calc(10px * 3); width: calc(3 * 10px);
/* 除法(除数必须无单位) */ width: calc(100px / 2);}🎯 运算符两边必须有空格(+ 和 -),否则会解析错误。
嵌套计算#
.nested { width: calc(100% - calc(20px * 2));
/* 简化写法(无需嵌套) */ width: calc(100% - 20px * 2);}与变量配合#
:root { --sidebar-width: 280px; --header-height: 64px;}
.main { width: calc(100% - var(--sidebar-width)); height: calc(100vh - var(--header-height));}实用案例#
/* 等分布局留间隙 */.column { width: calc((100% - 40px) / 3); /* 3列,间隙共40px */}
/* 黄金比例 */.golden { width: calc(100% / 1.618);}
/* 保持宽高比 */.ratio-box { width: 100%; padding-bottom: calc(100% * 9 / 16); /* 16:9 */}min() / max() 比较函数#
min() 取最小值#
.element { /* 取两者中较小的值 */ width: min(100%, 800px); /* 相当于 max-width: 800px 的效果 */
padding: min(5vw, 40px); font-size: min(4vw, 24px);}max() 取最大值#
.element { /* 取两者中较大的值 */ width: max(300px, 50%); /* 相当于 min-width: 300px 的效果 */
padding: max(2vw, 16px); font-size: max(16px, 1vw);}多个参数#
.element { /* 可以有多个参数 */ width: min(100%, 800px, 90vw); padding: max(16px, 2vw, 1rem);}嵌套使用#
.element { /* 设置范围 */ font-size: max(16px, min(4vw, 24px)); /* 最小 16px,最大 24px,中间响应式 */}clamp() 范围限制#
clamp(min, preferred, max) 是 min() 和 max() 的组合:
.element { /* clamp(最小值, 首选值, 最大值) */ font-size: clamp(16px, 4vw, 24px);
/* 等价于 */ font-size: max(16px, min(4vw, 24px));}响应式排版#
h1 { font-size: clamp(2rem, 5vw, 4rem);}
h2 { font-size: clamp(1.5rem, 4vw, 2.5rem);}
p { font-size: clamp(1rem, 2.5vw, 1.25rem);}响应式间距#
.container { padding: clamp(16px, 5vw, 64px);}
.section { margin-block: clamp(40px, 10vh, 120px);}
.gap { gap: clamp(16px, 3vw, 32px);}响应式宽度#
.content { width: clamp(320px, 90%, 1200px); margin-inline: auto;}
.sidebar { width: clamp(200px, 25%, 300px);}var() 变量函数#
基本用法#
:root { --primary: #3b82f6; --spacing: 16px;}
.button { background: var(--primary); padding: var(--spacing);}回退值#
.element { /* 变量不存在时使用回退值 */ color: var(--text-color, #333); padding: var(--padding, 16px);
/* 嵌套回退 */ background: var(--bg, var(--fallback-bg, white));}组合使用#
:root { --h: 217; --s: 91%; --l: 60%;}
.element { background: hsl(var(--h) var(--s) var(--l)); border-color: hsl(var(--h) var(--s) calc(var(--l) - 20%));}env() 环境函数#
��取用户代理定义的环境变量:
/* iPhone 刘海屏安全区域 */.container { padding-top: env(safe-area-inset-top); padding-right: env(safe-area-inset-right); padding-bottom: env(safe-area-inset-bottom); padding-left: env(safe-area-inset-left);}
/* 带回退值 */.header { padding-top: env(safe-area-inset-top, 20px);}安全区域#
/* 固定底部导航 */.bottom-nav { position: fixed; bottom: 0; left: 0; right: 0; padding-bottom: env(safe-area-inset-bottom); background: white;}
/* 全面屏适配 */body { padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);}attr() 属性函数#
获取 HTML 属性值(目前主要用于 content):
/* 显示 data 属性 */.tooltip::after { content: attr(data-tooltip);}
/* 显示链接地址 */a::after { content: ' (' attr(href) ')';}
/* 打印时显示 URL */@media print { a[href]::after { content: ' [' attr(href) ']'; }}url() 资源函数#
.element { background-image: url('/images/bg.jpg'); background-image: url('data:image/svg+xml,...');
/* 相对路径 */ background: url('../images/pattern.png');
/* 绝对路径 */ background: url('https://example.com/image.jpg');}颜色函数#
.colors { color: rgb(59 130 246); color: rgba(59, 130, 246, 0.5); color: hsl(217 91% 60%); color: hsla(217, 91%, 60%, 0.5); color: oklch(0.6 0.15 250); color: color-mix(in oklch, blue, red);}渐变函数#
.gradients { background: linear-gradient(135deg, #3b82f6, #8b5cf6); background: radial-gradient(circle at center, #3b82f6, transparent); background: conic-gradient(from 0deg, red, yellow, green, blue, red); background: repeating-linear-gradient(45deg, #3b82f6 0 10px, #fff 10px 20px);}变换函数#
.transform { transform: translate(100px, 50px); transform: rotate(45deg); transform: scale(1.5); transform: skew(10deg, 5deg); transform: matrix(1, 0, 0, 1, 0, 0);
/* 3D 变换 */ transform: translate3d(100px, 50px, 30px); transform: rotate3d(1, 1, 0, 45deg); transform: perspective(1000px) rotateY(30deg);}滤镜函数#
.filter { filter: blur(4px); filter: brightness(1.5); filter: contrast(1.2); filter: grayscale(1); filter: hue-rotate(90deg); filter: invert(1); filter: opacity(0.5); filter: saturate(2); filter: sepia(0.5); filter: drop-shadow(4px 4px 8px rgba(0, 0, 0, 0.3));}形状函数#
.shapes { clip-path: circle(50%); clip-path: ellipse(50% 30%); clip-path: polygon(50% 0%, 100% 100%, 0% 100%); clip-path: inset(10px 20px 30px 40px round 8px); clip-path: path('M 0 0 L 100 0 L 100 100 Z');}计数函数#
.counter { counter-reset: section;}
.counter h2::before { counter-increment: section; content: counter(section) '. ';}
/* 嵌套计数 */.nested::before { content: counters(section, '.') ' ';}实战案例#
流体排版系统#
:root { /* 基于视口的流体字号 */ --fluid-min-width: 320; --fluid-max-width: 1200;
--fluid-screen: 100vw; --fluid-bp: calc( (var(--fluid-screen) - var(--fluid-min-width) / 16 * 1rem) / (var(--fluid-max-width) - var(--fluid-min-width)) );}
h1 { font-size: clamp(2rem, 1.5rem + 3vw, 4rem); line-height: clamp(1.1, 1 + 0.5vw, 1.3);}
p { font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem); line-height: clamp(1.5, 1.4 + 0.3vw, 1.8);}响应式网格#
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: clamp(16px, 3vw, 32px); padding: clamp(16px, 5vw, 64px);}自适应容器#
.container { width: min(90%, 1200px); margin-inline: auto; padding-inline: max(16px, 5vw - 32px);}
/* 不同内容宽度 */.prose { max-width: min(65ch, 100%);}
.wide { max-width: min(1400px, 95%);}动态间距#
:root { --space-xs: clamp(4px, 1vw, 8px); --space-sm: clamp(8px, 2vw, 16px); --space-md: clamp(16px, 4vw, 32px); --space-lg: clamp(32px, 8vw, 64px); --space-xl: clamp(64px, 15vw, 128px);}
.section { padding-block: var(--space-lg);}
.card { padding: var(--space-md); gap: var(--space-sm);}安全区域适配#
.app { min-height: 100vh; min-height: calc( 100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom) );
padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);}
.fixed-bottom { position: fixed; bottom: 0; left: env(safe-area-inset-left); right: env(safe-area-inset-right); padding-bottom: calc(16px + env(safe-area-inset-bottom));}计算布局#
:root { --header-height: 64px; --sidebar-width: 280px; --footer-height: 120px;}
.layout { display: grid; grid-template-rows: var(--header-height) 1fr var(--footer-height); min-height: 100vh;}
.main-content { display: grid; grid-template-columns: var(--sidebar-width) 1fr;}
.page-content { height: calc(100vh - var(--header-height)); overflow-y: auto;}函数组合技巧#
clamp 与 calc 组合#
.element { /* 基于容器的响应式 */ padding: clamp(16px, calc(5% + 8px), 48px);
/* 带偏移的响应式字号 */ font-size: clamp(14px, calc(0.5rem + 1vw), 20px);}min/max 与 var 组合#
:root { --min-padding: 16px; --max-padding: 64px;}
.container { padding: max(var(--min-padding), min(5vw, var(--max-padding)));}常见问题#
🤔 calc() 中能用变量吗?
可以,但变量必须包含单位:
:root { --multiplier: 2; /* 错误:无单位 */ --multiplier: 2px; /* 正确 */}
/* 或者用 calc 添加单位 */.element { width: calc(var(--multiplier) * 1px * 100);}🤔 clamp() 和 媒体���询怎么选?
clamp() 适合平滑过渡的响应式值,媒体查询适合断点式变化。两者可以配合使用。
🤔 env() 不生效怎么办?
确保 HTML 的 viewport meta 标签包含 viewport-fit=cover:
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>CSS 函数极大增强了样式的表达能力。从 calc() 的数学计算到 clamp() 的响应式设计,掌握这些函数能让你写出更灵活、��强大的样式。下一篇我们将学习多列布局。