品牌指南的技术实现:从设计稿到代码的系统化落地

HTMLPAGE 团队
18 分钟阅读

详解如何将品牌视觉规范转化为可执行的技术方案,包括设计令牌、组件库集成、主题系统、品牌资源管理等核心实践。

#品牌设计 #设计系统 #设计令牌 #主题化 #视觉规范

品牌指南的技术实现:从设计稿到代码的系统化落地

品牌指南文档写得再漂亮,如果开发时无法精准还原,也只是一纸空文。本文将讲解如何将品牌规范转化为代码,让每一行 CSS 都散发着品牌气息。

品牌指南的技术视角

品牌规范包含什么

从技术实现角度,品牌指南可以拆解为:

类别设计元素技术实现
色彩主色、辅色、中性色CSS 变量、设计令牌
字体字族、字重、字号@font-face、排版系统
间距边距、内边距、行高间距尺度、布局网格
圆角按钮、卡片、输入框圆角令牌
阴影层级阴影、悬浮效果阴影令牌
图标风格、尺寸、颜色图标库、SVG 系统
Logo尺寸、间距、变体资源管理、组件封装
动效时长、缓动、过渡动画令牌

设计令牌系统

什么是设计令牌

设计令牌是品牌规范的最小单位——一个命名的值:

{
  "color": {
    "brand": {
      "primary": { "value": "#0066FF" },
      "secondary": { "value": "#FF6B35" }
    },
    "neutral": {
      "100": { "value": "#FFFFFF" },
      "900": { "value": "#1A1A1A" }
    }
  },
  "spacing": {
    "xs": { "value": "4px" },
    "sm": { "value": "8px" },
    "md": { "value": "16px" }
  }
}

令牌分层架构

全局令牌 (Global Tokens)
    ↓ 基础颜色、字体、间距
语义令牌 (Semantic Tokens)
    ↓ 背景色、文字色、边框色
组件令牌 (Component Tokens)
    → 按钮背景、输入框边框
/* 1. 全局令牌 - 品牌原始值 */
:root {
  --color-blue-500: #0066FF;
  --color-blue-600: #0052CC;
  --color-gray-100: #F5F5F5;
  --color-gray-900: #1A1A1A;
}

/* 2. 语义令牌 - 场景含义 */
:root {
  --color-bg-primary: var(--color-gray-100);
  --color-text-primary: var(--color-gray-900);
  --color-brand-primary: var(--color-blue-500);
  --color-brand-hover: var(--color-blue-600);
}

/* 3. 组件令牌 - 具体应用 */
:root {
  --button-bg: var(--color-brand-primary);
  --button-bg-hover: var(--color-brand-hover);
  --button-text: white;
}

Style Dictionary 自动化

使用 Style Dictionary 管理多平台令牌:

// config.json
{
  "source": ["tokens/**/*.json"],
  "platforms": {
    "css": {
      "transformGroup": "css",
      "buildPath": "build/css/",
      "files": [{
        "destination": "variables.css",
        "format": "css/variables"
      }]
    },
    "scss": {
      "transformGroup": "scss",
      "buildPath": "build/scss/",
      "files": [{
        "destination": "_variables.scss",
        "format": "scss/variables"
      }]
    },
    "js": {
      "transformGroup": "js",
      "buildPath": "build/js/",
      "files": [{
        "destination": "tokens.js",
        "format": "javascript/es6"
      }]
    }
  }
}
# 生成所有平台的令牌文件
npx style-dictionary build

色彩系统实现

品牌色彩定义

:root {
  /* 品牌主色 */
  --brand-primary: #0066FF;
  --brand-primary-light: #3385FF;
  --brand-primary-dark: #0052CC;
  
  /* 品牌辅色 */
  --brand-secondary: #FF6B35;
  --brand-accent: #00D4AA;
  
  /* 中性色阶 */
  --neutral-50: #FAFAFA;
  --neutral-100: #F5F5F5;
  --neutral-200: #EEEEEE;
  --neutral-300: #E0E0E0;
  --neutral-400: #BDBDBD;
  --neutral-500: #9E9E9E;
  --neutral-600: #757575;
  --neutral-700: #616161;
  --neutral-800: #424242;
  --neutral-900: #212121;
  
  /* 功能色 */
  --success: #22C55E;
  --warning: #F59E0B;
  --error: #EF4444;
  --info: #3B82F6;
}

语义化颜色映射

:root {
  /* 文本颜色 */
  --text-primary: var(--neutral-900);
  --text-secondary: var(--neutral-600);
  --text-disabled: var(--neutral-400);
  --text-inverse: var(--neutral-50);
  
  /* 背景颜色 */
  --bg-primary: var(--neutral-50);
  --bg-secondary: var(--neutral-100);
  --bg-elevated: white;
  
  /* 边框颜色 */
  --border-default: var(--neutral-200);
  --border-strong: var(--neutral-300);
  
  /* 交互颜色 */
  --interactive-primary: var(--brand-primary);
  --interactive-hover: var(--brand-primary-dark);
  --interactive-active: var(--brand-primary-dark);
}

