Skip to content

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;  // toast

theme.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-primarysys.colorPrimary#d42f29
--color-marketingsys.colorMarketing#fba629
--color-accentsys.colorAccent#2e3030
--color-rgbsys.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。