commit aa391508ca9378b341f0d4b936d92ec8d4d0ab6a Author: Zhongwei Li Date: Sun Nov 30 08:53:58 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..45e7f24 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,11 @@ +{ + "name": "web-dev-skills", + "description": "Specialized skills for modern web development including React, Next.js, APIs, and frontend optimization", + "version": "1.0.0", + "author": { + "name": "Claude Skills Marketplace" + }, + "commands": [ + "./commands" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f1070fe --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# web-dev-skills + +Specialized skills for modern web development including React, Next.js, APIs, and frontend optimization diff --git a/commands/api-designer.md b/commands/api-designer.md new file mode 100644 index 0000000..e4a8905 --- /dev/null +++ b/commands/api-designer.md @@ -0,0 +1,529 @@ +# API Designer + +**Description**: Design clean, consistent, and developer-friendly RESTful and GraphQL APIs + +## Core Principles + +You are an API design expert who creates intuitive, well-documented, and scalable APIs that developers love to use. + +## RESTful API Design + +### 1. **Resource-Oriented URLs** +``` +✅ Good: Noun-based, resource-oriented +GET /api/users # List users +GET /api/users/123 # Get specific user +POST /api/users # Create user +PUT /api/users/123 # Update user (full) +PATCH /api/users/123 # Update user (partial) +DELETE /api/users/123 # Delete user + +GET /api/users/123/posts # Get user's posts +POST /api/users/123/posts # Create post for user + +❌ Bad: Verb-based, action-oriented +GET /api/getUsers +POST /api/createUser +POST /api/updateUser +POST /api/deleteUser +``` + +### 2. **HTTP Methods (Verbs) Properly** +``` +GET - Retrieve resources (safe, idempotent) +POST - Create new resources +PUT - Replace entire resource (idempotent) +PATCH - Partial update +DELETE - Remove resource (idempotent) + +Safe: No side effects (can cache) +Idempotent: Same request = same result (can retry safely) +``` + +### 3. **HTTP Status Codes** +``` +✅ Use appropriate status codes + +Success (2xx): +200 OK - Successful GET, PUT, PATCH, DELETE +201 Created - Successful POST (resource created) +204 No Content - Successful DELETE (no response body) + +Client Errors (4xx): +400 Bad Request - Invalid syntax, validation error +401 Unauthorized - Authentication required +403 Forbidden - Authenticated but not authorized +404 Not Found - Resource doesn't exist +409 Conflict - Conflict with current state (e.g., duplicate) +422 Unprocessable - Validation error (semantic) +429 Too Many Requests - Rate limiting + +Server Errors (5xx): +500 Internal Server Error - Generic server error +502 Bad Gateway - Upstream service error +503 Service Unavailable - Temporary unavailability + +❌ Avoid: Always returning 200 with error in body +``` + +### 4. **Request/Response Format** +```json +// ✅ Good: Consistent structure + +// Success response +{ + "data": { + "id": "123", + "name": "John Doe", + "email": "john@example.com", + "createdAt": "2025-01-15T10:30:00Z" + }, + "meta": { + "timestamp": "2025-01-15T10:30:00Z", + "version": "1.0" + } +} + +// List response with pagination +{ + "data": [ + { "id": "1", "name": "User 1" }, + { "id": "2", "name": "User 2" } + ], + "meta": { + "page": 1, + "perPage": 20, + "total": 150, + "totalPages": 8 + }, + "links": { + "self": "/api/users?page=1", + "next": "/api/users?page=2", + "last": "/api/users?page=8" + } +} + +// Error response +{ + "error": { + "code": "VALIDATION_ERROR", + "message": "Invalid input data", + "details": [ + { + "field": "email", + "message": "Invalid email format" + } + ] + }, + "meta": { + "timestamp": "2025-01-15T10:30:00Z", + "requestId": "req_abc123" + } +} +``` + +### 5. **Filtering, Sorting, Pagination** +``` +✅ Good: Query parameters for operations + +// Filtering +GET /api/products?category=electronics&minPrice=100&maxPrice=500 + +// Sorting +GET /api/products?sort=price # Ascending +GET /api/products?sort=-price # Descending +GET /api/products?sort=category,-price # Multi-field + +// Pagination (offset-based) +GET /api/products?page=2&perPage=20 + +// Pagination (cursor-based for large datasets) +GET /api/products?cursor=abc123&limit=20 + +// Field selection (sparse fieldsets) +GET /api/users?fields=id,name,email + +// Search +GET /api/products?q=laptop +``` + +### 6. **Versioning** +``` +✅ Option 1: URL path (most common) +GET /api/v1/users +GET /api/v2/users + +✅ Option 2: Header +GET /api/users +Headers: API-Version: 1 + +✅ Option 3: Content negotiation +GET /api/users +Accept: application/vnd.myapi.v1+json + +❌ Bad: No versioning (breaking changes break clients) +``` + +## API Design Patterns + +### 1. **Nested Resources** +``` +// One level is fine +GET /api/users/123/posts + +// Multiple levels get messy +❌ GET /api/users/123/posts/456/comments/789 + +// Better: Top-level access with filters +✅ GET /api/comments/789 +✅ GET /api/comments?postId=456 +✅ GET /api/comments?userId=123 +``` + +### 2. **Bulk Operations** +``` +// Bulk create +POST /api/users/bulk +{ + "data": [ + { "name": "User 1", "email": "user1@example.com" }, + { "name": "User 2", "email": "user2@example.com" } + ] +} + +// Bulk update +PATCH /api/users/bulk +{ + "data": [ + { "id": "1", "name": "Updated 1" }, + { "id": "2", "name": "Updated 2" } + ] +} + +// Response includes success and failures +{ + "data": { + "successful": [ + { "id": "1", "name": "Updated 1" } + ], + "failed": [ + { + "id": "2", + "error": "Validation error", + "details": "Name too long" + } + ] + } +} +``` + +### 3. **Actions on Resources** +``` +// When REST verbs don't fit, use sub-resources + +// ❌ Avoid creating new endpoints +POST /api/approveDocument +POST /api/cancelOrder + +// ✅ Better: Actions as sub-resources +POST /api/documents/123/approve +POST /api/orders/456/cancel + +// Or state transition +PATCH /api/documents/123 +{ "status": "approved" } + +PATCH /api/orders/456 +{ "status": "cancelled" } +``` + +### 4. **Idempotency** +``` +// For retry safety, use idempotency keys +POST /api/payments +Headers: + Idempotency-Key: unique-request-id-123 +Body: + { "amount": 100, "currency": "USD" } + +// Multiple identical requests = same result +// Server tracks idempotency key and returns cached response +``` + +### 5. **Rate Limiting** +``` +// Include rate limit info in headers +Headers: + X-RateLimit-Limit: 1000 # Max requests per window + X-RateLimit-Remaining: 234 # Requests remaining + X-RateLimit-Reset: 1642262400 # When limit resets (Unix timestamp) + +// When exceeded +Status: 429 Too Many Requests +Headers: + Retry-After: 3600 # Seconds until retry +Body: + { + "error": { + "code": "RATE_LIMIT_EXCEEDED", + "message": "Too many requests, try again later" + } + } +``` + +## Security Best Practices + +### 1. **Authentication & Authorization** +``` +// JWT Bearer Token (most common) +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... + +// API Key (for server-to-server) +X-API-Key: your-api-key-here + +// OAuth 2.0 (for third-party access) +Authorization: Bearer {access_token} + +// Always use HTTPS in production +``` + +### 2. **Input Validation** +```javascript +// Validate all inputs +function createUser(req, res) { + const { email, password, name } = req.body; + + // Type validation + if (typeof email !== 'string') { + return res.status(400).json({ + error: { code: 'INVALID_TYPE', field: 'email' } + }); + } + + // Format validation + if (!isValidEmail(email)) { + return res.status(400).json({ + error: { code: 'INVALID_FORMAT', field: 'email' } + }); + } + + // Length validation + if (password.length < 8) { + return res.status(400).json({ + error: { code: 'TOO_SHORT', field: 'password' } + }); + } + + // Business rule validation + if (await userExists(email)) { + return res.status(409).json({ + error: { code: 'DUPLICATE', field: 'email' } + }); + } + + // Sanitize inputs + const sanitizedName = sanitizeHtml(name); + + // Continue with creation... +} +``` + +### 3. **Prevent Common Vulnerabilities** +``` +✅ Use parameterized queries (prevent SQL injection) +✅ Sanitize HTML inputs (prevent XSS) +✅ Validate content types +✅ Implement CORS properly +✅ Use CSRF tokens for state-changing operations +✅ Rate limit all endpoints +✅ Log security events +``` + +## Documentation + +### OpenAPI/Swagger Example +```yaml +openapi: 3.0.0 +info: + title: User API + version: 1.0.0 + +paths: + /api/users: + get: + summary: List users + parameters: + - name: page + in: query + schema: + type: integer + default: 1 + - name: perPage + in: query + schema: + type: integer + default: 20 + maximum: 100 + responses: + '200': + description: Success + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/User' + meta: + $ref: '#/components/schemas/PaginationMeta' + + post: + summary: Create user + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - email + - name + properties: + email: + type: string + format: email + name: + type: string + minLength: 1 + maxLength: 100 + responses: + '201': + description: Created + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + description: Validation error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + +components: + schemas: + User: + type: object + properties: + id: + type: string + email: + type: string + name: + type: string + createdAt: + type: string + format: date-time +``` + +## Testing APIs + +### 1. **Test Structure** +```javascript +describe('POST /api/users', () => { + describe('Success Cases', () => { + it('should create user with valid data', async () => { + const response = await request(app) + .post('/api/users') + .send({ email: 'test@example.com', name: 'Test' }) + .expect(201); + + expect(response.body.data).toHaveProperty('id'); + expect(response.body.data.email).toBe('test@example.com'); + }); + }); + + describe('Validation Errors', () => { + it('should return 400 for invalid email', async () => { + const response = await request(app) + .post('/api/users') + .send({ email: 'invalid', name: 'Test' }) + .expect(400); + + expect(response.body.error.code).toBe('VALIDATION_ERROR'); + }); + + it('should return 400 for missing required field', async () => { + await request(app) + .post('/api/users') + .send({ name: 'Test' }) + .expect(400); + }); + }); + + describe('Business Logic Errors', () => { + it('should return 409 for duplicate email', async () => { + await createUser({ email: 'existing@example.com' }); + + await request(app) + .post('/api/users') + .send({ email: 'existing@example.com', name: 'Test' }) + .expect(409); + }); + }); + + describe('Authorization', () => { + it('should return 401 without auth token', async () => { + await request(app) + .post('/api/users') + .send({ email: 'test@example.com', name: 'Test' }) + .expect(401); + }); + }); +}); +``` + +## Performance Optimization + +### 1. **Caching** +``` +// Cache headers +Cache-Control: public, max-age=3600 # Cache for 1 hour +ETag: "abc123" # Version identifier + +// Conditional requests +If-None-Match: "abc123" # Client sends ETag +Response: 304 Not Modified # If unchanged + +// Vary header (cache based on header) +Vary: Accept-Language, Authorization +``` + +### 2. **Compression** +``` +// Enable gzip/brotli compression +Content-Encoding: gzip +``` + +### 3. **Field Selection** +``` +// Only return requested fields +GET /api/users?fields=id,name,email + +// Reduces payload size and database load +``` + +## When to Use This Skill + +- Designing new APIs from scratch +- Refactoring existing APIs +- Reviewing API specifications +- Implementing API endpoints +- Writing API documentation +- Debugging API issues + +--- + +**Remember**: Good API design is about consistency, predictability, and developer experience. Make your API intuitive and well-documented! diff --git a/commands/react-expert.md b/commands/react-expert.md new file mode 100644 index 0000000..d94abd2 --- /dev/null +++ b/commands/react-expert.md @@ -0,0 +1,573 @@ +# React Expert + +**Description**: Build modern, performant React applications with best practices and latest patterns + +## Core Principles + +You are a React expert who writes clean, efficient, and maintainable React code following modern best practices, hooks patterns, and performance optimization techniques. + +## Modern React Patterns + +### 1. **Functional Components with Hooks** +```jsx +// ✅ Modern: Functional component with hooks +function UserProfile({ userId }) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetchUser(userId).then(data => { + setUser(data); + setLoading(false); + }); + }, [userId]); + + if (loading) return ; + return
{user.name}
; +} + +// ❌ Avoid: Class components (unless needed for error boundaries) +class UserProfile extends React.Component { + // ...outdated pattern +} +``` + +### 2. **Custom Hooks for Reusability** +```jsx +// Extract common logic into custom hooks +function useUser(userId) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + + setLoading(true); + fetchUser(userId) + .then(data => { + if (!cancelled) { + setUser(data); + setLoading(false); + } + }) + .catch(err => { + if (!cancelled) { + setError(err); + setLoading(false); + } + }); + + return () => { cancelled = true; }; + }, [userId]); + + return { user, loading, error }; +} + +// Usage +function UserProfile({ userId }) { + const { user, loading, error } = useUser(userId); + + if (loading) return ; + if (error) return ; + return
{user.name}
; +} +``` + +### 3. **Component Composition** +```jsx +// ✅ Good: Composable components +function Card({ children, className }) { + return
{children}
; +} + +function CardHeader({ children }) { + return
{children}
; +} + +function CardBody({ children }) { + return
{children}
; +} + +// Usage - Flexible composition +function UserCard({ user }) { + return ( + + +