暗色模式支持

/* 暗色主题令牌 */
[data-theme="dark"] {
  --text-primary: var(--neutral-100);
  --text-secondary: var(--neutral-400);
  --bg-primary: var(--neutral-900);
  --bg-secondary: var(--neutral-800);
  --bg-elevated: var(--neutral-800);
  --border-default: var(--neutral-700);
}

/* 跟随系统 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --text-primary: var(--neutral-100);
    /* ... */
  }
}

排版系统

字体家族定义

:root {
  /* 品牌字体 */
  --font-display: 'Brand Display', 'Noto Sans SC', sans-serif;
  --font-body: 'Inter', 'PingFang SC', sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
}

/* 字体加载 */
@font-face {
  font-family: 'Brand Display';
  src: url('/fonts/brand-display.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

字号尺度

:root {
  /* 模块化尺度 (1.25 比例) */
  --text-xs: 0.75rem;    /* 12px */
  --text-sm: 0.875rem;   /* 14px */
  --text-base: 1rem;     /* 16px */
  --text-lg: 1.25rem;    /* 20px */
  --text-xl: 1.5rem;     /* 24px */
  --text-2xl: 2rem;      /* 32px */
  --text-3xl: 2.5rem;    /* 40px */
  --text-4xl: 3rem;      /* 48px */
  
  /* 行高 */
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;
  
  /* 字重 */
  --font-normal: 400;
  --font-medium: 500;
  --font-semibold: 600;
  --font-bold: 700;
}

排版预设

/* 标题样式 */
.heading-1 {
  font-family: var(--font-display);
  font-size: var(--text-4xl);
  font-weight: var(--font-bold);
  line-height: var(--leading-tight);
  letter-spacing: -0.02em;
}

.heading-2 {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: var(--font-bold);
  line-height: var(--leading-tight);
}

/* 正文样式 */
.body-lg {
  font-family: var(--font-body);
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
}

.body-base {
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
}

Logo 组件封装

Logo 组件

<template>
  <component 
    :is="href ? 'a' : 'span'" 
    :href="href"
    class="brand-logo"
    :class="[`size-${size}`, { 'logo-mono': mono }]"
  >
    <!-- 完整 Logo -->
    <template v-if="variant === 'full'">
      <img 
        :src="mono ? logoMonoSrc : logoSrc" 
        :alt="alt"
        class="logo-image"
      >
    </template>
    
    <!-- 图标 Logo -->
    <template v-else-if="variant === 'icon'">
      <img 
        :src="iconSrc" 
        :alt="alt"
        class="logo-icon"
      >
    </template>
    
    <!-- 文字 Logo -->
    <template v-else-if="variant === 'wordmark'">
      <span class="logo-wordmark">{{ brandName }}</span>
    </template>
  </component>
</template>

<script setup>
defineProps({
  variant: {
    type: String,
    default: 'full',
    validator: v => ['full', 'icon', 'wordmark'].includes(v)
  },
  size: {
    type: String,
    default: 'md',
    validator: v => ['sm', 'md', 'lg'].includes(v)
  },
  mono: Boolean,
  href: String,
  alt: {
    type: String,
    default: 'Brand Logo'
  }
})

const brandName = 'BrandName'
const logoSrc = '/images/brand/logo-full.svg'
const logoMonoSrc = '/images/brand/logo-full-mono.svg'
const iconSrc = '/images/brand/logo-icon.svg'
</script>

<style scoped>
.brand-logo {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

/* 尺寸变体 */
.size-sm .logo-image { height: 24px; }
.size-md .logo-image { height: 32px; }
.size-lg .logo-image { height: 48px; }

.size-sm .logo-icon { width: 24px; height: 24px; }
.size-md .logo-icon { width: 32px; height: 32px; }
.size-lg .logo-icon { width: 48px; height: 48px; }

/* Logo 保护空间 */
.logo-image {
  display: block;
  padding: calc(var(--logo-height) * 0.25);  /* 25% 安全边距 */
}
</style>

Logo 使用规范

<template>
  <div class="logo-showcase">
    <!-- 标准使用 -->
    <BrandLogo size="md" />
    
    <!-- 最小尺寸限制 -->
    <BrandLogo size="sm" />  <!-- 最小 24px -->
    
    <!-- 单色版(深色背景) -->
    <div class="dark-bg">
      <BrandLogo mono />
    </div>
    
    <!-- 禁止用法提示 -->
    <!-- ❌ 不要拉伸 -->
    <!-- ❌ 不要旋转 -->
    <!-- ❌ 不要添加阴影 -->
    <!-- ❌ 不要使用非规定颜色 -->
  </div>
</template>

组件库品牌定制

Tailwind CSS 品牌配置

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#E6F0FF',
          100: '#CCE0FF',
          200: '#99C2FF',
          300: '#66A3FF',
          400: '#3385FF',
          500: '#0066FF',  // 主色
          600: '#0052CC',
          700: '#003D99',
          800: '#002966',
          900: '#001433',
        },
        // 语义色别名
        primary: 'var(--brand-primary)',
        secondary: 'var(--brand-secondary)',
      },
      fontFamily: {
        display: ['Brand Display', 'sans-serif'],
        body: ['Inter', 'sans-serif'],
      },
      borderRadius: {
        brand: '8px',  // 品牌圆角
      },
      boxShadow: {
        brand: '0 4px 12px rgba(0, 102, 255, 0.15)',
      }
    }
  }
}

