google-material-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Google Material Design

Google Material Design

Overview

概述

Material Design is Google's open-source design system that bridges the gap between the physical and digital worlds. Introduced in 2014 and evolved through Material Design 2 (2018) and Material Design 3 (2021), it provides a comprehensive framework for building beautiful, functional, and accessible interfaces across all platforms.
Material Design是Google推出的开源设计系统,旨在连接物理世界与数字世界。它于2014年首次发布,历经Material Design 2(2018年)和Material Design 3(2021年)的演进,为跨所有平台构建美观、实用且易用的界面提供了全面的框架。

References

参考资料

Core Philosophy

核心理念

"Material is the metaphor."
"Bold, graphic, intentional."
"Motion provides meaning."
"Adaptive design, delightful experiences."
Material Design creates a visual language that synthesizes classic principles of good design with the innovation and possibility of technology and science.
"材质即隐喻。"
"大胆、直观、有目的性。"
"动效传递意义。"
"自适应设计,愉悦体验。"
Material Design打造了一套视觉语言,将优秀设计的经典原则与科技带来的创新可能性相结合。

The Material Metaphor

材质隐喻

Physical World Inspiration

物理世界灵感

┌─────────────────────────────────────────────────────────┐
│  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       │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│  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 System

海拔高度系统

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
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

Design Principles

设计原则

1. Material is the Metaphor

1. 材质即隐喻

