本章概述
本章将深入学习如何自定义 Bootstrap,包括 Sass 变量定制、自定义主题创建、组件样式修改等高级技术。通过本章学习,你将能够创建独特的设计风格,满足项目的特定需求。
学习目标
- 掌握 Bootstrap Sass 变量系统
- 学会创建自定义主题
- 了解组件样式定制方法
- 掌握颜色系统和排版定制
- 学习构建工具集成
Sass 变量定制
1. Bootstrap Sass 变量系统
// _custom-variables.scss
// Bootstrap 核心变量定制
// 颜色系统定制
$primary: #6f42c1; // 主色调
$secondary: #6c757d; // 次要色
$success: #198754; // 成功色
$info: #0dcaf0; // 信息色
$warning: #ffc107; // 警告色
$danger: #dc3545; // 危险色
$light: #f8f9fa; // 浅色
$dark: #212529; // 深色
// 灰度色系
$white: #fff;
$gray-100: #f8f9fa;
$gray-200: #e9ecef;
$gray-300: #dee2e6;
$gray-400: #ced4da;
$gray-500: #adb5bd;
$gray-600: #6c757d;
$gray-700: #495057;
$gray-800: #343a40;
$gray-900: #212529;
$black: #000;
// 字体系统
$font-family-sans-serif: "Helvetica Neue", Arial, "Noto Sans", sans-serif;
$font-family-monospace: "SF Mono", Monaco, Inconsolata, "Roboto Mono", monospace;
$font-family-base: $font-family-sans-serif;
// 字体大小
$font-size-root: 16px;
$font-size-base: 1rem;
$font-size-sm: $font-size-base * 0.875;
$font-size-lg: $font-size-base * 1.25;
// 字重
$font-weight-lighter: lighter;
$font-weight-light: 300;
$font-weight-normal: 400;
$font-weight-medium: 500;
$font-weight-semibold: 600;
$font-weight-bold: 700;
$font-weight-bolder: bolder;
// 行高
$line-height-base: 1.5;
$line-height-sm: 1.25;
$line-height-lg: 2;
// 标题字体大小
$h1-font-size: $font-size-base * 2.5;
$h2-font-size: $font-size-base * 2;
$h3-font-size: $font-size-base * 1.75;
$h4-font-size: $font-size-base * 1.5;
$h5-font-size: $font-size-base * 1.25;
$h6-font-size: $font-size-base;
// 间距系统
$spacer: 1rem;
$spacers: (
0: 0,
1: $spacer * 0.25,
2: $spacer * 0.5,
3: $spacer,
4: $spacer * 1.5,
5: $spacer * 3,
6: $spacer * 4,
7: $spacer * 5
);
// 边框
$border-width: 1px;
$border-style: solid;
$border-color: $gray-300;
$border-radius: 0.375rem;
$border-radius-sm: 0.25rem;
$border-radius-lg: 0.5rem;
$border-radius-xl: 1rem;
$border-radius-2xl: 2rem;
$border-radius-pill: 50rem;
// 阴影
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15);
$box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075);
$box-shadow-lg: 0 1rem 3rem rgba($black, 0.175);
$box-shadow-inset: inset 0 1px 2px rgba($black, 0.075);
// 组件特定变量
// 按钮
$btn-padding-y: 0.375rem;
$btn-padding-x: 0.75rem;
$btn-font-family: null;
$btn-font-size: $font-size-base;
$btn-line-height: $line-height-base;
$btn-white-space: null;
$btn-padding-y-sm: 0.25rem;
$btn-padding-x-sm: 0.5rem;
$btn-font-size-sm: $font-size-sm;
$btn-padding-y-lg: 0.5rem;
$btn-padding-x-lg: 1rem;
$btn-font-size-lg: $font-size-lg;
$btn-border-width: $border-width;
$btn-font-weight: $font-weight-normal;
$btn-box-shadow: inset 0 1px 0 rgba($white, 0.15), 0 1px 1px rgba($black, 0.075);
$btn-focus-width: 0.25rem;
$btn-focus-box-shadow: 0 0 0 $btn-focus-width rgba(mix($primary, $white, 15%), 0.5);
$btn-disabled-opacity: 0.65;
$btn-active-box-shadow: inset 0 3px 5px rgba($black, 0.125);
$btn-border-radius: $border-radius;
$btn-border-radius-sm: $border-radius-sm;
$btn-border-radius-lg: $border-radius-lg;
$btn-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
// 卡片
$card-spacer-y: $spacer;
$card-spacer-x: $spacer;
$card-title-spacer-y: $spacer * 0.5;
$card-title-color: null;
$card-subtitle-color: $gray-600;
$card-border-width: $border-width;
$card-border-color: rgba($black, 0.125);
$card-border-radius: $border-radius;
$card-box-shadow: null;
$card-inner-border-radius: subtract($card-border-radius, $card-border-width);
$card-cap-padding-y: $card-spacer-y * 0.5;
$card-cap-padding-x: $card-spacer-x;
$card-cap-bg: rgba($black, 0.03);
$card-cap-color: null;
$card-height: null;
$card-color: null;
$card-bg: $white;
$card-img-overlay-padding: $spacer;
$card-group-margin: $grid-gutter-width * 0.5;
// 导航栏
$navbar-padding-y: $spacer * 0.5;
$navbar-padding-x: null;
$navbar-nav-link-padding-x: 0.5rem;
$navbar-brand-font-size: $font-size-lg;
$navbar-brand-height: $navbar-brand-font-size * $line-height-base;
$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) * 0.5;
$navbar-brand-margin-end: 1rem;
$navbar-toggler-padding-y: 0.25rem;
$navbar-toggler-padding-x: 0.75rem;
$navbar-toggler-font-size: $font-size-lg;
$navbar-toggler-border-radius: $btn-border-radius;
$navbar-toggler-focus-width: $btn-focus-width;
$navbar-toggler-transition: box-shadow 0.15s ease-in-out;
// 模态框
$modal-inner-padding: $spacer;
$modal-footer-margin-between: 0.5rem;
$modal-dialog-margin: 0.5rem;
$modal-dialog-margin-y-sm-up: 1.75rem;
$modal-title-line-height: $line-height-base;
$modal-content-color: null;
$modal-content-bg: $white;
$modal-content-border-color: rgba($black, 0.2);
$modal-content-border-width: $border-width;
$modal-content-border-radius: $border-radius-lg;
$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width);
$modal-content-box-shadow-xs: 0 0.25rem 0.5rem rgba($black, 0.5);
$modal-content-box-shadow-sm-up: 0 0.5rem 1rem rgba($black, 0.5);
$modal-backdrop-bg: $black;
$modal-backdrop-opacity: 0.5;
$modal-header-border-color: $border-color;
$modal-footer-border-color: $modal-header-border-color;
$modal-header-border-width: $modal-content-border-width;
$modal-footer-border-width: $modal-header-border-width;
$modal-header-padding-y: $modal-inner-padding;
$modal-header-padding-x: $modal-inner-padding;
$modal-header-padding: $modal-header-padding-y $modal-header-padding-x;
// 断点系统
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
// 容器最大宽度
$container-max-widths: (
sm: 540px,
md: 720px,
lg: 960px,
xl: 1140px,
xxl: 1320px
);
// 网格系统
$grid-columns: 12;
$grid-gutter-width: 1.5rem;
$grid-row-columns: 6;
2. 自定义主题配置
// _theme-config.scss
// 主题配置文件
// 导入 Bootstrap 函数和变量
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
// 自定义变量(覆盖默认值)
@import "custom-variables";
// 导入 Bootstrap mixins
@import "~bootstrap/scss/mixins";
// 自定义 mixins
@import "custom-mixins";
// 导入 Bootstrap 核心样式
@import "~bootstrap/scss/root";
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/images";
@import "~bootstrap/scss/containers";
@import "~bootstrap/scss/grid";
@import "~bootstrap/scss/tables";
@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/buttons";
@import "~bootstrap/scss/transitions";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/button-group";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
@import "~bootstrap/scss/card";
@import "~bootstrap/scss/accordion";
@import "~bootstrap/scss/breadcrumb";
@import "~bootstrap/scss/pagination";
@import "~bootstrap/scss/badge";
@import "~bootstrap/scss/alert";
@import "~bootstrap/scss/progress";
@import "~bootstrap/scss/list-group";
@import "~bootstrap/scss/close";
@import "~bootstrap/scss/toasts";
@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/tooltip";
@import "~bootstrap/scss/popover";
@import "~bootstrap/scss/carousel";
@import "~bootstrap/scss/spinners";
@import "~bootstrap/scss/offcanvas";
@import "~bootstrap/scss/placeholders";
// 工具类
@import "~bootstrap/scss/helpers";
@import "~bootstrap/scss/utilities/api";
// 自定义组件样式
@import "custom-components";
// 自定义工具类
@import "custom-utilities";
3. 自定义 Mixins
// _custom-mixins.scss
// 自定义混合器
// 渐变背景混合器
@mixin gradient-bg($color1, $color2, $direction: to right) {
background: linear-gradient($direction, $color1, $color2);
}
// 卡片阴影混合器
@mixin card-shadow($level: 1) {
@if $level == 1 {
box-shadow: 0 2px 4px rgba($black, 0.1);
}
}
// 动画关键帧
@keyframes checkmark {
0% {
transform: translate(-50%, -50%) scale(0);
}
50% {
transform: translate(-50%, -50%) scale(1.2);
}
100% {
transform: translate(-50%, -50%) scale(1);
}
}
@keyframes progress-bar-stripes {
0% {
background-position-x: 1rem;
}
}
2. 自定义工具类
// _custom-utilities.scss
// 自定义工具类
// 间距工具类扩展
$custom-spacers: (
6: $spacer * 4,
7: $spacer * 5,
8: $spacer * 6,
9: $spacer * 8,
10: $spacer * 10
);
@each $key, $value in $custom-spacers {
.m-#{$key} { margin: $value !important; }
.mt-#{$key} { margin-top: $value !important; }
.mr-#{$key} { margin-right: $value !important; }
.mb-#{$key} { margin-bottom: $value !important; }
.ml-#{$key} { margin-left: $value !important; }
.mx-#{$key} {
margin-right: $value !important;
margin-left: $value !important;
}
.my-#{$key} {
margin-top: $value !important;
margin-bottom: $value !important;
}
.p-#{$key} { padding: $value !important; }
.pt-#{$key} { padding-top: $value !important; }
.pr-#{$key} { padding-right: $value !important; }
.pb-#{$key} { padding-bottom: $value !important; }
.pl-#{$key} { padding-left: $value !important; }
.px-#{$key} {
padding-right: $value !important;
padding-left: $value !important;
}
.py-#{$key} {
padding-top: $value !important;
padding-bottom: $value !important;
}
}
// 字体大小工具类扩展
.fs-xs { font-size: 0.75rem !important; }
.fs-xxl { font-size: 2rem !important; }
.fs-xxxl { font-size: 2.5rem !important; }
// 行高工具类
.lh-1 { line-height: 1 !important; }
.lh-sm { line-height: 1.25 !important; }
.lh-base { line-height: 1.5 !important; }
.lh-lg { line-height: 2 !important; }
// 字重工具类扩展
.fw-medium { font-weight: 500 !important; }
.fw-semibold { font-weight: 600 !important; }
// 边框半径工具类扩展
.rounded-xs { border-radius: 0.125rem !important; }
.rounded-xl { border-radius: 1rem !important; }
.rounded-2xl { border-radius: 1.5rem !important; }
.rounded-3xl { border-radius: 2rem !important; }
// 阴影工具类
.shadow-xs { box-shadow: 0 1px 2px rgba($black, 0.05) !important; }
.shadow-xl { box-shadow: 0 20px 25px -5px rgba($black, 0.1), 0 10px 10px -5px rgba($black, 0.04) !important; }
.shadow-2xl { box-shadow: 0 25px 50px -12px rgba($black, 0.25) !important; }
.shadow-inner { box-shadow: inset 0 2px 4px 0 rgba($black, 0.06) !important; }
// 变换工具类
.transform { transform: translateZ(0) !important; }
.scale-50 { transform: scale(0.5) !important; }
.scale-75 { transform: scale(0.75) !important; }
.scale-90 { transform: scale(0.9) !important; }
.scale-95 { transform: scale(0.95) !important; }
.scale-105 { transform: scale(1.05) !important; }
.scale-110 { transform: scale(1.1) !important; }
.scale-125 { transform: scale(1.25) !important; }
.scale-150 { transform: scale(1.5) !important; }
.rotate-1 { transform: rotate(1deg) !important; }
.rotate-2 { transform: rotate(2deg) !important; }
.rotate-3 { transform: rotate(3deg) !important; }
.rotate-6 { transform: rotate(6deg) !important; }
.rotate-12 { transform: rotate(12deg) !important; }
.rotate-45 { transform: rotate(45deg) !important; }
.rotate-90 { transform: rotate(90deg) !important; }
.rotate-180 { transform: rotate(180deg) !important; }
.-rotate-1 { transform: rotate(-1deg) !important; }
.-rotate-2 { transform: rotate(-2deg) !important; }
.-rotate-3 { transform: rotate(-3deg) !important; }
.-rotate-6 { transform: rotate(-6deg) !important; }
.-rotate-12 { transform: rotate(-12deg) !important; }
.-rotate-45 { transform: rotate(-45deg) !important; }
.-rotate-90 { transform: rotate(-90deg) !important; }
.-rotate-180 { transform: rotate(-180deg) !important; }
// 过渡工具类
.transition { transition: all 0.15s ease-in-out !important; }
.transition-none { transition: none !important; }
.transition-colors { transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out !important; }
.transition-opacity { transition: opacity 0.15s ease-in-out !important; }
.transition-shadow { transition: box-shadow 0.15s ease-in-out !important; }
.transition-transform { transition: transform 0.15s ease-in-out !important; }
// 持续时间工具类
.duration-75 { transition-duration: 75ms !important; }
.duration-100 { transition-duration: 100ms !important; }
.duration-150 { transition-duration: 150ms !important; }
.duration-200 { transition-duration: 200ms !important; }
.duration-300 { transition-duration: 300ms !important; }
.duration-500 { transition-duration: 500ms !important; }
.duration-700 { transition-duration: 700ms !important; }
.duration-1000 { transition-duration: 1000ms !important; }
// 缓动函数工具类
.ease-linear { transition-timing-function: linear !important; }
.ease-in { transition-timing-function: cubic-bezier(0.4, 0, 1, 1) !important; }
.ease-out { transition-timing-function: cubic-bezier(0, 0, 0.2, 1) !important; }
.ease-in-out { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important; }
// 光标工具类
.cursor-auto { cursor: auto !important; }
.cursor-default { cursor: default !important; }
.cursor-pointer { cursor: pointer !important; }
.cursor-wait { cursor: wait !important; }
.cursor-text { cursor: text !important; }
.cursor-move { cursor: move !important; }
.cursor-help { cursor: help !important; }
.cursor-not-allowed { cursor: not-allowed !important; }
// 用户选择工具类
.select-none { user-select: none !important; }
.select-text { user-select: text !important; }
.select-all { user-select: all !important; }
.select-auto { user-select: auto !important; }
// 指针事件工具类
.pointer-events-none { pointer-events: none !important; }
.pointer-events-auto { pointer-events: auto !important; }
// 滚动行为工具类
.scroll-smooth { scroll-behavior: smooth !important; }
.scroll-auto { scroll-behavior: auto !important; }
// 对象适应工具类
.object-contain { object-fit: contain !important; }
.object-cover { object-fit: cover !important; }
.object-fill { object-fit: fill !important; }
.object-none { object-fit: none !important; }
.object-scale-down { object-fit: scale-down !important; }
// 对象位置工具类
.object-bottom { object-position: bottom !important; }
.object-center { object-position: center !important; }
.object-left { object-position: left !important; }
.object-left-bottom { object-position: left bottom !important; }
.object-left-top { object-position: left top !important; }
.object-right { object-position: right !important; }
.object-right-bottom { object-position: right bottom !important; }
.object-right-top { object-position: right top !important; }
.object-top { object-position: top !important; }
颜色系统定制
1. 扩展颜色调色板
// _color-system.scss
// 颜色系统定制
// 扩展颜色调色板
$custom-colors: (
"purple": #6f42c1,
"pink": #e83e8c,
"orange": #fd7e14,
"yellow": #ffc107,
"green": #28a745,
"teal": #20c997,
"cyan": #17a2b8,
"indigo": #6610f2,
"lime": #32cd32,
"emerald": #10b981,
"sky": #0ea5e9,
"violet": #8b5cf6,
"fuchsia": #d946ef,
"rose": #f43f5e,
"amber": #f59e0b,
"slate": #64748b
);
// 生成颜色变体
@each $color, $value in $custom-colors {
// 主色调
.text-#{$color} {
color: $value !important;
}
.bg-#{$color} {
background-color: $value !important;
}
.border-#{$color} {
border-color: $value !important;
}
// 浅色变体
.text-#{$color}-light {
color: tint-color($value, 40%) !important;
}
.bg-#{$color}-light {
background-color: tint-color($value, 80%) !important;
}
.border-#{$color}-light {
border-color: tint-color($value, 60%) !important;
}
// 深色变体
.text-#{$color}-dark {
color: shade-color($value, 40%) !important;
}
.bg-#{$color}-dark {
background-color: shade-color($value, 20%) !important;
}
.border-#{$color}-dark {
border-color: shade-color($value, 20%) !important;
}
// 按钮变体
.btn-#{$color} {
@include button-variant($value, $value);
}
.btn-outline-#{$color} {
@include button-outline-variant($value);
}
// 警告框变体
.alert-#{$color} {
@include alert-variant(
tint-color($value, 80%),
tint-color($value, 70%),
shade-color($value, 60%)
);
}
// 徽章变体
.badge-#{$color} {
color: color-contrast($value);
background-color: $value;
}
}
// 渐变色系统
$gradients: (
"primary-secondary": linear-gradient(135deg, $primary 0%, $secondary 100%),
"success-info": linear-gradient(135deg, $success 0%, $info 100%),
"warning-danger": linear-gradient(135deg, $warning 0%, $danger 100%),
"purple-pink": linear-gradient(135deg, map-get($custom-colors, "purple") 0%, map-get($custom-colors, "pink") 100%),
"blue-purple": linear-gradient(135deg, $primary 0%, map-get($custom-colors, "purple") 100%),
"green-blue": linear-gradient(135deg, $success 0%, $info 100%),
"orange-red": linear-gradient(135deg, map-get($custom-colors, "orange") 0%, $danger 100%),
"sunset": linear-gradient(135deg, #ff9a9e 0%, #fecfef 50%, #fecfef 100%),
"ocean": linear-gradient(135deg, #667eea 0%, #764ba2 100%),
"forest": linear-gradient(135deg, #134e5e 0%, #71b280 100%),
"fire": linear-gradient(135deg, #f093fb 0%, #f5576c 100%),
"sky": linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)
);
@each $name, $gradient in $gradients {
.bg-gradient-#{$name} {
background: $gradient !important;
}
.text-gradient-#{$name} {
background: $gradient;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
}
// 透明度工具类
$opacities: (0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 95, 100);
@each $opacity in $opacities {
.opacity-#{$opacity} {
opacity: $opacity * 0.01 !important;
}
}
// 颜色混合工具类
.mix-blend-normal { mix-blend-mode: normal !important; }
.mix-blend-multiply { mix-blend-mode: multiply !important; }
.mix-blend-screen { mix-blend-mode: screen !important; }
.mix-blend-overlay { mix-blend-mode: overlay !important; }
.mix-blend-darken { mix-blend-mode: darken !important; }
.mix-blend-lighten { mix-blend-mode: lighten !important; }
.mix-blend-color-dodge { mix-blend-mode: color-dodge !important; }
.mix-blend-color-burn { mix-blend-mode: color-burn !important; }
.mix-blend-hard-light { mix-blend-mode: hard-light !important; }
.mix-blend-soft-light { mix-blend-mode: soft-light !important; }
.mix-blend-difference { mix-blend-mode: difference !important; }
.mix-blend-exclusion { mix-blend-mode: exclusion !important; }
2. 动态颜色主题
// _dynamic-colors.scss
// 动态颜色主题
// CSS 自定义属性(CSS 变量)
:root {
// 主色调变量
--bs-primary-rgb: #{to-rgb($primary)};
--bs-secondary-rgb: #{to-rgb($secondary)};
--bs-success-rgb: #{to-rgb($success)};
--bs-info-rgb: #{to-rgb($info)};
--bs-warning-rgb: #{to-rgb($warning)};
--bs-danger-rgb: #{to-rgb($danger)};
--bs-light-rgb: #{to-rgb($light)};
--bs-dark-rgb: #{to-rgb($dark)};
// 动态主题变量
--theme-primary: #{$primary};
--theme-secondary: #{$secondary};
--theme-accent: #ff6b35;
--theme-background: #ffffff;
--theme-surface: #f8f9fa;
--theme-text: #212529;
--theme-text-secondary: #6c757d;
// 间距变量
--theme-spacing-xs: 0.25rem;
--theme-spacing-sm: 0.5rem;
--theme-spacing-md: 1rem;
--theme-spacing-lg: 1.5rem;
--theme-spacing-xl: 3rem;
// 边框半径变量
--theme-radius-sm: 0.25rem;
--theme-radius-md: 0.375rem;
--theme-radius-lg: 0.5rem;
--theme-radius-xl: 1rem;
// 阴影变量
--theme-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--theme-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--theme-shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--theme-shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1);
}
// 动态主题类
.theme-dynamic {
// 使用 CSS 变量的组件
.btn-dynamic {
background-color: var(--theme-primary);
border-color: var(--theme-primary);
color: #ffffff;
padding: var(--theme-spacing-sm) var(--theme-spacing-md);
border-radius: var(--theme-radius-md);
box-shadow: var(--theme-shadow-sm);
transition: all 0.2s ease;
&:hover {
background-color: color-mix(in srgb, var(--theme-primary) 85%, black);
border-color: color-mix(in srgb, var(--theme-primary) 85%, black);
box-shadow: var(--theme-shadow-md);
}
}
.card-dynamic {
background-color: var(--theme-surface);
border: 1px solid color-mix(in srgb, var(--theme-text) 10%, transparent);
border-radius: var(--theme-radius-lg);
box-shadow: var(--theme-shadow-sm);
.card-header {
background-color: var(--theme-primary);
color: #ffffff;
border-radius: var(--theme-radius-lg) var(--theme-radius-lg) 0 0;
}
}
.navbar-dynamic {
background-color: var(--theme-background);
border-bottom: 1px solid color-mix(in srgb, var(--theme-text) 10%, transparent);
.navbar-brand {
color: var(--theme-primary);
}
.nav-link {
color: var(--theme-text);
&:hover {
color: var(--theme-primary);
}
}
}
}
// 主题切换器 JavaScript 支持
.theme-switcher {
.theme-option {
width: 40px;
height: 40px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
transform: scale(1.1);
border-color: rgba(0, 0, 0, 0.2);
}
&.active {
border-color: var(--theme-primary);
box-shadow: 0 0 0 2px rgba(var(--bs-primary-rgb), 0.25);
}
}
}
排版定制
1. 字体系统定制
// _typography.scss
// 排版系统定制
// 导入自定义字体
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@400;500;600;700&family=Fira+Code:wght@300;400;500&display=swap');
// 字体变量定义
$font-family-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
$font-family-heading: 'Poppins', $font-family-primary;
$font-family-code: 'Fira Code', 'SF Mono', Monaco, Inconsolata, 'Roboto Mono', monospace;
// 字体大小比例系统
$font-scale-ratio: 1.25; // Major Third
$font-size-root: 16px;
$font-size-base: 1rem;
// 计算字体大小
$font-size-xs: $font-size-base / ($font-scale-ratio * $font-scale-ratio);
$font-size-sm: $font-size-base / $font-scale-ratio;
$font-size-md: $font-size-base;
$font-size-lg: $font-size-base * $font-scale-ratio;
$font-size-xl: $font-size-base * ($font-scale-ratio * $font-scale-ratio);
$font-size-2xl: $font-size-base * ($font-scale-ratio * $font-scale-ratio * $font-scale-ratio);
$font-size-3xl: $font-size-base * ($font-scale-ratio * $font-scale-ratio * $font-scale-ratio * $font-scale-ratio);
// 标题字体大小
$h1-font-size: $font-size-3xl;
$h2-font-size: $font-size-2xl;
$h3-font-size: $font-size-xl;
$h4-font-size: $font-size-lg;
$h5-font-size: $font-size-md;
$h6-font-size: $font-size-sm;
// 行高系统
$line-height-tight: 1.25;
$line-height-snug: 1.375;
$line-height-normal: 1.5;
$line-height-relaxed: 1.625;
$line-height-loose: 2;
// 字重系统
$font-weight-light: 300;
$font-weight-normal: 400;
$font-weight-medium: 500;
$font-weight-semibold: 600;
$font-weight-bold: 700;
// 字间距系统
$letter-spacing-tighter: -0.05em;
$letter-spacing-tight: -0.025em;
$letter-spacing-normal: 0;
$letter-spacing-wide: 0.025em;
$letter-spacing-wider: 0.05em;
$letter-spacing-widest: 0.1em;
// 全局排版样式
body {
font-family: $font-family-primary;
font-size: $font-size-base;
line-height: $line-height-normal;
letter-spacing: $letter-spacing-normal;
}
// 标题样式定制
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
font-family: $font-family-heading;
font-weight: $font-weight-semibold;
line-height: $line-height-tight;
letter-spacing: $letter-spacing-tight;
margin-bottom: 0.5em;
}
h1, .h1 {
font-size: $h1-font-size;
font-weight: $font-weight-bold;
letter-spacing: $letter-spacing-tighter;
}
h2, .h2 {
font-size: $h2-font-size;
}
h3, .h3 {
font-size: $h3-font-size;
}
h4, .h4 {
font-size: $h4-font-size;
}
h5, .h5 {
font-size: $h5-font-size;
}
h6, .h6 {
font-size: $h6-font-size;
font-weight: $font-weight-medium;
text-transform: uppercase;
letter-spacing: $letter-spacing-wide;
}
// 段落样式
p {
margin-bottom: 1em;
&.lead {
font-size: $font-size-lg;
font-weight: $font-weight-normal;
line-height: $line-height-relaxed;
}
&.small {
font-size: $font-size-sm;
line-height: $line-height-normal;
}
}
// 代码样式
code, kbd, pre, samp {
font-family: $font-family-code;
}
code {
font-size: 0.875em;
background-color: rgba($primary, 0.1);
color: shade-color($primary, 20%);
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
}
pre {
background-color: $gray-100;
border: 1px solid $gray-300;
border-radius: 0.5rem;
padding: 1rem;
overflow-x: auto;
code {
background-color: transparent;
color: inherit;
padding: 0;
}
}
// 引用样式
blockquote {
border-left: 4px solid $primary;
padding-left: 1rem;
margin: 1.5rem 0;
font-style: italic;
font-size: $font-size-lg;
line-height: $line-height-relaxed;
footer {
font-size: $font-size-sm;
color: $gray-600;
margin-top: 0.5rem;
&::before {
content: '— ';
}
}
}
// 列表样式
ul, ol {
padding-left: 1.5rem;
li {
margin-bottom: 0.25rem;
}
}
// 链接样式
a {
color: $primary;
text-decoration: none;
transition: color 0.15s ease-in-out;
&:hover {
color: shade-color($primary, 15%);
text-decoration: underline;
}
&:focus {
outline: 2px solid rgba($primary, 0.5);
outline-offset: 2px;
}
}
// 排版工具类
.font-primary { font-family: $font-family-primary !important; }
.font-heading { font-family: $font-family-heading !important; }
.font-code { font-family: $font-family-code !important; }
.text-xs { font-size: $font-size-xs !important; }
.text-sm { font-size: $font-size-sm !important; }
.text-base { font-size: $font-size-md !important; }
.text-lg { font-size: $font-size-lg !important; }
.text-xl { font-size: $font-size-xl !important; }
.text-2xl { font-size: $font-size-2xl !important; }
.text-3xl { font-size: $font-size-3xl !important; }
.leading-tight { line-height: $line-height-tight !important; }
.leading-snug { line-height: $line-height-snug !important; }
.leading-normal { line-height: $line-height-normal !important; }
.leading-relaxed { line-height: $line-height-relaxed !important; }
.leading-loose { line-height: $line-height-loose !important; }
.tracking-tighter { letter-spacing: $letter-spacing-tighter !important; }
.tracking-tight { letter-spacing: $letter-spacing-tight !important; }
.tracking-normal { letter-spacing: $letter-spacing-normal !important; }
.tracking-wide { letter-spacing: $letter-spacing-wide !important; }
.tracking-wider { letter-spacing: $letter-spacing-wider !important; }
.tracking-widest { letter-spacing: $letter-spacing-widest !important; }
.font-light { font-weight: $font-weight-light !important; }
.font-normal { font-weight: $font-weight-normal !important; }
.font-medium { font-weight: $font-weight-medium !important; }
.font-semibold { font-weight: $font-weight-semibold !important; }
.font-bold { font-weight: $font-weight-bold !important; }
2. 响应式排版
// _responsive-typography.scss
// 响应式排版
// 响应式字体大小混合器
@mixin responsive-font-size($min-size, $max-size, $min-width: 320px, $max-width: 1200px) {
font-size: $min-size;
@media (min-width: $min-width) {
font-size: calc(#{$min-size} + #{strip-unit($max-size - $min-size)} * ((100vw - #{$min-width}) / #{strip-unit($max-width - $min-width)}));
}
@media (min-width: $max-width) {
font-size: $max-size;
}
}
// 响应式标题
h1, .h1 {
@include responsive-font-size(2rem, 3.5rem);
}
h2, .h2 {
@include responsive-font-size(1.75rem, 2.5rem);
}
h3, .h3 {
@include responsive-font-size(1.5rem, 2rem);
}
h4, .h4 {
@include responsive-font-size(1.25rem, 1.5rem);
}
// 响应式段落
p {
&.lead {
@include responsive-font-size(1.125rem, 1.25rem);
}
}
// 响应式间距
@include media-breakpoint-down(md) {
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
margin-bottom: 0.75rem;
}
p {
margin-bottom: 1rem;
}
blockquote {
margin: 1rem 0;
padding-left: 0.75rem;
}
}
// 响应式排版工具类
@each $breakpoint in map-keys($grid-breakpoints) {
@include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
.text#{$infix}-xs { font-size: $font-size-xs !important; }
.text#{$infix}-sm { font-size: $font-size-sm !important; }
.text#{$infix}-base { font-size: $font-size-md !important; }
.text#{$infix}-lg { font-size: $font-size-lg !important; }
.text#{$infix}-xl { font-size: $font-size-xl !important; }
.text#{$infix}-2xl { font-size: $font-size-2xl !important; }
.text#{$infix}-3xl { font-size: $font-size-3xl !important; }
.leading#{$infix}-tight { line-height: $line-height-tight !important; }
.leading#{$infix}-normal { line-height: $line-height-normal !important; }
.leading#{$infix}-relaxed { line-height: $line-height-relaxed !important; }
}
}
构建工具集成
1. Webpack 配置
// webpack.config.js
// Webpack 配置文件
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
return {
entry: {
main: './src/scss/main.scss',
theme: './src/scss/theme.scss'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash].js',
clean: true
},
module: {
rules: [
{
test: /\.scss$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: !isProduction
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
['autoprefixer'],
isProduction && ['cssnano', { preset: 'default' }]
].filter(Boolean)
},
sourceMap: !isProduction
}
},
{
loader: 'sass-loader',
options: {
implementation: require('sass'),
sourceMap: !isProduction,
sassOptions: {
includePaths: ['node_modules']
}
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource',
generator: {
filename: 'fonts/[name][ext]'
}
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
type: 'asset/resource',
generator: {
filename: 'images/[name][ext]'
}
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css'
})
],
optimization: {
minimizer: [
new TerserPlugin(),
new OptimizeCSSAssetsPlugin()
],
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 3000,
hot: true,
open: true
},
devtool: isProduction ? 'source-map' : 'eval-source-map'
};
};
2. Gulp 构建任务
// gulpfile.js
// Gulp 构建任务
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const sourcemaps = require('gulp-sourcemaps');
const rename = require('gulp-rename');
const browserSync = require('browser-sync').create();
const del = require('del');
// 路径配置
const paths = {
scss: {
src: 'src/scss/**/*.scss',
dest: 'dist/css/'
},
html: {
src: 'src/**/*.html',
dest: 'dist/'
},
js: {
src: 'src/js/**/*.js',
dest: 'dist/js/'
}
};
// 清理任务
function clean() {
return del(['dist']);
}
// SCSS 编译任务
function styles() {
return gulp.src(paths.scss.src)
.pipe(sourcemaps.init())
.pipe(sass({
includePaths: ['node_modules'],
outputStyle: 'expanded'
}).on('error', sass.logError))
.pipe(postcss([
autoprefixer()
]))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(paths.scss.dest))
.pipe(browserSync.stream());
}
// SCSS 压缩任务
function stylesMin() {
return gulp.src(paths.scss.src)
.pipe(sass({
includePaths: ['node_modules'],
outputStyle: 'compressed'
}).on('error', sass.logError))
.pipe(postcss([
autoprefixer(),
cssnano()
]))
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest(paths.scss.dest));
}
// HTML 复制任务
function html() {
return gulp.src(paths.html.src)
.pipe(gulp.dest(paths.html.dest))
.pipe(browserSync.stream());
}
// JavaScript 复制任务
function scripts() {
return gulp.src(paths.js.src)
.pipe(gulp.dest(paths.js.dest))
.pipe(browserSync.stream());
}
// 开发服务器
function serve() {
browserSync.init({
server: {
baseDir: './dist'
},
port: 3000
});
gulp.watch(paths.scss.src, styles);
gulp.watch(paths.html.src, html);
gulp.watch(paths.js.src, scripts);
}
// 监听任务
function watch() {
gulp.watch(paths.scss.src, styles);
gulp.watch(paths.html.src, html);
gulp.watch(paths.js.src, scripts);
}
// 构建任务
const build = gulp.series(clean, gulp.parallel(styles, stylesMin, html, scripts));
const dev = gulp.series(clean, gulp.parallel(styles, html, scripts), serve);
// 导出任务
exports.clean = clean;
exports.styles = styles;
exports.stylesMin = stylesMin;
exports.html = html;
exports.scripts = scripts;
exports.watch = watch;
exports.serve = serve;
exports.build = build;
exports.dev = dev;
exports.default = dev;
3. package.json 配置
{
"name": "bootstrap-custom-theme",
"version": "1.0.0",
"description": "Bootstrap 自定义主题项目",
"main": "index.js",
"scripts": {
"dev": "webpack serve --mode development",
"build": "webpack --mode production",
"build:css": "sass src/scss/main.scss dist/css/main.css --style=expanded",
"build:css:min": "sass src/scss/main.scss dist/css/main.min.css --style=compressed",
"watch": "sass --watch src/scss:dist/css",
"gulp:dev": "gulp dev",
"gulp:build": "gulp build",
"lint:scss": "stylelint src/scss/**/*.scss",
"lint:scss:fix": "stylelint src/scss/**/*.scss --fix",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"bootstrap",
"sass",
"css",
"theme",
"custom"
],
"author": "Your Name",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"autoprefixer": "^10.3.0",
"babel-loader": "^8.2.0",
"browser-sync": "^2.27.0",
"css-loader": "^6.2.0",
"cssnano": "^5.0.0",
"del": "^6.0.0",
"gulp": "^4.0.0",
"gulp-postcss": "^9.0.0",
"gulp-rename": "^2.0.0",
"gulp-sass": "^5.0.0",
"gulp-sourcemaps": "^3.0.0",
"mini-css-extract-plugin": "^2.2.0",
"optimize-css-assets-webpack-plugin": "^6.0.0",
"postcss": "^8.3.0",
"postcss-loader": "^6.1.0",
"sass": "^1.38.0",
"sass-loader": "^12.1.0",
"style-loader": "^3.2.0",
"stylelint": "^13.13.0",
"stylelint-config-standard": "^22.0.0",
"stylelint-scss": "^3.20.0",
"terser-webpack-plugin": "^5.1.0",
"webpack": "^5.51.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.0.0"
},
"dependencies": {
"bootstrap": "^5.1.0"
}
}
## 实际应用案例
1. 企业级管理后台主题
// _admin-theme.scss
// 企业级管理后台主题
// 主题色彩定义
$admin-primary: #2563eb; // 专业蓝
$admin-secondary: #64748b; // 中性灰
$admin-success: #059669; // 成功绿
$admin-warning: #d97706; // 警告橙
$admin-danger: #dc2626; // 危险红
$admin-info: #0891b2; // 信息青
$admin-light: #f8fafc; // 浅色背景
$admin-dark: #1e293b; // 深色文本
// 侧边栏主题
.admin-sidebar {
background: linear-gradient(180deg, #1e293b 0%, #334155 100%);
border-right: 1px solid rgba(255, 255, 255, 0.1);
.nav-link {
color: rgba(255, 255, 255, 0.8);
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
margin: 0.25rem 0.75rem;
transition: all 0.2s ease;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
color: #ffffff;
}
&.active {
background-color: $admin-primary;
color: #ffffff;
box-shadow: 0 4px 6px rgba($admin-primary, 0.3);
}
i {
width: 20px;
margin-right: 0.75rem;
}
}
.sidebar-brand {
padding: 1.5rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
h4 {
color: #ffffff;
margin: 0;
font-weight: 600;
}
}
}
// 顶部导航栏
.admin-navbar {
background-color: #ffffff;
border-bottom: 1px solid #e2e8f0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
.navbar-brand {
font-weight: 600;
color: $admin-primary;
}
.nav-link {
color: $admin-dark;
&:hover {
color: $admin-primary;
}
}
.dropdown-menu {
border: none;
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
border-radius: 0.5rem;
}
}
// 主内容区域
.admin-content {
background-color: #f1f5f9;
min-height: 100vh;
.content-header {
background-color: #ffffff;
padding: 1.5rem;
margin-bottom: 1.5rem;
border-radius: 0.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
h1 {
margin: 0;
color: $admin-dark;
font-weight: 600;
}
.breadcrumb {
margin: 0;
background: none;
padding: 0;
.breadcrumb-item {
&.active {
color: $admin-secondary;
}
a {
color: $admin-primary;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
}
}
// 统计卡片
.admin-stat-card {
background: linear-gradient(135deg, $admin-primary 0%, lighten($admin-primary, 10%) 100%);
color: #ffffff;
border-radius: 1rem;
padding: 1.5rem;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 100%;
height: 100%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
}
.stat-icon {
font-size: 2.5rem;
opacity: 0.8;
}
.stat-number {
font-size: 2rem;
font-weight: 700;
margin: 0.5rem 0;
}
.stat-label {
font-size: 0.875rem;
opacity: 0.9;
}
&.success {
background: linear-gradient(135deg, $admin-success 0%, lighten($admin-success, 10%) 100%);
}
&.warning {
background: linear-gradient(135deg, $admin-warning 0%, lighten($admin-warning, 10%) 100%);
}
&.danger {
background: linear-gradient(135deg, $admin-danger 0%, lighten($admin-danger, 10%) 100%);
}
}
// 数据表格
.admin-table {
background-color: #ffffff;
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
.table {
margin: 0;
thead {
background-color: $admin-light;
th {
border: none;
font-weight: 600;
color: $admin-dark;
padding: 1rem;
}
}
tbody {
tr {
transition: background-color 0.2s ease;
&:hover {
background-color: rgba($admin-primary, 0.05);
}
td {
border: none;
border-top: 1px solid #e2e8f0;
padding: 1rem;
vertical-align: middle;
}
}
}
}
}
// 表单样式
.admin-form {
.form-label {
font-weight: 600;
color: $admin-dark;
margin-bottom: 0.5rem;
}
.form-control {
border: 2px solid #e2e8f0;
border-radius: 0.5rem;
padding: 0.75rem 1rem;
transition: all 0.2s ease;
&:focus {
border-color: $admin-primary;
box-shadow: 0 0 0 3px rgba($admin-primary, 0.1);
}
}
.btn-primary {
background-color: $admin-primary;
border-color: $admin-primary;
padding: 0.75rem 2rem;
font-weight: 600;
border-radius: 0.5rem;
&:hover {
background-color: darken($admin-primary, 5%);
border-color: darken($admin-primary, 5%);
}
}
}
2. 电商网站主题
// _ecommerce-theme.scss
// 电商网站主题
// 电商主题色彩
$ecom-primary: #ff6b35; // 活力橙
$ecom-secondary: #004e89; // 深海蓝
$ecom-accent: #ffd23f; // 金黄色
$ecom-success: #06d6a0; // 薄荷绿
$ecom-warning: #f18701; // 琥珀色
$ecom-danger: #e63946; // 珊瑚红
// 产品卡片
.product-card {
border: none;
border-radius: 1rem;
overflow: hidden;
transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
&:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.product-image {
position: relative;
overflow: hidden;
img {
transition: transform 0.3s ease;
}
&:hover img {
transform: scale(1.05);
}
.product-badge {
position: absolute;
top: 1rem;
left: 1rem;
background-color: $ecom-danger;
color: #ffffff;
padding: 0.25rem 0.75rem;
border-radius: 2rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
&.new {
background-color: $ecom-success;
}
&.sale {
background-color: $ecom-warning;
}
}
.product-actions {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
opacity: 0;
transition: opacity 0.3s ease;
.btn {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: #ffffff;
border: none;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
&:hover {
background-color: $ecom-primary;
color: #ffffff;
}
}
}
&:hover .product-actions {
opacity: 1;
}
}
.card-body {
padding: 1.5rem;
.product-title {
font-weight: 600;
margin-bottom: 0.5rem;
a {
color: inherit;
text-decoration: none;
&:hover {
color: $ecom-primary;
}
}
}
.product-rating {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
.stars {
color: $ecom-accent;
}
.rating-count {
color: #6c757d;
font-size: 0.875rem;
}
}
.product-price {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
.current-price {
font-size: 1.25rem;
font-weight: 700;
color: $ecom-primary;
}
.original-price {
font-size: 1rem;
color: #6c757d;
text-decoration: line-through;
}
.discount {
background-color: $ecom-success;
color: #ffffff;
padding: 0.125rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
}
}
.add-to-cart {
width: 100%;
background-color: $ecom-primary;
border-color: $ecom-primary;
color: #ffffff;
font-weight: 600;
padding: 0.75rem;
border-radius: 0.5rem;
transition: all 0.2s ease;
&:hover {
background-color: darken($ecom-primary, 10%);
border-color: darken($ecom-primary, 10%);
transform: translateY(-1px);
}
}
}
}
// 购物车样式
.shopping-cart {
.cart-item {
border-bottom: 1px solid #e9ecef;
padding: 1.5rem 0;
&:last-child {
border-bottom: none;
}
.item-image {
width: 80px;
height: 80px;
border-radius: 0.5rem;
overflow: hidden;
}
.item-details {
flex: 1;
.item-title {
font-weight: 600;
margin-bottom: 0.25rem;
}
.item-variant {
color: #6c757d;
font-size: 0.875rem;
}
}
.quantity-controls {
display: flex;
align-items: center;
gap: 0.5rem;
.btn {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #dee2e6;
background-color: #ffffff;
&:hover {
background-color: $ecom-primary;
border-color: $ecom-primary;
color: #ffffff;
}
}
.quantity {
min-width: 40px;
text-align: center;
font-weight: 600;
}
}
.item-price {
font-weight: 700;
color: $ecom-primary;
}
}
.cart-summary {
background-color: #f8f9fa;
padding: 1.5rem;
border-radius: 0.5rem;
.summary-row {
display: flex;
justify-content: space-between;
margin-bottom: 0.75rem;
&.total {
font-weight: 700;
font-size: 1.125rem;
border-top: 1px solid #dee2e6;
padding-top: 0.75rem;
margin-top: 1rem;
margin-bottom: 0;
}
}
.checkout-btn {
width: 100%;
background-color: $ecom-success;
border-color: $ecom-success;
color: #ffffff;
font-weight: 600;
padding: 1rem;
border-radius: 0.5rem;
margin-top: 1rem;
&:hover {
background-color: darken($ecom-success, 10%);
border-color: darken($ecom-success, 10%);
}
}
}
}
最佳实践
1. 主题开发流程
// 1. 规划阶段
// - 定义设计系统
// - 确定色彩方案
// - 制定组件规范
// - 设计响应式策略
// 2. 开发阶段
// - 创建变量文件
// - 开发自定义 mixins
// - 实现组件样式
// - 添加工具类
// 3. 测试阶段
// - 跨浏览器测试
// - 响应式测试
// - 可访问性测试
// - 性能测试
// 4. 优化阶段
// - CSS 压缩
// - 移除未使用样式
// - 优化加载性能
// - 文档编写
2. 代码组织结构
src/scss/
├── abstracts/
│ ├── _variables.scss # 自定义变量
│ ├── _mixins.scss # 自定义 mixins
│ └── _functions.scss # 自定义函数
├── base/
│ ├── _reset.scss # 重置样式
│ ├── _typography.scss # 排版样式
│ └── _utilities.scss # 工具类
├── components/
│ ├── _buttons.scss # 按钮组件
│ ├── _cards.scss # 卡片组件
│ ├── _forms.scss # 表单组件
│ └── _navigation.scss # 导航组件
├── layout/
│ ├── _header.scss # 头部布局
│ ├── _sidebar.scss # 侧边栏布局
│ └── _footer.scss # 底部布局
├── pages/
│ ├── _home.scss # 首页样式
│ ├── _about.scss # 关于页面
│ └── _contact.scss # 联系页面
├── themes/
│ ├── _light.scss # 浅色主题
│ ├── _dark.scss # 深色主题
│ └── _admin.scss # 管理主题
├── vendors/
│ └── _bootstrap.scss # Bootstrap 导入
└── main.scss # 主入口文件
3. 性能优化建议
// 1. 选择器优化
// 避免过深的嵌套
.card {
.header {
.title {
// 最多 3 层嵌套
}
}
}
// 2. 使用高效的选择器
// 优先使用类选择器
.btn-primary { } // 好
.btn.btn-primary { } // 避免
// 3. 合理使用 @extend
// 谨慎使用,可能导致 CSS 膨胀
%button-base {
padding: 0.5rem 1rem;
border-radius: 0.25rem;
}
.btn {
@extend %button-base;
}
// 4. 优化媒体查询
// 使用 Bootstrap 的断点 mixins
@include media-breakpoint-up(md) {
.custom-component {
// 样式
}
}
// 5. 移除未使用的样式
// 使用 PurgeCSS 或类似工具
// 在构建过程中自动移除
4. 可维护性建议
// 1. 使用语义化的变量名
$color-primary: #007bff; // 好
$blue: #007bff; // 避免
// 2. 添加注释说明
// 主要品牌色,用于按钮、链接等
$brand-primary: #007bff;
// 3. 保持一致的命名规范
// 使用 BEM 方法论
.card {
&__header {
// 卡片头部
}
&__body {
// 卡片主体
}
&--featured {
// 特色卡片修饰符
}
}
// 4. 模块化开发
// 每个组件独立文件
// 通过主文件导入
@import 'components/buttons';
@import 'components/cards';
@import 'components/forms';
// 5. 版本控制
// 使用语义化版本号
// 记录变更日志
// 保持向后兼容
本章总结
本章深入介绍了 Bootstrap 的自定义与主题化技术,涵盖了以下核心内容:
主要知识点
Sass 变量定制
- 颜色系统定制
- 字体和排版变量
- 间距和尺寸变量
- 组件特定变量
自定义主题配置
- Bootstrap 源码结构
- 自定义构建流程
- 主题文件组织
- 变量覆盖策略
自定义 Mixins
- 实用工具 mixins
- 组件样式 mixins
- 响应式 mixins
- 动画效果 mixins
自定义组件样式
- 按钮组件定制
- 卡片组件定制
- 导航组件定制
- 表单组件定制
自定义工具类
- 扩展间距系统
- 自定义字体工具
- 颜色工具类
- 布局工具类
颜色系统定制
- 扩展调色板
- 颜色变体生成
- 渐变色系统
- 动态主题支持
排版定制
- 字体系统设计
- 响应式排版
- 排版工具类
- 可读性优化
构建工具集成
- Webpack 配置
- Gulp 任务设置
- 开发环境配置
- 生产构建优化
实践要点
- 设计系统思维:建立一致的设计语言和组件规范
- 模块化开发:合理组织代码结构,提高可维护性
- 性能优化:注意 CSS 文件大小和加载性能
- 响应式设计:确保主题在各种设备上的表现
- 可访问性:遵循 Web 可访问性标准
- 浏览器兼容:测试主题在不同浏览器中的兼容性
开发建议
- 从小规模定制开始,逐步扩展
- 保持与 Bootstrap 更新的同步
- 建立完善的文档和示例
- 进行充分的测试和验证
- 考虑团队协作和代码规范
通过本章的学习,你应该能够: - 熟练使用 Sass 定制 Bootstrap 主题 - 创建符合项目需求的自定义组件 - 建立高效的开发和构建流程 - 实现响应式和可访问的主题设计 - 优化主题性能和可维护性
练习题
基础练习
变量定制练习
- 创建一个深色主题,定制所有主要颜色变量
- 实现一个大字体主题,调整所有字体大小变量
- 设计一个紧凑主题,减少所有间距变量
组件定制练习
- 创建一个圆角按钮主题,所有按钮都是圆形
- 设计一个卡片阴影主题,为卡片添加多层阴影效果
- 实现一个彩色导航主题,每个导航项使用不同颜色
工具类扩展练习
- 添加更多的间距工具类(如 .p-7, .m-8 等)
- 创建文本阴影工具类(如 .text-shadow-sm, .text-shadow-lg)
- 实现边框样式工具类(如 .border-dashed, .border-dotted)
进阶练习
响应式主题练习
- 创建一个在不同屏幕尺寸下使用不同颜色方案的主题
- 实现响应式字体大小系统,在移动端自动缩放
- 设计响应式间距系统,在小屏幕上减少间距
动态主题练习
- 使用 CSS 变量实现可切换的明暗主题
- 创建一个颜色主题选择器,用户可以选择不同的主色调
- 实现一个季节主题系统,根据时间自动切换主题
性能优化练习
- 使用 PurgeCSS 移除未使用的 Bootstrap 样式
- 实现 CSS 的懒加载,按需加载主题文件
- 优化 Sass 编译性能,减少构建时间
综合项目
企业级主题开发
- 为一个虚拟公司设计完整的品牌主题
- 包含所有常用组件的定制样式
- 实现完整的构建和部署流程
- 编写详细的使用文档
多主题系统
- 创建一个支持多个主题的系统架构
- 实现主题的动态切换功能
- 支持用户自定义主题配置
- 提供主题预览和导出功能
答案提示
练习答案和详细解释可以在以下资源中找到: - Bootstrap 官方文档 - 主题化 - Sass 官方文档 - CSS 自定义属性指南 - 响应式设计最佳实践
完成这些练习将帮助你深入理解 Bootstrap 主题化的各个方面,并能够在实际项目中灵活运用这些技术。 box-shadow: 0 16px 32px rgba($black, 0.25); } }
// 按钮变体混合器 @mixin button-variant-custom($background, $border, $color: color-contrast($background), $hover-background: shade-color($background, 15%), $hover-border: shade-color($border, 20%), $hover-color: color-contrast($hover-background), $active-background: shade-color($background, 20%), $active-border: shade-color($border, 25%), $active-color: color-contrast($active-background)) { color: $color; @include gradient-bg($background, shade-color($background, 10%)); border-color: $border; @include box-shadow($btn-box-shadow);
&:hover { color: $hover-color; @include gradient-bg($hover-background, shade-color($hover-background, 10%)); border-color: $hover-border; }
.btn-check:focus + &, &:focus { color: $hover-color; @include gradient-bg($hover-background, shade-color($hover-background, 10%)); border-color: $hover-border; @if $enable-shadows { @include box-shadow($btn-box-shadow, 0 0 0 $btn-focus-width rgba(mix($color, $border, 15%), .5)); } @else { box-shadow: 0 0 0 $btn-focus-width rgba(mix($color, $border, 15%), .5); } }
.btn-check:checked + &, .btn-check:active + &, &:active, &.active, .show > &.dropdown-toggle { color: $active-color; background-color: $active-background; background-image: if($enable-gradients, none, null); border-color: $active-border;
&:focus {
@if $enable-shadows {
@include box-shadow($btn-active-box-shadow, 0 0 0 $btn-focus-width rgba(mix($color, $border, 15%), .5));
} @else {
box-shadow: 0 0 0 $btn-focus-width rgba(mix($color, $border, 15%), .5);
}
}
}
&:disabled, &.disabled { color: $color; background-color: $background; background-image: if($enable-gradients, none, null); border-color: $border; } }
// 响应式字体大小混合器 @mixin responsive-font-size($min-size, $max-size, $min-width: 320px, $max-width: 1200px) { font-size: $min-size;
@media (min-width: $min-width) { font-size: calc(#{$min-size} + #{strip-unit($max-size - $min-size)} * ((100vw - #{$min-width}) / #{strip-unit($max-width - $min-width)})); }
@media (min-width: $max-width) { font-size: $max-size; } }
// 文本截断混合器 @mixin text-truncate($lines: 1) { @if $lines == 1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } @else { display: -webkit-box; -webkit-line-clamp: $lines; -webkit-box-orient: vertical; overflow: hidden; } }
// 居中混合器 @mixin center($position: absolute) { position: $position; top: 50%; left: 50%; transform: translate(-50%, -50%); }
// 清除浮动混合器 @mixin clearfix() { &::after { display: block; clear: both; content: “”; } }
// 三角形混合器 @mixin triangle($direction, $size, $color) { width: 0; height: 0;
@if $direction == up { border-left: $size solid transparent; border-right: $size solid transparent; border-bottom: $size solid $color; } @else if $direction == down { border-left: $size solid transparent; border-right: $size solid transparent; border-top: $size solid $color; } @else if $direction == left { border-top: $size solid transparent; border-bottom: $size solid transparent; border-right: $size solid $color; } @else if $direction == right { border-top: $size solid transparent; border-bottom: $size solid transparent; border-left: $size solid $color; } }
// 动画混合器 @mixin animation($name, $duration: 1s, $timing-function: ease, $delay: 0s, $iteration-count: 1, $direction: normal, $fill-mode: both) { animation: $name $duration $timing-function $delay $iteration-count $direction $fill-mode; }
// 过渡混合器 @mixin transition($properties…) { $transitions: ();
@each $property in $properties { $transitions: append($transitions, $property, comma); }
transition: $transitions; }
// 媒体查询混合器 @mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) { $min: breakpoint-min($lower, $breakpoints); $max: breakpoint-max($upper, $breakpoints);
@if $min != null and $max != null { @media (min-width: $min) and (max-width: $max) { @content; } } @else if $max == null { @include media-breakpoint-up($lower, $breakpoints) { @content; } } @else if $min == null { @include media-breakpoint-down($upper, $breakpoints) { @content; } } }
## 自定义主题创建
### 1. 深色主题
```scss
// _dark-theme.scss
// 深色主题定制
// 深色主题变量
$dark-theme-colors: (
"primary": #0d6efd,
"secondary": #6c757d,
"success": #198754,
"info": #0dcaf0,
"warning": #ffc107,
"danger": #dc3545,
"light": #f8f9fa,
"dark": #212529
);
$dark-theme-grays: (
"100": #1a1d20,
"200": #2c3034,
"300": #3e4348,
"400": #50565c,
"500": #626970,
"600": #747c84,
"700": #868f98,
"800": #98a2ac,
"900": #aab5c0
);
// 深色主题基础样式
[data-bs-theme="dark"] {
// 背景和文本颜色
--bs-body-color: #{map-get($dark-theme-grays, "900")};
--bs-body-bg: #{map-get($dark-theme-grays, "100")};
--bs-emphasis-color: #ffffff;
--bs-secondary-color: #{rgba(map-get($dark-theme-grays, "900"), 0.75)};
--bs-tertiary-color: #{rgba(map-get($dark-theme-grays, "900"), 0.5)};
// 边框颜色
--bs-border-color: #{map-get($dark-theme-grays, "300")};
--bs-border-color-translucent: #{rgba(#ffffff, 0.15)};
// 链接颜色
--bs-link-color: #{tint-color(map-get($dark-theme-colors, "primary"), 40%)};
--bs-link-hover-color: #{tint-color(map-get($dark-theme-colors, "primary"), 60%)};
// 表单控件
--bs-form-control-bg: #{map-get($dark-theme-grays, "200")};
--bs-form-control-border-color: #{map-get($dark-theme-grays, "400")};
// 卡片
.card {
--bs-card-bg: #{map-get($dark-theme-grays, "200")};
--bs-card-border-color: #{map-get($dark-theme-grays, "300")};
}
// 导航栏
.navbar {
&.navbar-dark {
--bs-navbar-color: #{rgba(#ffffff, 0.85)};
--bs-navbar-hover-color: #ffffff;
--bs-navbar-brand-color: #ffffff;
--bs-navbar-brand-hover-color: #ffffff;
--bs-navbar-toggler-border-color: #{rgba(#ffffff, 0.1)};
--bs-navbar-toggler-icon-bg: #{escape-svg(url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='#{rgba(#ffffff, 0.85)}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"))};
}
}
// 模态框
.modal {
--bs-modal-bg: #{map-get($dark-theme-grays, "200")};
--bs-modal-border-color: #{map-get($dark-theme-grays, "300")};
--bs-modal-header-border-color: #{map-get($dark-theme-grays, "300")};
--bs-modal-footer-border-color: #{map-get($dark-theme-grays, "300")};
}
// 下拉菜单
.dropdown-menu {
--bs-dropdown-bg: #{map-get($dark-theme-grays, "200")};
--bs-dropdown-border-color: #{map-get($dark-theme-grays, "300")};
--bs-dropdown-link-color: #{map-get($dark-theme-grays, "900")};
--bs-dropdown-link-hover-color: #ffffff;
--bs-dropdown-link-hover-bg: #{map-get($dark-theme-grays, "400")};
}
// 表格
.table {
--bs-table-bg: transparent;
--bs-table-border-color: #{map-get($dark-theme-grays, "300")};
--bs-table-striped-bg: #{rgba(#ffffff, 0.05)};
--bs-table-hover-bg: #{rgba(#ffffff, 0.075)};
}
// 警告框
.alert {
&.alert-primary {
--bs-alert-color: #{tint-color(map-get($dark-theme-colors, "primary"), 40%)};
--bs-alert-bg: #{rgba(map-get($dark-theme-colors, "primary"), 0.15)};
--bs-alert-border-color: #{rgba(map-get($dark-theme-colors, "primary"), 0.2)};
}
&.alert-success {
--bs-alert-color: #{tint-color(map-get($dark-theme-colors, "success"), 40%)};
--bs-alert-bg: #{rgba(map-get($dark-theme-colors, "success"), 0.15)};
--bs-alert-border-color: #{rgba(map-get($dark-theme-colors, "success"), 0.2)};
}
&.alert-warning {
--bs-alert-color: #{tint-color(map-get($dark-theme-colors, "warning"), 40%)};
--bs-alert-bg: #{rgba(map-get($dark-theme-colors, "warning"), 0.15)};
--bs-alert-border-color: #{rgba(map-get($dark-theme-colors, "warning"), 0.2)};
}
&.alert-danger {
--bs-alert-color: #{tint-color(map-get($dark-theme-colors, "danger"), 40%)};
--bs-alert-bg: #{rgba(map-get($dark-theme-colors, "danger"), 0.15)};
--bs-alert-border-color: #{rgba(map-get($dark-theme-colors, "danger"), 0.2)};
}
}
}
// 深色主题切换器
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
z-index: 1050;
.btn {
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
@include box-shadow(0 4px 8px rgba($black, 0.15));
.icon {
width: 20px;
height: 20px;
transition: transform 0.3s ease;
}
&:hover .icon {
transform: scale(1.1);
}
}
}
// 深色主题动画
@keyframes theme-transition {
0% {
opacity: 0;
transform: scale(0.95);
}
100% {
opacity: 1;
transform: scale(1);
}
}
[data-bs-theme="dark"] {
animation: theme-transition 0.3s ease;
}
2. 企业主题
// _corporate-theme.scss
// 企业主题定制
// 企业主题颜色
$corporate-primary: #003366; // 深蓝色
$corporate-secondary: #666666; // 中灰色
$corporate-accent: #ff6b35; // 橙色强调
$corporate-success: #28a745; // 绿色
$corporate-warning: #ffc107; // 黄色
$corporate-danger: #dc3545; // 红色
$corporate-light: #f8f9fa; // 浅灰色
$corporate-dark: #343a40; // 深灰色
// 企业主题字体
$corporate-font-family: "Roboto", "Helvetica Neue", Arial, sans-serif;
$corporate-heading-font-family: "Roboto Slab", serif;
// 企业主题样式
.theme-corporate {
// 全局字体
font-family: $corporate-font-family;
// 标题字体
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
font-family: $corporate-heading-font-family;
font-weight: 600;
color: $corporate-primary;
}
// 主色调按钮
.btn-primary {
@include button-variant-custom(
$corporate-primary,
$corporate-primary,
#ffffff,
shade-color($corporate-primary, 15%),
shade-color($corporate-primary, 20%),
#ffffff
);
}
// 强调色按钮
.btn-accent {
@include button-variant-custom(
$corporate-accent,
$corporate-accent,
#ffffff,
shade-color($corporate-accent, 15%),
shade-color($corporate-accent, 20%),
#ffffff
);
}
// 导航栏样式
.navbar {
&.navbar-corporate {
background: linear-gradient(135deg, $corporate-primary 0%, shade-color($corporate-primary, 20%) 100%);
box-shadow: 0 2px 4px rgba($black, 0.1);
.navbar-brand {
font-family: $corporate-heading-font-family;
font-weight: 700;
font-size: 1.5rem;
color: #ffffff !important;
&:hover {
color: tint-color($corporate-primary, 80%) !important;
}
}
.navbar-nav {
.nav-link {
color: rgba(#ffffff, 0.9) !important;
font-weight: 500;
transition: all 0.3s ease;
&:hover,
&:focus {
color: #ffffff !important;
background-color: rgba(#ffffff, 0.1);
border-radius: $border-radius;
}
&.active {
color: #ffffff !important;
background-color: rgba(#ffffff, 0.15);
border-radius: $border-radius;
}
}
}
}
}
// 卡片样式
.card {
&.card-corporate {
border: none;
@include card-shadow(2);
transition: all 0.3s ease;
&:hover {
@include card-shadow(3);
transform: translateY(-2px);
}
.card-header {
background: linear-gradient(135deg, $corporate-primary 0%, shade-color($corporate-primary, 10%) 100%);
color: #ffffff;
font-weight: 600;
border-bottom: none;
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
color: #ffffff;
margin-bottom: 0;
}
}
.card-footer {
background-color: $corporate-light;
border-top: 1px solid rgba($corporate-primary, 0.1);
}
}
}
// 表单样式
.form-control {
&:focus {
border-color: $corporate-primary;
box-shadow: 0 0 0 0.25rem rgba($corporate-primary, 0.25);
}
}
.form-check-input {
&:checked {
background-color: $corporate-primary;
border-color: $corporate-primary;
}
&:focus {
box-shadow: 0 0 0 0.25rem rgba($corporate-primary, 0.25);
}
}
// 警告框样式
.alert {
&.alert-corporate {
color: #ffffff;
background: linear-gradient(135deg, $corporate-primary 0%, shade-color($corporate-primary, 15%) 100%);
border: none;
.alert-link {
color: tint-color($corporate-primary, 80%);
}
}
}
// 进度条样式
.progress {
.progress-bar {
&.bg-corporate {
background: linear-gradient(90deg, $corporate-primary 0%, $corporate-accent 100%);
}
}
}
// 徽章样式
.badge {
&.bg-corporate {
background: linear-gradient(135deg, $corporate-primary 0%, shade-color($corporate-primary, 15%) 100%);
}
&.bg-accent {
background-color: $corporate-accent;
}
}
// 分页样式
.pagination {
.page-link {
color: $corporate-primary;
&:hover {
color: shade-color($corporate-primary, 15%);
background-color: tint-color($corporate-primary, 90%);
border-color: $corporate-primary;
}
}
.page-item {
&.active .page-link {
background-color: $corporate-primary;
border-color: $corporate-primary;
}
}
}
// 面包屑样式
.breadcrumb {
.breadcrumb-item {
&.active {
color: $corporate-primary;
}
a {
color: $corporate-secondary;
text-decoration: none;
&:hover {
color: $corporate-primary;
}
}
}
}
}
// 企业主题工具类
.text-corporate {
color: $corporate-primary !important;
}
.text-accent {
color: $corporate-accent !important;
}
.bg-corporate {
background-color: $corporate-primary !important;
}
.bg-accent {
background-color: $corporate-accent !important;
}
.border-corporate {
border-color: $corporate-primary !important;
}
.border-accent {
border-color: $corporate-accent !important;
}
3. 现代主题
// _modern-theme.scss
// 现代主题定制
// 现代主题颜色
$modern-primary: #6366f1; // 靛蓝色
$modern-secondary: #64748b; // 石板灰
$modern-accent: #f59e0b; // 琥珀色
$modern-success: #10b981; // 翠绿色
$modern-warning: #f59e0b; // 琥珀色
$modern-danger: #ef4444; // 红色
$modern-light: #f8fafc; // 极浅灰
$modern-dark: #0f172a; // 极深蓝
// 现代主题渐变
$modern-gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
$modern-gradient-secondary: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
$modern-gradient-success: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
$modern-gradient-warning: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
$modern-gradient-danger: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
// 现代主题字体
$modern-font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
$modern-heading-font-family: "Poppins", $modern-font-family;
// 现代主题样式
.theme-modern {
// 全局字体
font-family: $modern-font-family;
line-height: 1.6;
// 标题字体
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
font-family: $modern-heading-font-family;
font-weight: 600;
letter-spacing: -0.025em;
}
// 现代按钮样式
.btn {
font-weight: 500;
letter-spacing: 0.025em;
border-radius: 0.5rem;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
&.btn-modern-primary {
background: $modern-gradient-primary;
border: none;
color: #ffffff;
&:hover {
transform: translateY(-1px);
box-shadow: 0 10px 25px rgba($modern-primary, 0.3);
}
&:active {
transform: translateY(0);
}
}
&.btn-modern-glass {
background: rgba(#ffffff, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(#ffffff, 0.2);
color: #ffffff;
&:hover {
background: rgba(#ffffff, 0.2);
border-color: rgba(#ffffff, 0.3);
}
}
&.btn-modern-outline {
background: transparent;
border: 2px solid $modern-primary;
color: $modern-primary;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: $modern-gradient-primary;
transition: left 0.3s ease;
z-index: -1;
}
&:hover {
color: #ffffff;
border-color: transparent;
&::before {
left: 0;
}
}
}
}
// 现代卡片样式
.card {
&.card-modern {
border: none;
border-radius: 1rem;
background: rgba(#ffffff, 0.8);
backdrop-filter: blur(20px);
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(31, 38, 135, 0.5);
}
.card-header {
background: transparent;
border-bottom: 1px solid rgba($modern-primary, 0.1);
border-radius: 1rem 1rem 0 0;
}
.card-footer {
background: transparent;
border-top: 1px solid rgba($modern-primary, 0.1);
border-radius: 0 0 1rem 1rem;
}
}
&.card-gradient {
background: $modern-gradient-primary;
color: #ffffff;
border: none;
.card-header,
.card-footer {
background: transparent;
border-color: rgba(#ffffff, 0.2);
}
}
}
// 现代导航栏
.navbar {
&.navbar-modern {
background: rgba(#ffffff, 0.9);
backdrop-filter: blur(20px);
border-bottom: 1px solid rgba($modern-primary, 0.1);
.navbar-brand {
font-family: $modern-heading-font-family;
font-weight: 700;
background: $modern-gradient-primary;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.navbar-nav {
.nav-link {
font-weight: 500;
color: $modern-dark;
position: relative;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background: $modern-gradient-primary;
transition: all 0.3s ease;
transform: translateX(-50%);
}
&:hover,
&.active {
&::after {
width: 100%;
}
}
}
}
}
}
// 现代表单
.form-control {
&.form-control-modern {
border: 2px solid rgba($modern-primary, 0.1);
border-radius: 0.75rem;
padding: 0.75rem 1rem;
transition: all 0.2s ease;
&:focus {
border-color: $modern-primary;
box-shadow: 0 0 0 3px rgba($modern-primary, 0.1);
transform: translateY(-1px);
}
}
}
// 现代模态框
.modal {
&.modal-modern {
.modal-content {
border: none;
border-radius: 1.5rem;
background: rgba(#ffffff, 0.9);
backdrop-filter: blur(20px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
.modal-header {
border-bottom: 1px solid rgba($modern-primary, 0.1);
border-radius: 1.5rem 1.5rem 0 0;
.modal-title {
background: $modern-gradient-primary;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
}
.modal-footer {
border-top: 1px solid rgba($modern-primary, 0.1);
border-radius: 0 0 1.5rem 1.5rem;
}
}
}
}
// 现代警告框
.alert {
&.alert-modern {
border: none;
border-radius: 1rem;
border-left: 4px solid $modern-primary;
background: linear-gradient(135deg, rgba($modern-primary, 0.1) 0%, rgba($modern-primary, 0.05) 100%);
&.alert-success {
border-left-color: $modern-success;
background: linear-gradient(135deg, rgba($modern-success, 0.1) 0%, rgba($modern-success, 0.05) 100%);
}
&.alert-warning {
border-left-color: $modern-warning;
background: linear-gradient(135deg, rgba($modern-warning, 0.1) 0%, rgba($modern-warning, 0.05) 100%);
}
&.alert-danger {
border-left-color: $modern-danger;
background: linear-gradient(135deg, rgba($modern-danger, 0.1) 0%, rgba($modern-danger, 0.05) 100%);
}
}
}
// 现代进度条
.progress {
&.progress-modern {
height: 0.75rem;
border-radius: 1rem;
background-color: rgba($modern-primary, 0.1);
.progress-bar {
background: $modern-gradient-primary;
border-radius: 1rem;
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(#ffffff, 0.3), transparent);
animation: shimmer 2s infinite;
}
}
}
}
}
// 现代主题动画
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
// 现代主题工具类
.gradient-text {
background: $modern-gradient-primary;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.glass-effect {
background: rgba(#ffffff, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(#ffffff, 0.2);
}
.floating-animation {
animation: float 3s ease-in-out infinite;
}
组件样式定制
1. 自定义组件样式
”`scss // _custom-components.scss // 自定义组件样式
// 自定义按钮组件 .btn-custom { // 3D 按钮效果 &.btn-3d { position: relative; transform-style: preserve-3d; transition: all 0.2s ease;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: inherit;
transform: translateZ(-4px) translateY(4px);
filter: brightness(0.8);
border-radius: inherit;
}
&:hover {
transform: translateY(-2px);
}
&:active {
transform: translateY(0);
}
}
// 霓虹按钮效果 &.btn-neon { background: transparent; border: 2px solid $primary; color: $primary; text-shadow: 0 0 10px $primary; box-shadow: 0 0 10px $primary, inset 0 0 10px transparent; transition: all 0.3s ease;
&:hover {
background: $primary;
color: #ffffff;
text-shadow: 0 0 20px #ffffff;
box-shadow:
0 0 20px $primary,
0 0 40px $primary,
inset 0 0 20px rgba($primary, 0.2);
}
}
// 液体按钮效果 &.btn-liquid { position: relative; overflow: hidden;
&::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(#ffffff, 0.3);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: width 0.6s ease, height 0.6s ease;
}
&:hover::before {
width: 300px;
height: 300px;
}
} }
// 自定义卡片组件 .card-custom { // 翻转卡片 &.card-flip { perspective: 1000px; height: 300px;
.card-inner {
position: relative;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.6s;
transform-style: preserve-3d;
}
&:hover .card-inner {
transform: rotateY(180deg);
}
.card-front,
.card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: $border-radius;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.card-back {
transform: rotateY(180deg);
background: $primary;
color: #ffffff;
}
}
// 悬浮卡片 &.card-hover { transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); cursor: pointer;
&:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 20px 40px rgba($black, 0.15);
.card-img-top {
transform: scale(1.1);
}
}
.card-img-top {
transition: transform 0.3s ease;
overflow: hidden;
}
}
// 渐变边框卡片 &.card-gradient-border { position: relative; background: #ffffff; border: none;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 2px;
background: linear-gradient(45deg, $primary, $secondary, $success);
border-radius: inherit;
mask: linear-gradient(#ffffff 0 0) content-box, linear-gradient(#ffffff 0 0);
mask-composite: exclude;
}
} }
// 自定义导航组件 .navbar-custom { // 滚动时变化的导航栏 &.navbar-scroll { transition: all 0.3s ease;
&.scrolled {
background-color: rgba(#ffffff, 0.95) !important;
backdrop-filter: blur(10px);
box-shadow: 0 2px 20px rgba($black, 0.1);
.navbar-brand {
transform: scale(0.9);
}
.navbar-nav .nav-link {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
}
}
// 动画导航链接 .nav-link-animated { position: relative; overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba($primary, 0.1), transparent);
transition: left 0.5s ease;
}
&:hover::before {
left: 100%;
}
} }
// 自定义表单组件 .form-custom { // 浮动标签 .form-floating-custom { position: relative;
.form-control {
padding-top: 1.625rem;
padding-bottom: 0.625rem;
&:focus,
&:not(:placeholder-shown) {
~ label {
opacity: 0.65;
transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}
}
}
label {
position: absolute;
top: 0;
left: 0;
height: 100%;
padding: 1rem 0.75rem;
pointer-events: none;
border: 1px solid transparent;
transform-origin: 0 0;
transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out;
}
}
// 动画复选框 .form-check-animated { .form-check-input { position: relative;
&:checked {
&::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ffffff;
font-size: 0.75rem;
animation: checkmark 0.3s ease;
}
}
}
} }
// 自定义模态框组件 .modal-custom { // 全屏模态框 &.modal-fullscreen-custom { .modal-dialog { margin: 0; width: 100vw; height: 100vh; max-width: none;
.modal-content {
height: 100vh;
border: none;
border-radius: 0;
}
}
}
// 侧边滑入模态框 &.modal-slide { .modal-dialog { position: fixed; top: 0; right: 0; margin: 0; width: 400px; height: 100vh; transform: translateX(100%); transition: transform 0.3s ease;
.modal-content {
height: 100vh;
border: none;
border-radius: 0;
}
}
&.show .modal-dialog {
transform: translateX(0);
}
} }
// 自定义进度条组件 .progress-custom { // 条纹动画进度条 &.progress-striped-animated { .progress-bar { background-image: linear-gradient( 45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent ); background-size: 1rem 1rem; animation: progress-bar-stripes 1s linear infinite; } }
// 圆形进度条 &.progress-circle { width: 120px; height: 120px; border-radius: 50%; background: conic-gradient(from 0deg, $primary 0deg, $primary var(–progress, 0deg), $gray-200 var(–progress, 0deg)); display: flex; align-items: center; justify-content: center; position: relative;
&::before {
content: '';
position: absolute;
width: 80%;
height: 80%;
background: #ffffff;
border-radius: 50%;
}
.progress-text {
position: relative;
z-index: 1;
font-weight: bold;
font-size: 1.25rem;
}
}