mcpField
G
google-material-design

google-material-design

github.com/copyleftdev设计与 UI2148 次安装15 周前更新
代码仓库

关于 google-material-design

Google 的开源设计系统,为跨屏幕界面设计与实现提供原则、组件和规范,涵盖 Material 3 的动态色彩、响应式布局与无障碍设计。

主要功能

  1. 01Material 3 色彩与主题系统
  2. 02组件与交互状态规范
  3. 03自适应布局与导航模式
  4. 04排版、形状和层级规范
  5. 05触控与焦点无障碍指南

适用场景

  1. 01为应用建立一致的视觉语言
  2. 02根据屏幕尺寸调整导航布局
  3. 03为界面选择颜色、组件和动效
  4. 04检查触控目标与键盘焦点设计

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/copyleftdev/sk1llz --skill google-material-design

安装后在 Claude Code 中输入/google-material-design确认已加载。

详细介绍

Google Material Design⁠‍⁠​‌​‌​​‌‌‍​‌​​‌​‌‌‍​​‌‌​​​‌‍​‌​​‌‌​​‍​​​​​​​‌‍‌​​‌‌​‌​‍‌​​​​​​​‍‌‌​​‌‌‌‌‍‌‌​​​‌​​‍‌‌‌‌‌‌​‌‍‌‌​‌​​​​‍​‌​‌‌‌‌‌‍​‌​​‌​‌‌‍​‌‌​‌​​‌‍‌​‌​‌‌‌​‍​​‌​‌​​​‍‌‌‌​‌​‌‌‍‌​‌‌​​‌​‍‌​​‌​​​​‍​​​​​​‌​‍​​‌‌‌​​‌‍​​​​‌​​‌‍‌‌‌​​‌‌‌⁠‍⁠

概述

Material Design 是 Google 的开源设计系统,旨在弥合物理世界与数字世界之间的鸿沟。它于 2014 年推出,之后经历了 Material Design 2(2018 年)和 Material Design 3(2021 年)的演进,为构建美观、实用且易于访问的跨平台界面提供了全面的框架。

参考资料

核心理念

“Material 即隐喻。”

“大胆、图形化、目标明确。”

“动效赋予意义。”

“自适应设计,带来愉悦体验。”

Material Design 创造了一种视觉语言,将优秀设计的经典原则与技术和科学的创新及可能性融为一体。

Material 隐喻

物理世界的启发
┌─────────────────────────────────────────────────────────┐
│  MATERIAL = DIGITAL PAPER + INK + PHYSICAL PROPERTIES   │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌─────────┐     Light                                  │
│  │ Surface │ ◄── casts shadows                          │
│  │  (z=2)  │     based on elevation                     │
│  └─────────┘                                            │
│       │                                                 │
│       │ Elevation                                       │
│       ▼                                                 │
│  ╔═════════════════════════════════════════════════╗   │
│  ║              Ground Plane (z=0)                 ║   │
│  ╚═════════════════════════════════════════════════╝   │
│                                                         │
│  • Surfaces exist in 3D space with depth               │
│  • Material has physical properties and affordances    │
│  • Shadows communicate spatial relationships           │
│  • Content is printed on material, not within it       │
└─────────────────────────────────────────────────────────┘
海拔系统
Elevation (dp)    Component Examples           Shadow Intensity
─────────────────────────────────────────────────────────────
    0dp           Background, disabled         None
    1dp           Card (resting), Search bar   Subtle
    2dp           Card (raised), Button        Light
    3dp           Refresh indicator            Light-Medium
    4dp           App Bar                      Medium
    6dp           FAB (resting), Snackbar      Medium
    8dp           Bottom sheet, Menu           Pronounced
   12dp           FAB (pressed), Dialog        Strong
   16dp           Nav drawer                   Strong
   24dp           Modal bottom sheet           Maximum

设计原则

1. Material 即隐喻
/* Material surfaces have consistent properties */
.surface {
  /* Surfaces are opaque and cast shadows */
  background: var(--md-sys-color-surface);
  
  /* Elevation creates depth */
  box-shadow: var(--md-sys-elevation-level2);
  
  /* Corners define character */
  border-radius: var(--md-sys-shape-corner-medium);
}