css
/* 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);
}
css
/* 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. Bold, Graphic, Intentional

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
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 Provides Meaning

3. 动效传递意义

javascript
// 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',
  });
}
javascript
// 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',
  });
}

Color System (Material 3 Dynamic Color)

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

Tonal Palettes

色调调色板

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
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

Color Roles

色彩角色

css
/* 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;
}
css
/* 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 from Content

从内容提取动态色彩

javascript
// 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
    };
  }
}
javascript
// 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
    };
  }
}

Component Architecture

组件架构

Anatomy of a Material Component

Material组件构成

┌─────────────────────────────────────────────────────────┐
│                    BUTTON ANATOMY                       │
├─────────────────────────────────────────────────────────┤
│                                                         │
│    ┌─────────────────────────────────────────┐         │
│    │  [Icon]  Label Text                     │         │
│    └─────────────────────────────────────────┘         │
│    │         │                               │         │
│    │         │                               │         │
│    │    Leading Icon (optional)              │         │
│    │                                         │         │
│    │    Label text (required)           Container      │
│    │                                    (required)     │
│    │                                         │         │
│    └─────────────────────────────────────────┘         │
│                                                         │
│    States: Enabled, Disabled, Hovered,                 │
│            Focused, Pressed                            │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│                    BUTTON ANATOMY                       │
├─────────────────────────────────────────────────────────┤
│                                                         │
│    ┌─────────────────────────────────────────┐         │
│    │  [Icon]  Label Text                     │         │
│    └─────────────────────────────────────────┘         │
│    │         │                               │         │
│    │         │                               │         │
│    │    Leading Icon (optional)              │         │
│    │                                         │         │
│    │    Label text (required)           Container      │
│    │                                    (required)     │
│    │                                         │         │
│    └─────────────────────────────────────────┘         │
│                                                         │
│    States: Enabled, Disabled, Hovered,                 │
│            Focused, Pressed                            │
└─────────────────────────────────────────────────────────┘

Button Variants

按钮变体

tsx
// 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;
`;
tsx
// 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;
`;

State Layer System

状态层系统

css
/* 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;
}
css
/* 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 System

形状系统

Corner Styles

边角样式

Shape Scale (Material 3)
═══════════════════════════════════════════════════════════

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

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

Full (50%)
    ╭────╮
   │      │
   │      │
    ╰────╯
css
/* 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); }
Shape Scale (Material 3)
═══════════════════════════════════════════════════════════

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

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

Full (50%)
    ╭────╮
   │      │
   │      │
    ╰────╯
css
/* 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); }

Adaptive Layouts

自适应布局

Responsive Grid System

响应式网格系统

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
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

Layout Implementation

布局实现

tsx
// 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>
  );
}
tsx
// 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 Patterns

导航模式

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
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

Accessibility (A11y)

无障碍设计(A11y)

Touch Targets

触摸目标

css
/* 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;
}
css
/* 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;
}

Color Contrast

色彩对比度

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
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

Focus Indicators

焦点指示器

css
/* 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;
}
css
/* 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;
}

When Implementing

实施建议

Always

始终遵循

  • Use the design token system for colors, typography, and shape
  • Implement all interactive states (enabled, disabled, hover, focus, pressed)
  • Ensure 48dp minimum touch targets
  • Provide visible focus indicators
  • Use semantic color roles (primary, secondary, error) not raw values
  • Apply elevation system for spatial hierarchy
  • Include meaningful motion for state changes
  • 使用设计令牌系统管理颜色、排版和形状
  • 实现所有交互状态(启用、禁用、悬停、聚焦、按下)
  • 确保触摸目标最小尺寸为48x48dp
  • 提供清晰可见的焦点指示器
  • 使用语义化色彩角色(主色、辅助色、错误色)而非原始色值
  • 应用海拔高度系统构建空间层级
  • 为状态变化添加有意义的动效

Never

切勿执行

  • Hard-code color values instead of tokens
  • Ignore disabled states
  • Create custom colors outside the tonal palette
  • Skip focus states for keyboard users
  • Use elevation without purpose
  • Add motion that doesn't convey meaning
  • Violate touch target minimums
  • 硬编码颜色值而非使用令牌
  • 忽略禁用状态
  • 在色调调色板外创建自定义颜色
  • 为键盘用户跳过焦点状态
  • 无目的地使用海拔高度
  • 添加无意义的动效
  • 违反触摸目标最小尺寸要求

Prefer

优先选择

  • Filled buttons for primary actions, text buttons for tertiary
  • Tonal palette for consistent, harmonious colors
  • Standard easing for most transitions
  • Surface color variants over opacity for overlays
  • Component library over custom implementations
  • Adaptive layouts over fixed breakpoints
  • 主要操作使用填充按钮,次要操作使用文本按钮
  • 使用色调调色板确保色彩和谐一致
  • 大多数过渡使用标准缓动曲线
  • 使用表面颜色变体而非透明度实现叠加层
  • 使用组件库而非自定义实现
  • 使用自适应布局而非固定断点

Mental Model

思维模型

Material Design asks:
  1. Is it material? Does it behave like a physical surface with depth?
  2. Is it intentional? Does every element serve a clear purpose?
  3. Is it meaningful? Does motion communicate state and guide attention?
  4. Is it accessible? Can everyone use it regardless of ability?
  5. Is it adaptive? Does it work across all screen sizes and devices?
  6. Is it delightful? Does it create a positive emotional response?
Material Design提出以下问题:
  1. 它是材质吗? 它的行为是否像具有深度的物理表面?
  2. 它有目的性吗? 每个元素是否都有明确的用途?
  3. 它有意义吗? 动效是否能传达状态变化并引导注意力?
  4. 它易用吗? 无论能力如何,所有人都能使用它吗?
  5. 它自适应吗? 它能在所有屏幕尺寸和设备上正常工作吗?
  6. 它令人愉悦吗? 它能带来积极的情感反馈吗?

Signature Material Moves

Material Design标志性特性

  • Dynamic Color: Personalized themes from user content
  • Tonal Palettes: Harmonious color derived from single source
  • State Layers: Consistent interaction feedback via overlays
  • Container Transform: Seamless element-to-screen transitions
  • Elevation Hierarchy: Shadows that communicate importance
  • Adaptive Scaffolds: Navigation that transforms with screen size
  • Typography Scale: 15 styles for clear content hierarchy
  • Shape System: Corners that communicate component personality
  • 动态色彩:从用户内容生成个性化主题
  • 色调调色板:从单一源色衍生和谐色彩
  • 状态层:通过叠加层提供一致的交互反馈
  • 容器转换:无缝的元素到屏幕过渡
  • 海拔高度层级:通过阴影传达元素重要性
  • 自适应框架:随屏幕尺寸变化的导航模式
  • 排版层级:15种样式构建清晰的内容层级
  • 形状系统:通过边角样式传达组件特性