Element Plus 主题定制

// styles/element-theme.scss
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  $colors: (
    'primary': (
      'base': #0066FF,
    ),
    'success': (
      'base': #22C55E,
    ),
    'warning': (
      'base': #F59E0B,
    ),
    'danger': (
      'base': #EF4444,
    ),
  ),
  $font-family: (
    'base': "'Inter', 'PingFang SC', sans-serif",
  ),
  $border-radius: (
    'base': 8px,
    'small': 4px,
    'round': 20px,
  )
);

@use 'element-plus/theme-chalk/src/index.scss' as *;

品牌资源管理

资源目录结构

public/
└── brand/
    ├── logos/
    │   ├── logo-full.svg
    │   ├── logo-full-mono.svg
    │   ├── logo-icon.svg
    │   ├── logo-icon-mono.svg
    │   └── logo-wordmark.svg
    ├── icons/
    │   ├── icon-set.svg          # 图标雪碧图
    │   └── social/
    │       ├── wechat.svg
    │       └── weibo.svg
    ├── illustrations/
    │   ├── hero-illustration.svg
    │   └── empty-state.svg
    └── brand-guidelines.pdf       # 完整规范文档

资源引用辅助

// utils/brand-assets.ts
export const BRAND = {
  logo: {
    full: '/brand/logos/logo-full.svg',
    fullMono: '/brand/logos/logo-full-mono.svg',
    icon: '/brand/logos/logo-icon.svg',
    wordmark: '/brand/logos/logo-wordmark.svg',
  },
  social: {
    wechat: '/brand/icons/social/wechat.svg',
    weibo: '/brand/icons/social/weibo.svg',
  },
  illustrations: {
    hero: '/brand/illustrations/hero-illustration.svg',
    emptyState: '/brand/illustrations/empty-state.svg',
  }
}

// 使用
import { BRAND } from '@/utils/brand-assets'
<img :src="BRAND.logo.full" alt="Logo" />

品牌一致性检查

CSS 变量检查脚本

// scripts/check-brand-consistency.js
const fs = require('fs')
const path = require('path')
const css = require('css')

const BRAND_COLORS = ['#0066FF', '#FF6B35', '#00D4AA']

function checkCSSFiles(dir) {
  const issues = []
  
  function walkDir(currentPath) {
    const files = fs.readdirSync(currentPath)
    
    for (const file of files) {
      const filePath = path.join(currentPath, file)
      const stat = fs.statSync(filePath)
      
      if (stat.isDirectory()) {
        walkDir(filePath)
      } else if (file.endsWith('.css') || file.endsWith('.scss')) {
        const content = fs.readFileSync(filePath, 'utf8')
        
        // 检查硬编码颜色
        const hexColors = content.match(/#[0-9A-Fa-f]{6}/g) || []
        for (const color of hexColors) {
          if (!BRAND_COLORS.includes(color.toUpperCase())) {
            issues.push({
              file: filePath,
              issue: `发现非品牌色: ${color}`,
              suggestion: '请使用 CSS 变量 var(--brand-*)'
            })
          }
        }
      }
    }
  }
  
  walkDir(dir)
  return issues
}

const issues = checkCSSFiles('./src')
if (issues.length) {
  console.log('品牌一致性检查发现问题:')
  issues.forEach(i => console.log(`  ${i.file}: ${i.issue}`))
  process.exit(1)
}

Stylelint 规则

// .stylelintrc.js
module.exports = {
  rules: {
    // 禁止硬编码颜色
    'color-no-hex': true,
    
    // 必须使用 CSS 变量
    'declaration-property-value-allowed-list': {
      'color': ['/^var\\(--/', 'inherit', 'currentColor'],
      'background-color': ['/^var\\(--/', 'transparent', 'inherit'],
    },
    
    // 禁止使用非品牌字体
    'font-family-no-missing-generic-family-keyword': true,
  }
}

总结

品牌指南技术实现的核心要点:

维度实践
令牌化将品牌规范拆解为设计令牌
分层全局→语义→组件三层令牌
自动化Style Dictionary 多平台输出
组件化Logo、图标封装为组件
主题化支持暗色模式和多主题
检查自动化一致性校验

品牌不是静态的规范文档——它应该活在代码里,融入每个像素。当设计师说"主色",开发者应该条件反射地写出 var(--brand-primary)。这才是真正的品牌落地。

延伸阅读