/* Content rests ON material, not inside it */
.content-on-surface {
  /* Ink metaphor - content is printed on surface */
  color: var(--md-sys-color-on-surface);
}
2. 大胆、图形化、目标明确
Typography Hierarchy (Material 3)
═══════════════════════════════════════════════════════════

Display Large    57sp    Headlines for hero moments
Display Medium   45sp    Large display text
Display Small    36sp    Smaller display text
─────────────────────────────────────────────────────────
Headline Large   32sp    High-emphasis text
Headline Medium  28sp    Medium emphasis headers
Headline Small   24sp    Smaller headlines
─────────────────────────────────────────────────────────
Title Large      22sp    Prominent titles
Title Medium     16sp    Medium titles
Title Small      14sp    Smaller titles, tabs
─────────────────────────────────────────────────────────
Label Large      14sp    Buttons, prominent labels
Label Medium     12sp    Navigation labels
Label Small      11sp    Timestamps, hints
─────────────────────────────────────────────────────────
Body Large       16sp    Primary body text
Body Medium      14sp    Secondary body text
Body Small       12sp    Captions, annotations
3. 动效赋予意义
// motion_principles.js
// Motion in Material Design is choreographed and meaningful

const MaterialMotion = {
  // Duration tokens (Material 3)
  duration: {
    short1: 50,    // Micro-interactions
    short2: 100,   // Simple selections
    short3: 150,   // Checkbox, switch
    short4: 200,   // Small expand/collapse
    medium1: 250,  // Standard enter/exit
    medium2: 300,  // Card expand
    medium3: 350,  // Complex animations
    medium4: 400,  // Page transitions
    long1: 450,    // Large area transitions
    long2: 500,    // Complex choreography
    long3: 550,    // Elaborate animations
    long4: 600,    // Maximum duration
  },
  
  // Easing curves
  easing: {
    // Emphasized - for prominent elements
    emphasized: 'cubic-bezier(0.2, 0.0, 0, 1.0)',
    emphasizedDecelerate: 'cubic-bezier(0.05, 0.7, 0.1, 1.0)',
    emphasizedAccelerate: 'cubic-bezier(0.3, 0.0, 0.8, 0.15)',
    
    // Standard - for most transitions
    standard: 'cubic-bezier(0.2, 0.0, 0, 1.0)',
    standardDecelerate: 'cubic-bezier(0, 0, 0, 1)',
    standardAccelerate: 'cubic-bezier(0.3, 0, 1, 1)',
  },
  
  // Transition patterns
  patterns: {
    // Container transform - element becomes new screen
    containerTransform: {
      use: 'Element expands into a new screen',
      duration: 'medium2',
      easing: 'emphasized',
    },
    
    // Shared axis - elements share spatial relationship
    sharedAxis: {
      use: 'Navigation between related destinations',
      duration: 'medium1',
      easing: 'emphasized',
    },
    
    // Fade through - unrelated content transition
    fadeThrough: {
      use: 'Switching between unrelated content',
      duration: 'medium1',
      easing: 'emphasized',
    },
    
    // Fade - simple appearance/disappearance
    fade: {
      use: 'UI elements appearing or disappearing',
      duration: 'short4',
      easing: 'standard',
    },
  },
};

// Example: Container transform animation
function containerTransform(element, options) {
  const { duration, easing } = MaterialMotion.patterns.containerTransform;
  
  return element.animate([
    { transform: 'scale(0.85)', opacity: 0 },
    { transform: 'scale(1)', opacity: 1 },
  ], {
    duration: MaterialMotion.duration[duration],
    easing: MaterialMotion.easing[easing],
    fill: 'forwards',
  });
}

颜色系统(Material 3 动态颜色)

色调调色板
Source Color → Tonal Palette → Color Scheme
═══════════════════════════════════════════════════════════

