一键导入
frontend-design
Use when designing UI components, implementing accessibility, responsive layouts, or integrating with design systems.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when designing UI components, implementing accessibility, responsive layouts, or integrating with design systems.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when working with git operations including commits, branches, worktrees, and PRs. Covers the full git workflow from feature isolation to PR submission.
Complete PR submission pipeline with local sub-agent review before pushing, CI verification, and automated review integration. Always dispatches code-reviewer and code-simplifier for code changes, plus conditional reviewers (security, performance, dependency, accessibility, i18n, type-design, etc.) based on change type. Waits for CI with `gh pr checks --watch`. Integrates CodeRabbit and Greptile feedback.
GitHub CLI patterns for PR reviews, comments, and API operations. Use when working with gh api commands, especially for review threads and comments.
Use when following a written plan or task list. Checkpoint verification at each step. Triggers - execute plan, follow plan, implement plan, next step, continue, proceed.
Use for parallel branch development with workspace isolation.
Use when implementing analytics, event tracking, or setting up dashboards. Covers privacy-first tracking, event patterns, and common analytics tools.
| name | frontend-design |
| description | Use when designing UI components, implementing accessibility, responsive layouts, or integrating with design systems. |
UI/UX implementation patterns covering component design, accessibility requirements, responsive design, and design system integration. Focus on building consistent, accessible, and maintainable interfaces.
| Aspect | Key Principles |
|---|---|
| Components | Single responsibility, composable, typed props |
| Accessibility | Semantic HTML, ARIA, keyboard nav, focus |
| Responsive | Mobile-first, breakpoints, fluid sizing |
| Design System | Tokens, consistent spacing, reusable |
// Good: Single responsibility, typed props
interface ButtonProps {
variant: 'primary' | 'secondary' | 'ghost';
size: 'sm' | 'md' | 'lg';
isLoading?: boolean;
disabled?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export function Button({
variant,
size,
isLoading = false,
disabled = false,
children,
onClick,
}: ButtonProps) {
return (
<button
className={cn(
buttonBase,
variants[variant],
sizes[size],
isLoading && 'opacity-50 cursor-wait'
)}
disabled={disabled || isLoading}
onClick={onClick}
>
{isLoading ? <Spinner /> : children}
</button>
);
}
| Requirement | Implementation |
|---|---|
| Color Contrast | 4.5:1 text, 3:1 large text/graphics |
| Focus Visible | Clear focus ring on all interactive elements |
| Keyboard Nav | All functions accessible via keyboard |
| Alt Text | Descriptive alt for images |
| Form Labels | Every input has associated label |
| Error Messages | Clear, associated with field |
<!-- Good: Semantic structure -->
<header>
<nav aria-label="Main navigation">
<ul role="list">
<li><a href="/home">Home</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h1>Page Title</h1>
<section aria-labelledby="section-heading">
<h2 id="section-heading">Section</h2>
</section>
</article>
</main>
<footer>...</footer>
// Dialog/Modal
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-description"
>
<h2 id="dialog-title">Confirm Action</h2>
<p id="dialog-description">Are you sure?</p>
</div>
// Tab Panel
<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel-1">Tab 1</button>
<button role="tab" aria-selected="false" aria-controls="panel-2">Tab 2</button>
</div>
<div role="tabpanel" id="panel-1">Content 1</div>
// Live Region (announcements)
<div aria-live="polite" aria-atomic="true">
{statusMessage}
</div>
// Focus trap for modals
useEffect(() => {
const focusableElements = modalRef.current?.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusableElements?.[0];
const last = focusableElements?.[focusableElements.length - 1];
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last?.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first?.focus();
}
}
if (e.key === 'Escape') onClose();
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, []);
/* Tailwind default breakpoints */
/* sm: 640px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px */
/* Mobile-first approach */
.container {
padding: 1rem; /* Mobile default */
}
@media (min-width: 768px) {
.container {
padding: 2rem; /* Tablet+ */
}
}
@media (min-width: 1024px) {
.container {
padding: 4rem; /* Desktop */
}
}
<div className={cn(
// Mobile first
"flex flex-col gap-4 p-4",
// Tablet
"md:flex-row md:gap-6 md:p-6",
// Desktop
"lg:gap-8 lg:p-8"
)}>
// Responsive grid
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
// Hide/show by breakpoint
<nav className="hidden md:flex"> {/* Desktop nav */}
<button className="md:hidden"> {/* Mobile menu button */}
// Responsive typography
<h1 className="text-2xl md:text-4xl lg:text-5xl">
// Responsive spacing
<section className="py-8 md:py-12 lg:py-16">
// tokens.ts
export const tokens = {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
},
semantic: {
success: '#22c55e',
warning: '#f59e0b',
error: '#ef4444',
},
},
spacing: {
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
},
radius: {
sm: '0.25rem',
md: '0.5rem',
lg: '1rem',
full: '9999px',
},
};
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
primary: tokens.colors.primary,
success: tokens.colors.semantic.success,
},
spacing: tokens.spacing,
borderRadius: tokens.radius,
},
},
};
// All components follow same patterns
type Size = 'sm' | 'md' | 'lg';
type Variant = 'primary' | 'secondary' | 'ghost';
interface BaseProps {
size?: Size;
variant?: Variant;
className?: string;
}
// Button, Input, Card, etc. all use BaseProps
// Skeleton loader
<div className="animate-pulse">
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2" />
<div className="h-4 bg-gray-200 rounded w-1/2" />
</div>
// Spinner
<svg className="animate-spin h-5 w-5" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
<div role="alert" className="bg-red-50 border-l-4 border-red-500 p-4">
<div className="flex">
<AlertIcon className="text-red-500" />
<div className="ml-3">
<p className="text-red-700 font-medium">Error</p>
<p className="text-red-600 text-sm">{errorMessage}</p>
</div>
</div>
</div>
Never:
div with click handlers (use button)Always:
Related skills: react-patterns, nextjs-patterns Tools: Lighthouse, axe DevTools, WAVE