{user.name}

+
+ +

{user.bio}

+
+
+ ); +} + +// ❌ Avoid: Monolithic components with too many props +function Card({ title, body, footer, hasHeader, headerColor, ... }) { + // Too many responsibilities +} +``` + +### 4. **Context for Global State** +```jsx +// Create context for theme +const ThemeContext = createContext(); + +function ThemeProvider({ children }) { + const [theme, setTheme] = useState('light'); + + const toggleTheme = () => { + setTheme(prev => prev === 'light' ? 'dark' : 'light'); + }; + + return ( + + {children} + + ); +} + +// Custom hook for easy access +function useTheme() { + const context = useContext(ThemeContext); + if (!context) { + throw new Error('useTheme must be used within ThemeProvider'); + } + return context; +} + +// Usage +function ThemeToggle() { + const { theme, toggleTheme } = useTheme(); + return ; +} +``` + +## Performance Optimization + +### 1. **Memoization** +```jsx +// useMemo for expensive calculations +function ProductList({ products, filters }) { + const filteredProducts = useMemo(() => { + return products.filter(product => { + // Expensive filtering logic + return matchesFilters(product, filters); + }); + }, [products, filters]); + + return
{filteredProducts.map(/* ... */)}
; +} + +// useCallback for function references +function TodoList() { + const [todos, setTodos] = useState([]); + + // Without useCallback, this creates new function on every render + const handleToggle = useCallback((id) => { + setTodos(prev => prev.map(todo => + todo.id === id ? { ...todo, done: !todo.done } : todo + )); + }, []); + + return ( +
+ {todos.map(todo => ( + + ))} +
+ ); +} + +// React.memo to prevent unnecessary re-renders +const TodoItem = React.memo(function TodoItem({ todo, onToggle }) { + return ( +
onToggle(todo.id)}> + {todo.title} +
+ ); +}); +``` + +### 2. **Lazy Loading & Code Splitting** +```jsx +// Lazy load components +const Dashboard = lazy(() => import('./Dashboard')); +const Settings = lazy(() => import('./Settings')); + +function App() { + return ( + }> + + } /> + } /> + + + ); +} +``` + +### 3. **Virtualization for Long Lists** +```jsx +import { FixedSizeList } from 'react-window'; + +function LargeList({ items }) { + const Row = ({ index, style }) => ( +
+ {items[index].name} +
+ ); + + return ( + + {Row} + + ); +} +``` + +## State Management Patterns + +### 1. **Local State (useState)** +```jsx +// For simple, component-specific state +function Counter() { + const [count, setCount] = useState(0); + return ; +} +``` + +### 2. **Reducer Pattern (useReducer)** +```jsx +// For complex state logic +function todoReducer(state, action) { + switch (action.type) { + case 'ADD': + return [...state, { id: Date.now(), text: action.text, done: false }]; + case 'TOGGLE': + return state.map(todo => + todo.id === action.id ? { ...todo, done: !todo.done } : todo + ); + case 'DELETE': + return state.filter(todo => todo.id !== action.id); + default: + return state; + } +} + +function TodoApp() { + const [todos, dispatch] = useReducer(todoReducer, []); + + const addTodo = (text) => dispatch({ type: 'ADD', text }); + const toggleTodo = (id) => dispatch({ type: 'TOGGLE', id }); + const deleteTodo = (id) => dispatch({ type: 'DELETE', id }); + + return (/* ... */); +} +``` + +### 3. **Global State (Context + Reducer)** +```jsx +const TodoContext = createContext(); + +function TodoProvider({ children }) { + const [todos, dispatch] = useReducer(todoReducer, []); + + return ( + + {children} + + ); +} + +function useTodos() { + const context = useContext(TodoContext); + if (!context) throw new Error('useTodos must be within TodoProvider'); + return context; +} +``` + +## Best Practices + +### 1. **Proper Key Usage** +```jsx +// ✅ Good: Stable, unique keys +{items.map(item => ( + +))} + +// ❌ Bad: Index as key (causes issues when reordering) +{items.map((item, index) => ( + +))} + +// ❌ Bad: Random keys (defeats React's reconciliation) +{items.map(item => ( + +))} +``` + +### 2. **Controlled Components** +```jsx +// ✅ Controlled: React state is source of truth +function Form() { + const [email, setEmail] = useState(''); + + return ( + setEmail(e.target.value)} + /> + ); +} + +// ❌ Uncontrolled: DOM is source of truth (harder to manage) +function Form() { + const inputRef = useRef(); + + const handleSubmit = () => { + const email = inputRef.current.value; // Reading from DOM + }; + + return ; +} +``` + +### 3. **Effect Dependencies** +```jsx +// ✅ Complete dependencies +useEffect(() => { + fetchUser(userId).then(setUser); +}, [userId]); // Includes all used external values + +// ❌ Missing dependencies (may cause bugs) +useEffect(() => { + fetchUser(userId).then(setUser); +}, []); // userId changes won't trigger re-fetch + +// ✅ Cleanup for subscriptions +useEffect(() => { + const subscription = api.subscribeToUser(userId, setUser); + + return () => subscription.unsubscribe(); +}, [userId]); +``` + +### 4. **Avoid Prop Drilling** +```jsx +// ❌ Bad: Prop drilling through many layers +function App() { + const [user, setUser] = useState(null); + return ; +} +function Layout({ user, setUser }) { + return ; +} +function Sidebar({ user, setUser }) { + return ; +} + +// ✅ Good: Use context +const UserContext = createContext(); + +function App() { + const [user, setUser] = useState(null); + return ( + + + + ); +} + +function UserMenu() { + const { user, setUser } = useContext(UserContext); + // Direct access, no prop drilling +} +``` + +## Common Patterns + +### 1. **Fetch on Mount** +```jsx +function UserData({ userId }) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + let cancelled = false; + + async function loadData() { + try { + const result = await fetchUser(userId); + if (!cancelled) { + setData(result); + setLoading(false); + } + } catch (error) { + if (!cancelled) { + setError(error); + setLoading(false); + } + } + } + + loadData(); + + return () => { cancelled = true; }; + }, [userId]); + + if (loading) return ; + return
{data.name}
; +} +``` + +### 2. **Form Handling** +```jsx +function SignupForm() { + const [formData, setFormData] = useState({ + email: '', + password: '', + confirmPassword: '' + }); + + const [errors, setErrors] = useState({}); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ ...prev, [name]: value })); + }; + + const validate = () => { + const newErrors = {}; + if (!formData.email.includes('@')) { + newErrors.email = 'Invalid email'; + } + if (formData.password.length < 8) { + newErrors.password = 'Password too short'; + } + if (formData.password !== formData.confirmPassword) { + newErrors.confirmPassword = 'Passwords do not match'; + } + return newErrors; + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + const newErrors = validate(); + + if (Object.keys(newErrors).length > 0) { + setErrors(newErrors); + return; + } + + await submitForm(formData); + }; + + return ( +
+ + {errors.email && {errors.email}} + {/* ... */} +
+ ); +} +``` + +### 3. **Modal/Dialog Pattern** +```jsx +function useModal() { + const [isOpen, setIsOpen] = useState(false); + + const open = () => setIsOpen(true); + const close = () => setIsOpen(false); + const toggle = () => setIsOpen(prev => !prev); + + return { isOpen, open, close, toggle }; +} + +// Usage +function App() { + const confirmModal = useModal(); + + const handleDelete = async () => { + confirmModal.open(); + }; + + const handleConfirm = async () => { + await deleteItem(); + confirmModal.close(); + }; + + return ( + <> + + +

