11 - 样式与主题
样式系统总览
项目使用 SCSS(dart-sass),共 17 个样式文件,构成分层设计:
基础层 │ _md-colors.scss → Material Design 色板
│ theme.scss → 主题覆盖(品牌色、字号等)
│ variables.scss → SCSS 变量定义(间距、组件尺寸)
↓
Mixins 层│ _mixins.scss → 共享 mixins(文本溢出、1px边框、flex居中等)
│ weapp-mixins.scss → 微信小程序专用(aspect-ratio padding-top 降级)
│ h5-mixins.scss → H5 专用(aspect-ratio 原生CSS优先)
↓
全局层 │ imports.scss → 全局入口(import theme + variables + mixins)
│ global.scss → 原子化工具类(flex、间距、字号)
│ layout.scss → 布局系统(section、grid、form、modal)
↓
组件层 │ sp-comps.scss → sp-* 自定义组件样式
│ taro-ui-mod.scss → taro-ui 组件库样式覆盖
↓
增强层 │ animations.scss → 动画
│ rtl.scss → RTL 从右到左支持
│ iconfont.scss → 主图标库
│ biao-icon.scss → 标记图标库imports.scss — 全局入口
构建配置中,SCSS 全局预加载 imports.scss:
js
// config/index.js
sass: {
resource: process.env.TARO_ENV === 'weapp'
? ['src/style/imports.scss', 'src/style/weapp-mixins.scss']
: ['src/style/imports.scss', 'src/style/h5-mixins.scss']
}imports.scss 内容:
scss
@import 'theme'; // 主题覆盖(先加载)
@import 'variables'; // 变量定义(!default 不覆盖 theme)
@import 'mixins'; // 共享 mixins加载顺序:theme → variables → mixins。theme 中的赋值优先,variables 中的
!default不覆盖。
variables.scss — SCSS 变量
字体
scss
@import 'md-colors';
$font-family: PingFangSC-Regular, PingFang SC, '微软雅黑', 'Microsoft YaHei', Helvetica, Arial, sans-serif;
$font-size: 14px * 2 !default; // 基准字号 28px(rpx 双倍设计)
$font-size-small: floor($font-size * 0.9) !default;
$font-size-large: floor($font-size * 1.2) !default;间距
scss
$edge-size: 15px * 2 !default; // 内边距 30px
$edge-margin: 10px * 2 !default; // 外边距 20px组件尺寸
scss
$navigation-height: 80px !default;
$toolbar-height: 120px !default;
$navigate-height: 80px !default;
$navigate-height-h5: 92px !default;
$tabbar-height: 90px !default;
$searchbar-height: 84px !default;
$page-footer-height: 120px !default;
$page-ipx-footer-height: 140px !default;语义色
scss
$color-brand-primary: #c40000 !default;
$color-brand-accent: #fa7815 !default;
$color-brand-primary-text: #ffffff !default;
$color-primary-text: #222 !default;
$color-secondary-text: #777 !default;
$color-hint-text: #999 !default;
$color-disabled-text: #ccc !default;
$color-bg-gray: #f4f4f4 !default;z-index 层级
scss
$z-index-level-1: 100 !default; // toolbar
$z-index-level-3: 300 !default; // tooltip, dropdown
$z-index-level-5: 500 !default; // modal
$z-index-level-6: 600 !default; // toasttheme.scss — 主题覆盖
scss
$dark-text: #1a1a1a;
$default-text: #333;
$secondary-text: #666;
$muted-text: #999;
$light-text: #bfbfbf;
$border-base: #ebebeb;
$color-brand-primary: #d42f29 !default; // 红色主题
$color-brand-accent: #fba629 !default; // 橙色强调
$color-price: #ff5000;
$item-border-radius: 8px;
$cart-border-radius: 10px;
$navigate-height: 92px !default;
$toolbar-height: 100px !default;
$tabbar-height: 110px !default;
// taro-ui 主题
$at-tab-bar-color: #818181 !default;
$at-tab-bar-color-active: $color-brand-primary !default;
$at-button-height: 80px !default;750px 设计稿
项目使用 750px 设计稿宽度(对应 rpx 单位):
js
// config/index.js
designWidth: 750,
deviceRatio: {
'640': 2.34 / 2,
'750': 1,
'828': 1.81 / 2
}单位转换
js
// src/utils/index.js
export const pxToRpx = (px) => parseInt((750 * px) / screenWidth)
export const rpxToPx = (rpx) => parseInt(rpx * (windowWidth / 750))
export const pxToUnitRpx = (px) => Taro.pxTransform(px * 2)SCSS 中的尺寸
SCSS 变量中的 * 2 是因为 750px 设计稿下 rpx 与 px 的比例为 2:1:
scss
$font-size: 14px * 2 !default; // 设计稿 14px → SCSS 中 28px
$edge-size: 15px * 2 !default; // 设计稿 15px → SCSS 中 30px_mixins.scss — 共享 Mixins
文本溢出
scss
// 单行溢出
@mixin text-overflow() {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
// 多行溢出
@mixin multi-ellipsis($line) {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: $line;
-webkit-box-orient: vertical;
word-break: break-all;
}1px 细线边框
scss
@mixin hairline($color: $color-border-gray, $left-gap: 0, $sides...) {
// 使用伪元素 + transform scale 实现 0.5px 边框
&::after {
content: '';
position: absolute;
// ...
@media (-webkit-device-pixel-ratio: 2) { transform: scale(0.5); }
@media (-webkit-device-pixel-ratio: 3) { transform: scale(0.3333); }
}
}布局工具
scss
// Flex 居中
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
// 可滚动区域
@mixin page-scroll($padding-top: 0, $padding-bottom: 0, $z-index: 1) {
position: absolute;
top: $padding-top;
bottom: $padding-bottom;
left: 0;
right: 0;
overflow-y: auto;
z-index: $z-index;
}自动生成工具类(含 RTL)
scss
@mixin generate-margin($start, $end, $step) {
// 生成 .mt-1 ~ .mt-50, .mr-1 ~ .mr-50, .ml-*, .mb-*
@each $i in range($start, $end, $step) {
.mt-#{$i} { margin-top: $i + px; }
.mr-#{$i} { margin-right: $i + px; }
.ml-#{$i} { margin-left: $i + px; }
.mb-#{$i} { margin-bottom: $i + px; }
}
// RTL 反转
.rtl-layout {
.ml-#{$i} { margin-left: 0; margin-right: $i + px; }
.mr-#{$i} { margin-right: 0; margin-left: $i + px; }
}
}
@include generate-margin(1, 50, 1); // .mt-1 ~ .mt-50
@include generate-padding(1, 50, 1);
@include generate-fontsize(1, 50, 1);weapp/h5 分平台 Mixins
weapp-mixins.scss
scss
// 微信不支持 aspect-ratio CSS,使用 padding-top 降级
@mixin aspect-ratio($width: 1, $height: 1, $useFallback: true) {
$padding: percentage($height / $width);
position: relative;
padding-top: $padding;
width: 100%;
> view {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
}
}h5-mixins.scss
scss
// H5 优先使用原生 aspect-ratio
@mixin aspect-ratio($width: 1, $height: 1, $useFallback: true) {
@supports (aspect-ratio: #{$width}/#{$height}) {
aspect-ratio: #{$width}/#{$height}; // 现代浏览器
}
@supports not (aspect-ratio: #{$width}/#{$height}) {
// padding-top 降级方案
@if $useFallback { /* ... */ }
}
}主题系统(运行时)
CSS 变量注入
系统配置中的主题色通过 CSS 变量注入到页面根节点:
js
// src/utils/index.js
function getThemeStyle() {
const { colorPrimary, colorMarketing, colorAccent, rgb } = sysConfig
return {
'--color-primary': colorPrimary,
'--color-marketing': colorMarketing,
'--color-accent': colorAccent,
'--color-rgb': rgb
}
}在 SCSS 中使用 CSS 变量
scss
// global.scss
.at-button--primary {
background: var(--color-primary) !important;
}
.primary-text {
color: var(--color-primary);
}主题色来源
| CSS 变量 | Redux 来源 | 默认值 |
|---|---|---|
--color-primary | sys.colorPrimary | #d42f29 |
--color-marketing | sys.colorMarketing | #fba629 |
--color-accent | sys.colorAccent | #2e3030 |
--color-rgb | sys.rgb | - |
taro-ui 覆盖
taro-ui-mod.scss 覆盖 taro-ui 组件库样式:
scss
// Modal 遮罩
.at-modal__overlay { background-color: rgba(#000, 0.8); }
// Input
.at-input { padding: 24px 0; margin: 0 0 $edge-size; }
// TabBar
.at-tab-bar {
padding: 0;
align-items: center;
}
// Button 主题色
.at-button {
&--primary { background: var(--color-primary) !important; }
&--default { border-color: var(--color-primary) !important; }
}图标库
iconfont.scss — 主图标库
阿里 iconfont 项目 ID 869421:
scss
@font-face {
font-family: 'iconfont';
src: url('//at.alicdn.com/t/c/font_869421_3xift8kgic2.woff2') format('woff2'),
url('//at.alicdn.com/t/c/font_869421_3xift8kgic2.woff') format('woff'),
url('//at.alicdn.com/t/c/font_869421_3xift8kgic2.ttf') format('truetype');
}biao-icon.scss — 标记图标库
阿里 iconfont 项目 ID 2222481,用于业务状态标记图标。
RTL 支持
rtl.scss 提供完整的 RTL 布局支持(193行),详见 08-国际化。
页面样式编写
页面样式文件
每个页面目录下有 index.scss:
subpages/item/detail/
├── index.jsx
├── index.scss ← 页面样式
└── index.config.js在页面样式中使用全局变量
scss
// subpages/item/detail/index.scss
.item-detail {
padding: $edge-size;
background: $color-bg-gray;
&__title {
font-size: $font-size-large;
color: $dark-text;
@include text-overflow;
}
}由于
imports.scss在构建配置中全局预加载,所有页面样式可以直接使用全局变量和 mixins,无需手动 import。
