🎨 SCSS 高级用法完全指南:从入门到精通
🚀 想让 CSS 写得更爽?本文手把手教你 SCSS 的各种实用技巧,让你的样式代码又好写又好管理!
📚 目录
为了实时查看,我这边使用工程化来练习:
1. 变量与作用域
1.1 局部变量与全局变量
// 全局变量
$primary-color: #3498db;
.container {
// 局部变量
$padding: 20px;
padding: $padding;
.item {
// 可以访问父级局部变量
margin: $padding / 2;
color: $primary-color;
}
}
// $padding 在这里不可用
1.2 !global 标志
.element {
$local-var: 10px;
@if true {
// 使用 !global 将局部变量提升为全局
$local-var: 20px !global;
}
}
// 现在可以在外部访问
.another {
padding: $local-var; // 20px
}
1.3 !default 标志
// 设置默认值,如果变量已存在则不覆盖
$base-font-size: 16px !default;
$primary-color: #333 !default;
// 这在创建主题或库时非常有用
1.4 Map 变量
// 定义颜色系统
$colors: (
primary: #3498db,
secondary: #2ecc71,
danger: #e74c3c,
warning: #f39c12,
info: #9b59b6,
);
// 使用 map-get 获取值
.button {
background: map-get($colors, primary);
&.danger {
background: map-get($colors, danger);
}
}
// 深层嵌套的 Map
$theme: (
colors: (
light: (
bg: #ffffff,
text: #333333,
),
dark: (
bg: #1a1a1a,
text: #ffffff,
),
),
spacing: (
small: 8px,
medium: 16px,
large: 24px,
),
);
// 获取深层值
.dark-mode {
background: map-get(map-get(map-get($theme, colors), dark), bg);
}
2. 嵌套与父选择器
2.1 父选择器 & 的高级用法
// BEM 命名法
.card {
padding: 20px;
&__header {
font-size: 18px;
}
&__body {
margin: 10px 0;
}
&--featured {
border: 2px solid gold;
}
// 伪类
&:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
// 父选择器在后面
.dark-theme & {
background: #333;
}
}
2.2 嵌套属性
.button {
// 嵌套属性值
font: {
family: 'Helvetica', sans-serif;
size: 14px;
weight: bold;
}
border: {
top: 1px solid #ccc;
bottom: 2px solid #999;
radius: 4px;
}
transition: {
property: all;
duration: 0.3s;
timing-function: ease-in-out;
}
}
2.3 @at-root 跳出嵌套
.parent {
color: blue;
@at-root .child {
// 这会在根级别生成 .child 而不是 .parent .child
color: red;
}
@at-root {
.sibling-1 {
color: green;
}
.sibling-2 {
color: yellow;
}
}
}
3. Mixins 高级技巧
3.1 带参数的 Mixin
// 基础 Mixin
@mixin flex-center($direction: row) {
display: flex;
justify-content: center;
align-items: center;
flex-direction: $direction;
}
// 使用
.container {
@include flex-center(column);
}
3.2 可变参数 (...)
// 接收任意数量的参数
@mixin box-shadow($shadows...) {
-webkit-box-shadow: $shadows;
-moz-box-shadow: $shadows;
box-shadow: $shadows;
}
// 使用
.card {
@include box-shadow(0 2px 4px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.05));
}
// 传递多个值
@mixin transition($properties...) {
transition: $properties;
}
.button {
@include transition(background 0.3s ease, transform 0.2s ease-out);
}
3.3 @content 指令
// 响应式 Mixin
@mixin respond-to($breakpoint) {
@if $breakpoint == 'mobile' {
@media (max-width: 767px) {
@content;
}
} @else if $breakpoint == 'tablet' {
@media (min-width: 768px) and (max-width: 1023px) {
@content;
}
} @else if $breakpoint == 'desktop' {
@media (min-width: 1024px) {
@content;
}
}
}
// 使用
.sidebar {
width: 300px;
@include respond-to('mobile') {
width: 100%;
display: none;
}
@include respond-to('tablet') {
width: 200px;
}
}
3.4 高级响应式 Mixin
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px,
);
@mixin media-breakpoint-up($name) {
$min: map-get($breakpoints, $name);
@if $min {
@media (min-width: $min) {
@content;
}
} @else {
@content;
}
}
@mixin media-breakpoint-down($name) {
$max: map-get($breakpoints, $name) - 1px;
@if $max {
@media (max-width: $max) {
@content;
}
}
}
// 使用
.container {
padding: 15px;
@include media-breakpoint-up(md) {
padding: 30px;
}
@include media-breakpoint-up(lg) {
padding: 45px;
}
}
3.5 主题切换 Mixin
@mixin theme($theme-name) {
@if $theme-name == 'light' {
background: #ffffff;
color: #333333;
} @else if $theme-name == 'dark' {
background: #1a1a1a;
color: #ffffff;
}
}
// 更灵活的主题系统
$themes: (
light: (
bg: #ffffff,
text: #333333,
primary: #3498db,
),
dark: (
bg: #1a1a1a,
text: #ffffff,
primary: #5dade2,
),
);
@mixin themed() {
@each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: $map !global;
@content;
$theme-map: null !global;
}
}
}
@function t($key) {
@return map-get($theme-map, $key);
}
// 使用
.card {
@include themed() {
background: t(bg);
color: t(text);
border-color: t(primary);
}
}
4. 函数的妙用
4.1 自定义函数
// 计算 rem
@function rem($pixels, $base: 16px) {
@return ($pixels / $base) * 1rem;
}
.title {
font-size: rem(24px); // 1.5rem
margin-bottom: rem(16px); // 1rem
}
4.2 颜色操作函数
// 创建颜色变体
@function tint($color, $percentage) {
@return mix(white, $color, $percentage);
}
@function shade($color, $percentage) {
@return mix(black, $color, $percentage);
}
$primary: #3498db;
.button {
background: $primary;
&:hover {
background: shade($primary, 20%);
}
&.light {
background: tint($primary, 30%);
}
}
4.3 字符串操作
@function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index +
str-length($search)), $search, $replace);
}
@return $string;
}
// 使用
$font-family: str-replace('Arial, sans-serif', 'Arial', 'Helvetica');
4.4 深度获取 Map 值
@function deep-map-get($map, $keys...) {
@each $key in $keys {
$map: map-get($map, $key);
}
@return $map;
}
$config: (
theme: (
colors: (
primary: (
base: #3498db,
light: #5dade2,
),
),
),
);
.element {
color: deep-map-get($config, theme, colors, primary, base);
}
5. 继承与占位符
5.1 基础继承
.message {
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.success-message {
@extend .message;
border-color: #2ecc71;
background: #d5f4e6;
}
.error-message {
@extend .message;
border-color: #e74c3c;
background: #fadbd8;
}
5.2 占位符选择器 %
// 占位符不会单独生成 CSS
%flex-center {
display: flex;
justify-content: center;
align-items: center;
}
%text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-title {
@extend %text-truncate;
font-size: 18px;
}
.modal {
@extend %flex-center;
min-height: 100vh;
}
5.3 多重继承
%bordered {
border: 1px solid #ddd;
}
%rounded {
border-radius: 8px;
}
%shadowed {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card {
@extend %bordered;
@extend %rounded;
@extend %shadowed;
padding: 20px;
}
6. 控制指令
6.1 @if / @else
@mixin theme-color($theme) {
@if $theme == 'light' {
background: white;
color: black;
} @else if $theme == 'dark' {
background: black;
color: white;
} @else {
background: gray;
color: white;
}
}
.app {
@include theme-color('dark');
}
6.2 @for 循环
// 生成网格系统
@for $i from 1 through 12 {
.col-#{$i} {
width: percentage($i / 12);
}
}
// 生成间距工具类
$spacing: (5, 10, 15, 20, 25, 30);
@for $i from 1 through length($spacing) {
$space: nth($spacing, $i);
.m-#{$space} {
margin: #{$space}px;
}
.p-#{$space} {
padding: #{$space}px;
}
.mt-#{$space} {
margin-top: #{$space}px;
}
.pt-#{$space} {
padding-top: #{$space}px;
}
.mb-#{$space} {
margin-bottom: #{$space}px;
}
.pb-#{$space} {
padding-bottom: #{$space}px;
}
}
6.3 @each 循环
// 遍历列表
$colors: primary, secondary, success, danger, warning, info;
@each $color in $colors {
.btn-#{$color} {
background: var(--#{$color}-color);
}
}
// 遍历 Map
$social-colors: (
facebook: #3b5998,
twitter: #1da1f2,
instagram: #e4405f,
linkedin: #0077b5,
youtube: #ff0000,
);
@each $name, $color in $social-colors {
.btn-#{$name} {
background-color: $color;
&:hover {
background-color: darken($color, 10%);
}
}
}
// 多重值遍历
$sizes: (small, 12px, 500, medium, 14px, 600, large, 16px, 700);
@each $size, $font-size, $font-weight in $sizes {
.text-#{$size} {
font-size: $font-size;
font-weight: $font-weight;
}
}
6.4 @while 循环
// 生成渐进式字体大小
$i: 6;
@while $i > 0 {
h#{$i} {
font-size: 2em - ($i * 0.2);
}
$i: $i - 1;
}
7. 模块化系统
7.1 @use 和 @forward
// _variables.scss
$primary-color: #3498db;
$secondary-color: #2ecc71;
// _mixins.scss
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
// _functions.scss
@function rem($px) {
@return ($px / 16px) * 1rem;
}
// main.scss - 新的模块系统
@use 'variables' as vars;
@use 'mixins' as mix;
@use 'functions' as fn;
.container {
@include mix.flex-center;
color: vars.$primary-color;
padding: fn.rem(20px);
}
7.2 命名空间
// _config.scss
$primary: #3498db;
@mixin button {
padding: 10px 20px;
border-radius: 4px;
}
// styles.scss
@use 'config' as cfg;
.btn {
@include cfg.button;
background: cfg.$primary;
}
// 或者移除命名空间前缀
@use 'config' as *;
.btn {
@include button;
background: $primary;
}
7.3 @forward 创建索引文件
// styles/_index.scss
@forward 'variables';
@forward 'mixins';
@forward 'functions';
// main.scss
@use 'styles';
.element {
color: styles.$primary-color;
@include styles.flex-center;
}
8. 内置函数库
8.1 颜色函数
$base-color: #3498db;
.color-demo {
// 颜色调整
color: adjust-hue($base-color, 45deg);
// 亮度
background: lighten($base-color, 20%);
border-color: darken($base-color, 15%);
// 饱和度
&.vibrant {
background: saturate($base-color, 30%);
}
&.muted {
background: desaturate($base-color, 20%);
}
// 透明度
box-shadow: 0 2px 8px rgba($base-color, 0.3);
border: 1px solid transparentize($base-color, 0.5);
// 混合颜色
&.mixed {
background: mix(#3498db, #e74c3c, 50%);
}
// 补色
&.complement {
background: complement($base-color);
}
}
8.2 数学函数
.math-demo {
// 基础运算
width: percentage(5 / 12); // 41.66667%
padding: round(13.6px); // 14px
margin: ceil(10.1px); // 11px
height: floor(19.9px); // 19px
// 最大最小值
font-size: max(14px, 1rem);
width: min(100%, 1200px);
// 绝对值
top: abs(-20px); // 20px
// 随机数
opacity: random(100) / 100;
}
8.3 列表函数
$list: 10px 20px 30px 40px;
.list-demo {
// 获取长度
$length: length($list); // 4
// 获取元素
padding-top: nth($list, 1); // 10px
padding-right: nth($list, 2); // 20px
// 索引
$index: index($list, 20px); // 2
// 追加
$new-list: append($list, 50px);
// 合并
$merged: join($list, (60px 70px));
}
8.4 Map 函数
$theme: (
primary: #3498db,
secondary: #2ecc71,
danger: #e74c3c,
);
.map-demo {
// 获取值
color: map-get($theme, primary);
// 合并 Map
$extended: map-merge(
$theme,
(
success: #27ae60,
)
);
// 检查键是否存在
@if map-has-key($theme, primary) {
background: map-get($theme, primary);
}
// 获取所有键
$keys: map-keys($theme); // primary, secondary, danger
// 获取所有值
$values: map-values($theme);
}
8.5 字符串函数
$text: 'Hello World';
.string-demo {
// 转大写
content: to-upper-case($text); // "HELLO WORLD"
// 转小写
content: to-lower-case($text); // "hello world"
// 字符串长度
$length: str-length($text); // 11
// 查找索引
$index: str-index($text, 'World'); // 7
// 切片
content: str-slice($text, 1, 5); // "Hello"
// 插入
content: str-insert($text, ' Beautiful', 6); // "Hello Beautiful World"
// 去引号
font-family: unquote('"Arial"'); // Arial
}
9. 实战技巧
9.1 响应式字体大小
@function strip-unit($value) {
@return $value / ($value * 0 + 1);
}
@mixin fluid-type($min-vw, $max-vw, $min-font-size, $max-font-size) {
$u1: unit($min-vw);
$u2: unit($max-vw);
$u3: unit($min-font-size);
$u4: unit($max-font-size);
@if $u1 == $u2 and $u1 == $u3 and $u1 == $u4 {
& {
font-size: $min-font-size;
@media screen and (min-width: $min-vw) {
font-size: calc(
#{$min-font-size} + #{strip-unit($max-font-size - $min-font-size)} *
((100vw - #{$min-vw}) / #{strip-unit($max-vw - $min-vw)})
);
}
@media screen and (min-width: $max-vw) {
font-size: $max-font-size;
}
}
}
}
h1 {
@include fluid-type(320px, 1200px, 24px, 48px);
}
9.2 深色模式切换
$themes: (
light: (
bg: #ffffff,
text: #333333,
border: #e0e0e0,
primary: #3498db,
),
dark: (
bg: #1a1a1a,
text: #f0f0f0,
border: #404040,
primary: #5dade2,
),
);
@mixin themed-component {
@each $theme-name, $theme-colors in $themes {
[data-theme='#{$theme-name}'] & {
$theme-map: $theme-colors !global;
@content;
$theme-map: null !global;
}
}
}
@function theme-color($key) {
@return map-get($theme-map, $key);
}
.card {
@include themed-component {
background: theme-color(bg);
color: theme-color(text);
border: 1px solid theme-color(border);
}
&__button {
@include themed-component {
background: themed-component {
background: theme-color(primary);
color: theme-color(bg);
}
}
}
9.3 原子化 CSS 生成器
$spacing-map: (
0: 0,
1: 0.25rem,
2: 0.5rem,
3: 0.75rem,
4: 1rem,
5: 1.25rem,
6: 1.5rem,
8: 2rem,
10: 2.5rem,
12: 3rem,
16: 4rem,
20: 5rem,
);
$directions: (
'': '',
't': '-top',
'r': '-right',
'b': '-bottom',
'l': '-left',
'x': (
'-left',
'-right',
),
'y': (
'-top',
'-bottom',
),
);
@each $size-key, $size-value in $spacing-map {
@each $dir-key, $dir-value in $directions {
// Margin
.m#{$dir-key}-#{$size-key} {
@if type-of($dir-value) == 'list' {
@each $d in $dir-value {
margin#{$d}: $size-value;
}
} @else {
margin#{$dir-value}: $size-value;
}
}
// Padding
.p#{$dir-key}-#{$size-key} {
@if type-of($dir-value) == 'list' {
@each $d in $dir-value {
padding#{$d}: $size-value;
}
} @else {
padding#{$dir-value}: $size-value;
}
}
}
}
9.4 三角形生成器
@mixin triangle($direction, $size, $color) {
width: 0;
height: 0;
border: $size solid transparent;
@if $direction == 'up' {
border-bottom-color: $color;
} @else if $direction == 'down' {
border-top-color: $color;
} @else if $direction == 'left' {
border-right-color: $color;
} @else if $direction == 'right' {
border-left-color: $color;
}
}
.tooltip {
position: relative;
&::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
@include triangle(down, 8px, #333);
}
}
9.5 网格系统生成器
$grid-columns: 12;
$grid-gutter-width: 30px;
$container-max-widths: (
sm: 540px,
md: 720px,
lg: 960px,
xl: 1140px,
xxl: 1320px,
);
@mixin make-container($padding-x: $grid-gutter-width / 2) {
width: 100%;
padding-right: $padding-x;
padding-left: $padding-x;
margin-right: auto;
margin-left: auto;
}
@mixin make-row($gutter: $grid-gutter-width) {
display: flex;
flex-wrap: wrap;
margin-right: -$gutter / 2;
margin-left: -$gutter / 2;
}
@mixin make-col($size, $columns: $grid-columns) {
flex: 0 0 auto;
width: percentage($size / $columns);
padding-right: $grid-gutter-width / 2;
padding-left: $grid-gutter-width / 2;
}
.container {
@include make-container;
@each $breakpoint, $width in $container-max-widths {
@include media-breakpoint-up($breakpoint) {
max-width: $width;
}
}
}
.row {
@include make-row;
}
@for $i from 1 through $grid-columns {
.col-#{$i} {
@include make-col($i);
}
}
9.6 长阴影效果
@function long-shadow($length, $color, $opacity) {
$shadow: '';
@for $i from 0 through $length {
$shadow: $shadow +
'#{$i}px #{$i}px rgba(#{red($color)}, #{green($color)}, #{blue($color)}, #{$opacity})';
@if $i < $length {
$shadow: $shadow + ', ';
}
}
@return unquote($shadow);
}
.text-shadow {
text-shadow: long-shadow(50, #000, 0.05);
}
9.7 动画关键帧生成器
@mixin keyframes($name) {
@keyframes #{$name} {
@content;
}
}
@include keyframes(fadeIn) {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade {
animation: fadeIn 0.5s ease-out;
}
9.8 清除浮动
@mixin clearfix {
&::after {
content: '';
display: table;
clear: both;
}
}
.container {
@include clearfix;
}
🎯 总结
SCSS 的高级特性让我们能够:
- 提高代码复用性 - 通过 mixin、函数和继承
- 增强可维护性 - 使用变量、模块化和命名空间
- 提升开发效率 - 利用循环、条件判断自动生成样式
- 保持代码整洁 - 嵌套、占位符和模块系统
- 创建强大的工具库 - 自定义函数和 mixin 集合
最佳实践建议
-
✅ 变量命名要语义化
// Good $primary-color: #3498db; $spacing-unit: 8px; // Bad $blue: #3498db; $var1: 8px;
-
✅ 避免嵌套层级过深(建议不超过 3-4 层)
// Good .card { &__header { } &__body { } } // Bad - 嵌套太深 .card { .wrapper { .inner { .content { .text { } } } } }
-
✅ 优先使用 @use 而不是 @import
// Modern
@use 'variables';
@use 'mixins';
// Legacy
@import 'variables';
@import 'mixins';
-
✅ 使用占位符代替类继承
// Good %btn-base { } .btn { @extend %btn-base; } // Less optimal .btn-base { } .btn { @extend .btn-base; }
-
✅ 合理组织文件结构 styles/ ├── abstracts/ │ ├── _variables.scss │ ├── _functions.scss │ └── _mixins.scss ├── base/ │ ├── _reset.scss │ └── _typography.scss ├── components/ │ ├── _buttons.scss │ └── _cards.scss ├── layout/ │ ├── _header.scss │ └── _footer.scss └── main.scss
📚 参考资源
如果这篇文章对你有帮助,欢迎点赞收藏! 👍
有任何问题或补充,欢迎在评论区讨论~ 💬