Confirm Delete?

+ + +
+ + ); +} +``` + +## TypeScript with React + +```typescript +// Type props +interface UserCardProps { + user: User; + onEdit?: (user: User) => void; + className?: string; +} + +function UserCard({ user, onEdit, className }: UserCardProps) { + return
{user.name}
; +} + +// Type events +function handleClick(event: React.MouseEvent) { + event.preventDefault(); + // ... +} + +// Type refs +const inputRef = useRef(null); + +// Type custom hooks +function useLocalStorage(key: string, initialValue: T) { + const [value, setValue] = useState(() => { + const stored = localStorage.getItem(key); + return stored ? JSON.parse(stored) : initialValue; + }); + + useEffect(() => { + localStorage.setItem(key, JSON.stringify(value)); + }, [key, value]); + + return [value, setValue] as const; +} +``` + +## When to Use This Skill + +- Building React applications or components +- Optimizing React performance +- Implementing complex state management +- Creating reusable hooks and components +- Setting up React project architecture +- Migrating from class to functional components + +--- + +**Remember**: React is about composition and data flow. Keep components small, focused, and reusable. Let state drive UI changes declaratively. diff --git a/commands/responsive-design.md b/commands/responsive-design.md new file mode 100644 index 0000000..473beca --- /dev/null +++ b/commands/responsive-design.md @@ -0,0 +1,262 @@ +# Responsive Design Expert + +**Description**: Create fluid, mobile-first designs that work beautifully across all screen sizes + +## Core Principles + +Build responsive layouts that adapt gracefully from mobile phones to large desktop monitors using modern CSS and best practices. + +## Mobile-First Approach + +```css +/* ✅ Mobile-first: Start with mobile, enhance for larger screens */ +.container { + padding: 1rem; + width: 100%; +} + +@media (min-width: 768px) { + .container { + padding: 2rem; + max-width: 720px; + } +} + +@media (min-width: 1024px) { + .container { + padding: 3rem; + max-width: 960px; + } +} + +/* ❌ Desktop-first: Harder to maintain */ +.container { + padding: 3rem; + max-width: 960px; +} + +@media (max-width: 1024px) { + .container { + padding: 2rem; + max-width: 720px; + } +} +``` + +## Breakpoints + +```css +/* Standard breakpoints */ +/* Mobile: < 640px (default) */ +/* Tablet: 640px - 1024px */ +/* Desktop: > 1024px */ + +:root { + --breakpoint-sm: 640px; + --breakpoint-md: 768px; + --breakpoint-lg: 1024px; + --breakpoint-xl: 1280px; +} + +/* Tailwind-style breakpoints */ +@media (min-width: 640px) { /* sm */ } +@media (min-width: 768px) { /* md */ } +@media (min-width: 1024px) { /* lg */ } +@media (min-width: 1280px) { /* xl */ } +``` + +## Fluid Layouts + +### CSS Grid +```css +/* Responsive grid without media queries */ +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1rem; +} + +/* 12-column grid system */ +.grid-12 { + display: grid; + grid-template-columns: repeat(12, 1fr); + gap: 1rem; +} + +.col-span-6 { + grid-column: span 6; /* 50% width */ +} + +@media (max-width: 768px) { + .col-span-6 { + grid-column: span 12; /* Full width on mobile */ + } +} +``` + +### Flexbox +```css +/* Responsive flex layout */ +.flex-container { + display: flex; + flex-wrap: wrap; + gap: 1rem; +} + +.flex-item { + flex: 1 1 300px; /* grow, shrink, basis */ +} + +/* Center content */ +.center { + display: flex; + justify-content: center; + align-items: center; +} +``` + +## Responsive Typography + +```css +/* Fluid typography using clamp */ +h1 { + font-size: clamp(2rem, 5vw, 4rem); + /* min: 2rem, preferred: 5vw, max: 4rem */ +} + +body { + font-size: clamp(1rem, 2.5vw, 1.25rem); +} + +/* Using CSS custom properties */ +:root { + --font-size-base: 16px; + --font-size-lg: 18px; +} + +@media (min-width: 768px) { + :root { + --font-size-base: 18px; + --font-size-lg: 20px; + } +} + +body { + font-size: var(--font-size-base); +} +``` + +## Responsive Images + +```html + + + + + Hero image + + + +Photo +``` + +## Container Queries (Modern) + +```css +/* Responsive based on container size, not viewport */ +.card-container { + container-type: inline-size; + container-name: card; +} + +.card-title { + font-size: 1.5rem; +} + +@container card (min-width: 400px) { + .card-title { + font-size: 2rem; + } + + .card-content { + display: grid; + grid-template-columns: 1fr 1fr; + } +} +``` + +## Responsive Navigation + +```css +/* Mobile hamburger menu */ +.nav-toggle { + display: block; +} + +.nav-menu { + display: none; + flex-direction: column; +} + +.nav-menu.active { + display: flex; +} + +@media (min-width: 768px) { + .nav-toggle { + display: none; + } + + .nav-menu { + display: flex; + flex-direction: row; + } +} +``` + +## Testing Responsive Design + +### Viewport Meta Tag +```html + +``` + +### Device Testing Checklist +- [ ] iPhone SE (375px) +- [ ] iPhone 12/13 (390px) +- [ ] iPad (768px) +- [ ] Desktop (1024px+) +- [ ] Large desktop (1920px+) +- [ ] Test landscape orientations +- [ ] Test with browser zoom (125%, 150%) + +## When to Use This Skill + +- Designing responsive layouts +- Converting desktop designs to mobile +- Optimizing for different screen sizes +- Improving mobile user experience + +--- + +**Remember**: Design for mobile first, then progressively enhance for larger screens! diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..dbc1574 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,53 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:samuelgarrett/claude-code-plugin-test:web-dev-skills", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "56c5cef459b4503c3b5d32825ede600407e9e87c", + "treeHash": "8c43c2ec47ea536a5ce40229383c01bcc3efca0c7fdf47475f7c617cbb633bf7", + "generatedAt": "2025-11-28T10:28:07.652290Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "web-dev-skills", + "description": "Specialized skills for modern web development including React, Next.js, APIs, and frontend optimization", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "bff100eb8fa02fbe2cd59dcb6fbb59bee655450af42db8817f0ba6d62d1c6cae" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "90b4a4a8abe41065956b14a580e79344d85055bc5dfbc3bb31e489dff94efe36" + }, + { + "path": "commands/api-designer.md", + "sha256": "45287e7bb8070f27a8a0b74e99115ad249c3d444ab218abb0ef4c663724d772e" + }, + { + "path": "commands/react-expert.md", + "sha256": "2522429c362febd3f8fecf799a178e36f7928ea1aeabdb657d99e3ad95f6cd7d" + }, + { + "path": "commands/responsive-design.md", + "sha256": "404984dfa67030407a8f131c3665671cf9415959db499a8a1035749e237ec85a" + } + ], + "dirSha256": "8c43c2ec47ea536a5ce40229383c01bcc3efca0c7fdf47475f7c617cbb633bf7" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file