Primary Source (#6750A4)
    │
    ▼
┌─────────────────────────────────────────────────────────┐
│ Tonal Palette: Primary                                  │
│ ─────────────────────────────────────────────────────── │
│ 0    10   20   30   40   50   60   70   80   90   95 100│
│ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ████ ████ ░░░░ ░░░░ ░░░░ ░░░░ ░░░░ │
│ Darkest ◄─────────────────────────────────► Lightest   │
└─────────────────────────────────────────────────────────┘
    │
    ▼
Light Scheme:               Dark Scheme:
  primary: tone40             primary: tone80
  onPrimary: tone100          onPrimary: tone20
  primaryContainer: tone90    primaryContainer: tone30
  onPrimaryContainer: tone10  onPrimaryContainer: tone90
颜色角色
/* Material 3 Color Tokens */
:root {
  /* Primary - Key color, brand identity */
  --md-sys-color-primary: #6750A4;
  --md-sys-color-on-primary: #FFFFFF;
  --md-sys-color-primary-container: #EADDFF;
  --md-sys-color-on-primary-container: #21005D;
  
  /* Secondary - Supporting color */
  --md-sys-color-secondary: #625B71;
  --md-sys-color-on-secondary: #FFFFFF;
  --md-sys-color-secondary-container: #E8DEF8;
  --md-sys-color-on-secondary-container: #1D192B;
  
  /* Tertiary - Accent, contrast */
  --md-sys-color-tertiary: #7D5260;
  --md-sys-color-on-tertiary: #FFFFFF;
  --md-sys-color-tertiary-container: #FFD8E4;
  --md-sys-color-on-tertiary-container: #31111D;
  
  /* Error - Alerts and errors */
  --md-sys-color-error: #B3261E;
  --md-sys-color-on-error: #FFFFFF;
  --md-sys-color-error-container: #F9DEDC;
  --md-sys-color-on-error-container: #410E0B;
  
  /* Surface - Backgrounds */
  --md-sys-color-surface: #FFFBFE;
  --md-sys-color-on-surface: #1C1B1F;
  --md-sys-color-surface-variant: #E7E0EC;
  --md-sys-color-on-surface-variant: #49454F;
  
  /* Outline - Borders, dividers */
  --md-sys-color-outline: #79747E;
  --md-sys-color-outline-variant: #CAC4D0;
}
根据内容生成动态颜色
// dynamic_color.js
// Extract color scheme from images or user preference

class MaterialColorScheme {
  constructor(sourceColor) {
    this.source = sourceColor;
    this.palette = this.generateTonalPalette(sourceColor);
  }
  
  generateTonalPalette(color) {
    // HCT (Hue, Chroma, Tone) color space for perceptual uniformity
    const hct = rgbToHct(color);
    
    return {
      0: hctToRgb({ ...hct, tone: 0 }),
      10: hctToRgb({ ...hct, tone: 10 }),
      20: hctToRgb({ ...hct, tone: 20 }),
      30: hctToRgb({ ...hct, tone: 30 }),
      40: hctToRgb({ ...hct, tone: 40 }),
      50: hctToRgb({ ...hct, tone: 50 }),
      60: hctToRgb({ ...hct, tone: 60 }),
      70: hctToRgb({ ...hct, tone: 70 }),
      80: hctToRgb({ ...hct, tone: 80 }),
      90: hctToRgb({ ...hct, tone: 90 }),
      95: hctToRgb({ ...hct, tone: 95 }),
      99: hctToRgb({ ...hct, tone: 99 }),
      100: hctToRgb({ ...hct, tone: 100 }),
    };
  }
  
  getLightScheme() {
    return {
      primary: this.palette[40],
      onPrimary: this.palette[100],
      primaryContainer: this.palette[90],
      onPrimaryContainer: this.palette[10],
      // ... remaining roles
    };
  }
  
  getDarkScheme() {
    return {
      primary: this.palette[80],
      onPrimary: this.palette[20],
      primaryContainer: this.palette[30],
      onPrimaryContainer: this.palette[90],
      // ... remaining roles
    };
  }
}

组件架构

Material 组件剖析
┌─────────────────────────────────────────────────────────┐
│                    BUTTON ANATOMY                       │
├─────────────────────────────────────────────────────────┤
│                                                         │
│    ┌─────────────────────────────────────────┐         │
│    │  [Icon]  Label Text                     │         │
│    └─────────────────────────────────────────┘         │
│    │         │                               │         │
│    │         │                               │         │
│    │    Leading Icon (optional)              │         │
│    │                                         │         │
│    │    Label text (required)           Container      │
│    │                                    (required)     │
│    │                                         │         │
│    └─────────────────────────────────────────┘         │
│                                                         │
│    States: Enabled, Disabled, Hovered,                 │
│            Focused, Pressed                            │
└─────────────────────────────────────────────────────────┘
按钮变体
// MaterialButton.tsx
// Material 3 button implementations

interface ButtonProps {
  variant: 'elevated' | 'filled' | 'tonal' | 'outlined' | 'text';
  icon?: React.ReactNode;
  children: React.ReactNode;
  disabled?: boolean;
  onClick?: () => void;
}

// Elevated - For secondary actions requiring emphasis
const ElevatedButton = styled.button`
  background: var(--md-sys-color-surface-container-low);
  color: var(--md-sys-color-primary);
  box-shadow: var(--md-sys-elevation-level1);
  border: none;
  border-radius: 20px;
  padding: 10px 24px;
  font: var(--md-sys-typescale-label-large);
  
  &:hover {
    box-shadow: var(--md-sys-elevation-level2);
    background: color-mix(
      in srgb,
      var(--md-sys-color-primary) 8%,
      var(--md-sys-color-surface-container-low)
    );
  }
  
  &:focus-visible {
    outline: none;
    box-shadow: var(--md-sys-elevation-level1);
    background: color-mix(
      in srgb,
      var(--md-sys-color-primary) 12%,
      var(--md-sys-color-surface-container-low)
    );
  }
  
  &:active {
    box-shadow: var(--md-sys-elevation-level1);
    background: color-mix(
      in srgb,
      var(--md-sys-color-primary) 12%,
      var(--md-sys-color-surface-container-low)
    );
  }
`;

// Filled - For primary actions, high emphasis
const FilledButton = styled.button`
  background: var(--md-sys-color-primary);
  color: var(--md-sys-color-on-primary);
  border: none;
  border-radius: 20px;
  padding: 10px 24px;
  
  &:hover {
    box-shadow: var(--md-sys-elevation-level1);
    background: color-mix(
      in srgb,
      var(--md-sys-color-on-primary) 8%,
      var(--md-sys-color-primary)
    );
  }
`;

// Tonal - For secondary actions, medium emphasis
const TonalButton = styled.button`
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
  border: none;
  border-radius: 20px;
  padding: 10px 24px;
`;

// Outlined - For secondary actions, low emphasis
const OutlinedButton = styled.button`
  background: transparent;
  color: var(--md-sys-color-primary);
  border: 1px solid var(--md-sys-color-outline);
  border-radius: 20px;
  padding: 10px 24px;
`;

// Text - For lowest emphasis actions
const TextButton = styled.button`
  background: transparent;
  color: var(--md-sys-color-primary);
  border: none;
  border-radius: 20px;
  padding: 10px 12px;
`;
状态层系统
/* Material 3 State Layer Implementation */

.interactive-element {
  position: relative;
  overflow: hidden;
}

/* State layer overlay */
.interactive-element::before {
  content: '';
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity 150ms var(--md-sys-motion-easing-standard);
}

/* State layer opacities */
.interactive-element:hover::before {
  opacity: 0.08;  /* Hover state */
}

.interactive-element:focus-visible::before {
  opacity: 0.12;  /* Focus state */
}

.interactive-element:active::before {
  opacity: 0.12;  /* Pressed state */
}

.interactive-element[data-dragged]::before {
  opacity: 0.16;  /* Dragged state */
}

/* Disabled states - no state layer, reduced opacity */
.interactive-element:disabled {
  opacity: 0.38;
  pointer-events: none;
}

.interactive-element:disabled::before {
  display: none;
}

形状系统

圆角样式
Shape Scale (Material 3)
═══════════════════════════════════════════════════════════

None (0dp)        Extra Small (4dp)    Small (8dp)
┌──────────┐      ╭──────────╮        ╭──────────╮
│          │      │          │        │          │
│          │      │          │        │          │
│          │      │          │        │          │
└──────────┘      ╰──────────╯        ╰──────────╯

Medium (12dp)     Large (16dp)        Extra Large (28dp)
╭──────────╮      ╭──────────╮        ╭──────────╮
│          │      │          │        │          │
│          │      │          │        │          │
│          │      │          │        │          │
╰──────────╯      ╰──────────╯        ╰──────────╯

Full (50%)
    ╭────╮
   │      │
   │      │
    ╰────╯
/* Shape tokens */
:root {
  --md-sys-shape-corner-none: 0px;
  --md-sys-shape-corner-extra-small: 4px;
  --md-sys-shape-corner-small: 8px;
  --md-sys-shape-corner-medium: 12px;
  --md-sys-shape-corner-large: 16px;
  --md-sys-shape-corner-extra-large: 28px;
  --md-sys-shape-corner-full: 9999px;
}

/* Shape application by component */
.chip { border-radius: var(--md-sys-shape-corner-small); }
.card { border-radius: var(--md-sys-shape-corner-medium); }
.dialog { border-radius: var(--md-sys-shape-corner-extra-large); }
.fab { border-radius: var(--md-sys-shape-corner-large); }
.button { border-radius: var(--md-sys-shape-corner-full); }

自适应布局

响应式网格系统
Window Size Classes
═══════════════════════════════════════════════════════════

Compact (< 600dp)     Medium (600-839dp)   Expanded (840dp+)
┌─────────────┐       ┌────────────────┐   ┌───────────────────┐
│             │       │                │   │ Nav │             │
│   Content   │       │    Content     │   │ Bar │   Content   │
│             │       │                │   │     │             │
│             │       │                │   │     │             │
├─────────────┤       │                │   │     │             │
│  Nav Bar    │       ├────────────────┤   │     │             │
└─────────────┘       │    Nav Bar     │   │     │             │
                      └────────────────┘   └───────────────────┘
4 columns             8 columns (body)     12 columns (body)
16dp margins          24dp margins         24dp margins
8dp gutters           16dp gutters         24dp gutters
布局实现
// AdaptiveLayout.tsx
// Material 3 adaptive scaffold

import { useWindowSize } from './hooks';

type WindowSizeClass = 'compact' | 'medium' | 'expanded' | 'large' | 'extraLarge';

function getWindowSizeClass(width: number): WindowSizeClass {
  if (width < 600) return 'compact';
  if (width < 840) return 'medium';
  if (width < 1200) return 'expanded';
  if (width < 1600) return 'large';
  return 'extraLarge';
}

interface AdaptiveLayoutProps {
  navigation: React.ReactNode;
  content: React.ReactNode;
  detail?: React.ReactNode;
}

export function AdaptiveLayout({ navigation, content, detail }: AdaptiveLayoutProps) {
  const { width } = useWindowSize();
  const sizeClass = getWindowSizeClass(width);
  
  // Compact: Bottom navigation bar
  if (sizeClass === 'compact') {
    return (
      <div className="layout-compact">
        <main className="content">{content}</main>
        <nav className="navigation-bar">{navigation}</nav>
      </div>
    );
  }
  
  // Medium: Navigation rail
  if (sizeClass === 'medium') {
    return (
      <div className="layout-medium">
        <nav className="navigation-rail">{navigation}</nav>
        <main className="content">{content}</main>
      </div>
    );
  }
  
  // Expanded+: Navigation drawer with optional detail pane
  return (
    <div className="layout-expanded">
      <nav className="navigation-drawer">{navigation}</nav>
      <main className="content">{content}</main>
      {detail && sizeClass !== 'expanded' && (
        <aside className="detail-pane">{detail}</aside>
      )}
    </div>
  );
}
导航模式
Navigation Components by Screen Size
═══════════════════════════════════════════════════════════

           Compact         Medium          Expanded
           ─────────────────────────────────────────────
Primary    Bottom Bar      Rail            Drawer
           3-5 items       3-7 items       Full labels

Secondary  Modal Drawer    Modal Drawer    Persistent Drawer

Tertiary   Tabs           Tabs            Tabs
           Bottom Sheet   Side Sheet      Side Sheet

无障碍(A11y)

触控目标
/* Minimum touch target: 48x48dp */
.touch-target {
  min-width: 48px;
  min-height: 48px;
  
  /* Visual element can be smaller, but touch area must be 48dp */
  display: flex;
  align-items: center;
  justify-content: center;
}

.icon-button {
  /* Icon is 24dp, but touch target is 48dp */
  width: 48px;
  height: 48px;
  padding: 12px;
}

.icon-button svg {
  width: 24px;
  height: 24px;
}
颜色对比度
Contrast Requirements (WCAG 2.1)
═══════════════════════════════════════════════════════════

Text Size        Minimum (AA)    Enhanced (AAA)
─────────────────────────────────────────────────
Normal text      4.5:1           7:1
Large text       3:1             4.5:1
(18sp+ or 14sp bold)

UI Components    3:1             Not defined
(borders, icons)

Material Design ensures:
• on-primary meets 4.5:1 against primary
• on-surface meets 4.5:1 against surface
• All semantic color pairs are accessible
焦点指示器
/* Material 3 focus indicators */
.focusable {
  outline: none;
  position: relative;
}

.focusable:focus-visible {
  /* Focus ring using secondary color */
  outline: 3px solid var(--md-sys-color-secondary);
  outline-offset: 2px;
}

/* Alternative: Focus indicator within element */
.button:focus-visible {
  outline: none;
}

.button:focus-visible::after {
  content: '';
  position: absolute;
  inset: -4px;
  border: 3px solid var(--md-sys-color-secondary);
  border-radius: inherit;
  pointer-events: none;
}

实施时

始终
  • 使用设计令牌系统管理颜色、字体排版和形状
  • 实现所有交互状态(启用、禁用、悬停、聚焦、按下)
  • 确保触控目标至少为 48dp
  • 提供清晰可见的焦点指示器
  • 使用语义化颜色角色(主色、次色、错误色),而非原始值
  • 运用海拔系统构建空间层级
  • 为状态变化加入有意义的动效
切勿
  • 使用硬编码颜色值代替令牌
  • 忽略禁用状态
  • 在色调调色板之外创建自定义颜色
  • 忽略键盘用户的焦点状态
  • 无目的地使用海拔
  • 添加无法传达意义的动效
  • 违反触控目标的最小尺寸要求
优先选择
  • 主要操作使用填充按钮,次要操作使用文本按钮
  • 使用色调调色板,确保颜色一致协调
  • 大多数过渡使用标准缓动
  • 使用表面颜色变体覆盖叠加层,而非透明度
  • 优先使用组件库,而非自定义实现
  • 优先使用自适应布局,而非固定断点

思维模型

Material Design 提出以下问题:

  1. 它像 Material 吗? 它的行为是否如具有深度的物理表面?
  2. 它有明确目的吗? 每个元素是否都发挥着清晰的作用?
  3. 它有意义吗? 动效是否传达状态并引导注意力?
  4. 它易于访问吗? 所有人无论能力如何,都能使用它吗?
  5. 它能自适应吗? 它能否适用于所有屏幕尺寸和设备?
  6. 它令人愉悦吗? 它能否带来积极的情感体验?

标志性设计元素

  • 动态色彩:根据用户内容生成个性化主题
  • 色调调色板:从单一来源提取和谐的色彩
  • 状态层:通过叠加层提供一致的交互反馈
  • 容器变换:实现元素到屏幕的无缝过渡
  • 层级阴影:通过阴影传达重要程度
  • 自适应脚手架:随屏幕尺寸变化的导航
  • 排版比例:15 种样式,清晰呈现内容层级
  • 形状系统:通过圆角传达组件个性

评论

支持 Markdown