frontend-designer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFrontend Designer
前端设计师
A comprehensive skill for frontend designers and developers to build beautiful, accessible, and performant user interfaces with modern best practices.
这是一项面向前端设计师和开发者的综合技能,可借助现代最佳实践构建美观、可访问且高性能的用户界面。
What This Skill Does
此技能的作用
Helps frontend designers/developers with:
- Component Design & Development - Build reusable, accessible components
- Design Systems - Implement tokens, patterns, and documentation
- Responsive Design - Mobile-first, fluid layouts
- Accessibility (WCAG 2.1) - Inclusive design patterns
- Modern CSS - Flexbox, Grid, custom properties
- Performance Optimization - Fast, efficient frontends
- Framework Patterns - React, Vue, Svelte best practices
- Design-to-Code - Figma to production workflows
帮助前端设计师/开发者完成以下工作:
- 组件设计与开发 - 构建可复用、可访问的组件
- 设计系统 - 实现设计令牌、模式与文档
- 响应式设计 - 移动端优先的流式布局
- 可访问性(WCAG 2.1) - 包容性设计模式
- 现代CSS - Flexbox、Grid、自定义属性
- 性能优化 - 快速、高效的前端实现
- 框架模式 - React、Vue、Svelte最佳实践
- 设计转代码 - Figma到生产环境的工作流
Why This Skill Matters
此技能的重要性
Without systematic approach:
- Inconsistent component implementations
- Accessibility issues
- Poor responsive behavior
- Duplicate code and styles
- Hard to maintain
- Performance problems
With this skill:
- Consistent, reusable components
- WCAG AA compliant by default
- Mobile-first responsive design
- Design system aligned
- Maintainable codebase
- Fast, optimized delivery
缺乏系统化方法的问题:
- 组件实现不一致
- 可访问性问题
- 响应式表现不佳
- 代码与样式重复
- 维护难度大
- 性能问题
使用此技能的优势:
- 一致、可复用的组件
- 默认符合WCAG AA标准
- 移动端优先的响应式设计
- 与设计系统对齐
- 易于维护的代码库
- 快速、优化的交付
Core Principles
核心原则
1. Accessibility First
1. 可访问性优先
- WCAG 2.1 AA minimum
- Semantic HTML
- Keyboard navigation
- Screen reader support
- Focus management
- Color contrast
- 最低符合WCAG 2.1 AA标准
- 语义化HTML
- 键盘导航
- 屏幕阅读器支持
- 焦点管理
- 色彩对比度
2. Mobile-First Responsive
2. 移动端优先的响应式设计
- Start with mobile (320px)
- Progressive enhancement
- Fluid typography
- Flexible layouts
- Touch-friendly targets
- 从移动端(320px)开始
- 渐进式增强
- 流式排版
- 灵活布局
- 适合触摸的交互目标
3. Performance by Default
3. 默认高性能
- Minimal CSS/JS
- Lazy loading
- Optimized images
- Critical CSS
- Tree shaking
- 精简的CSS/JS
- 懒加载
- 优化的图片
- 关键CSS
- 摇树优化
4. Component-Driven
4. 组件驱动
- Atomic design methodology
- Reusable components
- Props-based customization
- Composition over inheritance
- 原子设计方法论
- 可复用组件
- 基于Props的自定义
- 组合优于继承
5. Design System Aligned
5. 与设计系统对齐
- Design tokens
- Consistent spacing
- Typography scale
- Color palette
- Component library
- 设计令牌
- 一致的间距
- 排版比例
- 调色板
- 组件库
Component Patterns
组件模式
Button Component
按钮组件
Accessible, flexible button pattern:
tsx
// React example
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
loading?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
disabled = false,
loading = false,
children,
onClick,
...props
}) => {
return (
<button
className={`btn btn--${variant} btn--${size}`}
disabled={disabled || loading}
onClick={onClick}
aria-busy={loading}
{...props}
>
{loading ? <Spinner /> : children}
</button>
);
};CSS (with design tokens):
css
.btn {
/* Base styles */
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
font-family: var(--font-sans);
font-weight: 600;
text-decoration: none;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.2s ease;
/* Accessibility */
min-height: 44px; /* WCAG touch target */
&:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
/* Variants */
.btn--primary {
background: var(--color-primary);
color: var(--color-on-primary);
&:hover:not(:disabled) {
background: var(--color-primary-hover);
}
}
.btn--secondary {
background: var(--color-secondary);
color: var(--color-on-secondary);
}
.btn--ghost {
background: transparent;
color: var(--color-primary);
border: 1px solid currentColor;
}
/* Sizes */
.btn--sm {
padding: var(--space-2) var(--space-3);
font-size: var(--text-sm);
}
.btn--md {
padding: var(--space-3) var(--space-4);
font-size: var(--text-base);
}
.btn--lg {
padding: var(--space-4) var(--space-6);
font-size: var(--text-lg);
}可访问、灵活的按钮模式:
tsx
// React example
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
loading?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
disabled = false,
loading = false,
children,
onClick,
...props
}) => {
return (
<button
className={`btn btn--${variant} btn--${size}`}
disabled={disabled || loading}
onClick={onClick}
aria-busy={loading}
{...props}
>
{loading ? <Spinner /> : children}
</button>
);
};CSS (with design tokens):
css
.btn {
/* Base styles */
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
font-family: var(--font-sans);
font-weight: 600;
text-decoration: none;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.2s ease;
/* Accessibility */
min-height: 44px; /* WCAG touch target */
&:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
/* Variants */
.btn--primary {
background: var(--color-primary);
color: var(--color-on-primary);
&:hover:not(:disabled) {
background: var(--color-primary-hover);
}
}
.btn--secondary {
background: var(--color-secondary);
color: var(--color-on-secondary);
}
.btn--ghost {
background: transparent;
color: var(--color-primary);
border: 1px solid currentColor;
}
/* Sizes */
.btn--sm {
padding: var(--space-2) var(--space-3);
font-size: var(--text-sm);
}
.btn--md {
padding: var(--space-3) var(--space-4);
font-size: var(--text-base);
}
.btn--lg {
padding: var(--space-4) var(--space-6);
font-size: var(--text-lg);
}Card Component
卡片组件
Flexible, accessible card:
tsx
interface CardProps {
variant?: 'elevated' | 'outlined' | 'filled';
padding?: 'none' | 'sm' | 'md' | 'lg';
interactive?: boolean;
children: React.ReactNode;
}
export const Card: React.FC<CardProps> = ({
variant = 'elevated',
padding = 'md',
interactive = false,
children,
}) => {
const Component = interactive ? 'button' : 'div';
return (
<Component
className={`
card
card--${variant}
card--padding-${padding}
${interactive ? 'card--interactive' : ''}
`}
{...(interactive && { role: 'button', tabIndex: 0 })}
>
{children}
</Component>
);
};CSS:
css
.card {
border-radius: var(--radius-lg);
background: var(--color-surface);
}
.card--elevated {
box-shadow: var(--shadow-md);
}
.card--outlined {
border: 1px solid var(--color-border);
}
.card--filled {
background: var(--color-surface-variant);
}
.card--interactive {
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
&:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
&:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
}
}
.card--padding-sm { padding: var(--space-3); }
.card--padding-md { padding: var(--space-4); }
.card--padding-lg { padding: var(--space-6); }灵活、可访问的卡片:
tsx
interface CardProps {
variant?: 'elevated' | 'outlined' | 'filled';
padding?: 'none' | 'sm' | 'md' | 'lg';
interactive?: boolean;
children: React.ReactNode;
}
export const Card: React.FC<CardProps> = ({
variant = 'elevated',
padding = 'md',
interactive = false,
children,
}) => {
const Component = interactive ? 'button' : 'div';
return (
<Component
className={`
card
card--${variant}
card--padding-${padding}
${interactive ? 'card--interactive' : ''}
`}
{...(interactive && { role: 'button', tabIndex: 0 })}
>
{children}
</Component>
);
};CSS:
css
.card {
border-radius: var(--radius-lg);
background: var(--color-surface);
}
.card--elevated {
box-shadow: var(--shadow-md);
}
.card--outlined {
border: 1px solid var(--color-border);
}
.card--filled {
background: var(--color-surface-variant);
}
.card--interactive {
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
&:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
&:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
}
}
.card--padding-sm { padding: var(--space-3); }
.card--padding-md { padding: var(--space-4); }
.card--padding-lg { padding: var(--space-6); }Form Input Component
表单输入组件
Accessible form input:
tsx
interface InputProps {
label: string;
error?: string;
hint?: string;
required?: boolean;
type?: 'text' | 'email' | 'password' | 'number';
}
export const Input: React.FC<InputProps> = ({
label,
error,
hint,
required = false,
type = 'text',
...props
}) => {
const id = useId();
const hintId = `${id}-hint`;
const errorId = `${id}-error`;
return (
<div className="input-wrapper">
<label htmlFor={id} className="input-label">
{label}
{required && <span aria-label="required">*</span>}
</label>
{hint && (
<p id={hintId} className="input-hint">
{hint}
</p>
)}
<input
id={id}
type={type}
className={`input ${error ? 'input--error' : ''}`}
aria-required={required}
aria-invalid={!!error}
aria-describedby={error ? errorId : hint ? hintId : undefined}
{...props}
/>
{error && (
<p id={errorId} className="input-error" role="alert">
{error}
</p>
)}
</div>
);
};可访问的表单输入:
tsx
interface InputProps {
label: string;
error?: string;
hint?: string;
required?: boolean;
type?: 'text' | 'email' | 'password' | 'number';
}
export const Input: React.FC<InputProps> = ({
label,
error,
hint,
required = false,
type = 'text',
...props
}) => {
const id = useId();
const hintId = `${id}-hint`;
const errorId = `${id}-error`;
return (
<div className="input-wrapper">
<label htmlFor={id} className="input-label">
{label}
{required && <span aria-label="required">*</span>}
</label>
{hint && (
<p id={hintId} className="input-hint">
{hint}
</p>
)}
<input
id={id}
type={type}
className={`input ${error ? 'input--error' : ''}`}
aria-required={required}
aria-invalid={!!error}
aria-describedby={error ? errorId : hint ? hintId : undefined}
{...props}
/>
{error && (
<p id={errorId} className="input-error" role="alert">
{error}
</p>
)}
</div>
);
};Design Tokens
设计令牌
CSS Custom Properties for design system:
css
:root {
/* Colors - Primary */
--color-primary: #0066FF;
--color-primary-hover: #0052CC;
--color-on-primary: #FFFFFF;
/* Colors - Surface */
--color-surface: #FFFFFF;
--color-surface-variant: #F5F5F5;
--color-on-surface: #1A1A1A;
/* Colors - Borders */
--color-border: #E0E0E0;
--color-border-hover: #BDBDBD;
/* Colors - Semantic */
--color-error: #D32F2F;
--color-success: #388E3C;
--color-warning: #F57C00;
--color-info: #1976D2;
/* Spacing Scale (8px base) */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.5rem; /* 24px */
--space-6: 2rem; /* 32px */
--space-8: 3rem; /* 48px */
--space-10: 4rem; /* 64px */
/* Typography Scale */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
/* Font Families */
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono: "SF Mono", Monaco, "Cascadia Code", monospace;
/* Line Heights */
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.75;
/* Border Radius */
--radius-sm: 0.25rem; /* 4px */
--radius-md: 0.5rem; /* 8px */
--radius-lg: 1rem; /* 16px */
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
/* Focus Ring */
--color-focus: #0066FF;
/* Transitions */
--transition-fast: 150ms ease;
--transition-base: 200ms ease;
--transition-slow: 300ms ease;
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {
--color-surface: #1A1A1A;
--color-surface-variant: #2A2A2A;
--color-on-surface: #FFFFFF;
--color-border: #3A3A3A;
}
}用于设计系统的CSS自定义属性:
css
:root {
/* Colors - Primary */
--color-primary: #0066FF;
--color-primary-hover: #0052CC;
--color-on-primary: #FFFFFF;
/* Colors - Surface */
--color-surface: #FFFFFF;
--color-surface-variant: #F5F5F5;
--color-on-surface: #1A1A1A;
/* Colors - Borders */
--color-border: #E0E0E0;
--color-border-hover: #BDBDBD;
/* Colors - Semantic */
--color-error: #D32F2F;
--color-success: #388E3C;
--color-warning: #F57C00;
--color-info: #1976D2;
/* Spacing Scale (8px base) */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.5rem; /* 24px */
--space-6: 2rem; /* 32px */
--space-8: 3rem; /* 48px */
--space-10: 4rem; /* 64px */
/* Typography Scale */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
/* Font Families */
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono: "SF Mono", Monaco, "Cascadia Code", monospace;
/* Line Heights */
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.75;
/* Border Radius */
--radius-sm: 0.25rem; /* 4px */
--radius-md: 0.5rem; /* 8px */
--radius-lg: 1rem; /* 16px */
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
/* Focus Ring */
--color-focus: #0066FF;
/* Transitions */
--transition-fast: 150ms ease;
--transition-base: 200ms ease;
--transition-slow: 300ms ease;
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {
--color-surface: #1A1A1A;
--color-surface-variant: #2A2A2A;
--color-on-surface: #FFFFFF;
--color-border: #3A3A3A;
}
}Responsive Design Patterns
响应式设计模式
Mobile-First Breakpoints
移动端优先断点
css
/* Mobile-first approach */
.container {
padding: var(--space-4);
/* Tablet: 768px and up */
@media (min-width: 48rem) {
padding: var(--space-6);
}
/* Desktop: 1024px and up */
@media (min-width: 64rem) {
padding: var(--space-8);
max-width: 1200px;
margin: 0 auto;
}
}css
/* Mobile-first approach */
.container {
padding: var(--space-4);
/* Tablet: 768px and up */
@media (min-width: 48rem) {
padding: var(--space-6);
}
/* Desktop: 1024px and up */
@media (min-width: 64rem) {
padding: var(--space-8);
max-width: 1200px;
margin: 0 auto;
}
}Fluid Typography
流式排版
css
/* Responsive typography */
h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: var(--leading-tight);
}
h2 {
font-size: clamp(1.5rem, 4vw, 2.5rem);
}
p {
font-size: clamp(1rem, 2vw, 1.125rem);
line-height: var(--leading-normal);
}css
/* Responsive typography */
h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: var(--leading-tight);
}
h2 {
font-size: clamp(1.5rem, 4vw, 2.5rem);
}
p {
font-size: clamp(1rem, 2vw, 1.125rem);
line-height: var(--leading-normal);
}Grid Layouts
网格布局
css
/* Responsive grid */
.grid {
display: grid;
gap: var(--space-4);
/* Auto-fit columns (min 280px) */
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
/* 12-column grid system */
.grid-12 {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--space-4);
}
.col-span-4 {
grid-column: span 4;
}
/* Stack on mobile */
@media (max-width: 48rem) {
.col-span-4 {
grid-column: span 12;
}
}css
/* Responsive grid */
.grid {
display: grid;
gap: var(--space-4);
/* Auto-fit columns (min 280px) */
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
/* 12-column grid system */
.grid-12 {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--space-4);
}
.col-span-4 {
grid-column: span 4;
}
/* Stack on mobile */
@media (max-width: 48rem) {
.col-span-4 {
grid-column: span 12;
}
}Accessibility Patterns
可访问性模式
Skip Links
跳转链接
tsx
export const SkipLink = () => (
<a href="#main-content" className="skip-link">
Skip to main content
</a>
);css
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--color-primary);
color: var(--color-on-primary);
padding: var(--space-2) var(--space-4);
text-decoration: none;
z-index: 100;
&:focus {
top: 0;
}
}tsx
export const SkipLink = () => (
<a href="#main-content" className="skip-link">
跳转到主内容
</a>
);css
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--color-primary);
color: var(--color-on-primary);
padding: var(--space-2) var(--space-4);
text-decoration: none;
z-index: 100;
&:focus {
top: 0;
}
}Focus Management
焦点管理
tsx
// Modal with focus trap
export const Modal = ({ isOpen, onClose, children }) => {
const modalRef = useRef(null);
useEffect(() => {
if (isOpen) {
// Save currently focused element
const previouslyFocused = document.activeElement;
// Focus first focusable element in modal
const firstFocusable = modalRef.current?.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
firstFocusable?.focus();
// Restore focus on close
return () => previouslyFocused?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
className="modal-overlay"
>
<div className="modal">
{children}
</div>
</div>
);
};tsx
// Modal with focus trap
export const Modal = ({ isOpen, onClose, children }) => {
const modalRef = useRef(null);
useEffect(() => {
if (isOpen) {
// Save currently focused element
const previouslyFocused = document.activeElement;
// Focus first focusable element in modal
const firstFocusable = modalRef.current?.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
firstFocusable?.focus();
// Restore focus on close
return () => previouslyFocused?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
className="modal-overlay"
>
<div className="modal">
{children}
</div>
</div>
);
};ARIA Labels
ARIA标签
tsx
// Icon button with accessible label
export const IconButton = ({ icon, label, ...props }) => (
<button
aria-label={label}
className="icon-button"
{...props}
>
<span aria-hidden="true">{icon}</span>
</button>
);
// Loading state
export const LoadingButton = ({ loading, children, ...props }) => (
<button
aria-busy={loading}
aria-live="polite"
disabled={loading}
{...props}
>
{loading ? 'Loading...' : children}
</button>
);tsx
// Icon button with accessible label
export const IconButton = ({ icon, label, ...props }) => (
<button
aria-label={label}
className="icon-button"
{...props}
>
<span aria-hidden="true">{icon}</span>
</button>
);
// Loading state
export const LoadingButton = ({ loading, children, ...props }) => (
<button
aria-busy={loading}
aria-live="polite"
disabled={loading}
{...props}
>
{loading ? '加载中...' : children}
</button>
);Performance Optimization
性能优化
Critical CSS
关键CSS
html
<!-- Inline critical CSS -->
<style>
/* Above-the-fold styles */
body { margin: 0; font-family: var(--font-sans); }
.header { /* critical header styles */ }
.hero { /* critical hero styles */ }
</style>
<!-- Load full stylesheet async -->
<link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">html
<!-- Inline critical CSS -->
<style>
/* Above-the-fold styles */
body { margin: 0; font-family: var(--font-sans); }
.header { /* critical header styles */ }
.hero { /* critical hero styles */ }
</style>
<!-- Load full stylesheet async -->
<link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">Lazy Loading Images
图片懒加载
tsx
export const LazyImage = ({ src, alt, ...props }) => (
<img
src={src}
alt={alt}
loading="lazy"
decoding="async"
{...props}
/>
);tsx
export const LazyImage = ({ src, alt, ...props }) => (
<img
src={src}
alt={alt}
loading="lazy"
decoding="async"
{...props}
/>
);Code Splitting
代码分割
tsx
// React lazy loading
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
);
}tsx
// React lazy loading
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
);
}Modern CSS Techniques
现代CSS技术
Container Queries
容器查询
css
.card {
container-type: inline-size;
}
.card-content {
display: flex;
flex-direction: column;
/* Switch to row layout when container > 400px */
@container (min-width: 400px) {
flex-direction: row;
}
}css
.card {
container-type: inline-size;
}
.card-content {
display: flex;
flex-direction: column;
/* Switch to row layout when container > 400px */
@container (min-width: 400px) {
flex-direction: row;
}
}CSS Grid Auto-Fit
CSS Grid自动适配
css
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--space-4);
}css
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--space-4);
}Custom Properties for Theming
用于主题的自定义属性
css
/* Light theme (default) */
:root {
--bg: #ffffff;
--text: #000000;
}
/* Dark theme */
[data-theme="dark"] {
--bg: #000000;
--text: #ffffff;
}
body {
background: var(--bg);
color: var(--text);
}css
/* Light theme (default) */
:root {
--bg: #ffffff;
--text: #000000;
}
/* Dark theme */
[data-theme="dark"] {
--bg: #000000;
--text: #ffffff;
}
body {
background: var(--bg);
color: var(--text);
}Component Library Structure
组件库结构
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.css
│ │ ├── Button.test.tsx
│ │ └── Button.stories.tsx
│ ├── Card/
│ ├── Input/
│ └── index.ts
├── tokens/
│ ├── colors.css
│ ├── spacing.css
│ ├── typography.css
│ └── index.css
├── utils/
│ ├── a11y.ts
│ └── responsive.ts
└── index.tssrc/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.css
│ │ ├── Button.test.tsx
│ │ └── Button.stories.tsx
│ ├── Card/
│ ├── Input/
│ └── index.ts
├── tokens/
│ ├── colors.css
│ ├── spacing.css
│ ├── typography.css
│ └── index.css
├── utils/
│ ├── a11y.ts
│ └── responsive.ts
└── index.tsUsing This Skill
如何使用此技能
Generate Component
生成组件
bash
./scripts/generate_component.sh ButtonCreates component with:
- TypeScript/JSX file
- CSS module
- Test file
- Storybook story
- Accessibility checks
bash
./scripts/generate_component.sh Button创建包含以下内容的组件:
- TypeScript/JSX文件
- CSS模块
- 测试文件
- Storybook故事
- 可访问性检查
Design System Setup
设计系统搭建
bash
./scripts/setup_design_system.shCreates:
- Design tokens (CSS custom properties)
- Base styles
- Component templates
- Documentation structure
bash
./scripts/setup_design_system.sh创建以下内容:
- 设计令牌(CSS自定义属性)
- 基础样式
- 组件模板
- 文档结构
Accessibility Audit
可访问性审计
bash
./scripts/audit_accessibility.shChecks:
- Color contrast ratios
- Keyboard navigation
- ARIA attributes
- Semantic HTML
- Focus management
bash
./scripts/audit_accessibility.sh检查以下内容:
- 色彩对比度
- 键盘导航
- ARIA属性
- 语义化HTML
- 焦点管理
Best Practices
最佳实践
Component Design
组件设计
✅ DO:
- Use semantic HTML
- Make components keyboard accessible
- Provide ARIA labels
- Support both light and dark modes
- Make touch targets 44x44px minimum
- Use proper heading hierarchy
- Handle loading and error states
❌ DON'T:
- Use divs for buttons
- Forget focus styles
- Hard-code colors
- Ignore mobile viewports
- Skip alt text on images
- Create inaccessible modals
✅ 建议:
- 使用语义化HTML
- 确保组件支持键盘访问
- 提供ARIA标签
- 支持亮色与暗色模式
- 触摸目标最小为44x44px
- 使用正确的标题层级
- 处理加载与错误状态
❌ 避免:
- 使用div替代按钮
- 忽略焦点样式
- 硬编码颜色
- 忽略移动端视口
- 图片缺少alt文本
- 创建不可访问的模态框
CSS Best Practices
CSS最佳实践
✅ DO:
- Use design tokens
- Mobile-first responsive
- BEM or CSS modules for naming
- Logical properties (inline-start vs left)
- Modern layout (flexbox, grid)
❌ DON'T:
- Use !important
- Deep nesting (> 3 levels)
- Fixed pixel values everywhere
- Browser-specific hacks
- Inline styles
✅ 建议:
- 使用设计令牌
- 移动端优先的响应式设计
- 使用BEM或CSS模块命名
- 使用逻辑属性(inline-start替代left)
- 使用现代布局(flexbox、grid)
❌ 避免:
- 使用!important
- 过深的嵌套(超过3层)
- 到处使用固定像素值
- 浏览器特定的hack
- 内联样式
Performance
性能
✅ DO:
- Lazy load images
- Code split routes
- Minimize CSS
- Use system fonts
- Optimize images (WebP)
- Critical CSS inline
❌ DON'T:
- Load unused CSS
- Large JavaScript bundles
- Unoptimized images
- Blocking resources
- Too many web fonts
✅ 建议:
- 懒加载图片
- 路由代码分割
- 精简CSS
- 使用系统字体
- 优化图片(WebP格式)
- 内联关键CSS
❌ 避免:
- 加载未使用的CSS
- 过大的JavaScript包
- 未优化的图片
- 阻塞性资源
- 过多的Web字体
Framework-Specific Patterns
框架特定模式
React
React
tsx
// Composition pattern
export const Card = ({ children }) => (
<div className="card">{children}</div>
);
export const CardHeader = ({ children }) => (
<div className="card-header">{children}</div>
);
// Usage
<Card>
<CardHeader>Title</CardHeader>
<CardBody>Content</CardBody>
</Card>tsx
// Composition pattern
export const Card = ({ children }) => (
<div className="card">{children}</div>
);
export const CardHeader = ({ children }) => (
<div className="card-header">{children}</div>
);
// 用法
<Card>
<CardHeader>标题</CardHeader>
<CardBody>内容</CardBody>
</Card>Vue
Vue
vue
<!-- Composable component -->
<template>
<div :class="cardClasses">
<slot />
</div>
</template>
<script setup>
const props = defineProps({
variant: String,
padding: String
});
const cardClasses = computed(() => [
'card',
`card--${props.variant}`,
`card--padding-${props.padding}`
]);
</script>vue
<!-- Composable component -->
<template>
<div :class="cardClasses">
<slot />
</div>
</template>
<script setup>
const props = defineProps({
variant: String,
padding: String
});
const cardClasses = computed(() => [
'card',
`card--${props.variant}`,
`card--padding-${props.padding}`
]);
</script>Resources
资源
All reference materials included:
- Design token systems
- Accessibility checklist (WCAG 2.1)
- Responsive design patterns
- Component library templates
- Performance optimization guide
包含所有参考资料:
- 设计令牌系统
- 可访问性检查清单(WCAG 2.1)
- 响应式设计模式
- 组件库模板
- 性能优化指南
Summary
总结
This skill provides:
- Accessible components - WCAG AA by default
- Responsive design - Mobile-first approach
- Design systems - Token-based consistency
- Modern CSS - Flexbox, Grid, custom properties
- Performance - Optimized delivery
- Best practices - Production-ready patterns
Use this skill to build beautiful, accessible, performant frontends.
"Good design is accessible design."
此技能提供:
- 可访问的组件 - 默认符合WCAG AA标准
- 响应式设计 - 移动端优先方法
- 设计系统 - 基于令牌的一致性
- 现代CSS - Flexbox、Grid、自定义属性
- 性能优化 - 优化的交付
- 最佳实践 - 生产就绪的模式
使用此技能构建美观、可访问、高性能的前端。
"优秀的设计是可访问的设计。"