{"owner":"midudev","repo":"preguntas-entrevista-react","hasSkills":true,"totalSkillsCount":136,"totalTokensCount":126312,"categories":["anthropic-skill","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/accessibility/SKILL.md",".agents/skills/accessibility/references/WCAG.md",".agents/skills/best-practices/SKILL.md",".agents/skills/core-web-vitals/SKILL.md",".agents/skills/core-web-vitals/references/LCP.md",".agents/skills/frontend-design/SKILL.md",".agents/skills/migrate-oxlint/SKILL.md",".agents/skills/next-best-practices/SKILL.md",".agents/skills/next-best-practices/async-patterns.md",".agents/skills/next-best-practices/bundling.md",".agents/skills/next-best-practices/data-patterns.md",".agents/skills/next-best-practices/debug-tricks.md",".agents/skills/next-best-practices/directives.md",".agents/skills/next-best-practices/error-handling.md",".agents/skills/next-best-practices/file-conventions.md",".agents/skills/next-best-practices/font.md",".agents/skills/next-best-practices/functions.md",".agents/skills/next-best-practices/hydration-error.md",".agents/skills/next-best-practices/image.md",".agents/skills/next-best-practices/metadata.md",".agents/skills/next-best-practices/parallel-routes.md",".agents/skills/next-best-practices/route-handlers.md",".agents/skills/next-best-practices/rsc-boundaries.md",".agents/skills/next-best-practices/runtime-selection.md",".agents/skills/next-best-practices/scripts.md",".agents/skills/next-best-practices/self-hosting.md",".agents/skills/next-best-practices/suspense-boundaries.md",".agents/skills/next-cache-components/SKILL.md",".agents/skills/next-upgrade/SKILL.md",".agents/skills/nodejs-backend-patterns/SKILL.md",".agents/skills/nodejs-backend-patterns/references/advanced-patterns.md",".agents/skills/nodejs-best-practices/SKILL.md",".agents/skills/performance/SKILL.md",".agents/skills/seo/SKILL.md",".agents/skills/tailwind-css-patterns/SKILL.md",".agents/skills/tailwind-css-patterns/references/accessibility.md",".agents/skills/tailwind-css-patterns/references/animations.md",".agents/skills/tailwind-css-patterns/references/component-patterns.md",".agents/skills/tailwind-css-patterns/references/configuration.md",".agents/skills/tailwind-css-patterns/references/layout-patterns.md",".agents/skills/tailwind-css-patterns/references/performance.md",".agents/skills/tailwind-css-patterns/references/reference.md",".agents/skills/tailwind-css-patterns/references/responsive-design.md",".agents/skills/typescript-advanced-types/SKILL.md",".agents/skills/vercel-composition-patterns/AGENTS.md",".agents/skills/vercel-composition-patterns/README.md",".agents/skills/vercel-composition-patterns/SKILL.md",".agents/skills/vercel-composition-patterns/metadata.json",".agents/skills/vercel-composition-patterns/rules/_sections.md",".agents/skills/vercel-composition-patterns/rules/_template.md",".agents/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md",".agents/skills/vercel-composition-patterns/rules/architecture-compound-components.md",".agents/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md",".agents/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md",".agents/skills/vercel-composition-patterns/rules/react19-no-forwardref.md",".agents/skills/vercel-composition-patterns/rules/state-context-interface.md",".agents/skills/vercel-composition-patterns/rules/state-decouple-implementation.md",".agents/skills/vercel-composition-patterns/rules/state-lift-state.md",".agents/skills/vercel-react-best-practices/AGENTS.md",".agents/skills/vercel-react-best-practices/README.md",".agents/skills/vercel-react-best-practices/SKILL.md",".agents/skills/vercel-react-best-practices/metadata.json",".agents/skills/vercel-react-best-practices/rules/_sections.md",".agents/skills/vercel-react-best-practices/rules/_template.md",".agents/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md",".agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md",".agents/skills/vercel-react-best-practices/rules/advanced-init-once.md",".agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md",".agents/skills/vercel-react-best-practices/rules/async-api-routes.md",".agents/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md",".agents/skills/vercel-react-best-practices/rules/async-defer-await.md",".agents/skills/vercel-react-best-practices/rules/async-dependencies.md",".agents/skills/vercel-react-best-practices/rules/async-parallel.md",".agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md",".agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md",".agents/skills/vercel-react-best-practices/rules/bundle-conditional.md",".agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md",".agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md",".agents/skills/vercel-react-best-practices/rules/bundle-preload.md",".agents/skills/vercel-react-best-practices/rules/client-event-listeners.md",".agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md",".agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md",".agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md",".agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md",".agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md",".agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md",".agents/skills/vercel-react-best-practices/rules/js-cache-storage.md",".agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md",".agents/skills/vercel-react-best-practices/rules/js-early-exit.md",".agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md",".agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md",".agents/skills/vercel-react-best-practices/rules/js-index-maps.md",".agents/skills/vercel-react-best-practices/rules/js-length-check-first.md",".agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md",".agents/skills/vercel-react-best-practices/rules/js-request-idle-callback.md",".agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md",".agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md",".agents/skills/vercel-react-best-practices/rules/rendering-activity.md",".agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md",".agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md",".agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md",".agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md",".agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md",".agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md",".agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md",".agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md",".agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md",".agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md",".agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md",".agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md",".agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md",".agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md",".agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md",".agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md",".agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md",".agents/skills/vercel-react-best-practices/rules/rerender-memo.md",".agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md",".agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md",".agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md",".agents/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md",".agents/skills/vercel-react-best-practices/rules/rerender-transitions.md",".agents/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md",".agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md",".agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md",".agents/skills/vercel-react-best-practices/rules/server-auth-actions.md",".agents/skills/vercel-react-best-practices/rules/server-cache-lru.md",".agents/skills/vercel-react-best-practices/rules/server-cache-react.md",".agents/skills/vercel-react-best-practices/rules/server-dedup-props.md",".agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md",".agents/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md",".agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md",".agents/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md",".agents/skills/vercel-react-best-practices/rules/server-serialization.md",".agents/skills/web-design-guidelines/SKILL.md",".agents/skills/web-quality-audit/SKILL.md","CLAUDE.md"],"skills":{".agents/skills/accessibility/SKILL.md":"---\nname: accessibility\ndescription: Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Accessibility (a11y)\n\nComprehensive accessibility guidelines based on WCAG 2.1 and Lighthouse accessibility audits. Goal: make content usable by everyone, including people with disabilities.\n\n## WCAG Principles: POUR\n\n| Principle | Description |\n|-----------|-------------|\n| **P**erceivable | Content can be perceived through different senses |\n| **O**perable | Interface can be operated by all users |\n| **U**nderstandable | Content and interface are understandable |\n| **R**obust | Content works with assistive technologies |\n\n## Conformance levels\n\n| Level | Requirement | Target |\n|-------|-------------|--------|\n| **A** | Minimum accessibility | Must pass |\n| **AA** | Standard compliance | Should pass (legal requirement in many jurisdictions) |\n| **AAA** | Enhanced accessibility | Nice to have |\n\n---\n\n## Perceivable\n\n### Text alternatives (1.1)\n\n**Images require alt text:**\n```html\n<!-- ❌ Missing alt -->\n<img src=\"chart.png\">\n\n<!-- ✅ Descriptive alt -->\n<img src=\"chart.png\" alt=\"Bar chart showing 40% increase in Q3 sales\">\n\n<!-- ✅ Decorative image (empty alt) -->\n<img src=\"decorative-border.png\" alt=\"\" role=\"presentation\">\n\n<!-- ✅ Complex image with longer description -->\n<figure>\n  <img src=\"infographic.png\" alt=\"2024 market trends infographic\" \n       aria-describedby=\"infographic-desc\">\n  <figcaption id=\"infographic-desc\">\n    <!-- Detailed description -->\n  </figcaption>\n</figure>\n```\n\n**Icon buttons need accessible names:**\n```html\n<!-- ❌ No accessible name -->\n<button><svg><!-- menu icon --></svg></button>\n\n<!-- ✅ Using aria-label -->\n<button aria-label=\"Open menu\">\n  <svg aria-hidden=\"true\"><!-- menu icon --></svg>\n</button>\n\n<!-- ✅ Using visually hidden text -->\n<button>\n  <svg aria-hidden=\"true\"><!-- menu icon --></svg>\n  <span class=\"visually-hidden\">Open menu</span>\n</button>\n```\n\n**Visually hidden class:**\n```css\n.visually-hidden {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n```\n\n### Color contrast (1.4.3, 1.4.6)\n\n| Text Size | AA minimum | AAA enhanced |\n|-----------|------------|--------------|\n| Normal text (< 18px / < 14px bold) | 4.5:1 | 7:1 |\n| Large text (≥ 18px / ≥ 14px bold) | 3:1 | 4.5:1 |\n| UI components & graphics | 3:1 | 3:1 |\n\n```css\n/* ❌ Low contrast (2.5:1) */\n.low-contrast {\n  color: #999;\n  background: #fff;\n}\n\n/* ✅ Sufficient contrast (7:1) */\n.high-contrast {\n  color: #333;\n  background: #fff;\n}\n\n/* ✅ Focus states need contrast too */\n:focus-visible {\n  outline: 2px solid #005fcc;\n  outline-offset: 2px;\n}\n```\n\n**Don't rely on color alone:**\n```html\n<!-- ❌ Only color indicates error -->\n<input class=\"error-border\">\n<style>.error-border { border-color: red; }</style>\n\n<!-- ✅ Color + icon + text -->\n<div class=\"field-error\">\n  <input aria-invalid=\"true\" aria-describedby=\"email-error\">\n  <span id=\"email-error\" class=\"error-message\">\n    <svg aria-hidden=\"true\"><!-- error icon --></svg>\n    Please enter a valid email address\n  </span>\n</div>\n```\n\n### Media alternatives (1.2)\n\n```html\n<!-- Video with captions -->\n<video controls>\n  <source src=\"video.mp4\" type=\"video/mp4\">\n  <track kind=\"captions\" src=\"captions.vtt\" srclang=\"en\" label=\"English\" default>\n  <track kind=\"descriptions\" src=\"descriptions.vtt\" srclang=\"en\" label=\"Descriptions\">\n</video>\n\n<!-- Audio with transcript -->\n<audio controls>\n  <source src=\"podcast.mp3\" type=\"audio/mp3\">\n</audio>\n<details>\n  <summary>Transcript</summary>\n  <p>Full transcript text...</p>\n</details>\n```\n\n---\n\n## Operable\n\n### Keyboard accessible (2.1)\n\n**All functionality must be keyboard accessible:**\n```javascript\n// ❌ Only handles click\nelement.addEventListener('click', handleAction);\n\n// ✅ Handles both click and keyboard\nelement.addEventListener('click', handleAction);\nelement.addEventListener('keydown', (e) => {\n  if (e.key === 'Enter' || e.key === ' ') {\n    e.preventDefault();\n    handleAction();\n  }\n});\n```\n\n**No keyboard traps:**\n```javascript\n// Modal focus management\nfunction openModal(modal) {\n  const focusableElements = modal.querySelectorAll(\n    'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n  );\n  const firstElement = focusableElements[0];\n  const lastElement = focusableElements[focusableElements.length - 1];\n  \n  // Trap focus within modal\n  modal.addEventListener('keydown', (e) => {\n    if (e.key === 'Tab') {\n      if (e.shiftKey && document.activeElement === firstElement) {\n        e.preventDefault();\n        lastElement.focus();\n      } else if (!e.shiftKey && document.activeElement === lastElement) {\n        e.preventDefault();\n        firstElement.focus();\n      }\n    }\n    if (e.key === 'Escape') {\n      closeModal();\n    }\n  });\n  \n  firstElement.focus();\n}\n```\n\n### Focus visible (2.4.7)\n\n```css\n/* ❌ Never remove focus outlines */\n*:focus { outline: none; }\n\n/* ✅ Use :focus-visible for keyboard-only focus */\n:focus {\n  outline: none;\n}\n\n:focus-visible {\n  outline: 2px solid #005fcc;\n  outline-offset: 2px;\n}\n\n/* ✅ Or custom focus styles */\nbutton:focus-visible {\n  box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);\n}\n```\n\n### Skip links (2.4.1)\n\n```html\n<body>\n  <a href=\"#main-content\" class=\"skip-link\">Skip to main content</a>\n  <header><!-- navigation --></header>\n  <main id=\"main-content\" tabindex=\"-1\">\n    <!-- main content -->\n  </main>\n</body>\n```\n\n```css\n.skip-link {\n  position: absolute;\n  top: -40px;\n  left: 0;\n  background: #000;\n  color: #fff;\n  padding: 8px 16px;\n  z-index: 100;\n}\n\n.skip-link:focus {\n  top: 0;\n}\n```\n\n### Timing (2.2)\n\n```javascript\n// Allow users to extend time limits\nfunction showSessionWarning() {\n  const modal = createModal({\n    title: 'Session Expiring',\n    content: 'Your session will expire in 2 minutes.',\n    actions: [\n      { label: 'Extend session', action: extendSession },\n      { label: 'Log out', action: logout }\n    ],\n    timeout: 120000 // 2 minutes to respond\n  });\n}\n```\n\n### Motion (2.3)\n\n```css\n/* Respect reduced motion preference */\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important;\n  }\n}\n```\n\n---\n\n## Understandable\n\n### Page language (3.1.1)\n\n```html\n<!-- ❌ No language specified -->\n<html>\n\n<!-- ✅ Language specified -->\n<html lang=\"en\">\n\n<!-- ✅ Language changes within page -->\n<p>The French word for hello is <span lang=\"fr\">bonjour</span>.</p>\n```\n\n### Consistent navigation (3.2.3)\n\n```html\n<!-- Navigation should be consistent across pages -->\n<nav aria-label=\"Main\">\n  <ul>\n    <li><a href=\"/\" aria-current=\"page\">Home</a></li>\n    <li><a href=\"/products\">Products</a></li>\n    <li><a href=\"/about\">About</a></li>\n  </ul>\n</nav>\n```\n\n### Form labels (3.3.2)\n\n```html\n<!-- ❌ No label association -->\n<input type=\"email\" placeholder=\"Email\">\n\n<!-- ✅ Explicit label -->\n<label for=\"email\">Email address</label>\n<input type=\"email\" id=\"email\" name=\"email\" \n       autocomplete=\"email\" required>\n\n<!-- ✅ Implicit label -->\n<label>\n  Email address\n  <input type=\"email\" name=\"email\" autocomplete=\"email\" required>\n</label>\n\n<!-- ✅ With instructions -->\n<label for=\"password\">Password</label>\n<input type=\"password\" id=\"password\" \n       aria-describedby=\"password-requirements\">\n<p id=\"password-requirements\">\n  Must be at least 8 characters with one number.\n</p>\n```\n\n### Error handling (3.3.1, 3.3.3)\n\n```html\n<!-- Announce errors to screen readers -->\n<form novalidate>\n  <div class=\"field\" aria-live=\"polite\">\n    <label for=\"email\">Email</label>\n    <input type=\"email\" id=\"email\" \n           aria-invalid=\"true\"\n           aria-describedby=\"email-error\">\n    <p id=\"email-error\" class=\"error\" role=\"alert\">\n      Please enter a valid email address (e.g., name@example.com)\n    </p>\n  </div>\n</form>\n```\n\n```javascript\n// Focus first error on submit\nform.addEventListener('submit', (e) => {\n  const firstError = form.querySelector('[aria-invalid=\"true\"]');\n  if (firstError) {\n    e.preventDefault();\n    firstError.focus();\n    \n    // Announce error summary\n    const errorSummary = document.getElementById('error-summary');\n    errorSummary.textContent = `${errors.length} errors found. Please fix them and try again.`;\n    errorSummary.focus();\n  }\n});\n```\n\n---\n\n## Robust\n\n### Valid HTML (4.1.1)\n\n```html\n<!-- ❌ Duplicate IDs -->\n<div id=\"content\">...</div>\n<div id=\"content\">...</div>\n\n<!-- ❌ Invalid nesting -->\n<a href=\"/\"><button>Click</button></a>\n\n<!-- ✅ Unique IDs -->\n<div id=\"main-content\">...</div>\n<div id=\"sidebar-content\">...</div>\n\n<!-- ✅ Proper nesting -->\n<a href=\"/\" class=\"button-link\">Click</a>\n```\n\n### ARIA usage (4.1.2)\n\n**Prefer native elements:**\n```html\n<!-- ❌ ARIA role on div -->\n<div role=\"button\" tabindex=\"0\">Click me</div>\n\n<!-- ✅ Native button -->\n<button>Click me</button>\n\n<!-- ❌ ARIA checkbox -->\n<div role=\"checkbox\" aria-checked=\"false\">Option</div>\n\n<!-- ✅ Native checkbox -->\n<label><input type=\"checkbox\"> Option</label>\n```\n\n**When ARIA is needed:**\n```html\n<!-- Custom tabs component -->\n<div role=\"tablist\" aria-label=\"Product information\">\n  <button role=\"tab\" id=\"tab-1\" aria-selected=\"true\" \n          aria-controls=\"panel-1\">Description</button>\n  <button role=\"tab\" id=\"tab-2\" aria-selected=\"false\" \n          aria-controls=\"panel-2\" tabindex=\"-1\">Reviews</button>\n</div>\n<div role=\"tabpanel\" id=\"panel-1\" aria-labelledby=\"tab-1\">\n  <!-- Panel content -->\n</div>\n<div role=\"tabpanel\" id=\"panel-2\" aria-labelledby=\"tab-2\" hidden>\n  <!-- Panel content -->\n</div>\n```\n\n### Live regions (4.1.3)\n\n```html\n<!-- Status updates -->\n<div aria-live=\"polite\" aria-atomic=\"true\" class=\"status\">\n  <!-- Content updates announced to screen readers -->\n</div>\n\n<!-- Urgent alerts -->\n<div role=\"alert\" aria-live=\"assertive\">\n  <!-- Interrupts current announcement -->\n</div>\n```\n\n```javascript\n// Announce dynamic content changes\nfunction showNotification(message, type = 'polite') {\n  const container = document.getElementById(`${type}-announcer`);\n  container.textContent = ''; // Clear first\n  requestAnimationFrame(() => {\n    container.textContent = message;\n  });\n}\n```\n\n---\n\n## Testing checklist\n\n### Automated testing\n```bash\n# Lighthouse accessibility audit\nnpx lighthouse https://example.com --only-categories=accessibility\n\n# axe-core\nnpm install @axe-core/cli -g\naxe https://example.com\n```\n\n### Manual testing\n\n- [ ] **Keyboard navigation:** Tab through entire page, use Enter/Space to activate\n- [ ] **Screen reader:** Test with VoiceOver (Mac), NVDA (Windows), or TalkBack (Android)\n- [ ] **Zoom:** Content usable at 200% zoom\n- [ ] **High contrast:** Test with Windows High Contrast Mode\n- [ ] **Reduced motion:** Test with `prefers-reduced-motion: reduce`\n- [ ] **Focus order:** Logical and follows visual order\n\n### Screen reader commands\n\n| Action | VoiceOver (Mac) | NVDA (Windows) |\n|--------|-----------------|----------------|\n| Start/Stop | ⌘ + F5 | Ctrl + Alt + N |\n| Next item | VO + → | ↓ |\n| Previous item | VO + ← | ↑ |\n| Activate | VO + Space | Enter |\n| Headings list | VO + U, then arrows | H / Shift + H |\n| Links list | VO + U | K / Shift + K |\n\n---\n\n## Common issues by impact\n\n### Critical (fix immediately)\n1. Missing form labels\n2. Missing image alt text\n3. Insufficient color contrast\n4. Keyboard traps\n5. No focus indicators\n\n### Serious (fix before launch)\n1. Missing page language\n2. Missing heading structure\n3. Non-descriptive link text\n4. Auto-playing media\n5. Missing skip links\n\n### Moderate (fix soon)\n1. Missing ARIA labels on icons\n2. Inconsistent navigation\n3. Missing error identification\n4. Timing without controls\n5. Missing landmark regions\n\n## References\n\n- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/)\n- [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)\n- [Deque axe Rules](https://dequeuniversity.com/rules/axe/)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n",".agents/skills/accessibility/references/WCAG.md":"# WCAG 2.1 Quick Reference\n\n## Success criteria by level\n\n### Level A (minimum)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.1.1** Non-text Content | All images, icons have text alternatives |\n| **1.2.1** Audio-only/Video-only | Provide transcript or audio description |\n| **1.2.2** Captions | Video with audio has captions |\n| **1.2.3** Audio Description | Video has audio description |\n| **1.3.1** Info and Relationships | Information conveyed through presentation is available programmatically |\n| **1.3.2** Meaningful Sequence | Reading order is logical |\n| **1.3.3** Sensory Characteristics | Instructions don't rely solely on shape, color, size, location, orientation, or sound |\n| **1.4.1** Use of Color | Color is not the only visual means of conveying information |\n| **1.4.2** Audio Control | Audio playing automatically can be paused/stopped |\n| **2.1.1** Keyboard | All functionality available via keyboard |\n| **2.1.2** No Keyboard Trap | Keyboard focus can be moved away from any component |\n| **2.1.4** Character Key Shortcuts | Single-key shortcuts can be turned off or remapped |\n| **2.2.1** Timing Adjustable | Time limits can be extended |\n| **2.2.2** Pause, Stop, Hide | Moving/blinking content can be paused |\n| **2.3.1** Three Flashes | Nothing flashes more than 3 times per second |\n| **2.4.1** Bypass Blocks | Skip link or landmark navigation available |\n| **2.4.2** Page Titled | Pages have descriptive titles |\n| **2.4.3** Focus Order | Focus order preserves meaning |\n| **2.4.4** Link Purpose | Link purpose clear from link text or context |\n| **2.5.1** Pointer Gestures | Multi-point gestures have single-pointer alternatives |\n| **2.5.2** Pointer Cancellation | Down-event doesn't trigger action (use up-event or click) |\n| **2.5.3** Label in Name | Accessible name contains visible label text |\n| **2.5.4** Motion Actuation | Motion-triggered functions have alternatives |\n| **3.1.1** Language of Page | Default language specified in HTML |\n| **3.2.1** On Focus | Focus doesn't trigger unexpected changes |\n| **3.2.2** On Input | Input doesn't trigger unexpected changes |\n| **3.3.1** Error Identification | Input errors clearly described |\n| **3.3.2** Labels or Instructions | Form inputs have labels or instructions |\n| **4.1.1** Parsing | HTML is well-formed (no duplicate IDs, proper nesting) |\n| **4.1.2** Name, Role, Value | UI components have accessible names and correct roles |\n\n### Level AA (standard)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.2.4** Captions (Live) | Live audio has captions |\n| **1.2.5** Audio Description | Pre-recorded video has audio description |\n| **1.3.4** Orientation | Content doesn't restrict orientation |\n| **1.3.5** Identify Input Purpose | Input purpose can be programmatically determined |\n| **1.4.3** Contrast (Minimum) | 4.5:1 for normal text, 3:1 for large text |\n| **1.4.4** Resize Text | Text can be resized to 200% without loss of functionality |\n| **1.4.5** Images of Text | Text used instead of images of text |\n| **1.4.10** Reflow | Content reflows at 320px width without horizontal scroll |\n| **1.4.11** Non-text Contrast | UI components have 3:1 contrast |\n| **1.4.12** Text Spacing | Content adapts to text spacing changes |\n| **1.4.13** Content on Hover/Focus | Additional content is dismissible, hoverable, persistent |\n| **2.4.5** Multiple Ways | Multiple ways to find pages |\n| **2.4.6** Headings and Labels | Headings and labels are descriptive |\n| **2.4.7** Focus Visible | Focus indicator is visible |\n| **3.1.2** Language of Parts | Language changes are marked |\n| **3.2.3** Consistent Navigation | Navigation is consistent across pages |\n| **3.2.4** Consistent Identification | Same functionality uses same labels |\n| **3.3.3** Error Suggestion | Error corrections suggested when known |\n| **3.3.4** Error Prevention (Legal) | Actions can be reversed or confirmed |\n| **4.1.3** Status Messages | Status messages announced to screen readers |\n\n### Level AAA (enhanced)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.4.6** Contrast (Enhanced) | 7:1 for normal text, 4.5:1 for large text |\n| **1.4.8** Visual Presentation | Foreground/background colors can be selected |\n| **1.4.9** Images of Text (No Exception) | No images of text |\n| **2.1.3** Keyboard (No Exception) | All functionality keyboard accessible |\n| **2.2.3** No Timing | No time limits |\n| **2.2.4** Interruptions | Interruptions can be postponed |\n| **2.2.5** Re-authenticating | Data preserved on re-authentication |\n| **2.2.6** Timeouts | Users warned about data loss from inactivity |\n| **2.3.2** Three Flashes | No content flashes more than 3 times |\n| **2.3.3** Animation from Interactions | Motion animation can be disabled |\n| **2.4.8** Location | User location within site is available |\n| **2.4.9** Link Purpose (Link Only) | Link purpose clear from link text alone |\n| **2.4.10** Section Headings | Sections have headings |\n| **3.1.3** Unusual Words | Definitions available for unusual words |\n| **3.1.4** Abbreviations | Abbreviations expanded |\n| **3.1.5** Reading Level | Alternative content for complex text |\n| **3.1.6** Pronunciation | Pronunciation available where needed |\n| **3.2.5** Change on Request | Changes initiated only by user |\n| **3.3.5** Help | Context-sensitive help available |\n| **3.3.6** Error Prevention (All) | All form submissions can be reviewed |\n\n## Common ARIA patterns\n\n### Buttons\n```html\n<button>Label</button>\n<!-- or -->\n<button aria-label=\"Close dialog\">×</button>\n```\n\n### Links\n```html\n<a href=\"/page\">Descriptive link text</a>\n<!-- External links -->\n<a href=\"https://external.com\" target=\"_blank\" rel=\"noopener\">\n  External site\n  <span class=\"visually-hidden\">(opens in new tab)</span>\n</a>\n```\n\n### Form fields\n```html\n<label for=\"email\">Email address</label>\n<input type=\"email\" id=\"email\" aria-describedby=\"email-hint\">\n<p id=\"email-hint\">We'll never share your email.</p>\n```\n\n### Error states\n```html\n<label for=\"email\">Email</label>\n<input type=\"email\" id=\"email\" aria-invalid=\"true\" aria-describedby=\"email-error\">\n<p id=\"email-error\" role=\"alert\">Please enter a valid email address.</p>\n```\n\n### Navigation\n```html\n<nav aria-label=\"Main\">\n  <ul>\n    <li><a href=\"/\" aria-current=\"page\">Home</a></li>\n    <li><a href=\"/about\">About</a></li>\n  </ul>\n</nav>\n```\n\n### Modals\n```html\n<div role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"dialog-title\">\n  <h2 id=\"dialog-title\">Confirm Action</h2>\n  <!-- content -->\n</div>\n```\n\n### Live regions\n```html\n<!-- Polite (waits for pause in speech) -->\n<div aria-live=\"polite\">Status update here</div>\n\n<!-- Assertive (interrupts immediately) -->\n<div aria-live=\"assertive\" role=\"alert\">Error message here</div>\n\n<!-- Status (polite, implicit) -->\n<div role=\"status\">Loading complete</div>\n```\n\n## Testing tools\n\n| Tool | Type | URL |\n|------|------|-----|\n| axe DevTools | Browser extension | [deque.com/axe](https://www.deque.com/axe/) |\n| WAVE | Browser extension | [wave.webaim.org](https://wave.webaim.org/) |\n| Lighthouse | Built into Chrome | DevTools → Lighthouse |\n| NVDA | Screen reader (Windows) | [nvaccess.org](https://www.nvaccess.org/) |\n| VoiceOver | Screen reader (Mac) | Built into macOS |\n| Colour Contrast Analyser | Desktop app | [tpgi.com](https://www.tpgi.com/color-contrast-checker/) |\n",".agents/skills/best-practices/SKILL.md":"---\nname: best-practices\ndescription: Apply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Best practices\n\nModern web development standards based on Lighthouse best practices audits. Covers security, browser compatibility, and code quality patterns.\n\n## Security\n\n### HTTPS everywhere\n\n**Enforce HTTPS:**\n```html\n<!-- ❌ Mixed content -->\n<img src=\"http://example.com/image.jpg\">\n<script src=\"http://cdn.example.com/script.js\"></script>\n\n<!-- ✅ HTTPS only -->\n<img src=\"https://example.com/image.jpg\">\n<script src=\"https://cdn.example.com/script.js\"></script>\n\n<!-- ✅ Protocol-relative (will use page's protocol) -->\n<img src=\"//example.com/image.jpg\">\n```\n\n**HSTS Header:**\n```\nStrict-Transport-Security: max-age=31536000; includeSubDomains; preload\n```\n\n### Content Security Policy (CSP)\n\n```html\n<!-- Basic CSP via meta tag -->\n<meta http-equiv=\"Content-Security-Policy\" \n      content=\"default-src 'self'; \n               script-src 'self' https://trusted-cdn.com; \n               style-src 'self' 'unsafe-inline';\n               img-src 'self' data: https:;\n               connect-src 'self' https://api.example.com;\">\n\n<!-- Better: HTTP header -->\n```\n\n**CSP Header (recommended):**\n```\nContent-Security-Policy: \n  default-src 'self';\n  script-src 'self' 'nonce-abc123' https://trusted.com;\n  style-src 'self' 'nonce-abc123';\n  img-src 'self' data: https:;\n  connect-src 'self' https://api.example.com;\n  frame-ancestors 'self';\n  base-uri 'self';\n  form-action 'self';\n```\n\n**Using nonces for inline scripts:**\n```html\n<script nonce=\"abc123\">\n  // This inline script is allowed\n</script>\n```\n\n### Security headers\n\n```\n# Prevent clickjacking\nX-Frame-Options: DENY\n\n# Prevent MIME type sniffing\nX-Content-Type-Options: nosniff\n\n# Enable XSS filter (legacy browsers)\nX-XSS-Protection: 1; mode=block\n\n# Control referrer information\nReferrer-Policy: strict-origin-when-cross-origin\n\n# Permissions policy (formerly Feature-Policy)\nPermissions-Policy: geolocation=(), microphone=(), camera=()\n```\n\n### No vulnerable libraries\n\n```bash\n# Check for vulnerabilities\nnpm audit\nyarn audit\n\n# Auto-fix when possible\nnpm audit fix\n\n# Check specific package\nnpm ls lodash\n```\n\n**Keep dependencies updated:**\n```json\n// package.json\n{\n  \"scripts\": {\n    \"audit\": \"npm audit --audit-level=moderate\",\n    \"update\": \"npm update && npm audit fix\"\n  }\n}\n```\n\n**Known vulnerable patterns to avoid:**\n```javascript\n// ❌ Prototype pollution vulnerable patterns\nObject.assign(target, userInput);\n_.merge(target, userInput);\n\n// ✅ Safer alternatives\nconst safeData = JSON.parse(JSON.stringify(userInput));\n```\n\n### Input sanitization\n\n```javascript\n// ❌ XSS vulnerable\nelement.innerHTML = userInput;\ndocument.write(userInput);\n\n// ✅ Safe text content\nelement.textContent = userInput;\n\n// ✅ If HTML needed, sanitize\nimport DOMPurify from 'dompurify';\nelement.innerHTML = DOMPurify.sanitize(userInput);\n```\n\n### Secure cookies\n\n```javascript\n// ❌ Insecure cookie\ndocument.cookie = \"session=abc123\";\n\n// ✅ Secure cookie (server-side)\nSet-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/\n```\n\n---\n\n## Browser compatibility\n\n### Doctype declaration\n\n```html\n<!-- ❌ Missing or invalid doctype -->\n<HTML>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n\n<!-- ✅ HTML5 doctype -->\n<!DOCTYPE html>\n<html lang=\"en\">\n```\n\n### Character encoding\n\n```html\n<!-- ❌ Missing or late charset -->\n<html>\n<head>\n  <title>Page</title>\n  <meta charset=\"UTF-8\">\n</head>\n\n<!-- ✅ Charset as first element in head -->\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Page</title>\n</head>\n```\n\n### Viewport meta tag\n\n```html\n<!-- ❌ Missing viewport -->\n<head>\n  <title>Page</title>\n</head>\n\n<!-- ✅ Responsive viewport -->\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>Page</title>\n</head>\n```\n\n### Feature detection\n\n```javascript\n// ❌ Browser detection (brittle)\nif (navigator.userAgent.includes('Chrome')) {\n  // Chrome-specific code\n}\n\n// ✅ Feature detection\nif ('IntersectionObserver' in window) {\n  // Use IntersectionObserver\n} else {\n  // Fallback\n}\n\n// ✅ Using @supports in CSS\n@supports (display: grid) {\n  .container {\n    display: grid;\n  }\n}\n\n@supports not (display: grid) {\n  .container {\n    display: flex;\n  }\n}\n```\n\n### Polyfills (when needed)\n\n```html\n<!-- Load polyfills conditionally -->\n<script>\n  if (!('fetch' in window)) {\n    document.write('<script src=\"/polyfills/fetch.js\"><\\/script>');\n  }\n</script>\n\n<!-- Or use polyfill.io -->\n<script src=\"https://polyfill.io/v3/polyfill.min.js?features=fetch,IntersectionObserver\"></script>\n```\n\n---\n\n## Deprecated APIs\n\n### Avoid these\n\n```javascript\n// ❌ document.write (blocks parsing)\ndocument.write('<script src=\"...\"></script>');\n\n// ✅ Dynamic script loading\nconst script = document.createElement('script');\nscript.src = '...';\ndocument.head.appendChild(script);\n\n// ❌ Synchronous XHR (blocks main thread)\nconst xhr = new XMLHttpRequest();\nxhr.open('GET', url, false); // false = synchronous\n\n// ✅ Async fetch\nconst response = await fetch(url);\n\n// ❌ Application Cache (deprecated)\n<html manifest=\"cache.manifest\">\n\n// ✅ Service Workers\nif ('serviceWorker' in navigator) {\n  navigator.serviceWorker.register('/sw.js');\n}\n```\n\n### Event listener passive\n\n```javascript\n// ❌ Non-passive touch/wheel (may block scrolling)\nelement.addEventListener('touchstart', handler);\nelement.addEventListener('wheel', handler);\n\n// ✅ Passive listeners (allows smooth scrolling)\nelement.addEventListener('touchstart', handler, { passive: true });\nelement.addEventListener('wheel', handler, { passive: true });\n\n// ✅ If you need preventDefault, be explicit\nelement.addEventListener('touchstart', handler, { passive: false });\n```\n\n---\n\n## Console & errors\n\n### No console errors\n\n```javascript\n// ❌ Errors in production\nconsole.log('Debug info'); // Remove in production\nthrow new Error('Unhandled'); // Catch all errors\n\n// ✅ Proper error handling\ntry {\n  riskyOperation();\n} catch (error) {\n  // Log to error tracking service\n  errorTracker.captureException(error);\n  // Show user-friendly message\n  showErrorMessage('Something went wrong. Please try again.');\n}\n```\n\n### Error boundaries (React)\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n  \n  static getDerivedStateFromError(error) {\n    return { hasError: true };\n  }\n  \n  componentDidCatch(error, info) {\n    errorTracker.captureException(error, { extra: info });\n  }\n  \n  render() {\n    if (this.state.hasError) {\n      return <FallbackUI />;\n    }\n    return this.props.children;\n  }\n}\n\n// Usage\n<ErrorBoundary>\n  <App />\n</ErrorBoundary>\n```\n\n### Global error handler\n\n```javascript\n// Catch unhandled errors\nwindow.addEventListener('error', (event) => {\n  errorTracker.captureException(event.error);\n});\n\n// Catch unhandled promise rejections\nwindow.addEventListener('unhandledrejection', (event) => {\n  errorTracker.captureException(event.reason);\n});\n```\n\n---\n\n## Source maps\n\n### Production configuration\n\n```javascript\n// ❌ Source maps exposed in production\n// webpack.config.js\nmodule.exports = {\n  devtool: 'source-map', // Exposes source code\n};\n\n// ✅ Hidden source maps (uploaded to error tracker)\nmodule.exports = {\n  devtool: 'hidden-source-map',\n};\n\n// ✅ Or no source maps in production\nmodule.exports = {\n  devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',\n};\n```\n\n---\n\n## Performance best practices\n\n### Avoid blocking patterns\n\n```javascript\n// ❌ Blocking script\n<script src=\"heavy-library.js\"></script>\n\n// ✅ Deferred script\n<script defer src=\"heavy-library.js\"></script>\n\n// ❌ Blocking CSS import\n@import url('other-styles.css');\n\n// ✅ Link tags (parallel loading)\n<link rel=\"stylesheet\" href=\"styles.css\">\n<link rel=\"stylesheet\" href=\"other-styles.css\">\n```\n\n### Efficient event handlers\n\n```javascript\n// ❌ Handler on every element\nitems.forEach(item => {\n  item.addEventListener('click', handleClick);\n});\n\n// ✅ Event delegation\ncontainer.addEventListener('click', (e) => {\n  if (e.target.matches('.item')) {\n    handleClick(e);\n  }\n});\n```\n\n### Memory management\n\n```javascript\n// ❌ Memory leak (never removed)\nconst handler = () => { /* ... */ };\nwindow.addEventListener('resize', handler);\n\n// ✅ Cleanup when done\nconst handler = () => { /* ... */ };\nwindow.addEventListener('resize', handler);\n\n// Later, when component unmounts:\nwindow.removeEventListener('resize', handler);\n\n// ✅ Using AbortController\nconst controller = new AbortController();\nwindow.addEventListener('resize', handler, { signal: controller.signal });\n\n// Cleanup:\ncontroller.abort();\n```\n\n---\n\n## Code quality\n\n### Valid HTML\n\n```html\n<!-- ❌ Invalid HTML -->\n<div id=\"header\">\n<div id=\"header\"> <!-- Duplicate ID -->\n\n<ul>\n  <div>Item</div> <!-- Invalid child -->\n</ul>\n\n<a href=\"/\"><button>Click</button></a> <!-- Invalid nesting -->\n\n<!-- ✅ Valid HTML -->\n<header id=\"site-header\">\n</header>\n\n<ul>\n  <li>Item</li>\n</ul>\n\n<a href=\"/\" class=\"button\">Click</a>\n```\n\n### Semantic HTML\n\n```html\n<!-- ❌ Non-semantic -->\n<div class=\"header\">\n  <div class=\"nav\">\n    <div class=\"nav-item\">Home</div>\n  </div>\n</div>\n<div class=\"main\">\n  <div class=\"article\">\n    <div class=\"title\">Headline</div>\n  </div>\n</div>\n\n<!-- ✅ Semantic HTML5 -->\n<header>\n  <nav>\n    <a href=\"/\">Home</a>\n  </nav>\n</header>\n<main>\n  <article>\n    <h1>Headline</h1>\n  </article>\n</main>\n```\n\n### Image aspect ratios\n\n```html\n<!-- ❌ Distorted images -->\n<img src=\"photo.jpg\" width=\"300\" height=\"100\">\n<!-- If actual ratio is 4:3, this squishes the image -->\n\n<!-- ✅ Preserve aspect ratio -->\n<img src=\"photo.jpg\" width=\"300\" height=\"225\">\n<!-- Actual 4:3 dimensions -->\n\n<!-- ✅ CSS object-fit for flexibility -->\n<img src=\"photo.jpg\" style=\"width: 300px; height: 200px; object-fit: cover;\">\n```\n\n---\n\n## Permissions & privacy\n\n### Request permissions properly\n\n```javascript\n// ❌ Request on page load (bad UX, often denied)\nnavigator.geolocation.getCurrentPosition(success, error);\n\n// ✅ Request in context, after user action\nfindNearbyButton.addEventListener('click', async () => {\n  // Explain why you need it\n  if (await showPermissionExplanation()) {\n    navigator.geolocation.getCurrentPosition(success, error);\n  }\n});\n```\n\n### Permissions policy\n\n```html\n<!-- Restrict powerful features -->\n<meta http-equiv=\"Permissions-Policy\" \n      content=\"geolocation=(), camera=(), microphone=()\">\n\n<!-- Or allow for specific origins -->\n<meta http-equiv=\"Permissions-Policy\" \n      content=\"geolocation=(self 'https://maps.example.com')\">\n```\n\n---\n\n## Audit checklist\n\n### Security (critical)\n- [ ] HTTPS enabled, no mixed content\n- [ ] No vulnerable dependencies (`npm audit`)\n- [ ] CSP headers configured\n- [ ] Security headers present\n- [ ] No exposed source maps\n\n### Compatibility\n- [ ] Valid HTML5 doctype\n- [ ] Charset declared first in head\n- [ ] Viewport meta tag present\n- [ ] No deprecated APIs used\n- [ ] Passive event listeners for scroll/touch\n\n### Code quality\n- [ ] No console errors\n- [ ] Valid HTML (no duplicate IDs)\n- [ ] Semantic HTML elements used\n- [ ] Proper error handling\n- [ ] Memory cleanup in components\n\n### UX\n- [ ] No intrusive interstitials\n- [ ] Permission requests in context\n- [ ] Clear error messages\n- [ ] Appropriate image aspect ratios\n\n## Tools\n\n| Tool | Purpose |\n|------|---------|\n| `npm audit` | Dependency vulnerabilities |\n| [SecurityHeaders.com](https://securityheaders.com) | Header analysis |\n| [W3C Validator](https://validator.w3.org) | HTML validation |\n| Lighthouse | Best practices audit |\n| [Observatory](https://observatory.mozilla.org) | Security scan |\n\n## References\n\n- [MDN Web Security](https://developer.mozilla.org/en-US/docs/Web/Security)\n- [OWASP Top 10](https://owasp.org/www-project-top-ten/)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n",".agents/skills/core-web-vitals/SKILL.md":"---\nname: core-web-vitals\ndescription: Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Core Web Vitals optimization\n\nTargeted optimization for the three Core Web Vitals metrics that affect Google Search ranking and user experience.\n\n## The three metrics\n\n| Metric | Measures | Good | Needs work | Poor |\n|--------|----------|------|------------|------|\n| **LCP** | Loading | ≤ 2.5s | 2.5s – 4s | > 4s |\n| **INP** | Interactivity | ≤ 200ms | 200ms – 500ms | > 500ms |\n| **CLS** | Visual Stability | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |\n\nGoogle measures at the **75th percentile** — 75% of page visits must meet \"Good\" thresholds.\n\n---\n\n## LCP: Largest Contentful Paint\n\nLCP measures when the largest visible content element renders. Usually this is:\n- Hero image or video\n- Large text block\n- Background image\n- `<svg>` element\n\n### Common LCP issues\n\n**1. Slow server response (TTFB > 800ms)**\n```\nFix: CDN, caching, optimized backend, edge rendering\n```\n\n**2. Render-blocking resources**\n```html\n<!-- ❌ Blocks rendering -->\n<link rel=\"stylesheet\" href=\"/all-styles.css\">\n\n<!-- ✅ Critical CSS inlined, rest deferred -->\n<style>/* Critical above-fold CSS */</style>\n<link rel=\"preload\" href=\"/styles.css\" as=\"style\" \n      onload=\"this.onload=null;this.rel='stylesheet'\">\n```\n\n**3. Slow resource load times**\n```html\n<!-- ❌ No hints, discovered late -->\n<img src=\"/hero.jpg\" alt=\"Hero\">\n\n<!-- ✅ Preloaded with high priority -->\n<link rel=\"preload\" href=\"/hero.webp\" as=\"image\" fetchpriority=\"high\">\n<img src=\"/hero.webp\" alt=\"Hero\" fetchpriority=\"high\">\n```\n\n**4. Client-side rendering delays**\n```javascript\n// ❌ Content loads after JavaScript\nuseEffect(() => {\n  fetch('/api/hero-text').then(r => r.json()).then(setHeroText);\n}, []);\n\n// ✅ Server-side or static rendering\n// Use SSR, SSG, or streaming to send HTML with content\nexport async function getServerSideProps() {\n  const heroText = await fetchHeroText();\n  return { props: { heroText } };\n}\n```\n\n### LCP optimization checklist\n\n```markdown\n- [ ] TTFB < 800ms (use CDN, edge caching)\n- [ ] LCP image preloaded with fetchpriority=\"high\"\n- [ ] LCP image optimized (WebP/AVIF, correct size)\n- [ ] Critical CSS inlined (< 14KB)\n- [ ] No render-blocking JavaScript in <head>\n- [ ] Fonts don't block text rendering (font-display: swap)\n- [ ] LCP element in initial HTML (not JS-rendered)\n```\n\n### LCP element identification\n```javascript\n// Find your LCP element\nnew PerformanceObserver((list) => {\n  const entries = list.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  console.log('LCP element:', lastEntry.element);\n  console.log('LCP time:', lastEntry.startTime);\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n---\n\n## INP: Interaction to Next Paint\n\nINP measures responsiveness across ALL interactions (clicks, taps, key presses) during a page visit. It reports the worst interaction (at 98th percentile for high-traffic pages).\n\n### INP breakdown\n\nTotal INP = **Input Delay** + **Processing Time** + **Presentation Delay**\n\n| Phase | Target | Optimization |\n|-------|--------|--------------|\n| Input Delay | < 50ms | Reduce main thread blocking |\n| Processing | < 100ms | Optimize event handlers |\n| Presentation | < 50ms | Minimize rendering work |\n\n### Common INP issues\n\n**1. Long tasks blocking main thread**\n```javascript\n// ❌ Long synchronous task\nfunction processLargeArray(items) {\n  items.forEach(item => expensiveOperation(item));\n}\n\n// ✅ Break into chunks with yielding\nasync function processLargeArray(items) {\n  const CHUNK_SIZE = 100;\n  for (let i = 0; i < items.length; i += CHUNK_SIZE) {\n    const chunk = items.slice(i, i + CHUNK_SIZE);\n    chunk.forEach(item => expensiveOperation(item));\n    \n    // Yield to main thread\n    await new Promise(r => setTimeout(r, 0));\n    // Or use scheduler.yield() when available\n  }\n}\n```\n\n**2. Heavy event handlers**\n```javascript\n// ❌ All work in handler\nbutton.addEventListener('click', () => {\n  // Heavy computation\n  const result = calculateComplexThing();\n  // DOM updates\n  updateUI(result);\n  // Analytics\n  trackEvent('click');\n});\n\n// ✅ Prioritize visual feedback\nbutton.addEventListener('click', () => {\n  // Immediate visual feedback\n  button.classList.add('loading');\n  \n  // Defer non-critical work\n  requestAnimationFrame(() => {\n    const result = calculateComplexThing();\n    updateUI(result);\n  });\n  \n  // Use requestIdleCallback for analytics\n  requestIdleCallback(() => trackEvent('click'));\n});\n```\n\n**3. Third-party scripts**\n```javascript\n// ❌ Eagerly loaded, blocks interactions\n<script src=\"https://heavy-widget.com/widget.js\"></script>\n\n// ✅ Lazy loaded on interaction or visibility\nconst loadWidget = () => {\n  import('https://heavy-widget.com/widget.js')\n    .then(widget => widget.init());\n};\nbutton.addEventListener('click', loadWidget, { once: true });\n```\n\n**4. Excessive re-renders (React/Vue)**\n```javascript\n// ❌ Re-renders entire tree\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Counter count={count} />\n      <ExpensiveComponent /> {/* Re-renders on every count change */}\n    </div>\n  );\n}\n\n// ✅ Memoized expensive components\nconst MemoizedExpensive = React.memo(ExpensiveComponent);\n\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Counter count={count} />\n      <MemoizedExpensive />\n    </div>\n  );\n}\n```\n\n### INP optimization checklist\n\n```markdown\n- [ ] No tasks > 50ms on main thread\n- [ ] Event handlers complete quickly (< 100ms)\n- [ ] Visual feedback provided immediately\n- [ ] Heavy work deferred with requestIdleCallback\n- [ ] Third-party scripts don't block interactions\n- [ ] Debounced input handlers where appropriate\n- [ ] Web Workers for CPU-intensive operations\n```\n\n### INP debugging\n```javascript\n// Identify slow interactions\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (entry.duration > 200) {\n      console.warn('Slow interaction:', {\n        type: entry.name,\n        duration: entry.duration,\n        processingStart: entry.processingStart,\n        processingEnd: entry.processingEnd,\n        target: entry.target\n      });\n    }\n  }\n}).observe({ type: 'event', buffered: true, durationThreshold: 16 });\n```\n\n---\n\n## CLS: Cumulative Layout Shift\n\nCLS measures unexpected layout shifts. A shift occurs when a visible element changes position between frames without user interaction.\n\n**CLS Formula:** `impact fraction × distance fraction`\n\n### Common CLS causes\n\n**1. Images without dimensions**\n```html\n<!-- ❌ Causes layout shift when loaded -->\n<img src=\"photo.jpg\" alt=\"Photo\">\n\n<!-- ✅ Space reserved -->\n<img src=\"photo.jpg\" alt=\"Photo\" width=\"800\" height=\"600\">\n\n<!-- ✅ Or use aspect-ratio -->\n<img src=\"photo.jpg\" alt=\"Photo\" style=\"aspect-ratio: 4/3; width: 100%;\">\n```\n\n**2. Ads, embeds, and iframes**\n```html\n<!-- ❌ Unknown size until loaded -->\n<iframe src=\"https://ad-network.com/ad\"></iframe>\n\n<!-- ✅ Reserve space with min-height -->\n<div style=\"min-height: 250px;\">\n  <iframe src=\"https://ad-network.com/ad\" height=\"250\"></iframe>\n</div>\n\n<!-- ✅ Or use aspect-ratio container -->\n<div style=\"aspect-ratio: 16/9;\">\n  <iframe src=\"https://youtube.com/embed/...\" \n          style=\"width: 100%; height: 100%;\"></iframe>\n</div>\n```\n\n**3. Dynamically injected content**\n```javascript\n// ❌ Inserts content above viewport\nnotifications.prepend(newNotification);\n\n// ✅ Insert below viewport or use transform\nconst insertBelow = viewport.bottom < newNotification.top;\nif (insertBelow) {\n  notifications.prepend(newNotification);\n} else {\n  // Animate in without shifting\n  newNotification.style.transform = 'translateY(-100%)';\n  notifications.prepend(newNotification);\n  requestAnimationFrame(() => {\n    newNotification.style.transform = '';\n  });\n}\n```\n\n**4. Web fonts causing FOUT**\n```css\n/* ❌ Font swap shifts text */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n}\n\n/* ✅ Optional font (no shift if slow) */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: optional;\n}\n\n/* ✅ Or match fallback metrics */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: swap;\n  size-adjust: 105%; /* Match fallback size */\n  ascent-override: 95%;\n  descent-override: 20%;\n}\n```\n\n**5. Animations triggering layout**\n```css\n/* ❌ Animates layout properties */\n.animate {\n  transition: height 0.3s, width 0.3s;\n}\n\n/* ✅ Use transform instead */\n.animate {\n  transition: transform 0.3s;\n}\n.animate.expanded {\n  transform: scale(1.2);\n}\n```\n\n### CLS optimization checklist\n\n```markdown\n- [ ] All images have width/height or aspect-ratio\n- [ ] All videos/embeds have reserved space\n- [ ] Ads have min-height containers\n- [ ] Fonts use font-display: optional or matched metrics\n- [ ] Dynamic content inserted below viewport\n- [ ] Animations use transform/opacity only\n- [ ] No content injected above existing content\n```\n\n### CLS debugging\n```javascript\n// Track layout shifts\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (!entry.hadRecentInput) {\n      console.log('Layout shift:', entry.value);\n      entry.sources?.forEach(source => {\n        console.log('  Shifted element:', source.node);\n        console.log('  Previous rect:', source.previousRect);\n        console.log('  Current rect:', source.currentRect);\n      });\n    }\n  }\n}).observe({ type: 'layout-shift', buffered: true });\n```\n\n---\n\n## Measurement tools\n\n### Lab testing\n- **Chrome DevTools** → Performance panel, Lighthouse\n- **WebPageTest** → Detailed waterfall, filmstrip\n- **Lighthouse CLI** → `npx lighthouse <url>`\n\n### Field data (real users)\n- **Chrome User Experience Report (CrUX)** → BigQuery or API\n- **Search Console** → Core Web Vitals report\n- **web-vitals library** → Send to your analytics\n\n```javascript\nimport {onLCP, onINP, onCLS} from 'web-vitals';\n\nfunction sendToAnalytics({name, value, rating}) {\n  gtag('event', name, {\n    event_category: 'Web Vitals',\n    value: Math.round(name === 'CLS' ? value * 1000 : value),\n    event_label: rating\n  });\n}\n\nonLCP(sendToAnalytics);\nonINP(sendToAnalytics);\nonCLS(sendToAnalytics);\n```\n\n---\n\n## Framework quick fixes\n\n### Next.js\n```jsx\n// LCP: Use next/image with priority\nimport Image from 'next/image';\n<Image src=\"/hero.jpg\" priority fill alt=\"Hero\" />\n\n// INP: Use dynamic imports\nconst HeavyComponent = dynamic(() => import('./Heavy'), { ssr: false });\n\n// CLS: Image component handles dimensions automatically\n```\n\n### React\n```jsx\n// LCP: Preload in head\n<link rel=\"preload\" href=\"/hero.jpg\" as=\"image\" fetchpriority=\"high\" />\n\n// INP: Memoize and useTransition\nconst [isPending, startTransition] = useTransition();\nstartTransition(() => setExpensiveState(newValue));\n\n// CLS: Always specify dimensions in img tags\n```\n\n### Vue/Nuxt\n```vue\n<!-- LCP: Use nuxt/image with preload -->\n<NuxtImg src=\"/hero.jpg\" preload loading=\"eager\" />\n\n<!-- INP: Use async components -->\n<component :is=\"() => import('./Heavy.vue')\" />\n\n<!-- CLS: Use aspect-ratio CSS -->\n<img :style=\"{ aspectRatio: '16/9' }\" />\n```\n\n## References\n\n- [web.dev LCP](https://web.dev/articles/lcp)\n- [web.dev INP](https://web.dev/articles/inp)\n- [web.dev CLS](https://web.dev/articles/cls)\n- [Performance skill](../performance/SKILL.md)\n",".agents/skills/core-web-vitals/references/LCP.md":"# LCP optimization reference\n\n## What is LCP?\n\nLargest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:\n\n- An `<img>` element\n- An `<image>` element inside `<svg>`\n- A `<video>` element with poster image\n- An element with a background image via `url()`\n- A block-level element containing text nodes\n\n## LCP timeline\n\n```\n[  Server Response  ][  Resource Load  ][  Render  ]\n       TTFB              Download         Paint\n       └─────────────────────────────────────┘\n                         LCP Time\n```\n\n## Detailed optimizations\n\n### 1. Server response time (TTFB)\n\nTarget: < 800ms\n\n**Causes:**\n- Slow server/database queries\n- No CDN/edge caching\n- Inefficient backend code\n- Cold starts (serverless)\n\n**Solutions:**\n```javascript\n// Use edge functions for dynamic content\n// Vercel example\nexport const config = { runtime: 'edge' };\n\n// Use stale-while-revalidate caching\n// Cache-Control header\nres.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate=300');\n```\n\n### 2. Resource load time\n\n**For images:**\n```html\n<!-- Preload LCP image -->\n<link rel=\"preload\" as=\"image\" href=\"/hero.webp\" \n      imagesrcset=\"/hero-400.webp 400w, /hero-800.webp 800w\"\n      imagesizes=\"100vw\"\n      fetchpriority=\"high\">\n\n<!-- Modern format with fallback -->\n<picture>\n  <source srcset=\"/hero.avif\" type=\"image/avif\">\n  <source srcset=\"/hero.webp\" type=\"image/webp\">\n  <img src=\"/hero.jpg\" width=\"1200\" height=\"600\" \n       fetchpriority=\"high\" alt=\"Hero\">\n</picture>\n```\n\n**For text (web fonts):**\n```css\n@font-face {\n  font-family: 'Heading';\n  src: url('/fonts/heading.woff2') format('woff2');\n  font-display: swap; /* Show fallback immediately */\n}\n```\n\n### 3. Render blocking resources\n\n**Critical CSS pattern:**\n```html\n<head>\n  <!-- Inline critical CSS -->\n  <style>\n    /* Only above-fold styles, < 14KB */\n    .hero { /* ... */ }\n    .nav { /* ... */ }\n  </style>\n  \n  <!-- Defer non-critical CSS -->\n  <link rel=\"preload\" href=\"/styles.css\" as=\"style\" \n        onload=\"this.onload=null;this.rel='stylesheet'\">\n</head>\n```\n\n**Defer JavaScript:**\n```html\n<!-- ❌ Blocks parsing -->\n<script src=\"/app.js\"></script>\n\n<!-- ✅ Deferred (runs after HTML parsed) -->\n<script defer src=\"/app.js\"></script>\n\n<!-- ✅ Module (deferred by default) -->\n<script type=\"module\" src=\"/app.mjs\"></script>\n```\n\n### 4. Client-side rendering\n\n**Problem:** Content not in initial HTML.\n\n**Solutions:**\n\n**Server-side rendering (SSR):**\n```javascript\n// Next.js\nexport async function getServerSideProps() {\n  const data = await fetchHeroContent();\n  return { props: { hero: data } };\n}\n```\n\n**Static site generation (SSG):**\n```javascript\n// Next.js\nexport async function getStaticProps() {\n  const data = await fetchHeroContent();\n  return { props: { hero: data }, revalidate: 3600 };\n}\n```\n\n**Streaming SSR:**\n```jsx\n// React 18+\nimport { Suspense } from 'react';\n\nfunction Page() {\n  return (\n    <Suspense fallback={<HeroSkeleton />}>\n      <Hero />\n    </Suspense>\n  );\n}\n```\n\n## Framework-specific tips\n\n### Next.js\n```jsx\nimport Image from 'next/image';\n\n// LCP image with priority\n<Image \n  src=\"/hero.jpg\"\n  priority\n  fill\n  sizes=\"100vw\"\n  alt=\"Hero\"\n/>\n```\n\n### Nuxt\n```vue\n<NuxtImg\n  src=\"/hero.jpg\"\n  preload\n  loading=\"eager\"\n  sizes=\"100vw\"\n/>\n```\n\n### Astro\n```astro\n---\nimport { Image } from 'astro:assets';\nimport hero from '../assets/hero.jpg';\n---\n<Image \n  src={hero} \n  loading=\"eager\" \n  decoding=\"sync\"\n  alt=\"Hero\" \n/>\n```\n\n## Debugging LCP\n\n```javascript\n// Identify LCP element\nnew PerformanceObserver((entryList) => {\n  const entries = entryList.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  \n  console.log('LCP:', {\n    element: lastEntry.element,\n    time: lastEntry.startTime,\n    size: lastEntry.size,\n    url: lastEntry.url,\n    renderTime: lastEntry.renderTime,\n    loadTime: lastEntry.loadTime\n  });\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n## Common issues\n\n| Issue | Impact | Fix |\n|-------|--------|-----|\n| No preload for LCP image | +500-1000ms | Add `<link rel=\"preload\">` |\n| Large unoptimized image | +300-800ms | Compress, use WebP/AVIF |\n| Render-blocking CSS | +200-500ms | Inline critical CSS |\n| Slow TTFB | +300-2000ms | CDN, edge caching |\n| Client-rendered content | +500-2000ms | SSR/SSG |\n",".agents/skills/frontend-design/SKILL.md":"---\nname: frontend-design\ndescription: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.\nlicense: Complete terms in LICENSE.txt\n---\n\nThis skill guides creation of distinctive, production-grade frontend interfaces that avoid generic \"AI slop\" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.\n\nThe user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.\n\n## Design Thinking\n\nBefore coding, understand the context and commit to a BOLD aesthetic direction:\n- **Purpose**: What problem does this interface solve? Who uses it?\n- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.\n- **Constraints**: Technical requirements (framework, performance, accessibility).\n- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?\n\n**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.\n\nThen implement working code (HTML/CSS/JS, React, Vue, etc.) that is:\n- Production-grade and functional\n- Visually striking and memorable\n- Cohesive with a clear aesthetic point-of-view\n- Meticulously refined in every detail\n\n## Frontend Aesthetics Guidelines\n\nFocus on:\n- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.\n- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.\n- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.\n- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.\n- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.\n\nNEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.\n\nInterpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.\n\n**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.\n\nRemember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.\n",".agents/skills/migrate-oxlint/SKILL.md":"---\nname: migrate-oxlint\ndescription: Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint.\n---\n\nThis skill guides you through migrating a JavaScript/TypeScript project from ESLint to [Oxlint](https://oxc.rs/docs/guide/usage/linter/).\n\n## Overview\n\nOxlint is a high-performance linter that implements many popular ESLint rules natively in Rust. It can be used alongside ESLint or as a full replacement.\n\nAn official migration tool is available, and will be used by this skill: [`@oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate)\n\n## Step 1: Run Automated Migration\n\nRun the migration tool in the project root:\n\n```bash\nnpx @oxlint/migrate\n```\n\nThis reads your ESLint flat config (`eslint.config.js` for example) and generates a `.oxlintrc.json` file from it. It will find your ESLint config file automatically in most cases.\n\nSee options below for more info.\n\n### Key Options\n\n| Option                      | Description                                                                                                                     |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `--type-aware`              | Include type-aware rules from `@typescript-eslint` (will require the `oxlint-tsgolint` package to be installed after migrating) |\n| `--with-nursery`            | Include experimental rules still under development, may not be fully stable or consistent with ESLint equivalents               |\n| `--js-plugins [bool]`       | Enable/disable ESLint plugin migration via `jsPlugins` (default: enabled)                                                       |\n| `--details`                 | List rules that could not be migrated                                                                                           |\n| `--replace-eslint-comments` | Convert all `// eslint-disable` comments to `// oxlint-disable`                                                                 |\n| `--output-file <file>`      | Specify a different output path (default: `.oxlintrc.json`)                                                                     |\n\nIf your ESLint config is not at the default location, pass the path explicitly:\n\n```bash\nnpx @oxlint/migrate ./path/to/eslint.config.js\n```\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxlintrc.json`.\n\n### Plugin Mapping\n\nThe migration tool automatically maps ESLint plugins to oxlint's built-in equivalents. The following table is for reference when reviewing the generated config:\n\n| ESLint Plugin                                       | Oxlint Plugin Name |\n| --------------------------------------------------- | ------------------ |\n| `@typescript-eslint/eslint-plugin`                  | `typescript`       |\n| `eslint-plugin-react` / `eslint-plugin-react-hooks` | `react`            |\n| `eslint-plugin-import` / `eslint-plugin-import-x`   | `import`           |\n| `eslint-plugin-unicorn`                             | `unicorn`          |\n| `eslint-plugin-jsx-a11y`                            | `jsx-a11y`         |\n| `eslint-plugin-react-perf`                          | `react-perf`       |\n| `eslint-plugin-promise`                             | `promise`          |\n| `eslint-plugin-jest`                                | `jest`             |\n| `@vitest/eslint-plugin`                             | `vitest`           |\n| `eslint-plugin-jsdoc`                               | `jsdoc`            |\n| `eslint-plugin-next`                                | `nextjs`           |\n| `eslint-plugin-node`                                | `node`             |\n| `eslint-plugin-vue`                                 | `vue`              |\n\nDefault plugins (enabled when `plugins` field is omitted): `unicorn`, `typescript`, `oxc`.\nSetting the `plugins` array explicitly overrides these defaults.\n\nESLint core rules are usable in oxlint without needing to configure a plugin in the config file.\n\n### Rule Categories\n\nOxlint groups rules into categories for bulk configuration, though only `correctness` is enabled by default:\n\n```json\n{\n  \"categories\": {\n    \"correctness\": \"error\",\n    \"suspicious\": \"warn\"\n  }\n}\n```\n\nAvailable categories: `correctness` (default: enabled), `suspicious`, `pedantic`, `perf`, `style`, `restriction`, `nursery`.\n\nIndividual rule settings in `rules` override category settings.\n\n`@oxlint/migrate` will turn `correctness` off to avoid enabling additional rules that weren't enabled by your ESLint config. You can choose to enable additional categories after migration if desired.\n\n### Check Unmigrated Rules\n\nRun with `--details` to see which ESLint rules could not be migrated:\n\n```bash\nnpx @oxlint/migrate --details\n```\n\nReview the output and decide whether to keep ESLint for those rules or not. Some rules may be mentioned in the output from `--details` as having equivalents in oxlint that were not automatically mapped by the migration tool. In those cases, consider enabling the equivalent oxlint rule manually after migration.\n\n## Step 3: Install Oxlint\n\nInstall the core oxlint package (use `yarn install`, `pnpm install`, `vp install`, `bun install`, etc. depending on your package manager):\n\n```bash\nnpm install -D oxlint\n```\n\nIf you want to add the `oxlint-tsgolint` package, if you intend to use type-aware rules that require TypeScript type information:\n\n```bash\nnpm install -D oxlint-tsgolint\n```\n\nNo other packages besides the above are needed by default, though you will need to keep/install any additional ESLint plugins that were migrated into `jsPlugins`. Do not add `@oxlint/migrate` to the package.json, it is meant for one-off usage.\n\n## Step 4: Handle Unsupported Features\n\nSome features require manual attention:\n\n- Local plugins (relative path imports): Must be migrated manually to `jsPlugins`\n- `eslint-plugin-prettier`: Supported, but very slow. It is recommended to use [oxfmt](https://oxc.rs/docs/guide/usage/formatter) instead, or switch to `prettier --check` as a separate step alongside oxlint.\n- `settings` in override configs: Oxlint does not support `settings` inside `overrides` blocks.\n- ESLint v9+ plugins: Not all work with oxlint's JS Plugins API, but the majority will.\n\n### Local Plugins\n\nIf you have any custom ESLint rules in the project repo itself, you can migrate them manually after running the migration tool by adding them to the `jsPlugins` field in `.oxlintrc.json`:\n\n```json\n{\n  \"jsPlugins\": [\"./path/to/my-plugin.js\"],\n  \"rules\": {\n    \"local-plugin/rule-name\": \"error\"\n  }\n}\n```\n\n### External ESLint Plugins\n\nFor ESLint plugins without a built-in oxlint equivalent, use the `jsPlugins` field to load them:\n\n```json\n{\n  \"jsPlugins\": [\"eslint-plugin-custom\"],\n  \"rules\": {\n    \"custom/my-rule\": \"warn\"\n  }\n}\n```\n\n## Step 5: Update CI and Scripts\n\nReplace ESLint commands with oxlint. Path arguments are optional; oxlint defaults to the current working directory.\n\n```bash\n# Before\nnpx eslint src/\nnpx eslint --fix src/\n\n# After\nnpx oxlint src/\nnpx oxlint --fix src/\n```\n\n### Common CLI Options\n\n| ESLint                    | oxlint equivalent                              |\n| ------------------------- | ---------------------------------------------- |\n| `eslint .`                | `oxlint` (default: lints the cwd)              |\n| `eslint src/`             | `oxlint src/`                                  |\n| `eslint --fix`            | `oxlint --fix`                                 |\n| `eslint --max-warnings 0` | `oxlint --deny-warnings` or `--max-warnings 0` |\n| `eslint --format json`    | `oxlint --format json`                         |\n\nAdditional oxlint options:\n\n- `--tsconfig <path>`: Specify tsconfig.json path, likely unnecessary unless you have a non-standard name for `tsconfig.json`.\n\n## Tips\n\n- You can run alongside ESLint if necessary: Oxlint is designed to complement ESLint during migration, but with JS Plugins many projects can switch over fully without losing many rules.\n- Disable comments work: `// eslint-disable` and `// eslint-disable-next-line` comments are supported by oxlint. Use `--replace-eslint-comments` when running @oxlint/migrate to convert them to `// oxlint-disable` equivalents if desired.\n- List available rules: Run `npx oxlint --rules` to see all supported rules, or refer to the [rule documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).\n- Schema support: Add `\"$schema\": \"./node_modules/oxlint/configuration_schema.json\"` to `.oxlintrc.json` for editor autocompletion if the migration tool didn't do it automatically.\n- Output formats: `default`, `stylish`, `json`, `github`, `gitlab`, `junit`, `checkstyle`, `unix`\n- Ignore files: `.eslintignore` is supported by oxlint if you have it, but it's recommended to move any ignore patterns into the `ignorePatterns` field in `.oxlintrc.json` for consistency and simplicity. All files and paths ignored via a `.gitignore` file will be ignored by oxlint by default as well.\n- If you ran the migration tool multiple times, remove the `.oxlintrc.json.bak` backup file created by the migration tool once you've finished migrating.\n- If you are not using any JS Plugins and have replaced your ESLint configuration, you can remove all ESLint packages from your project dependencies.\n- Ensure your editor is configured to use oxlint instead of ESLint for linting and error reporting. You may want to install the Oxc extension for your preferred editor. See https://oxc.rs/docs/guide/usage/linter/editors.html for more details.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/linter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html)\n- [Complete Oxlint rule list and docs](https://oxc.rs/docs/guide/usage/linter/rules.html)\n",".agents/skills/next-best-practices/SKILL.md":"---\nname: next-best-practices\ndescription: Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling\nuser-invocable: false\n---\n\n# Next.js Best Practices\n\nApply these rules when writing or reviewing Next.js code.\n\n## File Conventions\n\nSee [file-conventions.md](./file-conventions.md) for:\n- Project structure and special files\n- Route segments (dynamic, catch-all, groups)\n- Parallel and intercepting routes\n- Middleware rename in v16 (middleware → proxy)\n\n## RSC Boundaries\n\nDetect invalid React Server Component patterns.\n\nSee [rsc-boundaries.md](./rsc-boundaries.md) for:\n- Async client component detection (invalid)\n- Non-serializable props detection\n- Server Action exceptions\n\n## Async Patterns\n\nNext.js 15+ async API changes.\n\nSee [async-patterns.md](./async-patterns.md) for:\n- Async `params` and `searchParams`\n- Async `cookies()` and `headers()`\n- Migration codemod\n\n## Runtime Selection\n\nSee [runtime-selection.md](./runtime-selection.md) for:\n- Default to Node.js runtime\n- When Edge runtime is appropriate\n\n## Directives\n\nSee [directives.md](./directives.md) for:\n- `'use client'`, `'use server'` (React)\n- `'use cache'` (Next.js)\n\n## Functions\n\nSee [functions.md](./functions.md) for:\n- Navigation hooks: `useRouter`, `usePathname`, `useSearchParams`, `useParams`\n- Server functions: `cookies`, `headers`, `draftMode`, `after`\n- Generate functions: `generateStaticParams`, `generateMetadata`\n\n## Error Handling\n\nSee [error-handling.md](./error-handling.md) for:\n- `error.tsx`, `global-error.tsx`, `not-found.tsx`\n- `redirect`, `permanentRedirect`, `notFound`\n- `forbidden`, `unauthorized` (auth errors)\n- `unstable_rethrow` for catch blocks\n\n## Data Patterns\n\nSee [data-patterns.md](./data-patterns.md) for:\n- Server Components vs Server Actions vs Route Handlers\n- Avoiding data waterfalls (`Promise.all`, Suspense, preload)\n- Client component data fetching\n\n## Route Handlers\n\nSee [route-handlers.md](./route-handlers.md) for:\n- `route.ts` basics\n- GET handler conflicts with `page.tsx`\n- Environment behavior (no React DOM)\n- When to use vs Server Actions\n\n## Metadata & OG Images\n\nSee [metadata.md](./metadata.md) for:\n- Static and dynamic metadata\n- `generateMetadata` function\n- OG image generation with `next/og`\n- File-based metadata conventions\n\n## Image Optimization\n\nSee [image.md](./image.md) for:\n- Always use `next/image` over `<img>`\n- Remote images configuration\n- Responsive `sizes` attribute\n- Blur placeholders\n- Priority loading for LCP\n\n## Font Optimization\n\nSee [font.md](./font.md) for:\n- `next/font` setup\n- Google Fonts, local fonts\n- Tailwind CSS integration\n- Preloading subsets\n\n## Bundling\n\nSee [bundling.md](./bundling.md) for:\n- Server-incompatible packages\n- CSS imports (not link tags)\n- Polyfills (already included)\n- ESM/CommonJS issues\n- Bundle analysis\n\n## Scripts\n\nSee [scripts.md](./scripts.md) for:\n- `next/script` vs native script tags\n- Inline scripts need `id`\n- Loading strategies\n- Google Analytics with `@next/third-parties`\n\n## Hydration Errors\n\nSee [hydration-error.md](./hydration-error.md) for:\n- Common causes (browser APIs, dates, invalid HTML)\n- Debugging with error overlay\n- Fixes for each cause\n\n## Suspense Boundaries\n\nSee [suspense-boundaries.md](./suspense-boundaries.md) for:\n- CSR bailout with `useSearchParams` and `usePathname`\n- Which hooks require Suspense boundaries\n\n## Parallel & Intercepting Routes\n\nSee [parallel-routes.md](./parallel-routes.md) for:\n- Modal patterns with `@slot` and `(.)` interceptors\n- `default.tsx` for fallbacks\n- Closing modals correctly with `router.back()`\n\n## Self-Hosting\n\nSee [self-hosting.md](./self-hosting.md) for:\n- `output: 'standalone'` for Docker\n- Cache handlers for multi-instance ISR\n- What works vs needs extra setup\n\n## Debug Tricks\n\nSee [debug-tricks.md](./debug-tricks.md) for:\n- MCP endpoint for AI-assisted debugging\n- Rebuild specific routes with `--debug-build-paths`\n\n",".agents/skills/next-best-practices/async-patterns.md":"# Async Patterns\n\nIn Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.\n\n## Async Params and SearchParams\n\nAlways type them as `Promise<...>` and await them.\n\n### Pages and Layouts\n\n```tsx\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default async function Page({ params }: Props) {\n  const { slug } = await params\n}\n```\n\n### Route Handlers\n\n```tsx\nexport async function GET(\n  request: Request,\n  { params }: { params: Promise<{ id: string }> }\n) {\n  const { id } = await params\n}\n```\n\n### SearchParams\n\n```tsx\ntype Props = {\n  params: Promise<{ slug: string }>\n  searchParams: Promise<{ query?: string }>\n}\n\nexport default async function Page({ params, searchParams }: Props) {\n  const { slug } = await params\n  const { query } = await searchParams\n}\n```\n\n### Synchronous Components\n\nUse `React.use()` for non-async components:\n\n```tsx\nimport { use } from 'react'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default function Page({ params }: Props) {\n  const { slug } = use(params)\n}\n```\n\n### generateMetadata\n\n```tsx\ntype Props = { params: Promise<{ slug: string }> }\n\nexport async function generateMetadata({ params }: Props): Promise<Metadata> {\n  const { slug } = await params\n  return { title: slug }\n}\n```\n\n## Async Cookies and Headers\n\n```tsx\nimport { cookies, headers } from 'next/headers'\n\nexport default async function Page() {\n  const cookieStore = await cookies()\n  const headersList = await headers()\n\n  const theme = cookieStore.get('theme')\n  const userAgent = headersList.get('user-agent')\n}\n```\n\n## Migration Codemod\n\n```bash\nnpx @next/codemod@latest next-async-request-api .\n```\n",".agents/skills/next-best-practices/bundling.md":"# Bundling\n\nFix common bundling issues with third-party packages.\n\n## Server-Incompatible Packages\n\nSome packages use browser APIs (`window`, `document`, `localStorage`) and fail in Server Components.\n\n### Error Signs\n\n```\nReferenceError: window is not defined\nReferenceError: document is not defined\nReferenceError: localStorage is not defined\nModule not found: Can't resolve 'fs'\n```\n\n### Solution 1: Mark as Client-Only\n\nIf the package is only needed on client:\n\n```tsx\n// Bad: Fails - package uses window\nimport SomeChart from 'some-chart-library'\n\nexport default function Page() {\n  return <SomeChart />\n}\n\n// Good: Use dynamic import with ssr: false\nimport dynamic from 'next/dynamic'\n\nconst SomeChart = dynamic(() => import('some-chart-library'), {\n  ssr: false,\n})\n\nexport default function Page() {\n  return <SomeChart />\n}\n```\n\n### Solution 2: Externalize from Server Bundle\n\nFor packages that should run on server but have bundling issues:\n\n```js\n// next.config.js\nmodule.exports = {\n  serverExternalPackages: ['problematic-package'],\n}\n```\n\nUse this for:\n- Packages with native bindings (sharp, bcrypt)\n- Packages that don't bundle well (some ORMs)\n- Packages with circular dependencies\n\n### Solution 3: Client Component Wrapper\n\nWrap the entire usage in a client component:\n\n```tsx\n// components/ChartWrapper.tsx\n'use client'\n\nimport { Chart } from 'chart-library'\n\nexport function ChartWrapper(props) {\n  return <Chart {...props} />\n}\n\n// app/page.tsx (server component)\nimport { ChartWrapper } from '@/components/ChartWrapper'\n\nexport default function Page() {\n  return <ChartWrapper data={data} />\n}\n```\n\n## CSS Imports\n\nImport CSS files instead of using `<link>` tags. Next.js handles bundling and optimization.\n\n```tsx\n// Bad: Manual link tag\n<link rel=\"stylesheet\" href=\"/styles.css\" />\n\n// Good: Import CSS\nimport './styles.css'\n\n// Good: CSS Modules\nimport styles from './Button.module.css'\n```\n\n## Polyfills\n\nNext.js includes common polyfills automatically. Don't load redundant ones from polyfill.io or similar CDNs.\n\nAlready included: `Array.from`, `Object.assign`, `Promise`, `fetch`, `Map`, `Set`, `Symbol`, `URLSearchParams`, and 50+ others.\n\n```tsx\n// Bad: Redundant polyfills\n<script src=\"https://polyfill.io/v3/polyfill.min.js?features=fetch,Promise,Array.from\" />\n\n// Good: Next.js includes these automatically\n```\n\n## ESM/CommonJS Issues\n\n### Error Signs\n\n```\nSyntaxError: Cannot use import statement outside a module\nError: require() of ES Module\nModule not found: ESM packages need to be imported\n```\n\n### Solution: Transpile Package\n\n```js\n// next.config.js\nmodule.exports = {\n  transpilePackages: ['some-esm-package', 'another-package'],\n}\n```\n\n## Common Problematic Packages\n\n| Package | Issue | Solution |\n|---------|-------|----------|\n| `sharp` | Native bindings | `serverExternalPackages: ['sharp']` |\n| `bcrypt` | Native bindings | `serverExternalPackages: ['bcrypt']` or use `bcryptjs` |\n| `canvas` | Native bindings | `serverExternalPackages: ['canvas']` |\n| `recharts` | Uses window | `dynamic(() => import('recharts'), { ssr: false })` |\n| `react-quill` | Uses document | `dynamic(() => import('react-quill'), { ssr: false })` |\n| `mapbox-gl` | Uses window | `dynamic(() => import('mapbox-gl'), { ssr: false })` |\n| `monaco-editor` | Uses window | `dynamic(() => import('@monaco-editor/react'), { ssr: false })` |\n| `lottie-web` | Uses document | `dynamic(() => import('lottie-react'), { ssr: false })` |\n\n## Bundle Analysis\n\nAnalyze bundle size with the built-in analyzer (Next.js 16.1+):\n\n```bash\nnext experimental-analyze\n```\n\nThis opens an interactive UI to:\n- Filter by route, environment (client/server), and type\n- Inspect module sizes and import chains\n- View treemap visualization\n\nSave output for comparison:\n\n```bash\nnext experimental-analyze --output\n# Output saved to .next/diagnostics/analyze\n```\n\nReference: https://nextjs.org/docs/app/guides/package-bundling\n\n## Migrating from Webpack to Turbopack\n\nTurbopack is the default bundler in Next.js 15+. If you have custom webpack config, migrate to Turbopack-compatible alternatives:\n\n```js\n// next.config.js\nmodule.exports = {\n  // Good: Works with Turbopack\n  serverExternalPackages: ['package'],\n  transpilePackages: ['package'],\n\n  // Bad: Webpack-only - migrate away from this\n  webpack: (config) => {\n    // custom webpack config\n  },\n}\n```\n\nReference: https://nextjs.org/docs/app/building-your-application/upgrading/from-webpack-to-turbopack\n",".agents/skills/next-best-practices/data-patterns.md":"# Data Patterns\n\nChoose the right data fetching pattern for each use case.\n\n## Decision Tree\n\n```\nNeed to fetch data?\n├── From a Server Component?\n│   └── Use: Fetch directly (no API needed)\n│\n├── From a Client Component?\n│   ├── Is it a mutation (POST/PUT/DELETE)?\n│   │   └── Use: Server Action\n│   └── Is it a read (GET)?\n│       └── Use: Route Handler OR pass from Server Component\n│\n├── Need external API access (webhooks, third parties)?\n│   └── Use: Route Handler\n│\n└── Need REST API for mobile app / external clients?\n    └── Use: Route Handler\n```\n\n## Pattern 1: Server Components (Preferred for Reads)\n\nFetch data directly in Server Components - no API layer needed.\n\n```tsx\n// app/users/page.tsx\nasync function UsersPage() {\n  // Direct database access - no API round-trip\n  const users = await db.user.findMany();\n\n  // Or fetch from external API\n  const posts = await fetch('https://api.example.com/posts').then(r => r.json());\n\n  return (\n    <ul>\n      {users.map(user => <li key={user.id}>{user.name}</li>)}\n    </ul>\n  );\n}\n```\n\n**Benefits**:\n- No API to maintain\n- No client-server waterfall\n- Secrets stay on server\n- Direct database access\n\n## Pattern 2: Server Actions (Preferred for Mutations)\n\nServer Actions are the recommended way to handle mutations.\n\n```tsx\n// app/actions.ts\n'use server';\n\nimport { revalidatePath } from 'next/cache';\n\nexport async function createPost(formData: FormData) {\n  const title = formData.get('title') as string;\n\n  await db.post.create({ data: { title } });\n\n  revalidatePath('/posts');\n}\n\nexport async function deletePost(id: string) {\n  await db.post.delete({ where: { id } });\n\n  revalidateTag('posts');\n}\n```\n\n```tsx\n// app/posts/new/page.tsx\nimport { createPost } from '@/app/actions';\n\nexport default function NewPost() {\n  return (\n    <form action={createPost}>\n      <input name=\"title\" required />\n      <button type=\"submit\">Create</button>\n    </form>\n  );\n}\n```\n\n**Benefits**:\n- End-to-end type safety\n- Progressive enhancement (works without JS)\n- Automatic request handling\n- Integrated with React transitions\n\n**Constraints**:\n- POST only (no GET caching semantics)\n- Internal use only (no external access)\n- Cannot return non-serializable data\n\n## Pattern 3: Route Handlers (APIs)\n\nUse Route Handlers when you need a REST API.\n\n```tsx\n// app/api/posts/route.ts\nimport { NextRequest, NextResponse } from 'next/server';\n\n// GET is cacheable\nexport async function GET(request: NextRequest) {\n  const posts = await db.post.findMany();\n  return NextResponse.json(posts);\n}\n\n// POST for mutations\nexport async function POST(request: NextRequest) {\n  const body = await request.json();\n  const post = await db.post.create({ data: body });\n  return NextResponse.json(post, { status: 201 });\n}\n```\n\n**When to use**:\n- External API access (mobile apps, third parties)\n- Webhooks from external services\n- GET endpoints that need HTTP caching\n- OpenAPI/Swagger documentation needed\n\n**When NOT to use**:\n- Internal data fetching (use Server Components)\n- Mutations from your UI (use Server Actions)\n\n## Avoiding Data Waterfalls\n\n### Problem: Sequential Fetches\n\n```tsx\n// Bad: Sequential waterfalls\nasync function Dashboard() {\n  const user = await getUser();        // Wait...\n  const posts = await getPosts();      // Then wait...\n  const comments = await getComments(); // Then wait...\n\n  return <div>...</div>;\n}\n```\n\n### Solution 1: Parallel Fetching with Promise.all\n\n```tsx\n// Good: Parallel fetching\nasync function Dashboard() {\n  const [user, posts, comments] = await Promise.all([\n    getUser(),\n    getPosts(),\n    getComments(),\n  ]);\n\n  return <div>...</div>;\n}\n```\n\n### Solution 2: Streaming with Suspense\n\n```tsx\n// Good: Show content progressively\nimport { Suspense } from 'react';\n\nasync function Dashboard() {\n  return (\n    <div>\n      <Suspense fallback={<UserSkeleton />}>\n        <UserSection />\n      </Suspense>\n      <Suspense fallback={<PostsSkeleton />}>\n        <PostsSection />\n      </Suspense>\n    </div>\n  );\n}\n\nasync function UserSection() {\n  const user = await getUser(); // Fetches independently\n  return <div>{user.name}</div>;\n}\n\nasync function PostsSection() {\n  const posts = await getPosts(); // Fetches independently\n  return <PostList posts={posts} />;\n}\n```\n\n### Solution 3: Preload Pattern\n\n```tsx\n// lib/data.ts\nimport { cache } from 'react';\n\nexport const getUser = cache(async (id: string) => {\n  return db.user.findUnique({ where: { id } });\n});\n\nexport const preloadUser = (id: string) => {\n  void getUser(id); // Fire and forget\n};\n```\n\n```tsx\n// app/user/[id]/page.tsx\nimport { getUser, preloadUser } from '@/lib/data';\n\nexport default async function UserPage({ params }) {\n  const { id } = await params;\n\n  // Start fetching early\n  preloadUser(id);\n\n  // Do other work...\n\n  // Data likely ready by now\n  const user = await getUser(id);\n  return <div>{user.name}</div>;\n}\n```\n\n## Client Component Data Fetching\n\nWhen Client Components need data:\n\n### Option 1: Pass from Server Component (Preferred)\n\n```tsx\n// Server Component\nasync function Page() {\n  const data = await fetchData();\n  return <ClientComponent initialData={data} />;\n}\n\n// Client Component\n'use client';\nfunction ClientComponent({ initialData }) {\n  const [data, setData] = useState(initialData);\n  // ...\n}\n```\n\n### Option 2: Fetch on Mount (When Necessary)\n\n```tsx\n'use client';\nimport { useEffect, useState } from 'react';\n\nfunction ClientComponent() {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    fetch('/api/data')\n      .then(r => r.json())\n      .then(setData);\n  }, []);\n\n  if (!data) return <Loading />;\n  return <div>{data.value}</div>;\n}\n```\n\n### Option 3: Server Action for Reads (Works But Not Ideal)\n\nServer Actions can be called from Client Components for reads, but this is not their intended purpose:\n\n```tsx\n'use client';\nimport { getData } from './actions';\nimport { useEffect, useState } from 'react';\n\nfunction ClientComponent() {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    getData().then(setData);\n  }, []);\n\n  return <div>{data?.value}</div>;\n}\n```\n\n**Note**: Server Actions always use POST, so no HTTP caching. Prefer Route Handlers for cacheable reads.\n\n## Quick Reference\n\n| Pattern | Use Case | HTTP Method | Caching |\n|---------|----------|-------------|---------|\n| Server Component fetch | Internal reads | Any | Full Next.js caching |\n| Server Action | Mutations, form submissions | POST only | No |\n| Route Handler | External APIs, webhooks | Any | GET can be cached |\n| Client fetch to API | Client-side reads | Any | HTTP cache headers |\n",".agents/skills/next-best-practices/debug-tricks.md":"# Debug Tricks\n\nTricks to speed up debugging Next.js applications.\n\n## MCP Endpoint (Dev Server)\n\nNext.js exposes a `/_next/mcp` endpoint in development for AI-assisted debugging via MCP (Model Context Protocol).\n\n- **Next.js 16+**: Enabled by default, use `next-devtools-mcp`\n- **Next.js < 16**: Requires `experimental.mcpServer: true` in next.config.js\n\nReference: https://nextjs.org/docs/app/guides/mcp\n\n**Important**: Find the actual port of the running Next.js dev server (check terminal output or `package.json` scripts). Don't assume port 3000.\n\n### Request Format\n\nThe endpoint uses JSON-RPC 2.0 over HTTP POST:\n\n```bash\ncurl -X POST http://localhost:<port>/_next/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": \"1\",\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"<tool-name>\",\n      \"arguments\": {}\n    }\n  }'\n```\n\n### Available Tools\n\n#### `get_errors`\nGet current errors from dev server (build errors, runtime errors with source-mapped stacks):\n```json\n{ \"name\": \"get_errors\", \"arguments\": {} }\n```\n\n#### `get_routes`\nDiscover all routes by scanning filesystem:\n```json\n{ \"name\": \"get_routes\", \"arguments\": {} }\n// Optional: { \"name\": \"get_routes\", \"arguments\": { \"routerType\": \"app\" } }\n```\nReturns: `{ \"appRouter\": [\"/\", \"/api/users/[id]\", ...], \"pagesRouter\": [...] }`\n\n#### `get_project_metadata`\nGet project path and dev server URL:\n```json\n{ \"name\": \"get_project_metadata\", \"arguments\": {} }\n```\nReturns: `{ \"projectPath\": \"/path/to/project\", \"devServerUrl\": \"http://localhost:3000\" }`\n\n#### `get_page_metadata`\nGet runtime metadata about current page render (requires active browser session):\n```json\n{ \"name\": \"get_page_metadata\", \"arguments\": {} }\n```\nReturns segment trie data showing layouts, boundaries, and page components.\n\n#### `get_logs`\nGet path to Next.js development log file:\n```json\n{ \"name\": \"get_logs\", \"arguments\": {} }\n```\nReturns path to `<distDir>/logs/next-development.log`\n\n#### `get_server_action_by_id`\nLocate a Server Action by ID:\n```json\n{ \"name\": \"get_server_action_by_id\", \"arguments\": { \"actionId\": \"<action-id>\" } }\n```\n\n### Example: Get Errors\n\n```bash\ncurl -X POST http://localhost:<port>/_next/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"method\":\"tools/call\",\"params\":{\"name\":\"get_errors\",\"arguments\":{}}}'\n```\n\n## Rebuild Specific Routes (Next.js 16+)\n\nUse `--debug-build-paths` to rebuild only specific routes instead of the entire app:\n\n```bash\n# Rebuild a specific route\nnext build --debug-build-paths \"/dashboard\"\n\n# Rebuild routes matching a glob\nnext build --debug-build-paths \"/api/*\"\n\n# Dynamic routes\nnext build --debug-build-paths \"/blog/[slug]\"\n```\n\nUse this to:\n- Quickly verify a build fix without full rebuild\n- Debug static generation issues for specific pages\n- Iterate faster on build errors\n",".agents/skills/next-best-practices/directives.md":"# Directives\n\n## React Directives\n\nThese are React directives, not Next.js specific.\n\n### `'use client'`\n\nMarks a component as a Client Component. Required for:\n- React hooks (`useState`, `useEffect`, etc.)\n- Event handlers (`onClick`, `onChange`)\n- Browser APIs (`window`, `localStorage`)\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\n\nexport function Counter() {\n  const [count, setCount] = useState(0)\n  return <button onClick={() => setCount(count + 1)}>{count}</button>\n}\n```\n\nReference: https://react.dev/reference/rsc/use-client\n\n### `'use server'`\n\nMarks a function as a Server Action. Can be passed to Client Components.\n\n```tsx\n'use server'\n\nexport async function submitForm(formData: FormData) {\n  // Runs on server\n}\n```\n\nOr inline within a Server Component:\n\n```tsx\nexport default function Page() {\n  async function submit() {\n    'use server'\n    // Runs on server\n  }\n  return <form action={submit}>...</form>\n}\n```\n\nReference: https://react.dev/reference/rsc/use-server\n\n---\n\n## Next.js Directive\n\n### `'use cache'`\n\nMarks a function or component for caching. Part of Next.js Cache Components.\n\n```tsx\n'use cache'\n\nexport async function getCachedData() {\n  return await fetchData()\n}\n```\n\nRequires `cacheComponents: true` in `next.config.ts`.\n\nFor detailed usage including cache profiles, `cacheLife()`, `cacheTag()`, and `updateTag()`, see the `next-cache-components` skill.\n\nReference: https://nextjs.org/docs/app/api-reference/directives/use-cache\n",".agents/skills/next-best-practices/error-handling.md":"# Error Handling\n\nHandle errors gracefully in Next.js applications.\n\nReference: https://nextjs.org/docs/app/getting-started/error-handling\n\n## Error Boundaries\n\n### `error.tsx`\n\nCatches errors in a route segment and its children:\n\n```tsx\n'use client'\n\nexport default function Error({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  return (\n    <div>\n      <h2>Something went wrong!</h2>\n      <button onClick={() => reset()}>Try again</button>\n    </div>\n  )\n}\n```\n\n**Important:** `error.tsx` must be a Client Component.\n\n### `global-error.tsx`\n\nCatches errors in root layout:\n\n```tsx\n'use client'\n\nexport default function GlobalError({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  return (\n    <html>\n      <body>\n        <h2>Something went wrong!</h2>\n        <button onClick={() => reset()}>Try again</button>\n      </body>\n    </html>\n  )\n}\n```\n\n**Important:** Must include `<html>` and `<body>` tags.\n\n## Server Actions: Navigation API Gotcha\n\n**Do NOT wrap navigation APIs in try-catch.** They throw special errors that Next.js handles internally.\n\nReference: https://nextjs.org/docs/app/api-reference/functions/redirect#behavior\n\n```tsx\n'use server'\n\nimport { redirect } from 'next/navigation'\nimport { notFound } from 'next/navigation'\n\n// Bad: try-catch catches the navigation \"error\"\nasync function createPost(formData: FormData) {\n  try {\n    const post = await db.post.create({ ... })\n    redirect(`/posts/${post.id}`)  // This throws!\n  } catch (error) {\n    // redirect() throw is caught here - navigation fails!\n    return { error: 'Failed to create post' }\n  }\n}\n\n// Good: Call navigation APIs outside try-catch\nasync function createPost(formData: FormData) {\n  let post\n  try {\n    post = await db.post.create({ ... })\n  } catch (error) {\n    return { error: 'Failed to create post' }\n  }\n  redirect(`/posts/${post.id}`)  // Outside try-catch\n}\n\n// Good: Re-throw navigation errors\nasync function createPost(formData: FormData) {\n  try {\n    const post = await db.post.create({ ... })\n    redirect(`/posts/${post.id}`)\n  } catch (error) {\n    if (error instanceof Error && error.message === 'NEXT_REDIRECT') {\n      throw error  // Re-throw navigation errors\n    }\n    return { error: 'Failed to create post' }\n  }\n}\n```\n\nSame applies to:\n- `redirect()` - 307 temporary redirect\n- `permanentRedirect()` - 308 permanent redirect\n- `notFound()` - 404 not found\n- `forbidden()` - 403 forbidden\n- `unauthorized()` - 401 unauthorized\n\nUse `unstable_rethrow()` to re-throw these errors in catch blocks:\n\n```tsx\nimport { unstable_rethrow } from 'next/navigation'\n\nasync function action() {\n  try {\n    // ...\n    redirect('/success')\n  } catch (error) {\n    unstable_rethrow(error) // Re-throws Next.js internal errors\n    return { error: 'Something went wrong' }\n  }\n}\n```\n\n## Redirects\n\n```tsx\nimport { redirect, permanentRedirect } from 'next/navigation'\n\n// 307 Temporary - use for most cases\nredirect('/new-path')\n\n// 308 Permanent - use for URL migrations (cached by browsers)\npermanentRedirect('/new-url')\n```\n\n## Auth Errors\n\nTrigger auth-related error pages:\n\n```tsx\nimport { forbidden, unauthorized } from 'next/navigation'\n\nasync function Page() {\n  const session = await getSession()\n\n  if (!session) {\n    unauthorized() // Renders unauthorized.tsx (401)\n  }\n\n  if (!session.hasAccess) {\n    forbidden() // Renders forbidden.tsx (403)\n  }\n\n  return <Dashboard />\n}\n```\n\nCreate corresponding error pages:\n\n```tsx\n// app/forbidden.tsx\nexport default function Forbidden() {\n  return <div>You don't have access to this resource</div>\n}\n\n// app/unauthorized.tsx\nexport default function Unauthorized() {\n  return <div>Please log in to continue</div>\n}\n```\n\n## Not Found\n\n### `not-found.tsx`\n\nCustom 404 page for a route segment:\n\n```tsx\nexport default function NotFound() {\n  return (\n    <div>\n      <h2>Not Found</h2>\n      <p>Could not find the requested resource</p>\n    </div>\n  )\n}\n```\n\n### Triggering Not Found\n\n```tsx\nimport { notFound } from 'next/navigation'\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const post = await getPost(id)\n\n  if (!post) {\n    notFound()  // Renders closest not-found.tsx\n  }\n\n  return <div>{post.title}</div>\n}\n```\n\n## Error Hierarchy\n\nErrors bubble up to the nearest error boundary:\n\n```\napp/\n├── error.tsx           # Catches errors from all children\n├── blog/\n│   ├── error.tsx       # Catches errors in /blog/*\n│   └── [slug]/\n│       ├── error.tsx   # Catches errors in /blog/[slug]\n│       └── page.tsx\n└── layout.tsx          # Errors here go to global-error.tsx\n```\n",".agents/skills/next-best-practices/file-conventions.md":"# File Conventions\n\nNext.js App Router uses file-based routing with special file conventions.\n\n## Project Structure\n\nReference: https://nextjs.org/docs/app/getting-started/project-structure\n\n```\napp/\n├── layout.tsx          # Root layout (required)\n├── page.tsx            # Home page (/)\n├── loading.tsx         # Loading UI\n├── error.tsx           # Error UI\n├── not-found.tsx       # 404 UI\n├── global-error.tsx    # Global error UI\n├── route.ts            # API endpoint\n├── template.tsx        # Re-rendered layout\n├── default.tsx         # Parallel route fallback\n├── blog/\n│   ├── page.tsx        # /blog\n│   └── [slug]/\n│       └── page.tsx    # /blog/:slug\n└── (group)/            # Route group (no URL impact)\n    └── page.tsx\n```\n\n## Special Files\n\n| File | Purpose |\n|------|---------|\n| `page.tsx` | UI for a route segment |\n| `layout.tsx` | Shared UI for segment and children |\n| `loading.tsx` | Loading UI (Suspense boundary) |\n| `error.tsx` | Error UI (Error boundary) |\n| `not-found.tsx` | 404 UI |\n| `route.ts` | API endpoint |\n| `template.tsx` | Like layout but re-renders on navigation |\n| `default.tsx` | Fallback for parallel routes |\n\n## Route Segments\n\n```\napp/\n├── blog/               # Static segment: /blog\n├── [slug]/             # Dynamic segment: /:slug\n├── [...slug]/          # Catch-all: /a/b/c\n├── [[...slug]]/        # Optional catch-all: / or /a/b/c\n└── (marketing)/        # Route group (ignored in URL)\n```\n\n## Parallel Routes\n\n```\napp/\n├── @analytics/\n│   └── page.tsx\n├── @sidebar/\n│   └── page.tsx\n└── layout.tsx          # Receives { analytics, sidebar } as props\n```\n\n## Intercepting Routes\n\n```\napp/\n├── feed/\n│   └── page.tsx\n├── @modal/\n│   └── (.)photo/[id]/  # Intercepts /photo/[id] from /feed\n│       └── page.tsx\n└── photo/[id]/\n    └── page.tsx\n```\n\nConventions:\n- `(.)` - same level\n- `(..)` - one level up\n- `(..)(..)` - two levels up\n- `(...)` - from root\n\n## Private Folders\n\n```\napp/\n├── _components/        # Private folder (not a route)\n│   └── Button.tsx\n└── page.tsx\n```\n\nPrefix with `_` to exclude from routing.\n\n## Middleware / Proxy\n\n### Next.js 14-15: `middleware.ts`\n\n```ts\n// middleware.ts (root of project)\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\n\nexport function middleware(request: NextRequest) {\n  // Auth, redirects, rewrites, etc.\n  return NextResponse.next();\n}\n\nexport const config = {\n  matcher: ['/dashboard/:path*', '/api/:path*'],\n};\n```\n\n### Next.js 16+: `proxy.ts`\n\nRenamed for clarity - same capabilities, different names:\n\n```ts\n// proxy.ts (root of project)\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\n\nexport function proxy(request: NextRequest) {\n  // Same logic as middleware\n  return NextResponse.next();\n}\n\nexport const proxyConfig = {\n  matcher: ['/dashboard/:path*', '/api/:path*'],\n};\n```\n\n| Version | File | Export | Config |\n|---------|------|--------|--------|\n| v14-15 | `middleware.ts` | `middleware()` | `config` |\n| v16+ | `proxy.ts` | `proxy()` | `proxyConfig` |\n\n**Migration**: Run `npx @next/codemod@latest upgrade` to auto-rename.\n\n## File Conventions Reference\n\nReference: https://nextjs.org/docs/app/api-reference/file-conventions\n",".agents/skills/next-best-practices/font.md":"# Font Optimization\n\nUse `next/font` for automatic font optimization with zero layout shift.\n\n## Google Fonts\n\n```tsx\n// app/layout.tsx\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({ subsets: ['latin'] })\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\" className={inter.className}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n## Multiple Fonts\n\n```tsx\nimport { Inter, Roboto_Mono } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  variable: '--font-inter',\n})\n\nconst robotoMono = Roboto_Mono({\n  subsets: ['latin'],\n  variable: '--font-roboto-mono',\n})\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\" className={`${inter.variable} ${robotoMono.variable}`}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\nUse in CSS:\n```css\nbody {\n  font-family: var(--font-inter);\n}\n\ncode {\n  font-family: var(--font-roboto-mono);\n}\n```\n\n## Font Weights and Styles\n\n```tsx\n// Single weight\nconst inter = Inter({\n  subsets: ['latin'],\n  weight: '400',\n})\n\n// Multiple weights\nconst inter = Inter({\n  subsets: ['latin'],\n  weight: ['400', '500', '700'],\n})\n\n// Variable font (recommended) - includes all weights\nconst inter = Inter({\n  subsets: ['latin'],\n  // No weight needed - variable fonts support all weights\n})\n\n// With italic\nconst inter = Inter({\n  subsets: ['latin'],\n  style: ['normal', 'italic'],\n})\n```\n\n## Local Fonts\n\n```tsx\nimport localFont from 'next/font/local'\n\nconst myFont = localFont({\n  src: './fonts/MyFont.woff2',\n})\n\n// Multiple files for different weights\nconst myFont = localFont({\n  src: [\n    {\n      path: './fonts/MyFont-Regular.woff2',\n      weight: '400',\n      style: 'normal',\n    },\n    {\n      path: './fonts/MyFont-Bold.woff2',\n      weight: '700',\n      style: 'normal',\n    },\n  ],\n})\n\n// Variable font\nconst myFont = localFont({\n  src: './fonts/MyFont-Variable.woff2',\n  variable: '--font-my-font',\n})\n```\n\n## Tailwind CSS Integration\n\n```tsx\n// app/layout.tsx\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  variable: '--font-inter',\n})\n\nexport default function RootLayout({ children }) {\n  return (\n    <html lang=\"en\" className={inter.variable}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    extend: {\n      fontFamily: {\n        sans: ['var(--font-inter)'],\n      },\n    },\n  },\n}\n```\n\n## Preloading Subsets\n\nOnly load needed character subsets:\n\n```tsx\n// Latin only (most common)\nconst inter = Inter({ subsets: ['latin'] })\n\n// Multiple subsets\nconst inter = Inter({ subsets: ['latin', 'latin-ext', 'cyrillic'] })\n```\n\n## Display Strategy\n\nControl font loading behavior:\n\n```tsx\nconst inter = Inter({\n  subsets: ['latin'],\n  display: 'swap', // Default - shows fallback, swaps when loaded\n})\n\n// Options:\n// 'auto' - browser decides\n// 'block' - short block period, then swap\n// 'swap' - immediate fallback, swap when ready (recommended)\n// 'fallback' - short block, short swap, then fallback\n// 'optional' - short block, no swap (use if font is optional)\n```\n\n## Don't Use Manual Font Links\n\nAlways use `next/font` instead of `<link>` tags for Google Fonts.\n\n```tsx\n// Bad: Manual link tag (blocks rendering, no optimization)\n<link href=\"https://fonts.googleapis.com/css2?family=Inter\" rel=\"stylesheet\" />\n\n// Bad: Missing display and preconnect\n<link href=\"https://fonts.googleapis.com/css2?family=Inter\" rel=\"stylesheet\" />\n\n// Good: Use next/font (self-hosted, zero layout shift)\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({ subsets: ['latin'] })\n```\n\n## Common Mistakes\n\n```tsx\n// Bad: Importing font in every component\n// components/Button.tsx\nimport { Inter } from 'next/font/google'\nconst inter = Inter({ subsets: ['latin'] }) // Creates new instance each time!\n\n// Good: Import once in layout, use CSS variable\n// app/layout.tsx\nconst inter = Inter({ subsets: ['latin'], variable: '--font-inter' })\n\n// Bad: Using @import in CSS (blocks rendering)\n/* globals.css */\n@import url('https://fonts.googleapis.com/css2?family=Inter');\n\n// Good: Use next/font (self-hosted, no network request)\nimport { Inter } from 'next/font/google'\n\n// Bad: Loading all weights when only using a few\nconst inter = Inter({ subsets: ['latin'] }) // Loads all weights\n\n// Good: Specify only needed weights (for non-variable fonts)\nconst inter = Inter({ subsets: ['latin'], weight: ['400', '700'] })\n\n// Bad: Missing subset - loads all characters\nconst inter = Inter({})\n\n// Good: Always specify subset\nconst inter = Inter({ subsets: ['latin'] })\n```\n\n## Font in Specific Components\n\n```tsx\n// For component-specific fonts, export from a shared file\n// lib/fonts.ts\nimport { Inter, Playfair_Display } from 'next/font/google'\n\nexport const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })\nexport const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-playfair' })\n\n// components/Heading.tsx\nimport { playfair } from '@/lib/fonts'\n\nexport function Heading({ children }) {\n  return <h1 className={playfair.className}>{children}</h1>\n}\n```\n",".agents/skills/next-best-practices/functions.md":"# Functions\n\nNext.js function APIs.\n\nReference: https://nextjs.org/docs/app/api-reference/functions\n\n## Navigation Hooks (Client)\n\n| Hook | Purpose | Reference |\n|------|---------|-----------|\n| `useRouter` | Programmatic navigation (`push`, `replace`, `back`, `refresh`) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-router) |\n| `usePathname` | Get current pathname | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-pathname) |\n| `useSearchParams` | Read URL search parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-search-params) |\n| `useParams` | Access dynamic route parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-params) |\n| `useSelectedLayoutSegment` | Active child segment (one level) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment) |\n| `useSelectedLayoutSegments` | All active segments below layout | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segments) |\n| `useLinkStatus` | Check link prefetch status | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-link-status) |\n| `useReportWebVitals` | Report Core Web Vitals metrics | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals) |\n\n## Server Functions\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `cookies` | Read/write cookies | [Docs](https://nextjs.org/docs/app/api-reference/functions/cookies) |\n| `headers` | Read request headers | [Docs](https://nextjs.org/docs/app/api-reference/functions/headers) |\n| `draftMode` | Enable preview of unpublished CMS content | [Docs](https://nextjs.org/docs/app/api-reference/functions/draft-mode) |\n| `after` | Run code after response finishes streaming | [Docs](https://nextjs.org/docs/app/api-reference/functions/after) |\n| `connection` | Wait for connection before dynamic rendering | [Docs](https://nextjs.org/docs/app/api-reference/functions/connection) |\n| `userAgent` | Parse User-Agent header | [Docs](https://nextjs.org/docs/app/api-reference/functions/userAgent) |\n\n## Generate Functions\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `generateStaticParams` | Pre-render dynamic routes at build time | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) |\n| `generateMetadata` | Dynamic metadata | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) |\n| `generateViewport` | Dynamic viewport config | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-viewport) |\n| `generateSitemaps` | Multiple sitemaps for large sites | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-sitemaps) |\n| `generateImageMetadata` | Multiple OG images per route | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-image-metadata) |\n\n## Request/Response\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `NextRequest` | Extended Request with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-request) |\n| `NextResponse` | Extended Response with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-response) |\n| `ImageResponse` | Generate OG images | [Docs](https://nextjs.org/docs/app/api-reference/functions/image-response) |\n\n## Common Examples\n\n### Navigation\n\nUse `next/link` for internal navigation instead of `<a>` tags.\n\n```tsx\n// Bad: Plain anchor tag\n<a href=\"/about\">About</a>\n\n// Good: Next.js Link\nimport Link from 'next/link'\n\n<Link href=\"/about\">About</Link>\n```\n\nActive link styling:\n\n```tsx\n'use client'\n\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\n\nexport function NavLink({ href, children }) {\n  const pathname = usePathname()\n\n  return (\n    <Link href={href} className={pathname === href ? 'active' : ''}>\n      {children}\n    </Link>\n  )\n}\n```\n\n### Static Generation\n\n```tsx\n// app/blog/[slug]/page.tsx\nexport async function generateStaticParams() {\n  const posts = await getPosts()\n  return posts.map((post) => ({ slug: post.slug }))\n}\n```\n\n### After Response\n\n```tsx\nimport { after } from 'next/server'\n\nexport async function POST(request: Request) {\n  const data = await processRequest(request)\n\n  after(async () => {\n    await logAnalytics(data)\n  })\n\n  return Response.json({ success: true })\n}\n```\n",".agents/skills/next-best-practices/hydration-error.md":"# Hydration Errors\n\nDiagnose and fix React hydration mismatch errors.\n\n## Error Signs\n\n- \"Hydration failed because the initial UI does not match\"\n- \"Text content does not match server-rendered HTML\"\n\n## Debugging\n\nIn development, click the hydration error to see the server/client diff.\n\n## Common Causes and Fixes\n\n### Browser-only APIs\n\n```tsx\n// Bad: Causes mismatch - window doesn't exist on server\n<div>{window.innerWidth}</div>\n\n// Good: Use client component with mounted check\n'use client'\nimport { useState, useEffect } from 'react'\n\nexport function ClientOnly({ children }: { children: React.ReactNode }) {\n  const [mounted, setMounted] = useState(false)\n  useEffect(() => setMounted(true), [])\n  return mounted ? children : null\n}\n```\n\n### Date/Time Rendering\n\nServer and client may be in different timezones:\n\n```tsx\n// Bad: Causes mismatch\n<span>{new Date().toLocaleString()}</span>\n\n// Good: Render on client only\n'use client'\nconst [time, setTime] = useState<string>()\nuseEffect(() => setTime(new Date().toLocaleString()), [])\n```\n\n### Random Values or IDs\n\n```tsx\n// Bad: Random values differ between server and client\n<div id={Math.random().toString()}>\n\n// Good: Use useId hook\nimport { useId } from 'react'\n\nfunction Input() {\n  const id = useId()\n  return <input id={id} />\n}\n```\n\n### Invalid HTML Nesting\n\n```tsx\n// Bad: Invalid - div inside p\n<p><div>Content</div></p>\n\n// Bad: Invalid - p inside p\n<p><p>Nested</p></p>\n\n// Good: Valid nesting\n<div><p>Content</p></div>\n```\n\n### Third-party Scripts\n\nScripts that modify DOM during hydration.\n\n```tsx\n// Good: Use next/script with afterInteractive\nimport Script from 'next/script'\n\nexport default function Page() {\n  return (\n    <Script\n      src=\"https://example.com/script.js\"\n      strategy=\"afterInteractive\"\n    />\n  )\n}\n```\n",".agents/skills/next-best-practices/image.md":"# Image Optimization\n\nUse `next/image` for automatic image optimization.\n\n## Always Use next/image\n\n```tsx\n// Bad: Avoid native img\n<img src=\"/hero.png\" alt=\"Hero\" />\n\n// Good: Use next/image\nimport Image from 'next/image'\n<Image src=\"/hero.png\" alt=\"Hero\" width={800} height={400} />\n```\n\n## Required Props\n\nImages need explicit dimensions to prevent layout shift:\n\n```tsx\n// Local images - dimensions inferred automatically\nimport heroImage from './hero.png'\n<Image src={heroImage} alt=\"Hero\" />\n\n// Remote images - must specify width/height\n<Image src=\"https://example.com/image.jpg\" alt=\"Hero\" width={800} height={400} />\n\n// Or use fill for parent-relative sizing\n<div style={{ position: 'relative', width: '100%', height: 400 }}>\n  <Image src=\"/hero.png\" alt=\"Hero\" fill style={{ objectFit: 'cover' }} />\n</div>\n```\n\n## Remote Images Configuration\n\nRemote domains must be configured in `next.config.js`:\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: 'https',\n        hostname: 'example.com',\n        pathname: '/images/**',\n      },\n      {\n        protocol: 'https',\n        hostname: '*.cdn.com', // Wildcard subdomain\n      },\n    ],\n  },\n}\n```\n\n## Responsive Images\n\nUse `sizes` to tell the browser which size to download:\n\n```tsx\n// Full-width hero\n<Image\n  src=\"/hero.png\"\n  alt=\"Hero\"\n  fill\n  sizes=\"100vw\"\n/>\n\n// Responsive grid (3 columns on desktop, 1 on mobile)\n<Image\n  src=\"/card.png\"\n  alt=\"Card\"\n  fill\n  sizes=\"(max-width: 768px) 100vw, 33vw\"\n/>\n\n// Fixed sidebar image\n<Image\n  src=\"/avatar.png\"\n  alt=\"Avatar\"\n  width={200}\n  height={200}\n  sizes=\"200px\"\n/>\n```\n\n## Blur Placeholder\n\nPrevent layout shift with placeholders:\n\n```tsx\n// Local images - automatic blur hash\nimport heroImage from './hero.png'\n<Image src={heroImage} alt=\"Hero\" placeholder=\"blur\" />\n\n// Remote images - provide blurDataURL\n<Image\n  src=\"https://example.com/image.jpg\"\n  alt=\"Hero\"\n  width={800}\n  height={400}\n  placeholder=\"blur\"\n  blurDataURL=\"data:image/jpeg;base64,/9j/4AAQSkZJRg...\"\n/>\n\n// Or use color placeholder\n<Image\n  src=\"https://example.com/image.jpg\"\n  alt=\"Hero\"\n  width={800}\n  height={400}\n  placeholder=\"empty\"\n  style={{ backgroundColor: '#e0e0e0' }}\n/>\n```\n\n## Priority Loading\n\nUse `priority` for above-the-fold images (LCP):\n\n```tsx\n// Hero image - loads immediately\n<Image src=\"/hero.png\" alt=\"Hero\" fill priority />\n\n// Below-fold images - lazy loaded by default (no priority needed)\n<Image src=\"/card.png\" alt=\"Card\" width={400} height={300} />\n```\n\n## Common Mistakes\n\n```tsx\n// Bad: Missing sizes with fill - downloads largest image\n<Image src=\"/hero.png\" alt=\"Hero\" fill />\n\n// Good: Add sizes for proper responsive behavior\n<Image src=\"/hero.png\" alt=\"Hero\" fill sizes=\"100vw\" />\n\n// Bad: Using width/height for aspect ratio only\n<Image src=\"/hero.png\" alt=\"Hero\" width={16} height={9} />\n\n// Good: Use actual display dimensions or fill with sizes\n<Image src=\"/hero.png\" alt=\"Hero\" fill sizes=\"100vw\" style={{ objectFit: 'cover' }} />\n\n// Bad: Remote image without config\n<Image src=\"https://untrusted.com/image.jpg\" alt=\"Image\" width={400} height={300} />\n// Error: Invalid src prop, hostname not configured\n\n// Good: Add hostname to next.config.js remotePatterns\n```\n\n## Static Export\n\nWhen using `output: 'export'`, use `unoptimized` or custom loader:\n\n```tsx\n// Option 1: Disable optimization\n<Image src=\"/hero.png\" alt=\"Hero\" width={800} height={400} unoptimized />\n\n// Option 2: Global config\n// next.config.js\nmodule.exports = {\n  output: 'export',\n  images: { unoptimized: true },\n}\n\n// Option 3: Custom loader (Cloudinary, Imgix, etc.)\nconst cloudinaryLoader = ({ src, width, quality }) => {\n  return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`\n}\n\n<Image loader={cloudinaryLoader} src=\"sample.jpg\" alt=\"Sample\" width={800} height={400} />\n```\n",".agents/skills/next-best-practices/metadata.md":"# Metadata\n\nAdd SEO metadata to Next.js pages using the Metadata API.\n\n## Important: Server Components Only\n\nThe `metadata` object and `generateMetadata` function are **only supported in Server Components**. They cannot be used in Client Components.\n\nIf the target page has `'use client'`:\n1. Remove `'use client'` if possible, move client logic to child components\n2. Or extract metadata to a parent Server Component layout\n3. Or split the file: Server Component with metadata imports Client Components\n\n## Static Metadata\n\n```tsx\nimport type { Metadata } from 'next'\n\nexport const metadata: Metadata = {\n  title: 'Page Title',\n  description: 'Page description for search engines',\n}\n```\n\n## Dynamic Metadata\n\n```tsx\nimport type { Metadata } from 'next'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport async function generateMetadata({ params }: Props): Promise<Metadata> {\n  const { slug } = await params\n  const post = await getPost(slug)\n  return { title: post.title, description: post.description }\n}\n```\n\n## Avoid Duplicate Fetches\n\nUse React `cache()` when the same data is needed for both metadata and page:\n\n```tsx\nimport { cache } from 'react'\n\nexport const getPost = cache(async (slug: string) => {\n  return await db.posts.findFirst({ where: { slug } })\n})\n```\n\n## Viewport\n\nSeparate from metadata for streaming support:\n\n```tsx\nimport type { Viewport } from 'next'\n\nexport const viewport: Viewport = {\n  width: 'device-width',\n  initialScale: 1,\n  themeColor: '#000000',\n}\n\n// Or dynamic\nexport function generateViewport({ params }): Viewport {\n  return { themeColor: getThemeColor(params) }\n}\n```\n\n## Title Templates\n\nIn root layout for consistent naming:\n\n```tsx\nexport const metadata: Metadata = {\n  title: { default: 'Site Name', template: '%s | Site Name' },\n}\n```\n\n## Metadata File Conventions\n\nReference: https://nextjs.org/docs/app/getting-started/project-structure#metadata-file-conventions\n\nPlace these files in `app/` directory (or route segments):\n\n| File | Purpose |\n|------|---------|\n| `favicon.ico` | Favicon |\n| `icon.png` / `icon.svg` | App icon |\n| `apple-icon.png` | Apple app icon |\n| `opengraph-image.png` | OG image |\n| `twitter-image.png` | Twitter card image |\n| `sitemap.ts` / `sitemap.xml` | Sitemap (use `generateSitemaps` for multiple) |\n| `robots.ts` / `robots.txt` | Robots directives |\n| `manifest.ts` / `manifest.json` | Web app manifest |\n\n## SEO Best Practice: Static Files Are Often Enough\n\nFor most sites, **static metadata files provide excellent SEO coverage**:\n\n```\napp/\n├── favicon.ico\n├── opengraph-image.png     # Works for both OG and Twitter\n├── sitemap.ts\n├── robots.ts\n└── layout.tsx              # With title/description metadata\n```\n\n**Tips:**\n- A single `opengraph-image.png` covers both Open Graph and Twitter (Twitter falls back to OG)\n- Static `title` and `description` in layout metadata is sufficient for most pages\n- Only use dynamic `generateMetadata` when content varies per page\n\n---\n\n# OG Image Generation\n\nGenerate dynamic Open Graph images using `next/og`.\n\n## Important Rules\n\n1. **Use `next/og`** - not `@vercel/og` (it's built into Next.js)\n2. **No searchParams** - OG images can't access search params, use route params instead\n3. **Avoid Edge runtime** - Use default Node.js runtime\n\n```tsx\n// Good\nimport { ImageResponse } from 'next/og'\n\n// Bad\n// import { ImageResponse } from '@vercel/og'\n// export const runtime = 'edge'\n```\n\n## Basic OG Image\n\n```tsx\n// app/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport const alt = 'Site Name'\nexport const size = { width: 1200, height: 630 }\nexport const contentType = 'image/png'\n\nexport default function Image() {\n  return new ImageResponse(\n    (\n      <div\n        style={{\n          fontSize: 128,\n          background: 'white',\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n        }}\n      >\n        Hello World\n      </div>\n    ),\n    { ...size }\n  )\n}\n```\n\n## Dynamic OG Image\n\n```tsx\n// app/blog/[slug]/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport const alt = 'Blog Post'\nexport const size = { width: 1200, height: 630 }\nexport const contentType = 'image/png'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default async function Image({ params }: Props) {\n  const { slug } = await params\n  const post = await getPost(slug)\n\n  return new ImageResponse(\n    (\n      <div\n        style={{\n          fontSize: 48,\n          background: 'linear-gradient(to bottom, #1a1a1a, #333)',\n          color: 'white',\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          flexDirection: 'column',\n          alignItems: 'center',\n          justifyContent: 'center',\n          padding: 48,\n        }}\n      >\n        <div style={{ fontSize: 64, fontWeight: 'bold' }}>{post.title}</div>\n        <div style={{ marginTop: 24, opacity: 0.8 }}>{post.description}</div>\n      </div>\n    ),\n    { ...size }\n  )\n}\n```\n\n## Custom Fonts\n\n```tsx\nimport { ImageResponse } from 'next/og'\nimport { join } from 'path'\nimport { readFile } from 'fs/promises'\n\nexport default async function Image() {\n  const fontPath = join(process.cwd(), 'assets/fonts/Inter-Bold.ttf')\n  const fontData = await readFile(fontPath)\n\n  return new ImageResponse(\n    (\n      <div style={{ fontFamily: 'Inter', fontSize: 64 }}>\n        Custom Font Text\n      </div>\n    ),\n    {\n      width: 1200,\n      height: 630,\n      fonts: [{ name: 'Inter', data: fontData, style: 'normal' }],\n    }\n  )\n}\n```\n\n## File Naming\n\n- `opengraph-image.tsx` - Open Graph (Facebook, LinkedIn)\n- `twitter-image.tsx` - Twitter/X cards (optional, falls back to OG)\n\n## Styling Notes\n\nImageResponse uses Flexbox layout:\n- Use `display: 'flex'`\n- No CSS Grid support\n- Styles must be inline objects\n\n## Multiple OG Images\n\nUse `generateImageMetadata` for multiple images per route:\n\n```tsx\n// app/blog/[slug]/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport async function generateImageMetadata({ params }) {\n  const images = await getPostImages(params.slug)\n  return images.map((img, idx) => ({\n    id: idx,\n    alt: img.alt,\n    size: { width: 1200, height: 630 },\n    contentType: 'image/png',\n  }))\n}\n\nexport default async function Image({ params, id }) {\n  const images = await getPostImages(params.slug)\n  const image = images[id]\n  return new ImageResponse(/* ... */)\n}\n```\n\n## Multiple Sitemaps\n\nUse `generateSitemaps` for large sites:\n\n```tsx\n// app/sitemap.ts\nimport type { MetadataRoute } from 'next'\n\nexport async function generateSitemaps() {\n  // Return array of sitemap IDs\n  return [{ id: 0 }, { id: 1 }, { id: 2 }]\n}\n\nexport default async function sitemap({\n  id,\n}: {\n  id: number\n}): Promise<MetadataRoute.Sitemap> {\n  const start = id * 50000\n  const end = start + 50000\n  const products = await getProducts(start, end)\n\n  return products.map((product) => ({\n    url: `https://example.com/product/${product.id}`,\n    lastModified: product.updatedAt,\n  }))\n}\n```\n\nGenerates `/sitemap/0.xml`, `/sitemap/1.xml`, etc.\n",".agents/skills/next-best-practices/parallel-routes.md":"# Parallel & Intercepting Routes\n\nParallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.\n\n## File Structure\n\n```\napp/\n├── @modal/                    # Parallel route slot\n│   ├── default.tsx            # Required! Returns null\n│   ├── (.)photos/             # Intercepts /photos/*\n│   │   └── [id]/\n│   │       └── page.tsx       # Modal content\n│   └── [...]catchall/         # Optional: catch unmatched\n│       └── page.tsx\n├── photos/\n│   └── [id]/\n│       └── page.tsx           # Full page (direct access)\n├── layout.tsx                 # Renders both children and @modal\n└── page.tsx\n```\n\n## Step 1: Root Layout with Slot\n\n```tsx\n// app/layout.tsx\nexport default function RootLayout({\n  children,\n  modal,\n}: {\n  children: React.ReactNode;\n  modal: React.ReactNode;\n}) {\n  return (\n    <html>\n      <body>\n        {children}\n        {modal}\n      </body>\n    </html>\n  );\n}\n```\n\n## Step 2: Default File (Critical!)\n\n**Every parallel route slot MUST have a `default.tsx`** to prevent 404s on hard navigation.\n\n```tsx\n// app/@modal/default.tsx\nexport default function Default() {\n  return null;\n}\n```\n\nWithout this file, refreshing any page will 404 because Next.js can't determine what to render in the `@modal` slot.\n\n## Step 3: Intercepting Route (Modal)\n\nThe `(.)` prefix intercepts routes at the same level.\n\n```tsx\n// app/@modal/(.)photos/[id]/page.tsx\nimport { Modal } from '@/components/modal';\n\nexport default async function PhotoModal({\n  params\n}: {\n  params: Promise<{ id: string }>\n}) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  return (\n    <Modal>\n      <img src={photo.url} alt={photo.title} />\n    </Modal>\n  );\n}\n```\n\n## Step 4: Full Page (Direct Access)\n\n```tsx\n// app/photos/[id]/page.tsx\nexport default async function PhotoPage({\n  params\n}: {\n  params: Promise<{ id: string }>\n}) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  return (\n    <div className=\"full-page\">\n      <img src={photo.url} alt={photo.title} />\n      <h1>{photo.title}</h1>\n    </div>\n  );\n}\n```\n\n## Step 5: Modal Component with Correct Closing\n\n**Critical: Use `router.back()` to close modals, NOT `router.push()` or `<Link>`.**\n\n```tsx\n// components/modal.tsx\n'use client';\n\nimport { useRouter } from 'next/navigation';\nimport { useCallback, useEffect, useRef } from 'react';\n\nexport function Modal({ children }: { children: React.ReactNode }) {\n  const router = useRouter();\n  const overlayRef = useRef<HTMLDivElement>(null);\n\n  // Close on escape key\n  useEffect(() => {\n    function onKeyDown(e: KeyboardEvent) {\n      if (e.key === 'Escape') {\n        router.back(); // Correct\n      }\n    }\n    document.addEventListener('keydown', onKeyDown);\n    return () => document.removeEventListener('keydown', onKeyDown);\n  }, [router]);\n\n  // Close on overlay click\n  const handleOverlayClick = useCallback((e: React.MouseEvent) => {\n    if (e.target === overlayRef.current) {\n      router.back(); // Correct\n    }\n  }, [router]);\n\n  return (\n    <div\n      ref={overlayRef}\n      onClick={handleOverlayClick}\n      className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\"\n    >\n      <div className=\"bg-white rounded-lg p-6 max-w-2xl w-full mx-4\">\n        <button\n          onClick={() => router.back()} // Correct!\n          className=\"absolute top-4 right-4\"\n        >\n          Close\n        </button>\n        {children}\n      </div>\n    </div>\n  );\n}\n```\n\n### Why NOT `router.push('/')` or `<Link href=\"/\">`?\n\nUsing `push` or `Link` to \"close\" a modal:\n1. Adds a new history entry (back button shows modal again)\n2. Doesn't properly clear the intercepted route\n3. Can cause the modal to flash or persist unexpectedly\n\n`router.back()` correctly:\n1. Removes the intercepted route from history\n2. Returns to the previous page\n3. Properly unmounts the modal\n\n## Route Matcher Reference\n\nMatchers match **route segments**, not filesystem paths:\n\n| Matcher | Matches | Example |\n|---------|---------|---------|\n| `(.)` | Same level | `@modal/(.)photos` intercepts `/photos` |\n| `(..)` | One level up | `@modal/(..)settings` from `/dashboard/@modal` intercepts `/settings` |\n| `(..)(..)` | Two levels up | Rarely used |\n| `(...)` | From root | `@modal/(...)photos` intercepts `/photos` from anywhere |\n\n**Common mistake**: Thinking `(..)` means \"parent folder\" - it means \"parent route segment\".\n\n## Handling Hard Navigation\n\nWhen users directly visit `/photos/123` (bookmark, refresh, shared link):\n- The intercepting route is bypassed\n- The full `photos/[id]/page.tsx` renders\n- Modal doesn't appear (expected behavior)\n\nIf you want the modal to appear on direct access too, you need additional logic:\n\n```tsx\n// app/photos/[id]/page.tsx\nimport { Modal } from '@/components/modal';\n\nexport default async function PhotoPage({ params }) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  // Option: Render as modal on direct access too\n  return (\n    <Modal>\n      <img src={photo.url} alt={photo.title} />\n    </Modal>\n  );\n}\n```\n\n## Common Gotchas\n\n### 1. Missing `default.tsx` → 404 on Refresh\n\nEvery `@slot` folder needs a `default.tsx` that returns `null` (or appropriate content).\n\n### 2. Modal Persists After Navigation\n\nYou're using `router.push()` instead of `router.back()`.\n\n### 3. Nested Parallel Routes Need Defaults Too\n\nIf you have `@modal` inside a route group, each level needs its own `default.tsx`:\n\n```\napp/\n├── (marketing)/\n│   ├── @modal/\n│   │   └── default.tsx     # Needed!\n│   └── layout.tsx\n└── layout.tsx\n```\n\n### 4. Intercepted Route Shows Wrong Content\n\nCheck your matcher:\n- `(.)photos` intercepts `/photos` from the same route level\n- If your `@modal` is in `app/dashboard/@modal`, use `(.)photos` to intercept `/dashboard/photos`, not `/photos`\n\n### 5. TypeScript Errors with `params`\n\nIn Next.js 15+, `params` is a Promise:\n\n```tsx\n// Correct\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params;\n}\n```\n\n## Complete Example: Photo Gallery Modal\n\n```\napp/\n├── @modal/\n│   ├── default.tsx\n│   └── (.)photos/\n│       └── [id]/\n│           └── page.tsx\n├── photos/\n│   ├── page.tsx           # Gallery grid\n│   └── [id]/\n│       └── page.tsx       # Full photo page\n├── layout.tsx\n└── page.tsx\n```\n\nLinks in the gallery:\n\n```tsx\n// app/photos/page.tsx\nimport Link from 'next/link';\n\nexport default async function Gallery() {\n  const photos = await getPhotos();\n\n  return (\n    <div className=\"grid grid-cols-3 gap-4\">\n      {photos.map(photo => (\n        <Link key={photo.id} href={`/photos/${photo.id}`}>\n          <img src={photo.thumbnail} alt={photo.title} />\n        </Link>\n      ))}\n    </div>\n  );\n}\n```\n\nClicking a photo → Modal opens (intercepted)\nDirect URL → Full page renders\nRefresh while modal open → Full page renders\n",".agents/skills/next-best-practices/route-handlers.md":"# Route Handlers\n\nCreate API endpoints with `route.ts` files.\n\n## Basic Usage\n\n```tsx\n// app/api/users/route.ts\nexport async function GET() {\n  const users = await getUsers()\n  return Response.json(users)\n}\n\nexport async function POST(request: Request) {\n  const body = await request.json()\n  const user = await createUser(body)\n  return Response.json(user, { status: 201 })\n}\n```\n\n## Supported Methods\n\n`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`\n\n## GET Handler Conflicts with page.tsx\n\n**A `route.ts` and `page.tsx` cannot coexist in the same folder.**\n\n```\napp/\n├── api/\n│   └── users/\n│       └── route.ts    # /api/users\n└── users/\n    ├── page.tsx        # /users (page)\n    └── route.ts        # Warning: Conflicts with page.tsx!\n```\n\nIf you need both a page and an API at the same path, use different paths:\n\n```\napp/\n├── users/\n│   └── page.tsx        # /users (page)\n└── api/\n    └── users/\n        └── route.ts    # /api/users (API)\n```\n\n## Environment Behavior\n\nRoute handlers run in a **Server Component-like environment**:\n\n- Yes: Can use `async/await`\n- Yes: Can access `cookies()`, `headers()`\n- Yes: Can use Node.js APIs\n- No: Cannot use React hooks\n- No: Cannot use React DOM APIs\n- No: Cannot use browser APIs\n\n```tsx\n// Bad: This won't work - no React DOM in route handlers\nimport { renderToString } from 'react-dom/server'\n\nexport async function GET() {\n  const html = renderToString(<Component />)  // Error!\n  return new Response(html)\n}\n```\n\n## Dynamic Route Handlers\n\n```tsx\n// app/api/users/[id]/route.ts\nexport async function GET(\n  request: Request,\n  { params }: { params: Promise<{ id: string }> }\n) {\n  const { id } = await params\n  const user = await getUser(id)\n\n  if (!user) {\n    return Response.json({ error: 'Not found' }, { status: 404 })\n  }\n\n  return Response.json(user)\n}\n```\n\n## Request Helpers\n\n```tsx\nexport async function GET(request: Request) {\n  // URL and search params\n  const { searchParams } = new URL(request.url)\n  const query = searchParams.get('q')\n\n  // Headers\n  const authHeader = request.headers.get('authorization')\n\n  // Cookies (Next.js helper)\n  const cookieStore = await cookies()\n  const token = cookieStore.get('token')\n\n  return Response.json({ query, token })\n}\n```\n\n## Response Helpers\n\n```tsx\n// JSON response\nreturn Response.json({ data })\n\n// With status\nreturn Response.json({ error: 'Not found' }, { status: 404 })\n\n// With headers\nreturn Response.json(data, {\n  headers: {\n    'Cache-Control': 'max-age=3600',\n  },\n})\n\n// Redirect\nreturn Response.redirect(new URL('/login', request.url))\n\n// Stream\nreturn new Response(stream, {\n  headers: { 'Content-Type': 'text/event-stream' },\n})\n```\n\n## When to Use Route Handlers vs Server Actions\n\n| Use Case | Route Handlers | Server Actions |\n|----------|----------------|----------------|\n| Form submissions | No | Yes |\n| Data mutations from UI | No | Yes |\n| Third-party webhooks | Yes | No |\n| External API consumption | Yes | No |\n| Public REST API | Yes | No |\n| File uploads | Both work | Both work |\n\n**Prefer Server Actions** for mutations triggered from your UI.\n**Use Route Handlers** for external integrations and public APIs.\n",".agents/skills/next-best-practices/rsc-boundaries.md":"# RSC Boundaries\n\nDetect and prevent invalid patterns when crossing Server/Client component boundaries.\n\n## Detection Rules\n\n### 1. Async Client Components Are Invalid\n\nClient components **cannot** be async functions. Only Server Components can be async.\n\n**Detect:** File has `'use client'` AND component is `async function` or returns `Promise`\n\n```tsx\n// Bad: async client component\n'use client'\nexport default async function UserProfile() {\n  const user = await getUser() // Cannot await in client component\n  return <div>{user.name}</div>\n}\n\n// Good: Remove async, fetch data in parent server component\n// page.tsx (server component - no 'use client')\nexport default async function Page() {\n  const user = await getUser()\n  return <UserProfile user={user} />\n}\n\n// UserProfile.tsx (client component)\n'use client'\nexport function UserProfile({ user }: { user: User }) {\n  return <div>{user.name}</div>\n}\n```\n\n```tsx\n// Bad: async arrow function client component\n'use client'\nconst Dashboard = async () => {\n  const data = await fetchDashboard()\n  return <div>{data}</div>\n}\n\n// Good: Fetch in server component, pass data down\n```\n\n### 2. Non-Serializable Props to Client Components\n\nProps passed from Server → Client must be JSON-serializable.\n\n**Detect:** Server component passes these to a client component:\n- Functions (except Server Actions with `'use server'`)\n- `Date` objects\n- `Map`, `Set`, `WeakMap`, `WeakSet`\n- Class instances\n- `Symbol` (unless globally registered)\n- Circular references\n\n```tsx\n// Bad: Function prop\n// page.tsx (server)\nexport default function Page() {\n  const handleClick = () => console.log('clicked')\n  return <ClientButton onClick={handleClick} />\n}\n\n// Good: Define function inside client component\n// ClientButton.tsx\n'use client'\nexport function ClientButton() {\n  const handleClick = () => console.log('clicked')\n  return <button onClick={handleClick}>Click</button>\n}\n```\n\n```tsx\n// Bad: Date object (silently becomes string, then crashes)\n// page.tsx (server)\nexport default async function Page() {\n  const post = await getPost()\n  return <PostCard createdAt={post.createdAt} /> // Date object\n}\n\n// PostCard.tsx (client) - will crash on .getFullYear()\n'use client'\nexport function PostCard({ createdAt }: { createdAt: Date }) {\n  return <span>{createdAt.getFullYear()}</span> // Runtime error!\n}\n\n// Good: Serialize to string on server\n// page.tsx (server)\nexport default async function Page() {\n  const post = await getPost()\n  return <PostCard createdAt={post.createdAt.toISOString()} />\n}\n\n// PostCard.tsx (client)\n'use client'\nexport function PostCard({ createdAt }: { createdAt: string }) {\n  const date = new Date(createdAt)\n  return <span>{date.getFullYear()}</span>\n}\n```\n\n```tsx\n// Bad: Class instance\nconst user = new UserModel(data)\n<ClientProfile user={user} /> // Methods will be stripped\n\n// Good: Pass plain object\nconst user = await getUser()\n<ClientProfile user={{ id: user.id, name: user.name }} />\n```\n\n```tsx\n// Bad: Map/Set\n<ClientComponent items={new Map([['a', 1]])} />\n\n// Good: Convert to array/object\n<ClientComponent items={Object.fromEntries(map)} />\n<ClientComponent items={Array.from(set)} />\n```\n\n### 3. Server Actions Are the Exception\n\nFunctions marked with `'use server'` CAN be passed to client components.\n\n```tsx\n// Valid: Server Action can be passed\n// actions.ts\n'use server'\nexport async function submitForm(formData: FormData) {\n  // server-side logic\n}\n\n// page.tsx (server)\nimport { submitForm } from './actions'\nexport default function Page() {\n  return <ClientForm onSubmit={submitForm} /> // OK!\n}\n\n// ClientForm.tsx (client)\n'use client'\nexport function ClientForm({ onSubmit }: { onSubmit: (data: FormData) => Promise<void> }) {\n  return <form action={onSubmit}>...</form>\n}\n```\n\n## Quick Reference\n\n| Pattern | Valid? | Fix |\n|---------|--------|-----|\n| `'use client'` + `async function` | No | Fetch in server parent, pass data |\n| Pass `() => {}` to client | No | Define in client or use server action |\n| Pass `new Date()` to client | No | Use `.toISOString()` |\n| Pass `new Map()` to client | No | Convert to object/array |\n| Pass class instance to client | No | Pass plain object |\n| Pass server action to client | Yes | - |\n| Pass `string/number/boolean` | Yes | - |\n| Pass plain object/array | Yes | - |\n",".agents/skills/next-best-practices/runtime-selection.md":"# Runtime Selection\n\n## Use Node.js Runtime by Default\n\nUse the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.\n\n```tsx\n// Good: Default - no runtime config needed (uses Node.js)\nexport default function Page() { ... }\n\n// Caution: Only if already used in project or specifically required\nexport const runtime = 'edge'\n```\n\n## When to Use Each\n\n### Node.js Runtime (Default)\n\n- Full Node.js API support\n- File system access (`fs`)\n- Full `crypto` support\n- Database connections\n- Most npm packages work\n\n### Edge Runtime\n\n- Only for specific edge-location latency requirements\n- Limited API (no `fs`, limited `crypto`)\n- Smaller cold start\n- Geographic distribution needs\n\n## Detection\n\n**Before adding `runtime = 'edge'`**, check:\n1. Does the project already use Edge runtime?\n2. Is there a specific latency requirement?\n3. Are all dependencies Edge-compatible?\n\nIf unsure, use Node.js runtime.\n",".agents/skills/next-best-practices/scripts.md":"# Scripts\n\nLoading third-party scripts in Next.js.\n\n## Use next/script\n\nAlways use `next/script` instead of native `<script>` tags for better performance.\n\n```tsx\n// Bad: Native script tag\n<script src=\"https://example.com/script.js\"></script>\n\n// Good: Next.js Script component\nimport Script from 'next/script'\n\n<Script src=\"https://example.com/script.js\" />\n```\n\n## Inline Scripts Need ID\n\nInline scripts require an `id` attribute for Next.js to track them.\n\n```tsx\n// Bad: Missing id\n<Script dangerouslySetInnerHTML={{ __html: 'console.log(\"hi\")' }} />\n\n// Good: Has id\n<Script id=\"my-script\" dangerouslySetInnerHTML={{ __html: 'console.log(\"hi\")' }} />\n\n// Good: Inline with id\n<Script id=\"show-banner\">\n  {`document.getElementById('banner').classList.remove('hidden')`}\n</Script>\n```\n\n## Don't Put Script in Head\n\n`next/script` should not be placed inside `next/head`. It handles its own positioning.\n\n```tsx\n// Bad: Script inside Head\nimport Head from 'next/head'\nimport Script from 'next/script'\n\n<Head>\n  <Script src=\"/analytics.js\" />\n</Head>\n\n// Good: Script outside Head\n<Head>\n  <title>Page</title>\n</Head>\n<Script src=\"/analytics.js\" />\n```\n\n## Loading Strategies\n\n```tsx\n// afterInteractive (default) - Load after page is interactive\n<Script src=\"/analytics.js\" strategy=\"afterInteractive\" />\n\n// lazyOnload - Load during idle time\n<Script src=\"/widget.js\" strategy=\"lazyOnload\" />\n\n// beforeInteractive - Load before page is interactive (use sparingly)\n// Only works in app/layout.tsx or pages/_document.js\n<Script src=\"/critical.js\" strategy=\"beforeInteractive\" />\n\n// worker - Load in web worker (experimental)\n<Script src=\"/heavy.js\" strategy=\"worker\" />\n```\n\n## Google Analytics\n\nUse `@next/third-parties` instead of inline GA scripts.\n\n```tsx\n// Bad: Inline GA script\n<Script src=\"https://www.googletagmanager.com/gtag/js?id=G-XXXXX\" />\n<Script id=\"ga-init\">\n  {`window.dataLayer = window.dataLayer || [];\n    function gtag(){dataLayer.push(arguments);}\n    gtag('js', new Date());\n    gtag('config', 'G-XXXXX');`}\n</Script>\n\n// Good: Next.js component\nimport { GoogleAnalytics } from '@next/third-parties/google'\n\nexport default function Layout({ children }) {\n  return (\n    <html>\n      <body>{children}</body>\n      <GoogleAnalytics gaId=\"G-XXXXX\" />\n    </html>\n  )\n}\n```\n\n## Google Tag Manager\n\n```tsx\nimport { GoogleTagManager } from '@next/third-parties/google'\n\nexport default function Layout({ children }) {\n  return (\n    <html>\n      <GoogleTagManager gtmId=\"GTM-XXXXX\" />\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n## Other Third-Party Scripts\n\n```tsx\n// YouTube embed\nimport { YouTubeEmbed } from '@next/third-parties/google'\n\n<YouTubeEmbed videoid=\"dQw4w9WgXcQ\" />\n\n// Google Maps\nimport { GoogleMapsEmbed } from '@next/third-parties/google'\n\n<GoogleMapsEmbed\n  apiKey=\"YOUR_API_KEY\"\n  mode=\"place\"\n  q=\"Brooklyn+Bridge,New+York,NY\"\n/>\n```\n\n## Quick Reference\n\n| Pattern | Issue | Fix |\n|---------|-------|-----|\n| `<script src=\"...\">` | No optimization | Use `next/script` |\n| `<Script>` without id | Can't track inline scripts | Add `id` attribute |\n| `<Script>` inside `<Head>` | Wrong placement | Move outside Head |\n| Inline GA/GTM scripts | No optimization | Use `@next/third-parties` |\n| `strategy=\"beforeInteractive\"` outside layout | Won't work | Only use in root layout |\n",".agents/skills/next-best-practices/self-hosting.md":"# Self-Hosting Next.js\n\nDeploy Next.js outside of Vercel with confidence.\n\n## Quick Start: Standalone Output\n\nFor Docker or any containerized deployment, use standalone output:\n\n```js\n// next.config.js\nmodule.exports = {\n  output: 'standalone',\n};\n```\n\nThis creates a minimal `standalone` folder with only production dependencies:\n\n```\n.next/\n├── standalone/\n│   ├── server.js          # Entry point\n│   ├── node_modules/      # Only production deps\n│   └── .next/             # Build output\n└── static/                # Must be copied separately\n```\n\n## Docker Deployment\n\n### Dockerfile\n\n```dockerfile\nFROM node:20-alpine AS base\n\n# Install dependencies\nFROM base AS deps\nWORKDIR /app\nCOPY package.json package-lock.json* ./\nRUN npm ci\n\n# Build\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\nRUN npm run build\n\n# Production\nFROM base AS runner\nWORKDIR /app\n\nENV NODE_ENV=production\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\n# Copy standalone output\nCOPY --from=builder /app/.next/standalone ./\nCOPY --from=builder /app/.next/static ./.next/static\nCOPY --from=builder /app/public ./public\n\nUSER nextjs\n\nEXPOSE 3000\nENV PORT=3000\nENV HOSTNAME=\"0.0.0.0\"\n\nCMD [\"node\", \"server.js\"]\n```\n\n### Docker Compose\n\n```yaml\nversion: '3.8'\n\nservices:\n  web:\n    build: .\n    ports:\n      - \"3000:3000\"\n    environment:\n      - NODE_ENV=production\n    restart: unless-stopped\n    healthcheck:\n      test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:3000/api/health\"]\n      interval: 30s\n      timeout: 10s\n      retries: 3\n```\n\n## PM2 Deployment\n\nFor traditional server deployments:\n\n```js\n// ecosystem.config.js\nmodule.exports = {\n  apps: [{\n    name: 'nextjs',\n    script: '.next/standalone/server.js',\n    instances: 'max',\n    exec_mode: 'cluster',\n    env: {\n      NODE_ENV: 'production',\n      PORT: 3000,\n    },\n  }],\n};\n```\n\n```bash\nnpm run build\npm2 start ecosystem.config.js\n```\n\n## ISR and Cache Handlers\n\n### The Problem\n\nISR (Incremental Static Regeneration) uses filesystem caching by default. This **breaks with multiple instances**:\n\n- Instance A regenerates page → saves to its local disk\n- Instance B serves stale page → doesn't see Instance A's cache\n- Load balancer sends users to random instances → inconsistent content\n\n### Solution: Custom Cache Handler\n\nNext.js 14+ supports custom cache handlers for shared storage:\n\n```js\n// next.config.js\nmodule.exports = {\n  cacheHandler: require.resolve('./cache-handler.js'),\n  cacheMaxMemorySize: 0, // Disable in-memory cache\n};\n```\n\n#### Redis Cache Handler Example\n\n```js\n// cache-handler.js\nconst Redis = require('ioredis');\n\nconst redis = new Redis(process.env.REDIS_URL);\nconst CACHE_PREFIX = 'nextjs:';\n\nmodule.exports = class CacheHandler {\n  constructor(options) {\n    this.options = options;\n  }\n\n  async get(key) {\n    const data = await redis.get(CACHE_PREFIX + key);\n    if (!data) return null;\n\n    const parsed = JSON.parse(data);\n    return {\n      value: parsed.value,\n      lastModified: parsed.lastModified,\n    };\n  }\n\n  async set(key, data, ctx) {\n    const cacheData = {\n      value: data,\n      lastModified: Date.now(),\n    };\n\n    // Set TTL based on revalidate option\n    if (ctx?.revalidate) {\n      await redis.setex(\n        CACHE_PREFIX + key,\n        ctx.revalidate,\n        JSON.stringify(cacheData)\n      );\n    } else {\n      await redis.set(CACHE_PREFIX + key, JSON.stringify(cacheData));\n    }\n  }\n\n  async revalidateTag(tags) {\n    // Implement tag-based invalidation\n    // This requires tracking which keys have which tags\n  }\n};\n```\n\n#### S3 Cache Handler Example\n\n```js\n// cache-handler.js\nconst { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3');\n\nconst s3 = new S3Client({ region: process.env.AWS_REGION });\nconst BUCKET = process.env.CACHE_BUCKET;\n\nmodule.exports = class CacheHandler {\n  async get(key) {\n    try {\n      const response = await s3.send(new GetObjectCommand({\n        Bucket: BUCKET,\n        Key: `cache/${key}`,\n      }));\n      const body = await response.Body.transformToString();\n      return JSON.parse(body);\n    } catch (err) {\n      if (err.name === 'NoSuchKey') return null;\n      throw err;\n    }\n  }\n\n  async set(key, data, ctx) {\n    await s3.send(new PutObjectCommand({\n      Bucket: BUCKET,\n      Key: `cache/${key}`,\n      Body: JSON.stringify({\n        value: data,\n        lastModified: Date.now(),\n      }),\n      ContentType: 'application/json',\n    }));\n  }\n};\n```\n\n## What Works vs What Needs Setup\n\n| Feature | Single Instance | Multi-Instance | Notes |\n|---------|----------------|----------------|-------|\n| SSR | Yes | Yes | No special setup |\n| SSG | Yes | Yes | Built at deploy time |\n| ISR | Yes | Needs cache handler | Filesystem cache breaks |\n| Image Optimization | Yes | Yes | CPU-intensive, consider CDN |\n| Middleware | Yes | Yes | Runs on Node.js |\n| Edge Runtime | Limited | Limited | Some features Node-only |\n| `revalidatePath/Tag` | Yes | Needs cache handler | Must share cache |\n| `next/font` | Yes | Yes | Fonts bundled at build |\n| Draft Mode | Yes | Yes | Cookie-based |\n\n## Image Optimization\n\nNext.js Image Optimization works out of the box but is CPU-intensive.\n\n### Option 1: Built-in (Simple)\n\nWorks automatically, but consider:\n- Set `deviceSizes` and `imageSizes` in config to limit variants\n- Use `minimumCacheTTL` to reduce regeneration\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    minimumCacheTTL: 60 * 60 * 24, // 24 hours\n    deviceSizes: [640, 750, 1080, 1920], // Limit sizes\n  },\n};\n```\n\n### Option 2: External Loader (Recommended for Scale)\n\nOffload to Cloudinary, Imgix, or similar:\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    loader: 'custom',\n    loaderFile: './lib/image-loader.js',\n  },\n};\n```\n\n```js\n// lib/image-loader.js\nexport default function cloudinaryLoader({ src, width, quality }) {\n  const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`];\n  return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}${src}`;\n}\n```\n\n## Environment Variables\n\n### Build-time vs Runtime\n\n```js\n// Available at build time only (baked into bundle)\nNEXT_PUBLIC_API_URL=https://api.example.com\n\n// Available at runtime (server-side only)\nDATABASE_URL=postgresql://...\nAPI_SECRET=...\n```\n\n### Runtime Configuration\n\nFor truly dynamic config, don't use `NEXT_PUBLIC_*`. Instead:\n\n```tsx\n// app/api/config/route.ts\nexport async function GET() {\n  return Response.json({\n    apiUrl: process.env.API_URL,\n    features: process.env.FEATURES?.split(','),\n  });\n}\n```\n\n## OpenNext: Serverless Without Vercel\n\n[OpenNext](https://open-next.js.org/) adapts Next.js for AWS Lambda, Cloudflare Workers, etc.\n\n```bash\nnpx create-sst@latest\n# or\nnpx @opennextjs/aws build\n```\n\nSupports:\n- AWS Lambda + CloudFront\n- Cloudflare Workers\n- Netlify Functions\n- Deno Deploy\n\n## Health Check Endpoint\n\nAlways include a health check for load balancers:\n\n```tsx\n// app/api/health/route.ts\nexport async function GET() {\n  try {\n    // Optional: check database connection\n    // await db.$queryRaw`SELECT 1`;\n\n    return Response.json({ status: 'healthy' }, { status: 200 });\n  } catch (error) {\n    return Response.json({ status: 'unhealthy' }, { status: 503 });\n  }\n}\n```\n\n## Pre-Deployment Checklist\n\n1. **Build locally first**: `npm run build` - catch errors before deploy\n2. **Test standalone output**: `node .next/standalone/server.js`\n3. **Set `output: 'standalone'`** for Docker\n4. **Configure cache handler** for multi-instance ISR\n5. **Set `HOSTNAME=\"0.0.0.0\"`** for containers\n6. **Copy `public/` and `.next/static/`** - not included in standalone\n7. **Add health check endpoint**\n8. **Test ISR revalidation** after deployment\n9. **Monitor memory usage** - Node.js defaults may need tuning\n\n## Testing Cache Handler\n\n**Critical**: Test your cache handler on every Next.js upgrade:\n\n```bash\n# Start multiple instances\nPORT=3001 node .next/standalone/server.js &\nPORT=3002 node .next/standalone/server.js &\n\n# Trigger ISR revalidation\ncurl http://localhost:3001/api/revalidate?path=/posts\n\n# Verify both instances see the update\ncurl http://localhost:3001/posts\ncurl http://localhost:3002/posts\n# Should return identical content\n```\n",".agents/skills/next-best-practices/suspense-boundaries.md":"# Suspense Boundaries\n\nClient hooks that cause CSR bailout without Suspense boundaries.\n\n## useSearchParams\n\nAlways requires Suspense boundary in static routes. Without it, the entire page becomes client-side rendered.\n\n```tsx\n// Bad: Entire page becomes CSR\n'use client'\n\nimport { useSearchParams } from 'next/navigation'\n\nexport default function SearchBar() {\n  const searchParams = useSearchParams()\n  return <div>Query: {searchParams.get('q')}</div>\n}\n```\n\n```tsx\n// Good: Wrap in Suspense\nimport { Suspense } from 'react'\nimport SearchBar from './search-bar'\n\nexport default function Page() {\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <SearchBar />\n    </Suspense>\n  )\n}\n```\n\n## usePathname\n\nRequires Suspense boundary when route has dynamic parameters.\n\n```tsx\n// In dynamic route [slug]\n// Bad: No Suspense\n'use client'\nimport { usePathname } from 'next/navigation'\n\nexport function Breadcrumb() {\n  const pathname = usePathname()\n  return <nav>{pathname}</nav>\n}\n```\n\n```tsx\n// Good: Wrap in Suspense\n<Suspense fallback={<BreadcrumbSkeleton />}>\n  <Breadcrumb />\n</Suspense>\n```\n\nIf you use `generateStaticParams`, Suspense is optional.\n\n## Quick Reference\n\n| Hook | Suspense Required |\n|------|-------------------|\n| `useSearchParams()` | Yes |\n| `usePathname()` | Yes (dynamic routes) |\n| `useParams()` | No |\n| `useRouter()` | No |\n",".agents/skills/next-cache-components/SKILL.md":"---\nname: next-cache-components\ndescription: Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag\n---\n\n# Cache Components (Next.js 16+)\n\nCache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route.\n\n## Enable Cache Components\n\n```ts\n// next.config.ts\nimport type { NextConfig } from 'next'\n\nconst nextConfig: NextConfig = {\n  cacheComponents: true,\n}\n\nexport default nextConfig\n```\n\nThis replaces the old `experimental.ppr` flag.\n\n---\n\n## Three Content Types\n\nWith Cache Components enabled, content falls into three categories:\n\n### 1. Static (Auto-Prerendered)\n\nSynchronous code, imports, pure computations - prerendered at build time:\n\n```tsx\nexport default function Page() {\n  return (\n    <header>\n      <h1>Our Blog</h1>  {/* Static - instant */}\n      <nav>...</nav>\n    </header>\n  )\n}\n```\n\n### 2. Cached (`use cache`)\n\nAsync data that doesn't need fresh fetches every request:\n\n```tsx\nasync function BlogPosts() {\n  'use cache'\n  cacheLife('hours')\n\n  const posts = await db.posts.findMany()\n  return <PostList posts={posts} />\n}\n```\n\n### 3. Dynamic (Suspense)\n\nRuntime data that must be fresh - wrap in Suspense:\n\n```tsx\nimport { Suspense } from 'react'\n\nexport default function Page() {\n  return (\n    <>\n      <BlogPosts />  {/* Cached */}\n\n      <Suspense fallback={<p>Loading...</p>}>\n        <UserPreferences />  {/* Dynamic - streams in */}\n      </Suspense>\n    </>\n  )\n}\n\nasync function UserPreferences() {\n  const theme = (await cookies()).get('theme')?.value\n  return <p>Theme: {theme}</p>\n}\n```\n\n---\n\n## `use cache` Directive\n\n### File Level\n\n```tsx\n'use cache'\n\nexport default async function Page() {\n  // Entire page is cached\n  const data = await fetchData()\n  return <div>{data}</div>\n}\n```\n\n### Component Level\n\n```tsx\nexport async function CachedComponent() {\n  'use cache'\n  const data = await fetchData()\n  return <div>{data}</div>\n}\n```\n\n### Function Level\n\n```tsx\nexport async function getData() {\n  'use cache'\n  return db.query('SELECT * FROM posts')\n}\n```\n\n---\n\n## Cache Profiles\n\n### Built-in Profiles\n\n```tsx\n'use cache'                    // Default: 5m stale, 15m revalidate\n```\n\n```tsx\n'use cache: remote'           // Platform-provided cache (Redis, KV)\n```\n\n```tsx\n'use cache: private'          // For compliance, allows runtime APIs\n```\n\n### `cacheLife()` - Custom Lifetime\n\n```tsx\nimport { cacheLife } from 'next/cache'\n\nasync function getData() {\n  'use cache'\n  cacheLife('hours')  // Built-in profile\n  return fetch('/api/data')\n}\n```\n\nBuilt-in profiles: `'default'`, `'minutes'`, `'hours'`, `'days'`, `'weeks'`, `'max'`\n\n### Inline Configuration\n\n```tsx\nasync function getData() {\n  'use cache'\n  cacheLife({\n    stale: 3600,      // 1 hour - serve stale while revalidating\n    revalidate: 7200, // 2 hours - background revalidation interval\n    expire: 86400,    // 1 day - hard expiration\n  })\n  return fetch('/api/data')\n}\n```\n\n---\n\n## Cache Invalidation\n\n### `cacheTag()` - Tag Cached Content\n\n```tsx\nimport { cacheTag } from 'next/cache'\n\nasync function getProducts() {\n  'use cache'\n  cacheTag('products')\n  return db.products.findMany()\n}\n\nasync function getProduct(id: string) {\n  'use cache'\n  cacheTag('products', `product-${id}`)\n  return db.products.findUnique({ where: { id } })\n}\n```\n\n### `updateTag()` - Immediate Invalidation\n\nUse when you need the cache refreshed within the same request:\n\n```tsx\n'use server'\n\nimport { updateTag } from 'next/cache'\n\nexport async function updateProduct(id: string, data: FormData) {\n  await db.products.update({ where: { id }, data })\n  updateTag(`product-${id}`)  // Immediate - same request sees fresh data\n}\n```\n\n### `revalidateTag()` - Background Revalidation\n\nUse for stale-while-revalidate behavior:\n\n```tsx\n'use server'\n\nimport { revalidateTag } from 'next/cache'\n\nexport async function createPost(data: FormData) {\n  await db.posts.create({ data })\n  revalidateTag('posts')  // Background - next request sees fresh data\n}\n```\n\n---\n\n## Runtime Data Constraint\n\n**Cannot** access `cookies()`, `headers()`, or `searchParams` inside `use cache`.\n\n### Solution: Pass as Arguments\n\n```tsx\n// Wrong - runtime API inside use cache\nasync function CachedProfile() {\n  'use cache'\n  const session = (await cookies()).get('session')?.value  // Error!\n  return <div>{session}</div>\n}\n\n// Correct - extract outside, pass as argument\nasync function ProfilePage() {\n  const session = (await cookies()).get('session')?.value\n  return <CachedProfile sessionId={session} />\n}\n\nasync function CachedProfile({ sessionId }: { sessionId: string }) {\n  'use cache'\n  // sessionId becomes part of cache key automatically\n  const data = await fetchUserData(sessionId)\n  return <div>{data.name}</div>\n}\n```\n\n### Exception: `use cache: private`\n\nFor compliance requirements when you can't refactor:\n\n```tsx\nasync function getData() {\n  'use cache: private'\n  const session = (await cookies()).get('session')?.value  // Allowed\n  return fetchData(session)\n}\n```\n\n---\n\n## Cache Key Generation\n\nCache keys are automatic based on:\n- **Build ID** - invalidates all caches on deploy\n- **Function ID** - hash of function location\n- **Serializable arguments** - props become part of key\n- **Closure variables** - outer scope values included\n\n```tsx\nasync function Component({ userId }: { userId: string }) {\n  const getData = async (filter: string) => {\n    'use cache'\n    // Cache key = userId (closure) + filter (argument)\n    return fetch(`/api/users/${userId}?filter=${filter}`)\n  }\n  return getData('active')\n}\n```\n\n---\n\n## Complete Example\n\n```tsx\nimport { Suspense } from 'react'\nimport { cookies } from 'next/headers'\nimport { cacheLife, cacheTag } from 'next/cache'\n\nexport default function DashboardPage() {\n  return (\n    <>\n      {/* Static shell - instant from CDN */}\n      <header><h1>Dashboard</h1></header>\n      <nav>...</nav>\n\n      {/* Cached - fast, revalidates hourly */}\n      <Stats />\n\n      {/* Dynamic - streams in with fresh data */}\n      <Suspense fallback={<NotificationsSkeleton />}>\n        <Notifications />\n      </Suspense>\n    </>\n  )\n}\n\nasync function Stats() {\n  'use cache'\n  cacheLife('hours')\n  cacheTag('dashboard-stats')\n\n  const stats = await db.stats.aggregate()\n  return <StatsDisplay stats={stats} />\n}\n\nasync function Notifications() {\n  const userId = (await cookies()).get('userId')?.value\n  const notifications = await db.notifications.findMany({\n    where: { userId, read: false }\n  })\n  return <NotificationList items={notifications} />\n}\n```\n\n---\n\n## Migration from Previous Versions\n\n| Old Config | Replacement |\n|-----------|-------------|\n| `experimental.ppr` | `cacheComponents: true` |\n| `dynamic = 'force-dynamic'` | Remove (default behavior) |\n| `dynamic = 'force-static'` | `'use cache'` + `cacheLife('max')` |\n| `revalidate = N` | `cacheLife({ revalidate: N })` |\n| `unstable_cache()` | `'use cache'` directive |\n\n### Migrating `unstable_cache` to `use cache`\n\n`unstable_cache` has been replaced by the `use cache` directive in Next.js 16. When `cacheComponents` is enabled, convert `unstable_cache` calls to `use cache` functions:\n\n**Before (`unstable_cache`):**\n\n```tsx\nimport { unstable_cache } from 'next/cache'\n\nconst getCachedUser = unstable_cache(\n  async (id) => getUser(id),\n  ['my-app-user'],\n  {\n    tags: ['users'],\n    revalidate: 60,\n  }\n)\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const user = await getCachedUser(id)\n  return <div>{user.name}</div>\n}\n```\n\n**After (`use cache`):**\n\n```tsx\nimport { cacheLife, cacheTag } from 'next/cache'\n\nasync function getCachedUser(id: string) {\n  'use cache'\n  cacheTag('users')\n  cacheLife({ revalidate: 60 })\n  return getUser(id)\n}\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const user = await getCachedUser(id)\n  return <div>{user.name}</div>\n}\n```\n\nKey differences:\n- **No manual cache keys** - `use cache` generates keys automatically from function arguments and closures. The `keyParts` array from `unstable_cache` is no longer needed.\n- **Tags** - Replace `options.tags` with `cacheTag()` calls inside the function.\n- **Revalidation** - Replace `options.revalidate` with `cacheLife({ revalidate: N })` or a built-in profile like `cacheLife('minutes')`.\n- **Dynamic data** - `unstable_cache` did not support `cookies()` or `headers()` inside the callback. The same restriction applies to `use cache`, but you can use `'use cache: private'` if needed.\n\n---\n\n## Limitations\n\n- **Edge runtime not supported** - requires Node.js\n- **Static export not supported** - needs server\n- **Non-deterministic values** (`Math.random()`, `Date.now()`) execute once at build time inside `use cache`\n\nFor request-time randomness outside cache:\n\n```tsx\nimport { connection } from 'next/server'\n\nasync function DynamicContent() {\n  await connection()  // Defer to request time\n  const id = crypto.randomUUID()  // Different per request\n  return <div>{id}</div>\n}\n```\n\nSources:\n- [Cache Components Guide](https://nextjs.org/docs/app/getting-started/cache-components)\n- [use cache Directive](https://nextjs.org/docs/app/api-reference/directives/use-cache)\n- [unstable_cache (legacy)](https://nextjs.org/docs/app/api-reference/functions/unstable_cache)\n",".agents/skills/next-upgrade/SKILL.md":"---\nname: next-upgrade\ndescription: Upgrade Next.js to the latest version following official migration guides and codemods\nargument-hint: \"[target-version]\"\n---\n\n# Upgrade Next.js\n\nUpgrade the current project to the latest Next.js version following official migration guides.\n\n## Instructions\n\n1. **Detect current version**: Read `package.json` to identify the current Next.js version and related dependencies (React, React DOM, etc.)\n\n2. **Fetch the latest upgrade guide**: Use WebFetch to get the official upgrade documentation:\n   - Codemods: https://nextjs.org/docs/app/guides/upgrading/codemods\n   - Version-specific guides (adjust version as needed):\n     - https://nextjs.org/docs/app/guides/upgrading/version-16 \n     - https://nextjs.org/docs/app/guides/upgrading/version-15\n     - https://nextjs.org/docs/app/guides/upgrading/version-14\n\n3. **Determine upgrade path**: Based on current version, identify which migration steps apply. For major version jumps, upgrade incrementally (e.g., 13 → 14 → 15).\n\n4. **Run codemods first**: Next.js provides codemods to automate breaking changes:\n   ```bash\n   npx @next/codemod@latest <transform> <path>\n   ```\n   Common transforms:\n   - `next-async-request-api` - Updates async Request APIs (v15)\n   - `next-request-geo-ip` - Migrates geo/ip properties (v15)\n   - `next-dynamic-access-named-export` - Transforms dynamic imports (v15)\n\n5. **Update dependencies**: Upgrade Next.js and peer dependencies together:\n   ```bash\n   npm install next@latest react@latest react-dom@latest\n   ```\n\n6. **Review breaking changes**: Check the upgrade guide for manual changes needed:\n   - API changes (e.g., async params in v15)\n   - Configuration changes in `next.config.js`\n   - Deprecated features being removed\n\n7. **Update TypeScript types** (if applicable):\n   ```bash\n   npm install @types/react@latest @types/react-dom@latest\n   ```\n\n8. **Test the upgrade**:\n   - Run `npm run build` to check for build errors\n   - Run `npm run dev` and test key functionality\n",".agents/skills/nodejs-backend-patterns/SKILL.md":"---\nname: nodejs-backend-patterns\ndescription: Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.\n---\n\n# Node.js Backend Patterns\n\nComprehensive guidance for building scalable, maintainable, and production-ready Node.js backend applications with modern frameworks, architectural patterns, and best practices.\n\n## When to Use This Skill\n\n- Building REST APIs or GraphQL servers\n- Creating microservices with Node.js\n- Implementing authentication and authorization\n- Designing scalable backend architectures\n- Setting up middleware and error handling\n- Integrating databases (SQL and NoSQL)\n- Building real-time applications with WebSockets\n- Implementing background job processing\n\n## Core Frameworks\n\n### Express.js - Minimalist Framework\n\n**Basic Setup:**\n\n```typescript\nimport express, { Request, Response, NextFunction } from \"express\";\nimport helmet from \"helmet\";\nimport cors from \"cors\";\nimport compression from \"compression\";\n\nconst app = express();\n\n// Security middleware\napp.use(helmet());\napp.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(\",\") }));\napp.use(compression());\n\n// Body parsing\napp.use(express.json({ limit: \"10mb\" }));\napp.use(express.urlencoded({ extended: true, limit: \"10mb\" }));\n\n// Request logging\napp.use((req: Request, res: Response, next: NextFunction) => {\n  console.log(`${req.method} ${req.path}`);\n  next();\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => {\n  console.log(`Server running on port ${PORT}`);\n});\n```\n\n### Fastify - High Performance Framework\n\n**Basic Setup:**\n\n```typescript\nimport Fastify from \"fastify\";\nimport helmet from \"@fastify/helmet\";\nimport cors from \"@fastify/cors\";\nimport compress from \"@fastify/compress\";\n\nconst fastify = Fastify({\n  logger: {\n    level: process.env.LOG_LEVEL || \"info\",\n    transport: {\n      target: \"pino-pretty\",\n      options: { colorize: true },\n    },\n  },\n});\n\n// Plugins\nawait fastify.register(helmet);\nawait fastify.register(cors, { origin: true });\nawait fastify.register(compress);\n\n// Type-safe routes with schema validation\nfastify.post<{\n  Body: { name: string; email: string };\n  Reply: { id: string; name: string };\n}>(\n  \"/users\",\n  {\n    schema: {\n      body: {\n        type: \"object\",\n        required: [\"name\", \"email\"],\n        properties: {\n          name: { type: \"string\", minLength: 1 },\n          email: { type: \"string\", format: \"email\" },\n        },\n      },\n    },\n  },\n  async (request, reply) => {\n    const { name, email } = request.body;\n    return { id: \"123\", name };\n  },\n);\n\nawait fastify.listen({ port: 3000, host: \"0.0.0.0\" });\n```\n\n## Architectural Patterns\n\n### Pattern 1: Layered Architecture\n\n**Structure:**\n\n```\nsrc/\n├── controllers/     # Handle HTTP requests/responses\n├── services/        # Business logic\n├── repositories/    # Data access layer\n├── models/          # Data models\n├── middleware/      # Express/Fastify middleware\n├── routes/          # Route definitions\n├── utils/           # Helper functions\n├── config/          # Configuration\n└── types/           # TypeScript types\n```\n\n**Controller Layer:**\n\n```typescript\n// controllers/user.controller.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { UserService } from \"../services/user.service\";\nimport { CreateUserDTO, UpdateUserDTO } from \"../types/user.types\";\n\nexport class UserController {\n  constructor(private userService: UserService) {}\n\n  async createUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const userData: CreateUserDTO = req.body;\n      const user = await this.userService.createUser(userData);\n      res.status(201).json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async getUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const user = await this.userService.getUserById(id);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async updateUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const updates: UpdateUserDTO = req.body;\n      const user = await this.userService.updateUser(id, updates);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async deleteUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      await this.userService.deleteUser(id);\n      res.status(204).send();\n    } catch (error) {\n      next(error);\n    }\n  }\n}\n```\n\n**Service Layer:**\n\n```typescript\n// services/user.service.ts\nimport { UserRepository } from \"../repositories/user.repository\";\nimport { CreateUserDTO, UpdateUserDTO, User } from \"../types/user.types\";\nimport { NotFoundError, ValidationError } from \"../utils/errors\";\nimport bcrypt from \"bcrypt\";\n\nexport class UserService {\n  constructor(private userRepository: UserRepository) {}\n\n  async createUser(userData: CreateUserDTO): Promise<User> {\n    // Validation\n    const existingUser = await this.userRepository.findByEmail(userData.email);\n    if (existingUser) {\n      throw new ValidationError(\"Email already exists\");\n    }\n\n    // Hash password\n    const hashedPassword = await bcrypt.hash(userData.password, 10);\n\n    // Create user\n    const user = await this.userRepository.create({\n      ...userData,\n      password: hashedPassword,\n    });\n\n    // Remove password from response\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async getUserById(id: string): Promise<User> {\n    const user = await this.userRepository.findById(id);\n    if (!user) {\n      throw new NotFoundError(\"User not found\");\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async updateUser(id: string, updates: UpdateUserDTO): Promise<User> {\n    const user = await this.userRepository.update(id, updates);\n    if (!user) {\n      throw new NotFoundError(\"User not found\");\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async deleteUser(id: string): Promise<void> {\n    const deleted = await this.userRepository.delete(id);\n    if (!deleted) {\n      throw new NotFoundError(\"User not found\");\n    }\n  }\n}\n```\n\n**Repository Layer:**\n\n```typescript\n// repositories/user.repository.ts\nimport { Pool } from \"pg\";\nimport { CreateUserDTO, UpdateUserDTO, UserEntity } from \"../types/user.types\";\n\nexport class UserRepository {\n  constructor(private db: Pool) {}\n\n  async create(\n    userData: CreateUserDTO & { password: string },\n  ): Promise<UserEntity> {\n    const query = `\n      INSERT INTO users (name, email, password)\n      VALUES ($1, $2, $3)\n      RETURNING id, name, email, password, created_at, updated_at\n    `;\n    const { rows } = await this.db.query(query, [\n      userData.name,\n      userData.email,\n      userData.password,\n    ]);\n    return rows[0];\n  }\n\n  async findById(id: string): Promise<UserEntity | null> {\n    const query = \"SELECT * FROM users WHERE id = $1\";\n    const { rows } = await this.db.query(query, [id]);\n    return rows[0] || null;\n  }\n\n  async findByEmail(email: string): Promise<UserEntity | null> {\n    const query = \"SELECT * FROM users WHERE email = $1\";\n    const { rows } = await this.db.query(query, [email]);\n    return rows[0] || null;\n  }\n\n  async update(id: string, updates: UpdateUserDTO): Promise<UserEntity | null> {\n    const fields = Object.keys(updates);\n    const values = Object.values(updates);\n\n    const setClause = fields\n      .map((field, idx) => `${field} = $${idx + 2}`)\n      .join(\", \");\n\n    const query = `\n      UPDATE users\n      SET ${setClause}, updated_at = CURRENT_TIMESTAMP\n      WHERE id = $1\n      RETURNING *\n    `;\n\n    const { rows } = await this.db.query(query, [id, ...values]);\n    return rows[0] || null;\n  }\n\n  async delete(id: string): Promise<boolean> {\n    const query = \"DELETE FROM users WHERE id = $1\";\n    const { rowCount } = await this.db.query(query, [id]);\n    return rowCount > 0;\n  }\n}\n```\n\n### Pattern 2: Dependency Injection\n\nUse a DI container to wire up repositories, services, and controllers. For a full container implementation, see [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Middleware Patterns\n\n### Authentication Middleware\n\n```typescript\n// middleware/auth.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport jwt from \"jsonwebtoken\";\nimport { UnauthorizedError } from \"../utils/errors\";\n\ninterface JWTPayload {\n  userId: string;\n  email: string;\n}\n\ndeclare global {\n  namespace Express {\n    interface Request {\n      user?: JWTPayload;\n    }\n  }\n}\n\nexport const authenticate = async (\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  try {\n    const token = req.headers.authorization?.replace(\"Bearer \", \"\");\n\n    if (!token) {\n      throw new UnauthorizedError(\"No token provided\");\n    }\n\n    const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;\n\n    req.user = payload;\n    next();\n  } catch (error) {\n    next(new UnauthorizedError(\"Invalid token\"));\n  }\n};\n\nexport const authorize = (...roles: string[]) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) {\n      return next(new UnauthorizedError(\"Not authenticated\"));\n    }\n\n    // Check if user has required role\n    const hasRole = roles.some((role) => req.user?.roles?.includes(role));\n\n    if (!hasRole) {\n      return next(new UnauthorizedError(\"Insufficient permissions\"));\n    }\n\n    next();\n  };\n};\n```\n\n### Validation Middleware\n\n```typescript\n// middleware/validation.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { AnyZodObject, ZodError } from \"zod\";\nimport { ValidationError } from \"../utils/errors\";\n\nexport const validate = (schema: AnyZodObject) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    try {\n      await schema.parseAsync({\n        body: req.body,\n        query: req.query,\n        params: req.params,\n      });\n      next();\n    } catch (error) {\n      if (error instanceof ZodError) {\n        const errors = error.errors.map((err) => ({\n          field: err.path.join(\".\"),\n          message: err.message,\n        }));\n        next(new ValidationError(\"Validation failed\", errors));\n      } else {\n        next(error);\n      }\n    }\n  };\n};\n\n// Usage with Zod\nimport { z } from \"zod\";\n\nconst createUserSchema = z.object({\n  body: z.object({\n    name: z.string().min(1),\n    email: z.string().email(),\n    password: z.string().min(8),\n  }),\n});\n\nrouter.post(\"/users\", validate(createUserSchema), userController.createUser);\n```\n\n### Rate Limiting Middleware\n\n```typescript\n// middleware/rate-limit.middleware.ts\nimport rateLimit from \"express-rate-limit\";\nimport RedisStore from \"rate-limit-redis\";\nimport Redis from \"ioredis\";\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || \"6379\"),\n});\n\nexport const apiLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: \"rl:\",\n  }),\n  windowMs: 15 * 60 * 1000, // 15 minutes\n  max: 100, // Limit each IP to 100 requests per windowMs\n  message: \"Too many requests from this IP, please try again later\",\n  standardHeaders: true,\n  legacyHeaders: false,\n});\n\nexport const authLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: \"rl:auth:\",\n  }),\n  windowMs: 15 * 60 * 1000,\n  max: 5, // Stricter limit for auth endpoints\n  skipSuccessfulRequests: true,\n});\n```\n\n### Request Logging Middleware\n\n```typescript\n// middleware/logger.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport pino from \"pino\";\n\nconst logger = pino({\n  level: process.env.LOG_LEVEL || \"info\",\n  transport: {\n    target: \"pino-pretty\",\n    options: { colorize: true },\n  },\n});\n\nexport const requestLogger = (\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  const start = Date.now();\n\n  // Log response when finished\n  res.on(\"finish\", () => {\n    const duration = Date.now() - start;\n    logger.info({\n      method: req.method,\n      url: req.url,\n      status: res.statusCode,\n      duration: `${duration}ms`,\n      userAgent: req.headers[\"user-agent\"],\n      ip: req.ip,\n    });\n  });\n\n  next();\n};\n\nexport { logger };\n```\n\n## Error Handling\n\n### Custom Error Classes\n\n```typescript\n// utils/errors.ts\nexport class AppError extends Error {\n  constructor(\n    public message: string,\n    public statusCode: number = 500,\n    public isOperational: boolean = true,\n  ) {\n    super(message);\n    Object.setPrototypeOf(this, AppError.prototype);\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(\n    message: string,\n    public errors?: any[],\n  ) {\n    super(message, 400);\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(message: string = \"Resource not found\") {\n    super(message, 404);\n  }\n}\n\nexport class UnauthorizedError extends AppError {\n  constructor(message: string = \"Unauthorized\") {\n    super(message, 401);\n  }\n}\n\nexport class ForbiddenError extends AppError {\n  constructor(message: string = \"Forbidden\") {\n    super(message, 403);\n  }\n}\n\nexport class ConflictError extends AppError {\n  constructor(message: string) {\n    super(message, 409);\n  }\n}\n```\n\n### Global Error Handler\n\n```typescript\n// middleware/error-handler.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { AppError } from \"../utils/errors\";\nimport { logger } from \"./logger.middleware\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  if (err instanceof AppError) {\n    return res.status(err.statusCode).json({\n      status: \"error\",\n      message: err.message,\n      ...(err instanceof ValidationError && { errors: err.errors }),\n    });\n  }\n\n  // Log unexpected errors\n  logger.error({\n    error: err.message,\n    stack: err.stack,\n    url: req.url,\n    method: req.method,\n  });\n\n  // Don't leak error details in production\n  const message =\n    process.env.NODE_ENV === \"production\"\n      ? \"Internal server error\"\n      : err.message;\n\n  res.status(500).json({\n    status: \"error\",\n    message,\n  });\n};\n\n// Async error wrapper\nexport const asyncHandler = (\n  fn: (req: Request, res: Response, next: NextFunction) => Promise<any>,\n) => {\n  return (req: Request, res: Response, next: NextFunction) => {\n    Promise.resolve(fn(req, res, next)).catch(next);\n  };\n};\n```\n\n## Database Patterns\n\nNode.js supports both SQL and NoSQL databases. Use connection pooling for all production databases.\n\nKey patterns covered in [references/advanced-patterns.md](references/advanced-patterns.md):\n- **PostgreSQL with connection pool** — `pg` Pool configuration and graceful shutdown\n- **MongoDB with Mongoose** — connection management and schema definition\n- **Transaction pattern** — `BEGIN`/`COMMIT`/`ROLLBACK` with `pg` client\n\n## Authentication & Authorization\n\nJWT-based auth with access tokens (short-lived, 15m) and refresh tokens (7d). Full `AuthService` implementation with `bcrypt` password comparison in [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Caching Strategies\n\nRedis-backed `CacheService` with get/set/delete/invalidatePattern, plus a `@Cacheable` decorator for method-level caching. See [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## API Response Format\n\nStandardized `ApiResponse` helper with `success`, `error`, and `paginated` static methods. See [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Best Practices\n\n1. **Use TypeScript**: Type safety prevents runtime errors\n2. **Implement proper error handling**: Use custom error classes\n3. **Validate input**: Use libraries like Zod or Joi\n4. **Use environment variables**: Never hardcode secrets\n5. **Implement logging**: Use structured logging (Pino, Winston)\n6. **Add rate limiting**: Prevent abuse\n7. **Use HTTPS**: Always in production\n8. **Implement CORS properly**: Don't use `*` in production\n9. **Use dependency injection**: Easier testing and maintenance\n10. **Write tests**: Unit, integration, and E2E tests\n11. **Handle graceful shutdown**: Clean up resources\n12. **Use connection pooling**: For databases\n13. **Implement health checks**: For monitoring\n14. **Use compression**: Reduce response size\n15. **Monitor performance**: Use APM tools\n\n## Testing Patterns\n\nSee `javascript-testing-patterns` skill for comprehensive testing guidance.\n",".agents/skills/nodejs-backend-patterns/references/advanced-patterns.md":"# Node.js Advanced Patterns\n\nAdvanced patterns for dependency injection, database integration, authentication, caching, and API response formatting.\n\n## Dependency Injection\n\n### DI Container\n\n```typescript\n// di-container.ts\nimport { Pool } from \"pg\";\nimport { UserRepository } from \"./repositories/user.repository\";\nimport { UserService } from \"./services/user.service\";\nimport { UserController } from \"./controllers/user.controller\";\nimport { AuthService } from \"./services/auth.service\";\n\nclass Container {\n  private instances = new Map<string, any>();\n\n  register<T>(key: string, factory: () => T): void {\n    this.instances.set(key, factory);\n  }\n\n  resolve<T>(key: string): T {\n    const factory = this.instances.get(key);\n    if (!factory) {\n      throw new Error(`No factory registered for ${key}`);\n    }\n    return factory();\n  }\n\n  singleton<T>(key: string, factory: () => T): void {\n    let instance: T;\n    this.instances.set(key, () => {\n      if (!instance) {\n        instance = factory();\n      }\n      return instance;\n    });\n  }\n}\n\nexport const container = new Container();\n\n// Register dependencies\ncontainer.singleton(\n  \"db\",\n  () =>\n    new Pool({\n      host: process.env.DB_HOST,\n      port: parseInt(process.env.DB_PORT || \"5432\"),\n      database: process.env.DB_NAME,\n      user: process.env.DB_USER,\n      password: process.env.DB_PASSWORD,\n      max: 20,\n      idleTimeoutMillis: 30000,\n      connectionTimeoutMillis: 2000,\n    }),\n);\n\ncontainer.singleton(\n  \"userRepository\",\n  () => new UserRepository(container.resolve(\"db\")),\n);\n\ncontainer.singleton(\n  \"userService\",\n  () => new UserService(container.resolve(\"userRepository\")),\n);\n\ncontainer.register(\n  \"userController\",\n  () => new UserController(container.resolve(\"userService\")),\n);\n\ncontainer.singleton(\n  \"authService\",\n  () => new AuthService(container.resolve(\"userRepository\")),\n);\n```\n\n## Database Patterns\n\n### PostgreSQL with Connection Pool\n\n```typescript\n// config/database.ts\nimport { Pool, PoolConfig } from \"pg\";\n\nconst poolConfig: PoolConfig = {\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT || \"5432\"),\n  database: process.env.DB_NAME,\n  user: process.env.DB_USER,\n  password: process.env.DB_PASSWORD,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n};\n\nexport const pool = new Pool(poolConfig);\n\n// Test connection\npool.on(\"connect\", () => {\n  console.log(\"Database connected\");\n});\n\npool.on(\"error\", (err) => {\n  console.error(\"Unexpected database error\", err);\n  process.exit(-1);\n});\n\n// Graceful shutdown\nexport const closeDatabase = async () => {\n  await pool.end();\n  console.log(\"Database connection closed\");\n};\n```\n\n### MongoDB with Mongoose\n\n```typescript\n// config/mongoose.ts\nimport mongoose from \"mongoose\";\n\nconst connectDB = async () => {\n  try {\n    await mongoose.connect(process.env.MONGODB_URI!, {\n      maxPoolSize: 10,\n      serverSelectionTimeoutMS: 5000,\n      socketTimeoutMS: 45000,\n    });\n\n    console.log(\"MongoDB connected\");\n  } catch (error) {\n    console.error(\"MongoDB connection error:\", error);\n    process.exit(1);\n  }\n};\n\nmongoose.connection.on(\"disconnected\", () => {\n  console.log(\"MongoDB disconnected\");\n});\n\nmongoose.connection.on(\"error\", (err) => {\n  console.error(\"MongoDB error:\", err);\n});\n\nexport { connectDB };\n\n// Model example\nimport { Schema, model, Document } from \"mongoose\";\n\ninterface IUser extends Document {\n  name: string;\n  email: string;\n  password: string;\n  createdAt: Date;\n  updatedAt: Date;\n}\n\nconst userSchema = new Schema<IUser>(\n  {\n    name: { type: String, required: true },\n    email: { type: String, required: true, unique: true },\n    password: { type: String, required: true },\n  },\n  {\n    timestamps: true,\n  },\n);\n\n// Indexes\nuserSchema.index({ email: 1 });\n\nexport const User = model<IUser>(\"User\", userSchema);\n```\n\n### Transaction Pattern\n\n```typescript\n// services/order.service.ts\nimport { Pool } from \"pg\";\n\nexport class OrderService {\n  constructor(private db: Pool) {}\n\n  async createOrder(userId: string, items: any[]) {\n    const client = await this.db.connect();\n\n    try {\n      await client.query(\"BEGIN\");\n\n      // Create order\n      const orderResult = await client.query(\n        \"INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id\",\n        [userId, calculateTotal(items)],\n      );\n      const orderId = orderResult.rows[0].id;\n\n      // Create order items\n      for (const item of items) {\n        await client.query(\n          \"INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)\",\n          [orderId, item.productId, item.quantity, item.price],\n        );\n\n        // Update inventory\n        await client.query(\n          \"UPDATE products SET stock = stock - $1 WHERE id = $2\",\n          [item.quantity, item.productId],\n        );\n      }\n\n      await client.query(\"COMMIT\");\n      return orderId;\n    } catch (error) {\n      await client.query(\"ROLLBACK\");\n      throw error;\n    } finally {\n      client.release();\n    }\n  }\n}\n```\n\n## Authentication & Authorization\n\n### JWT Authentication\n\n```typescript\n// services/auth.service.ts\nimport jwt from \"jsonwebtoken\";\nimport bcrypt from \"bcrypt\";\nimport { UserRepository } from \"../repositories/user.repository\";\nimport { UnauthorizedError } from \"../utils/errors\";\n\nexport class AuthService {\n  constructor(private userRepository: UserRepository) {}\n\n  async login(email: string, password: string) {\n    const user = await this.userRepository.findByEmail(email);\n\n    if (!user) {\n      throw new UnauthorizedError(\"Invalid credentials\");\n    }\n\n    const isValid = await bcrypt.compare(password, user.password);\n\n    if (!isValid) {\n      throw new UnauthorizedError(\"Invalid credentials\");\n    }\n\n    const token = this.generateToken({\n      userId: user.id,\n      email: user.email,\n    });\n\n    const refreshToken = this.generateRefreshToken({\n      userId: user.id,\n    });\n\n    return {\n      token,\n      refreshToken,\n      user: {\n        id: user.id,\n        name: user.name,\n        email: user.email,\n      },\n    };\n  }\n\n  async refreshToken(refreshToken: string) {\n    try {\n      const payload = jwt.verify(\n        refreshToken,\n        process.env.REFRESH_TOKEN_SECRET!,\n      ) as { userId: string };\n\n      const user = await this.userRepository.findById(payload.userId);\n\n      if (!user) {\n        throw new UnauthorizedError(\"User not found\");\n      }\n\n      const token = this.generateToken({\n        userId: user.id,\n        email: user.email,\n      });\n\n      return { token };\n    } catch (error) {\n      throw new UnauthorizedError(\"Invalid refresh token\");\n    }\n  }\n\n  private generateToken(payload: any): string {\n    return jwt.sign(payload, process.env.JWT_SECRET!, {\n      expiresIn: \"15m\",\n    });\n  }\n\n  private generateRefreshToken(payload: any): string {\n    return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {\n      expiresIn: \"7d\",\n    });\n  }\n}\n```\n\n## Caching Strategies\n\n```typescript\n// utils/cache.ts\nimport Redis from \"ioredis\";\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || \"6379\"),\n  retryStrategy: (times) => {\n    const delay = Math.min(times * 50, 2000);\n    return delay;\n  },\n});\n\nexport class CacheService {\n  async get<T>(key: string): Promise<T | null> {\n    const data = await redis.get(key);\n    return data ? JSON.parse(data) : null;\n  }\n\n  async set(key: string, value: any, ttl?: number): Promise<void> {\n    const serialized = JSON.stringify(value);\n    if (ttl) {\n      await redis.setex(key, ttl, serialized);\n    } else {\n      await redis.set(key, serialized);\n    }\n  }\n\n  async delete(key: string): Promise<void> {\n    await redis.del(key);\n  }\n\n  async invalidatePattern(pattern: string): Promise<void> {\n    const keys = await redis.keys(pattern);\n    if (keys.length > 0) {\n      await redis.del(...keys);\n    }\n  }\n}\n\n// Cache decorator\nexport function Cacheable(ttl: number = 300) {\n  return function (\n    target: any,\n    propertyKey: string,\n    descriptor: PropertyDescriptor,\n  ) {\n    const originalMethod = descriptor.value;\n\n    descriptor.value = async function (...args: any[]) {\n      const cache = new CacheService();\n      const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;\n\n      const cached = await cache.get(cacheKey);\n      if (cached) {\n        return cached;\n      }\n\n      const result = await originalMethod.apply(this, args);\n      await cache.set(cacheKey, result, ttl);\n\n      return result;\n    };\n\n    return descriptor;\n  };\n}\n```\n\n## API Response Format\n\n```typescript\n// utils/response.ts\nimport { Response } from \"express\";\n\nexport class ApiResponse {\n  static success<T>(\n    res: Response,\n    data: T,\n    message?: string,\n    statusCode = 200,\n  ) {\n    return res.status(statusCode).json({\n      status: \"success\",\n      message,\n      data,\n    });\n  }\n\n  static error(res: Response, message: string, statusCode = 500, errors?: any) {\n    return res.status(statusCode).json({\n      status: \"error\",\n      message,\n      ...(errors && { errors }),\n    });\n  }\n\n  static paginated<T>(\n    res: Response,\n    data: T[],\n    page: number,\n    limit: number,\n    total: number,\n  ) {\n    return res.json({\n      status: \"success\",\n      data,\n      pagination: {\n        page,\n        limit,\n        total,\n        pages: Math.ceil(total / limit),\n      },\n    });\n  }\n}\n```\n",".agents/skills/nodejs-best-practices/SKILL.md":"---\nname: nodejs-best-practices\ndescription: \"Node.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.\"\nrisk: unknown\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# Node.js Best Practices\n\n> Principles and decision-making for Node.js development in 2025.\n> **Learn to THINK, not memorize code patterns.**\n\n## When to Use\nUse this skill when making Node.js architecture decisions, choosing frameworks, designing async patterns, or applying security and deployment best practices.\n\n---\n\n## ⚠️ How to Use This Skill\n\nThis skill teaches **decision-making principles**, not fixed code to copy.\n\n- ASK user for preferences when unclear\n- Choose framework/pattern based on CONTEXT\n- Don't default to same solution every time\n\n---\n\n## 1. Framework Selection (2025)\n\n### Decision Tree\n\n```\nWhat are you building?\n│\n├── Edge/Serverless (Cloudflare, Vercel)\n│   └── Hono (zero-dependency, ultra-fast cold starts)\n│\n├── High Performance API\n│   └── Fastify (2-3x faster than Express)\n│\n├── Enterprise/Team familiarity\n│   └── NestJS (structured, DI, decorators)\n│\n├── Legacy/Stable/Maximum ecosystem\n│   └── Express (mature, most middleware)\n│\n└── Full-stack with frontend\n    └── Next.js API Routes or tRPC\n```\n\n### Comparison Principles\n\n| Factor | Hono | Fastify | Express |\n|--------|------|---------|---------|\n| **Best for** | Edge, serverless | Performance | Legacy, learning |\n| **Cold start** | Fastest | Fast | Moderate |\n| **Ecosystem** | Growing | Good | Largest |\n| **TypeScript** | Native | Excellent | Good |\n| **Learning curve** | Low | Medium | Low |\n\n### Selection Questions to Ask:\n1. What's the deployment target?\n2. Is cold start time critical?\n3. Does team have existing experience?\n4. Is there legacy code to maintain?\n\n---\n\n## 2. Runtime Considerations (2025)\n\n### Native TypeScript\n\n```\nNode.js 22+: --experimental-strip-types\n├── Run .ts files directly\n├── No build step needed for simple projects\n└── Consider for: scripts, simple APIs\n```\n\n### Module System Decision\n\n```\nESM (import/export)\n├── Modern standard\n├── Better tree-shaking\n├── Async module loading\n└── Use for: new projects\n\nCommonJS (require)\n├── Legacy compatibility\n├── More npm packages support\n└── Use for: existing codebases, some edge cases\n```\n\n### Runtime Selection\n\n| Runtime | Best For |\n|---------|----------|\n| **Node.js** | General purpose, largest ecosystem |\n| **Bun** | Performance, built-in bundler |\n| **Deno** | Security-first, built-in TypeScript |\n\n---\n\n## 3. Architecture Principles\n\n### Layered Structure Concept\n\n```\nRequest Flow:\n│\n├── Controller/Route Layer\n│   ├── Handles HTTP specifics\n│   ├── Input validation at boundary\n│   └── Calls service layer\n│\n├── Service Layer\n│   ├── Business logic\n│   ├── Framework-agnostic\n│   └── Calls repository layer\n│\n└── Repository Layer\n    ├── Data access only\n    ├── Database queries\n    └── ORM interactions\n```\n\n### Why This Matters:\n- **Testability**: Mock layers independently\n- **Flexibility**: Swap database without touching business logic\n- **Clarity**: Each layer has single responsibility\n\n### When to Simplify:\n- Small scripts → Single file OK\n- Prototypes → Less structure acceptable\n- Always ask: \"Will this grow?\"\n\n---\n\n## 4. Error Handling Principles\n\n### Centralized Error Handling\n\n```\nPattern:\n├── Create custom error classes\n├── Throw from any layer\n├── Catch at top level (middleware)\n└── Format consistent response\n```\n\n### Error Response Philosophy\n\n```\nClient gets:\n├── Appropriate HTTP status\n├── Error code for programmatic handling\n├── User-friendly message\n└── NO internal details (security!)\n\nLogs get:\n├── Full stack trace\n├── Request context\n├── User ID (if applicable)\n└── Timestamp\n```\n\n### Status Code Selection\n\n| Situation | Status | When |\n|-----------|--------|------|\n| Bad input | 400 | Client sent invalid data |\n| No auth | 401 | Missing or invalid credentials |\n| No permission | 403 | Valid auth, but not allowed |\n| Not found | 404 | Resource doesn't exist |\n| Conflict | 409 | Duplicate or state conflict |\n| Validation | 422 | Schema valid but business rules fail |\n| Server error | 500 | Our fault, log everything |\n\n---\n\n## 5. Async Patterns Principles\n\n### When to Use Each\n\n| Pattern | Use When |\n|---------|----------|\n| `async/await` | Sequential async operations |\n| `Promise.all` | Parallel independent operations |\n| `Promise.allSettled` | Parallel where some can fail |\n| `Promise.race` | Timeout or first response wins |\n\n### Event Loop Awareness\n\n```\nI/O-bound (async helps):\n├── Database queries\n├── HTTP requests\n├── File system\n└── Network operations\n\nCPU-bound (async doesn't help):\n├── Crypto operations\n├── Image processing\n├── Complex calculations\n└── → Use worker threads or offload\n```\n\n### Avoiding Event Loop Blocking\n\n- Never use sync methods in production (fs.readFileSync, etc.)\n- Offload CPU-intensive work\n- Use streaming for large data\n\n---\n\n## 6. Validation Principles\n\n### Validate at Boundaries\n\n```\nWhere to validate:\n├── API entry point (request body/params)\n├── Before database operations\n├── External data (API responses, file uploads)\n└── Environment variables (startup)\n```\n\n### Validation Library Selection\n\n| Library | Best For |\n|---------|----------|\n| **Zod** | TypeScript first, inference |\n| **Valibot** | Smaller bundle (tree-shakeable) |\n| **ArkType** | Performance critical |\n| **Yup** | Existing React Form usage |\n\n### Validation Philosophy\n\n- Fail fast: Validate early\n- Be specific: Clear error messages\n- Don't trust: Even \"internal\" data\n\n---\n\n## 7. Security Principles\n\n### Security Checklist (Not Code)\n\n- [ ] **Input validation**: All inputs validated\n- [ ] **Parameterized queries**: No string concatenation for SQL\n- [ ] **Password hashing**: bcrypt or argon2\n- [ ] **JWT verification**: Always verify signature and expiry\n- [ ] **Rate limiting**: Protect from abuse\n- [ ] **Security headers**: Helmet.js or equivalent\n- [ ] **HTTPS**: Everywhere in production\n- [ ] **CORS**: Properly configured\n- [ ] **Secrets**: Environment variables only\n- [ ] **Dependencies**: Regularly audited\n\n### Security Mindset\n\n```\nTrust nothing:\n├── Query params → validate\n├── Request body → validate\n├── Headers → verify\n├── Cookies → validate\n├── File uploads → scan\n└── External APIs → validate response\n```\n\n---\n\n## 8. Testing Principles\n\n### Test Strategy Selection\n\n| Type | Purpose | Tools |\n|------|---------|-------|\n| **Unit** | Business logic | node:test, Vitest |\n| **Integration** | API endpoints | Supertest |\n| **E2E** | Full flows | Playwright |\n\n### What to Test (Priorities)\n\n1. **Critical paths**: Auth, payments, core business\n2. **Edge cases**: Empty inputs, boundaries\n3. **Error handling**: What happens when things fail?\n4. **Not worth testing**: Framework code, trivial getters\n\n### Built-in Test Runner (Node.js 22+)\n\n```\nnode --test src/**/*.test.ts\n├── No external dependency\n├── Good coverage reporting\n└── Watch mode available\n```\n\n---\n\n## 10. Anti-Patterns to Avoid\n\n### ❌ DON'T:\n- Use Express for new edge projects (use Hono)\n- Use sync methods in production code\n- Put business logic in controllers\n- Skip input validation\n- Hardcode secrets\n- Trust external data without validation\n- Block event loop with CPU work\n\n### ✅ DO:\n- Choose framework based on context\n- Ask user for preferences when unclear\n- Use layered architecture for growing projects\n- Validate all inputs\n- Use environment variables for secrets\n- Profile before optimizing\n\n---\n\n## 11. Decision Checklist\n\nBefore implementing:\n\n- [ ] **Asked user about stack preference?**\n- [ ] **Chosen framework for THIS context?** (not just default)\n- [ ] **Considered deployment target?**\n- [ ] **Planned error handling strategy?**\n- [ ] **Identified validation points?**\n- [ ] **Considered security requirements?**\n\n---\n\n> **Remember**: Node.js best practices are about decision-making, not memorizing patterns. Every project deserves fresh consideration based on its requirements.\n",".agents/skills/performance/SKILL.md":"---\nname: performance\ndescription: Optimize web performance for faster loading and better user experience. Use when asked to \"speed up my site\", \"optimize performance\", \"reduce load time\", \"fix slow loading\", \"improve page speed\", or \"performance audit\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Performance optimization\n\nDeep performance optimization based on Lighthouse performance audits. Focuses on loading speed, runtime efficiency, and resource optimization.\n\n## How it works\n\n1. Identify performance bottlenecks in code and assets\n2. Prioritize by impact on Core Web Vitals\n3. Provide specific optimizations with code examples\n4. Measure improvement with before/after metrics\n\n## Performance budget\n\n| Resource | Budget | Rationale |\n|----------|--------|-----------|\n| Total page weight | < 1.5 MB | 3G loads in ~4s |\n| JavaScript (compressed) | < 300 KB | Parsing + execution time |\n| CSS (compressed) | < 100 KB | Render blocking |\n| Images (above-fold) | < 500 KB | LCP impact |\n| Fonts | < 100 KB | FOIT/FOUT prevention |\n| Third-party | < 200 KB | Uncontrolled latency |\n\n## Critical rendering path\n\n### Server response\n* **TTFB < 800ms.** Time to First Byte should be fast. Use CDN, caching, and efficient backends.\n* **Enable compression.** Gzip or Brotli for text assets. Brotli preferred (15-20% smaller).\n* **HTTP/2 or HTTP/3.** Multiplexing reduces connection overhead.\n* **Edge caching.** Cache HTML at CDN edge when possible.\n\n### Resource loading\n\n**Preconnect to required origins:**\n```html\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://cdn.example.com\" crossorigin>\n```\n\n**Preload critical resources:**\n```html\n<!-- LCP image -->\n<link rel=\"preload\" href=\"/hero.webp\" as=\"image\" fetchpriority=\"high\">\n\n<!-- Critical font -->\n<link rel=\"preload\" href=\"/font.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n```\n\n**Defer non-critical CSS:**\n```html\n<!-- Critical CSS inlined -->\n<style>/* Above-fold styles */</style>\n\n<!-- Non-critical CSS -->\n<link rel=\"preload\" href=\"/styles.css\" as=\"style\" onload=\"this.onload=null;this.rel='stylesheet'\">\n<noscript><link rel=\"stylesheet\" href=\"/styles.css\"></noscript>\n```\n\n### JavaScript optimization\n\n**Defer non-essential scripts:**\n```html\n<!-- Parser-blocking (avoid) -->\n<script src=\"/critical.js\"></script>\n\n<!-- Deferred (preferred) -->\n<script defer src=\"/app.js\"></script>\n\n<!-- Async (for independent scripts) -->\n<script async src=\"/analytics.js\"></script>\n\n<!-- Module (deferred by default) -->\n<script type=\"module\" src=\"/app.mjs\"></script>\n```\n\n**Code splitting patterns:**\n```javascript\n// Route-based splitting\nconst Dashboard = lazy(() => import('./Dashboard'));\n\n// Component-based splitting\nconst HeavyChart = lazy(() => import('./HeavyChart'));\n\n// Feature-based splitting\nif (user.isPremium) {\n  const PremiumFeatures = await import('./PremiumFeatures');\n}\n```\n\n**Tree shaking best practices:**\n```javascript\n// ❌ Imports entire library\nimport _ from 'lodash';\n_.debounce(fn, 300);\n\n// ✅ Imports only what's needed\nimport debounce from 'lodash/debounce';\ndebounce(fn, 300);\n```\n\n## Image optimization\n\n### Format selection\n| Format | Use case | Browser support |\n|--------|----------|-----------------|\n| AVIF | Photos, best compression | 92%+ |\n| WebP | Photos, good fallback | 97%+ |\n| PNG | Graphics with transparency | Universal |\n| SVG | Icons, logos, illustrations | Universal |\n\n### Responsive images\n```html\n<picture>\n  <!-- AVIF for modern browsers -->\n  <source \n    type=\"image/avif\"\n    srcset=\"hero-400.avif 400w,\n            hero-800.avif 800w,\n            hero-1200.avif 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\">\n  \n  <!-- WebP fallback -->\n  <source \n    type=\"image/webp\"\n    srcset=\"hero-400.webp 400w,\n            hero-800.webp 800w,\n            hero-1200.webp 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\">\n  \n  <!-- JPEG fallback -->\n  <img \n    src=\"hero-800.jpg\"\n    srcset=\"hero-400.jpg 400w,\n            hero-800.jpg 800w,\n            hero-1200.jpg 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\"\n    width=\"1200\" \n    height=\"600\"\n    alt=\"Hero image\"\n    loading=\"lazy\"\n    decoding=\"async\">\n</picture>\n```\n\n### LCP image priority\n```html\n<!-- Above-fold LCP image: eager loading, high priority -->\n<img \n  src=\"hero.webp\" \n  fetchpriority=\"high\"\n  loading=\"eager\"\n  decoding=\"sync\"\n  alt=\"Hero\">\n\n<!-- Below-fold images: lazy loading -->\n<img \n  src=\"product.webp\" \n  loading=\"lazy\"\n  decoding=\"async\"\n  alt=\"Product\">\n```\n\n## Font optimization\n\n### Loading strategy\n```css\n/* System font stack as fallback */\nbody {\n  font-family: 'Custom Font', -apple-system, BlinkMacSystemFont, \n               'Segoe UI', Roboto, sans-serif;\n}\n\n/* Prevent invisible text */\n@font-face {\n  font-family: 'Custom Font';\n  src: url('/fonts/custom.woff2') format('woff2');\n  font-display: swap; /* or optional for non-critical */\n  font-weight: 400;\n  font-style: normal;\n  unicode-range: U+0000-00FF; /* Subset to Latin */\n}\n```\n\n### Preloading critical fonts\n```html\n<link rel=\"preload\" href=\"/fonts/heading.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n```\n\n### Variable fonts\n```css\n/* One file instead of multiple weights */\n@font-face {\n  font-family: 'Inter';\n  src: url('/fonts/Inter-Variable.woff2') format('woff2-variations');\n  font-weight: 100 900;\n  font-display: swap;\n}\n```\n\n## Caching strategy\n\n### Cache-Control headers\n```\n# HTML (short or no cache)\nCache-Control: no-cache, must-revalidate\n\n# Static assets with hash (immutable)\nCache-Control: public, max-age=31536000, immutable\n\n# Static assets without hash\nCache-Control: public, max-age=86400, stale-while-revalidate=604800\n\n# API responses\nCache-Control: private, max-age=0, must-revalidate\n```\n\n### Service worker caching\n```javascript\n// Cache-first for static assets\nself.addEventListener('fetch', (event) => {\n  if (event.request.destination === 'image' ||\n      event.request.destination === 'style' ||\n      event.request.destination === 'script') {\n    event.respondWith(\n      caches.match(event.request).then((cached) => {\n        return cached || fetch(event.request).then((response) => {\n          const clone = response.clone();\n          caches.open('static-v1').then((cache) => cache.put(event.request, clone));\n          return response;\n        });\n      })\n    );\n  }\n});\n```\n\n## Runtime performance\n\n### Avoid layout thrashing\n```javascript\n// ❌ Forces multiple reflows\nelements.forEach(el => {\n  const height = el.offsetHeight; // Read\n  el.style.height = height + 10 + 'px'; // Write\n});\n\n// ✅ Batch reads, then batch writes\nconst heights = elements.map(el => el.offsetHeight); // All reads\nelements.forEach((el, i) => {\n  el.style.height = heights[i] + 10 + 'px'; // All writes\n});\n```\n\n### Debounce expensive operations\n```javascript\nfunction debounce(fn, delay) {\n  let timeout;\n  return (...args) => {\n    clearTimeout(timeout);\n    timeout = setTimeout(() => fn(...args), delay);\n  };\n}\n\n// Debounce scroll/resize handlers\nwindow.addEventListener('scroll', debounce(handleScroll, 100));\n```\n\n### Use requestAnimationFrame\n```javascript\n// ❌ May cause jank\nsetInterval(animate, 16);\n\n// ✅ Synced with display refresh\nfunction animate() {\n  // Animation logic\n  requestAnimationFrame(animate);\n}\nrequestAnimationFrame(animate);\n```\n\n### Virtualize long lists\n```javascript\n// For lists > 100 items, render only visible items\n// Use libraries like react-window, vue-virtual-scroller, or native CSS:\n.virtual-list {\n  content-visibility: auto;\n  contain-intrinsic-size: 0 50px; /* Estimated item height */\n}\n```\n\n## Third-party scripts\n\n### Load strategies\n```javascript\n// ❌ Blocks main thread\n<script src=\"https://analytics.example.com/script.js\"></script>\n\n// ✅ Async loading\n<script async src=\"https://analytics.example.com/script.js\"></script>\n\n// ✅ Delay until interaction\n<script>\ndocument.addEventListener('DOMContentLoaded', () => {\n  const observer = new IntersectionObserver((entries) => {\n    if (entries[0].isIntersecting) {\n      const script = document.createElement('script');\n      script.src = 'https://widget.example.com/embed.js';\n      document.body.appendChild(script);\n      observer.disconnect();\n    }\n  });\n  observer.observe(document.querySelector('#widget-container'));\n});\n</script>\n```\n\n### Facade pattern\n```html\n<!-- Show static placeholder until interaction -->\n<div class=\"youtube-facade\" \n     data-video-id=\"abc123\" \n     onclick=\"loadYouTube(this)\">\n  <img src=\"/thumbnails/abc123.jpg\" alt=\"Video title\">\n  <button aria-label=\"Play video\">▶</button>\n</div>\n```\n\n## Measurement\n\n### Key metrics\n| Metric | Target | Tool |\n|--------|--------|------|\n| LCP | < 2.5s | Lighthouse, CrUX |\n| FCP | < 1.8s | Lighthouse |\n| Speed Index | < 3.4s | Lighthouse |\n| TBT | < 200ms | Lighthouse |\n| TTI | < 3.8s | Lighthouse |\n\n### Testing commands\n```bash\n# Lighthouse CLI\nnpx lighthouse https://example.com --output html --output-path report.html\n\n# Web Vitals library\nimport {onLCP, onINP, onCLS} from 'web-vitals';\nonLCP(console.log);\nonINP(console.log);\nonCLS(console.log);\n```\n\n## References\n\nFor Core Web Vitals specific optimizations, see [Core Web Vitals](../core-web-vitals/SKILL.md).\n",".agents/skills/seo/SKILL.md":"---\nname: seo\ndescription: Optimize for search engine visibility and ranking. Use when asked to \"improve SEO\", \"optimize for search\", \"fix meta tags\", \"add structured data\", \"sitemap optimization\", or \"search engine optimization\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# SEO optimization\n\nSearch engine optimization based on Lighthouse SEO audits and Google Search guidelines. Focus on technical SEO, on-page optimization, and structured data.\n\n## SEO fundamentals\n\nSearch ranking factors (approximate influence):\n\n| Factor | Influence | This Skill |\n|--------|-----------|------------|\n| Content quality & relevance | ~40% | Partial (structure) |\n| Backlinks & authority | ~25% | ✗ |\n| Technical SEO | ~15% | ✓ |\n| Page experience (Core Web Vitals) | ~10% | See [Core Web Vitals](../core-web-vitals/SKILL.md) |\n| On-page SEO | ~10% | ✓ |\n\n---\n\n## Technical SEO\n\n### Crawlability\n\n**robots.txt:**\n```text\n# /robots.txt\nUser-agent: *\nAllow: /\n\n# Block admin/private areas\nDisallow: /admin/\nDisallow: /api/\nDisallow: /private/\n\n# Don't block resources needed for rendering\n# ❌ Disallow: /static/\n\nSitemap: https://example.com/sitemap.xml\n```\n\n**Meta robots:**\n```html\n<!-- Default: indexable, followable -->\n<meta name=\"robots\" content=\"index, follow\">\n\n<!-- Noindex specific pages -->\n<meta name=\"robots\" content=\"noindex, nofollow\">\n\n<!-- Indexable but don't follow links -->\n<meta name=\"robots\" content=\"index, nofollow\">\n\n<!-- Control snippets -->\n<meta name=\"robots\" content=\"max-snippet:150, max-image-preview:large\">\n```\n\n**Canonical URLs:**\n```html\n<!-- Prevent duplicate content issues -->\n<link rel=\"canonical\" href=\"https://example.com/page\">\n\n<!-- Self-referencing canonical (recommended) -->\n<link rel=\"canonical\" href=\"https://example.com/current-page\">\n\n<!-- For paginated content -->\n<link rel=\"canonical\" href=\"https://example.com/products\">\n<!-- Or use rel=\"prev\" / rel=\"next\" for explicit pagination -->\n```\n\n### XML sitemap\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n  <url>\n    <loc>https://example.com/</loc>\n    <lastmod>2024-01-15</lastmod>\n    <changefreq>daily</changefreq>\n    <priority>1.0</priority>\n  </url>\n  <url>\n    <loc>https://example.com/products</loc>\n    <lastmod>2024-01-14</lastmod>\n    <changefreq>weekly</changefreq>\n    <priority>0.8</priority>\n  </url>\n</urlset>\n```\n\n**Sitemap best practices:**\n- Maximum 50,000 URLs or 50MB per sitemap\n- Use sitemap index for larger sites\n- Include only canonical, indexable URLs\n- Update `lastmod` when content changes\n- Submit to Google Search Console\n\n### URL structure\n\n```\n✅ Good URLs:\nhttps://example.com/products/blue-widget\nhttps://example.com/blog/how-to-use-widgets\n\n❌ Poor URLs:\nhttps://example.com/p?id=12345\nhttps://example.com/products/item/category/subcategory/blue-widget-2024-sale-discount\n```\n\n**URL guidelines:**\n- Use hyphens, not underscores\n- Lowercase only\n- Keep short (< 75 characters)\n- Include target keywords naturally\n- Avoid parameters when possible\n- Use HTTPS always\n\n### HTTPS & security\n\n```html\n<!-- Ensure all resources use HTTPS -->\n<img src=\"https://example.com/image.jpg\">\n\n<!-- Not: -->\n<img src=\"http://example.com/image.jpg\">\n```\n\n**Security headers for SEO trust signals:**\n```\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff\nX-Frame-Options: DENY\n```\n\n---\n\n## On-page SEO\n\n### Title tags\n\n```html\n<!-- ❌ Missing or generic -->\n<title>Page</title>\n<title>Home</title>\n\n<!-- ✅ Descriptive with primary keyword -->\n<title>Blue Widgets for Sale | Premium Quality | Example Store</title>\n```\n\n**Title tag guidelines:**\n- 50-60 characters (Google truncates ~60)\n- Primary keyword near the beginning\n- Unique for every page\n- Brand name at end (unless homepage)\n- Action-oriented when appropriate\n\n### Meta descriptions\n\n```html\n<!-- ❌ Missing or duplicate -->\n<meta name=\"description\" content=\"\">\n\n<!-- ✅ Compelling and unique -->\n<meta name=\"description\" content=\"Shop premium blue widgets with free shipping. 30-day returns. Rated 4.9/5 by 10,000+ customers. Order today and save 20%.\">\n```\n\n**Meta description guidelines:**\n- 150-160 characters\n- Include primary keyword naturally\n- Compelling call-to-action\n- Unique for every page\n- Matches page content\n\n### Heading structure\n\n```html\n<!-- ❌ Poor structure -->\n<h2>Welcome to Our Store</h2>\n<h4>Products</h4>\n<h1>Contact Us</h1>\n\n<!-- ✅ Proper hierarchy -->\n<h1>Blue Widgets - Premium Quality</h1>\n  <h2>Product Features</h2>\n    <h3>Durability</h3>\n    <h3>Design</h3>\n  <h2>Customer Reviews</h2>\n  <h2>Pricing</h2>\n```\n\n**Heading guidelines:**\n- Single `<h1>` per page (the main topic)\n- Logical hierarchy (don't skip levels)\n- Include keywords naturally\n- Descriptive, not generic\n\n### Image SEO\n\n```html\n<!-- ❌ Poor image SEO -->\n<img src=\"IMG_12345.jpg\">\n\n<!-- ✅ Optimized image -->\n<img src=\"blue-widget-product-photo.webp\"\n     alt=\"Blue widget with chrome finish, side view showing control panel\"\n     width=\"800\"\n     height=\"600\"\n     loading=\"lazy\">\n```\n\n**Image guidelines:**\n- Descriptive filenames with keywords\n- Alt text describes the image content\n- Compressed and properly sized\n- WebP/AVIF with fallbacks\n- Lazy load below-fold images\n\n### Internal linking\n\n```html\n<!-- ❌ Non-descriptive -->\n<a href=\"/products\">Click here</a>\n<a href=\"/widgets\">Read more</a>\n\n<!-- ✅ Descriptive anchor text -->\n<a href=\"/products/blue-widgets\">Browse our blue widget collection</a>\n<a href=\"/guides/widget-maintenance\">Learn how to maintain your widgets</a>\n```\n\n**Linking guidelines:**\n- Descriptive anchor text with keywords\n- Link to relevant internal pages\n- Reasonable number of links per page\n- Fix broken links promptly\n- Use breadcrumbs for hierarchy\n\n---\n\n## Structured data (JSON-LD)\n\n### Organization\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Organization\",\n  \"name\": \"Example Company\",\n  \"url\": \"https://example.com\",\n  \"logo\": \"https://example.com/logo.png\",\n  \"sameAs\": [\n    \"https://twitter.com/example\",\n    \"https://linkedin.com/company/example\"\n  ],\n  \"contactPoint\": {\n    \"@type\": \"ContactPoint\",\n    \"telephone\": \"+1-555-123-4567\",\n    \"contactType\": \"customer service\"\n  }\n}\n</script>\n```\n\n### Article\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Article\",\n  \"headline\": \"How to Choose the Right Widget\",\n  \"description\": \"Complete guide to selecting widgets for your needs.\",\n  \"image\": \"https://example.com/article-image.jpg\",\n  \"author\": {\n    \"@type\": \"Person\",\n    \"name\": \"Jane Smith\",\n    \"url\": \"https://example.com/authors/jane-smith\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"Example Blog\",\n    \"logo\": {\n      \"@type\": \"ImageObject\",\n      \"url\": \"https://example.com/logo.png\"\n    }\n  },\n  \"datePublished\": \"2024-01-15\",\n  \"dateModified\": \"2024-01-20\"\n}\n</script>\n```\n\n### Product\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Product\",\n  \"name\": \"Blue Widget Pro\",\n  \"image\": \"https://example.com/blue-widget.jpg\",\n  \"description\": \"Premium blue widget with advanced features.\",\n  \"brand\": {\n    \"@type\": \"Brand\",\n    \"name\": \"WidgetCo\"\n  },\n  \"offers\": {\n    \"@type\": \"Offer\",\n    \"price\": \"49.99\",\n    \"priceCurrency\": \"USD\",\n    \"availability\": \"https://schema.org/InStock\",\n    \"url\": \"https://example.com/products/blue-widget\"\n  },\n  \"aggregateRating\": {\n    \"@type\": \"AggregateRating\",\n    \"ratingValue\": \"4.8\",\n    \"reviewCount\": \"1250\"\n  }\n}\n</script>\n```\n\n### FAQ\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What colors are available?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Our widgets come in blue, red, and green.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What is the warranty?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"All widgets include a 2-year warranty.\"\n      }\n    }\n  ]\n}\n</script>\n```\n\n### Breadcrumbs\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https://example.com\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Products\",\n      \"item\": \"https://example.com/products\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Blue Widgets\",\n      \"item\": \"https://example.com/products/blue-widgets\"\n    }\n  ]\n}\n</script>\n```\n\n### Validation\n\nTest structured data at:\n- [Google Rich Results Test](https://search.google.com/test/rich-results)\n- [Schema.org Validator](https://validator.schema.org/)\n\n---\n\n## Mobile SEO\n\n### Responsive design\n\n```html\n<!-- ❌ Not mobile-friendly -->\n<meta name=\"viewport\" content=\"width=1024\">\n\n<!-- ✅ Responsive viewport -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n```\n\n### Tap targets\n\n```css\n/* ❌ Too small for mobile */\n.small-link {\n  padding: 4px;\n  font-size: 12px;\n}\n\n/* ✅ Adequate tap target */\n.mobile-friendly-link {\n  padding: 12px;\n  font-size: 16px;\n  min-height: 48px;\n  min-width: 48px;\n}\n```\n\n### Font sizes\n\n```css\n/* ❌ Too small on mobile */\nbody {\n  font-size: 10px;\n}\n\n/* ✅ Readable without zooming */\nbody {\n  font-size: 16px;\n  line-height: 1.5;\n}\n```\n\n---\n\n## International SEO\n\n### Hreflang tags\n\n```html\n<!-- For multi-language sites -->\n<link rel=\"alternate\" hreflang=\"en\" href=\"https://example.com/page\">\n<link rel=\"alternate\" hreflang=\"es\" href=\"https://example.com/es/page\">\n<link rel=\"alternate\" hreflang=\"fr\" href=\"https://example.com/fr/page\">\n<link rel=\"alternate\" hreflang=\"x-default\" href=\"https://example.com/page\">\n```\n\n### Language declaration\n\n```html\n<html lang=\"en\">\n<!-- or -->\n<html lang=\"es-MX\">\n```\n\n---\n\n## SEO audit checklist\n\n### Critical\n- [ ] HTTPS enabled\n- [ ] robots.txt allows crawling\n- [ ] No `noindex` on important pages\n- [ ] Title tags present and unique\n- [ ] Single `<h1>` per page\n\n### High priority\n- [ ] Meta descriptions present\n- [ ] Sitemap submitted\n- [ ] Canonical URLs set\n- [ ] Mobile-responsive\n- [ ] Core Web Vitals passing\n\n### Medium priority\n- [ ] Structured data implemented\n- [ ] Internal linking strategy\n- [ ] Image alt text\n- [ ] Descriptive URLs\n- [ ] Breadcrumb navigation\n\n### Ongoing\n- [ ] Fix crawl errors in Search Console\n- [ ] Update sitemap when content changes\n- [ ] Monitor ranking changes\n- [ ] Check for broken links\n- [ ] Review Search Console insights\n\n---\n\n## Tools\n\n| Tool | Use |\n|------|-----|\n| Google Search Console | Monitor indexing, fix issues |\n| Google PageSpeed Insights | Performance + Core Web Vitals |\n| Rich Results Test | Validate structured data |\n| Lighthouse | Full SEO audit |\n| Screaming Frog | Crawl analysis |\n\n## References\n\n- [Google Search Central](https://developers.google.com/search)\n- [Schema.org](https://schema.org/)\n- [Core Web Vitals](../core-web-vitals/SKILL.md)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n",".agents/skills/tailwind-css-patterns/SKILL.md":"---\nname: tailwind-css-patterns\ndescription: Provides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or optimizing CSS workflow.\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash\n---\n\n# Tailwind CSS Development Patterns\n\nExpert guide for building modern, responsive user interfaces with Tailwind CSS utility-first framework. Covers v4.1+ features including CSS-first configuration, custom utilities, and enhanced developer experience.\n\n## Overview\n\nProvides actionable patterns for responsive, accessible UIs with Tailwind CSS v4.1+. Covers utility composition, dark mode, component patterns, and performance optimization.\n\n## When to Use\n\n- Styling React/Vue/Svelte components\n- Building responsive layouts and grids\n- Implementing design systems\n- Adding dark mode support\n- Optimizing CSS workflow\n\n## Quick Reference\n\n### Responsive Breakpoints\n\n| Prefix | Min Width | Description |\n|--------|-----------|-------------|\n| `sm:` | 640px | Small screens |\n| `md:` | 768px | Tablets |\n| `lg:` | 1024px | Desktops |\n| `xl:` | 1280px | Large screens |\n| `2xl:` | 1536px | Extra large |\n\n### Common Patterns\n\n```html\n<!-- Center content -->\n<div class=\"flex items-center justify-center min-h-screen\">\n  Content\n</div>\n\n<!-- Responsive grid -->\n<div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n  <!-- Items -->\n</div>\n\n<!-- Card component -->\n<div class=\"bg-white rounded-lg shadow-lg p-6\">\n  <h3 class=\"text-xl font-bold\">Title</h3>\n  <p class=\"text-gray-600\">Description</p>\n</div>\n```\n\n## Instructions\n\n1. **Start Mobile-First**: Write base styles for mobile, add responsive prefixes (`sm:`, `md:`, `lg:`) for larger screens\n2. **Use Design Tokens**: Leverage Tailwind's spacing, color, and typography scales\n3. **Compose Utilities**: Combine multiple utilities for complex styles\n4. **Extract Components**: Create reusable component classes for repeated patterns\n5. **Configure Theme**: Customize design tokens in `tailwind.config.js` or using `@theme`\n6. **Verify Changes**: Test at each breakpoint using DevTools responsive mode. Check for visual regressions and accessibility issues before committing.\n\n## Examples\n\n### Responsive Card Component\n\n```tsx\nfunction ProductCard({ product }: { product: Product }) {\n  return (\n    <div className=\"bg-white rounded-lg shadow-lg overflow-hidden sm:flex\">\n      <img className=\"h-48 w-full object-cover sm:h-auto sm:w-48\" src={product.image} />\n      <div className=\"p-6\">\n        <h3 className=\"text-lg font-semibold\">{product.name}</h3>\n        <button className=\"mt-4 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700\">\n          Add to Cart\n        </button>\n      </div>\n    </div>\n  );\n}\n```\n\n### Dark Mode Toggle\n\n```html\n<div class=\"bg-white dark:bg-gray-900 text-gray-900 dark:text-white\">\n  <h1 class=\"dark:text-white\">Title</h1>\n</div>\n```\n\n### Form Input\n\n```html\n<input\n  class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n  placeholder=\"you@example.com\"\n/>\n```\n\n## Best Practices\n\n1. **Consistent Spacing**: Use Tailwind's spacing scale (4, 8, 12, 16, etc.)\n2. **Color Palette**: Stick to Tailwind's color system for consistency\n3. **Component Extraction**: Extract repeated patterns into reusable components\n4. **Utility Composition**: Prefer utility classes over `@apply` for maintainability\n5. **Semantic HTML**: Use proper HTML elements with Tailwind classes\n6. **Performance**: Ensure content paths include all template files for optimal purging\n7. **Accessibility**: Include focus styles, ARIA labels, and respect user preferences (reduced-motion)\n\n## Troubleshooting\n\n### Classes Not Applying\n- **Check content paths**: Ensure all template files are included in `content: []` in config\n- **Verify build**: Run `npm run build` to regenerate purged CSS\n- **Dev mode**: Use `npx tailwindcss -o` with `--watch` flag for live updates\n\n### Responsive Styles Not Working\n- **Order matters**: Responsive prefixes must come before non-responsive (e.g., `md:flex` not `flex md:flex`)\n- **Check breakpoint values**: Verify breakpoints match your design requirements\n- **DevTools**: Use browser DevTools responsive mode to test at each breakpoint\n\n### Dark Mode Issues\n- **Verify config**: Ensure `darkMode: 'class'` or `'media'` is set correctly\n- **Toggle implementation**: Use `document.documentElement.classList.toggle('dark')` for class strategy\n- **Initial flash**: Add `dark` class to `<html>` before body renders\n\n## Constraints and Warnings\n\n- **Class Proliferation**: Long class strings reduce readability; extract into components\n- **Content Paths**: Misconfigured paths cause classes to be purged in production\n- **Arbitrary Values**: Use sparingly; prefer design tokens for consistency\n- **Specificity Issues**: Avoid `@apply` with complex selectors\n- **Dark Mode**: Requires correct configuration (`class` or `media` strategy)\n- **Browser Support**: Check Tailwind docs for compatibility notes\n\n## References\n\n- **[references/layout-patterns.md](references/layout-patterns.md)** — Flexbox, grid, spacing, typography, colors\n- **[references/component-patterns.md](references/component-patterns.md)** — Cards, navigation, forms, modals, React patterns\n- **[references/responsive-design.md](references/responsive-design.md)** — Responsive patterns, dark mode, container queries\n- **[references/animations.md](references/animations.md)** — Transitions, transforms, built-in animations, motion preferences\n- **[references/performance.md](references/performance.md)** — Bundle optimization, CSS optimization, production builds\n- **[references/accessibility.md](references/accessibility.md)** — Focus management, screen readers, color contrast, ARIA\n- **[references/configuration.md](references/configuration.md)** — CSS-first config, JavaScript config, plugins, presets\n- **[references/reference.md](references/reference.md)** — Additional reference materials\n\n## External Resources\n\n- [Tailwind CSS Docs](https://tailwindcss.com/docs)\n- [Tailwind UI](https://tailwindui.com)\n- [Tailwind Play](https://play.tailwindcss.com)\n",".agents/skills/tailwind-css-patterns/references/accessibility.md":"# Tailwind CSS Accessibility Guidelines\n\n## Focus Management\n\n```html\n<!-- Custom focus styles that meet WCAG AA -->\n<button class=\"focus:outline-none focus:ring-4 focus:ring-blue-500 focus:ring-offset-2\">\n  Accessible Button\n</button>\n\n<!-- Skip links for keyboard navigation -->\n<a href=\"#main-content\" class=\"sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4\">\n  Skip to main content\n</a>\n```\n\n### Focus Visible vs Focus\n\n```html\n<!-- Only show focus ring on keyboard navigation -->\n<button class=\"focus-visible:ring-2 focus-visible:ring-blue-500\">\n  Keyboard-only focus indicator\n</button>\n```\n\n---\n\n## Screen Reader Support\n\n```html\n<!-- Semantic buttons with ARIA labels -->\n<button aria-label=\"Close dialog\" class=\"p-2\">\n  <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\">\n    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n  </svg>\n</button>\n\n<!-- Descriptive links -->\n<a href=\"/docs\" aria-describedby=\"docs-description\">\n  Documentation\n</a>\n<p id=\"docs-description\" class=\"sr-only\">\n  Learn how to use our API and integration guides\n</p>\n\n<!-- Live regions for dynamic content -->\n<div aria-live=\"polite\" class=\"sr-only\">\n  <p>3 new notifications</p>\n</div>\n```\n\n### Screen Reader Only Content\n\n```html\n<span class=\"sr-only\">Opens in new window</span>\n```\n\n---\n\n## Color Contrast\n\n```html\n<!-- Ensure sufficient contrast ratios -->\n<div class=\"bg-gray-900 text-white\">\n  High contrast text (WCAG AAA)\n</div>\n\n<div class=\"bg-blue-500 text-blue-100\">\n  Good contrast on colored backgrounds\n</div>\n\n<!-- Use contrast utilities for testing -->\n<div class=\"bg-red-500 text-white contrast-more:bg-red-600 contrast-more:text-red-100\">\n  Adjusts for high contrast mode\n</div>\n```\n\n### Contrast Guidelines\n\n- **Normal text**: Minimum 4.5:1 contrast ratio\n- **Large text**: Minimum 3:1 contrast ratio\n- **UI components**: Minimum 3:1 contrast ratio\n\n---\n\n## Motion Preferences\n\n```html\n<!-- Respect prefers-reduced-motion -->\n<div class=\"transform transition-transform motion-reduce:transition-none\">\n  Doesn't animate when user prefers reduced motion\n</div>\n\n<!-- Conditional animations -->\n<div class=\"animate-pulse motion-safe:hover:animate-spin\">\n  Only animates when motion is preferred\n</div>\n```\n\n### Reduced Motion Media Query\n\n```css\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n  }\n}\n```\n\n---\n\n## ARIA Patterns with Tailwind\n\n### Toggle Button\n\n```html\n<button\n  type=\"button\"\n  role=\"switch\"\n  aria-checked=\"false\"\n  class=\"relative inline-flex h-6 w-11 items-center rounded-full bg-gray-200 aria-checked:bg-blue-600\"\n>\n  <span class=\"sr-only\">Enable notifications</span>\n  <span class=\"inline-block h-4 w-4 transform rounded-full bg-white transition translate-x-1 aria-checked:translate-x-6\"></span>\n</button>\n```\n\n### Alert Dialog\n\n```html\n<div\n  role=\"alertdialog\"\n  aria-labelledby=\"alert-title\"\n  aria-describedby=\"alert-description\"\n  class=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50\"\n>\n  <div class=\"max-w-md w-full bg-white rounded-lg shadow-xl p-6\">\n    <h2 id=\"alert-title\" class=\"text-lg font-bold mb-2\">\n      Are you sure?\n    </h2>\n    <p id=\"alert-description\" class=\"text-gray-600 mb-4\">\n      This action cannot be undone.\n    </p>\n    <div class=\"flex justify-end gap-2\">\n      <button class=\"px-4 py-2 text-gray-600\">Cancel</button>\n      <button class=\"px-4 py-2 bg-red-500 text-white rounded\">Delete</button>\n    </div>\n  </div>\n</div>\n```\n\n---\n\n## Accessibility Checklist\n\n- [ ] All interactive elements have visible focus indicators\n- [ ] Color contrast meets WCAG standards\n- [ ] Images have alt text\n- [ ] Form inputs have associated labels\n- [ ] ARIA labels used for icon-only buttons\n- [ ] Animations respect reduced motion preference\n- [ ] Skip links provided for keyboard navigation\n- [ ] Landmarks (header, main, nav, footer) used appropriately\n",".agents/skills/tailwind-css-patterns/references/animations.md":"# Tailwind CSS Animations & Transitions\n\n## Basic Transitions\n\n```html\n<button class=\"bg-blue-500 hover:bg-blue-700 transition duration-300\">\n  Smooth transition\n</button>\n```\n\n### Transition Properties\n\n| Class | Description |\n|-------|-------------|\n| `transition` | All properties |\n| `transition-colors` | Color properties only |\n| `transition-opacity` | Opacity only |\n| `transition-transform` | Transform only |\n| `duration-150` | 150ms duration |\n| `duration-300` | 300ms duration |\n| `ease-in` | Ease in timing |\n| `ease-out` | Ease out timing |\n| `ease-in-out` | Ease in-out timing |\n\n---\n\n## Transform Effects\n\n```html\n<!-- Scale on hover -->\n<div class=\"transform hover:scale-110 transition duration-300\">\n  Scale on hover\n</div>\n\n<!-- Rotate on hover -->\n<img class=\"transform hover:rotate-6 transition duration-300\" />\n\n<!-- Multiple transforms -->\n<div class=\"transform hover:scale-105 hover:-translate-y-1 transition\">\n  Scale up and move up\n</div>\n```\n\n---\n\n## Built-in Animations\n\n```html\n<div class=\"animate-spin\">Spinning</div>\n<div class=\"animate-pulse\">Pulsing</div>\n<div class=\"animate-bounce\">Bouncing</div>\n<div class=\"animate-ping\">Pinging (radar effect)</div>\n```\n\n### Common Use Cases\n\n```html\n<!-- Loading spinner -->\n<svg class=\"animate-spin h-5 w-5 text-blue-500\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n  <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n  <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n</svg>\n\n<!-- Skeleton loading -->\n<div class=\"animate-pulse space-y-4\">\n  <div class=\"h-4 bg-gray-200 rounded w-3/4\"></div>\n  <div class=\"h-4 bg-gray-200 rounded\"></div>\n  <div class=\"h-4 bg-gray-200 rounded w-5/6\"></div>\n</div>\n\n<!-- Notification badge -->\n<span class=\"absolute -top-1 -right-1 h-3 w-3 animate-ping rounded-full bg-red-400 opacity-75\"></span>\n<span class=\"absolute -top-1 -right-1 h-3 w-3 rounded-full bg-red-500\"></span>\n```\n\n---\n\n## Custom Animations (v4.1+)\n\n```css\n/* In your CSS with @theme */\n@theme {\n  --animate-fade-in: fadeIn 0.5s ease-in-out;\n  --animate-slide-up: slideUp 0.3s ease-out;\n}\n\n@keyframes fadeIn {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n@keyframes slideUp {\n  from {\n    opacity: 0;\n    transform: translateY(20px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n```\n\nUsage:\n\n```html\n<div class=\"animate-fade-in\">Fades in on load</div>\n<div class=\"animate-slide-up\">Slides up on load</div>\n```\n\n---\n\n## Motion Preferences\n\nRespect user's motion preferences:\n\n```html\n<!-- Disable animations for users who prefer reduced motion -->\n<div class=\"transform transition-transform motion-reduce:transition-none\">\n  Doesn't animate when user prefers reduced motion\n</div>\n\n<!-- Only animate when motion is preferred -->\n<div class=\"animate-pulse motion-safe:hover:animate-spin\">\n  Only animates when motion is preferred\n</div>\n```\n\n### Global Reduced Motion Support\n\n```css\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n  }\n}\n```\n",".agents/skills/tailwind-css-patterns/references/component-patterns.md":"# Tailwind CSS Component Patterns\n\n## Card Component\n\n```html\n<div class=\"bg-white rounded-lg shadow-lg overflow-hidden\">\n  <img class=\"w-full h-48 object-cover\" src=\"image.jpg\" alt=\"Card image\" />\n  <div class=\"p-6\">\n    <h3 class=\"text-xl font-bold mb-2\">Card Title</h3>\n    <p class=\"text-gray-700 mb-4\">Card description text goes here.</p>\n    <button class=\"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded\">\n      Action\n    </button>\n  </div>\n</div>\n```\n\n## Responsive User Card\n\n```html\n<div class=\"max-w-sm mx-auto bg-white rounded-xl shadow-lg overflow-hidden sm:flex sm:max-w-2xl\">\n  <img class=\"h-48 w-full object-cover sm:h-auto sm:w-48\"\n       src=\"profile.jpg\"\n       alt=\"Profile\" />\n  <div class=\"p-8\">\n    <div class=\"uppercase tracking-wide text-sm text-indigo-500 font-semibold\">\n      Product Engineer\n    </div>\n    <h2 class=\"mt-1 text-xl font-semibold text-gray-900\">\n      John Doe\n    </h2>\n    <p class=\"mt-2 text-gray-500\">\n      Building amazing products with modern technology.\n    </p>\n    <button class=\"mt-4 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition\">\n      Contact\n    </button>\n  </div>\n</div>\n```\n\n## Navigation Bar\n\n```html\n<nav class=\"bg-white shadow-lg\">\n  <div class=\"container mx-auto px-4\">\n    <div class=\"flex justify-between items-center h-16\">\n      <div class=\"flex items-center\">\n        <a href=\"#\" class=\"text-xl font-bold text-gray-800\">Logo</a>\n      </div>\n      <div class=\"hidden md:flex space-x-8\">\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Home</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">About</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Services</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Contact</a>\n      </div>\n      <button class=\"md:hidden\">\n        <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n          <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path>\n        </svg>\n      </button>\n    </div>\n  </div>\n</nav>\n```\n\n## Form Elements\n\n```html\n<form class=\"space-y-6 max-w-md mx-auto\">\n  <div>\n    <label class=\"block text-sm font-medium text-gray-700 mb-2\">\n      Email\n    </label>\n    <input\n      type=\"email\"\n      class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n      placeholder=\"you@example.com\"\n    />\n  </div>\n\n  <div>\n    <label class=\"block text-sm font-medium text-gray-700 mb-2\">\n      Password\n    </label>\n    <input\n      type=\"password\"\n      class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n    />\n  </div>\n\n  <div class=\"flex items-center\">\n    <input type=\"checkbox\" class=\"mr-2\" />\n    <label class=\"text-sm text-gray-600\">Remember me</label>\n  </div>\n\n  <button\n    type=\"submit\"\n    class=\"w-full bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition\"\n  >\n    Sign In\n  </button>\n</form>\n```\n\n## Modal/Dialog\n\n```html\n<div class=\"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4\">\n  <div class=\"bg-white rounded-lg shadow-xl max-w-md w-full p-6\">\n    <div class=\"flex justify-between items-center mb-4\">\n      <h3 class=\"text-xl font-bold\">Modal Title</h3>\n      <button class=\"text-gray-500 hover:text-gray-700\">\n        <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n          <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n        </svg>\n      </button>\n    </div>\n    <p class=\"text-gray-700 mb-6\">\n      Modal content goes here.\n    </p>\n    <div class=\"flex justify-end space-x-4\">\n      <button class=\"px-4 py-2 text-gray-600 hover:text-gray-800\">\n        Cancel\n      </button>\n      <button class=\"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700\">\n        Confirm\n      </button>\n    </div>\n  </div>\n</div>\n```\n\n## React Button Component with Variants\n\n```tsx\nimport { useState } from 'react';\n\nfunction Button({\n  variant = 'primary',\n  size = 'md',\n  children\n}: {\n  variant?: 'primary' | 'secondary';\n  size?: 'sm' | 'md' | 'lg';\n  children: React.ReactNode;\n}) {\n  const baseClasses = 'font-semibold rounded transition';\n\n  const variantClasses = {\n    primary: 'bg-blue-600 text-white hover:bg-blue-700',\n    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',\n  };\n\n  const sizeClasses = {\n    sm: 'px-3 py-1 text-sm',\n    md: 'px-4 py-2 text-base',\n    lg: 'px-6 py-3 text-lg',\n  };\n\n  return (\n    <button\n      className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}\n    >\n      {children}\n    </button>\n  );\n}\n```\n",".agents/skills/tailwind-css-patterns/references/configuration.md":"# Tailwind CSS Configuration\n\n## CSS-First Configuration (v4.1+)\n\nUse the `@theme` directive for CSS-based configuration:\n\n```css\n/* src/styles.css */\n@import \"tailwindcss\";\n\n@theme {\n  /* Custom colors */\n  --color-brand-50: #f0f9ff;\n  --color-brand-500: #3b82f6;\n  --color-brand-900: #1e3a8a;\n\n  /* Custom fonts */\n  --font-display: \"Inter\", system-ui, sans-serif;\n  --font-mono: \"Fira Code\", monospace;\n\n  /* Custom spacing */\n  --spacing-128: 32rem;\n\n  /* Custom animations */\n  --animate-fade-in: fadeIn 0.5s ease-in-out;\n\n  /* Custom breakpoints */\n  --breakpoint-3xl: 1920px;\n}\n\n/* Define custom animations */\n@keyframes fadeIn {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n/* Custom utilities */\n@utility content-auto {\n  content-visibility: auto;\n}\n```\n\n---\n\n## JavaScript Configuration (Legacy)\n\n```javascript\n/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\n    \"./index.html\",\n    \"./src/**/*.{js,ts,jsx,tsx,vue,svelte}\",\n  ],\n  theme: {\n    extend: {\n      colors: {\n        primary: {\n          50: '#f0f9ff',\n          500: '#3b82f6',\n          900: '#1e3a8a',\n        },\n      },\n      fontFamily: {\n        sans: ['Inter', 'system-ui', 'sans-serif'],\n      },\n      spacing: {\n        '128': '32rem',\n      },\n    },\n  },\n  plugins: [],\n}\n```\n\n---\n\n## Vite Integration (v4.1+)\n\n```javascript\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n  plugins: [\n    tailwindcss(),\n  ],\n})\n```\n\n---\n\n## Advanced v4.1 Features\n\n### Native CSS Custom Properties\n\n```html\n<div class=\"bg-[var(--color-brand-500)] text-[var(--color-white)]\">\n  Using CSS custom properties directly\n</div>\n```\n\n### Enhanced Arbitrary Values\n\n```html\n<!-- Complex grid with custom tracks -->\n<div class=\"grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4\">\n  Responsive grid without custom CSS\n</div>\n\n<!-- Custom animation timing -->\n<div class=\"animate-bounce ease-[cubic-bezier(0.68,-0.55,0.265,1.55)]\">\n  Bounce with custom easing\n</div>\n```\n\n### Custom Utilities\n\n```css\n@utility content-auto {\n  content-visibility: auto;\n}\n\n@utility text-shadow {\n  text-shadow: 0 2px 4px rgba(0,0,0,0.1);\n}\n```\n\n---\n\n## Plugins\n\n### Custom Plugin Example\n\n```javascript\n// tailwind.config.js\nconst plugin = require('tailwindcss/plugin')\n\nmodule.exports = {\n  plugins: [\n    plugin(function({ addUtilities, addComponents, e, config }) {\n      // Add custom utilities\n      addUtilities({\n        '.content-auto': {\n          contentVisibility: 'auto',\n        },\n      })\n\n      // Add custom components\n      addComponents({\n        '.btn': {\n          padding: '.5rem 1rem',\n          borderRadius: '.25rem',\n          fontWeight: '600',\n        },\n      })\n    }),\n  ],\n}\n```\n\n---\n\n## Presets\n\n### Creating a Reusable Preset\n\n```javascript\n// tailwind-preset.js\nmodule.exports = {\n  theme: {\n    extend: {\n      colors: {\n        brand: {\n          DEFAULT: '#3b82f6',\n          light: '#60a5fa',\n          dark: '#1d4ed8',\n        },\n      },\n    },\n  },\n  plugins: [\n    require('@tailwindcss/forms'),\n    require('@tailwindcss/typography'),\n  ],\n}\n```\n\n### Using a Preset\n\n```javascript\n// tailwind.config.js\nmodule.exports = {\n  presets: [\n    require('./tailwind-preset.js'),\n  ],\n}\n```\n",".agents/skills/tailwind-css-patterns/references/layout-patterns.md":"# Tailwind CSS Layout Patterns\n\n## Layout Utilities\n\n### Flexbox Layouts\n\nBasic flex container:\n\n```html\n<div class=\"flex items-center justify-between\">\n  <div>Left</div>\n  <div>Center</div>\n  <div>Right</div>\n</div>\n```\n\nResponsive flex direction:\n\n```html\n<div class=\"flex flex-col md:flex-row gap-4\">\n  <div class=\"flex-1\">Item 1</div>\n  <div class=\"flex-1\">Item 2</div>\n  <div class=\"flex-1\">Item 3</div>\n</div>\n```\n\nCommon flex patterns:\n\n```html\n<!-- Center content -->\n<div class=\"flex items-center justify-center min-h-screen\">\n  <div>Centered Content</div>\n</div>\n\n<!-- Space between items -->\n<div class=\"flex justify-between items-center\">\n  <span>Left</span>\n  <span>Right</span>\n</div>\n\n<!-- Vertical stack with gap -->\n<div class=\"flex flex-col gap-4\">\n  <div>Item 1</div>\n  <div>Item 2</div>\n</div>\n```\n\n### Grid Layouts\n\nBasic grid:\n\n```html\n<div class=\"grid grid-cols-3 gap-4\">\n  <div>Column 1</div>\n  <div>Column 2</div>\n  <div>Column 3</div>\n</div>\n```\n\nResponsive grid:\n\n```html\n<div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6\">\n  <!-- 1 column mobile, 2 tablet, 4 desktop -->\n  <div>Item 1</div>\n  <div>Item 2</div>\n  <div>Item 3</div>\n  <div>Item 4</div>\n</div>\n```\n\nAuto-fit columns:\n\n```html\n<div class=\"grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4\">\n  <!-- Automatically fit columns based on container width -->\n</div>\n```\n\n### Container & Max Width\n\nCentered container with max width:\n\n```html\n<div class=\"container mx-auto px-4 max-w-7xl\">\n  <!-- Centered content with padding -->\n</div>\n```\n\nResponsive max width:\n\n```html\n<div class=\"w-full max-w-md mx-auto\">\n  <!-- Max 448px width, centered -->\n</div>\n```\n\n---\n\n## Spacing\n\n### Padding & Margin\n\nUniform spacing:\n\n```html\n<div class=\"p-4\">Padding all sides</div>\n<div class=\"m-4\">Margin all sides</div>\n```\n\nIndividual sides:\n\n```html\n<div class=\"pt-4 pr-8 pb-4 pl-8\">\n  <!-- Top 1rem, Right 2rem, Bottom 1rem, Left 2rem -->\n</div>\n```\n\nAxis-based spacing:\n\n```html\n<div class=\"px-4 py-8\">\n  <!-- Horizontal padding 1rem, Vertical padding 2rem -->\n</div>\n```\n\nResponsive spacing:\n\n```html\n<div class=\"p-4 md:p-8 lg:p-12\">\n  <!-- Increases padding at larger breakpoints -->\n</div>\n```\n\nSpace between children:\n\n```html\n<div class=\"space-y-4\">\n  <div>Item 1</div>\n  <div>Item 2</div>\n  <div>Item 3</div>\n</div>\n```\n\n---\n\n## Typography\n\n### Font Size & Weight\n\n```html\n<h1 class=\"text-4xl font-bold\">Large Heading</h1>\n<h2 class=\"text-2xl font-semibold\">Subheading</h2>\n<p class=\"text-base font-normal\">Body text</p>\n<small class=\"text-sm text-gray-600\">Small text</small>\n```\n\nResponsive typography:\n\n```html\n<h1 class=\"text-2xl md:text-4xl lg:text-6xl font-bold\">\n  Responsive Heading\n</h1>\n```\n\n### Line Height & Letter Spacing\n\n```html\n<p class=\"leading-relaxed tracking-wide\">\n  Text with relaxed line height and wide letter spacing\n</p>\n```\n\n### Text Alignment\n\n```html\n<p class=\"text-left md:text-center\">\n  Left aligned on mobile, centered on tablet+\n</p>\n```\n\n---\n\n## Colors\n\n### Background Colors\n\n```html\n<div class=\"bg-blue-500\">Blue background</div>\n<div class=\"bg-gray-100\">Light gray background</div>\n<div class=\"bg-gradient-to-r from-blue-500 to-purple-600\">\n  Gradient background\n</div>\n```\n\n### Text Colors\n\n```html\n<p class=\"text-gray-900\">Dark text</p>\n<p class=\"text-blue-600\">Blue text</p>\n<p class=\"text-red-500\">Error text</p>\n```\n\n### Opacity\n\n```html\n<div class=\"bg-blue-500 bg-opacity-50\">\n  Semi-transparent blue\n</div>\n```\n\n---\n\n## Responsive Breakpoints Reference\n\n| Prefix | Min Width | CSS Equivalent |\n|--------|-----------|----------------|\n| `sm:` | 640px | `@media (min-width: 640px)` |\n| `md:` | 768px | `@media (min-width: 768px)` |\n| `lg:` | 1024px | `@media (min-width: 1024px)` |\n| `xl:` | 1280px | `@media (min-width: 1280px)` |\n| `2xl:` | 1536px | `@media (min-width: 1536px)` |\n",".agents/skills/tailwind-css-patterns/references/performance.md":"# Tailwind CSS Performance Optimization\n\n## Bundle Size Optimization\n\nConfigure content sources for optimal purging:\n\n```javascript\n// tailwind.config.js\nexport default {\n  content: [\n    \"./index.html\",\n    \"./src/**/*.{js,ts,jsx,tsx,vue,svelte}\",\n    \"./node_modules/@mycompany/ui-lib/**/*.{js,ts,jsx,tsx}\",\n  ],\n  // Enable JIT for faster builds\n  jit: true,\n}\n```\n\n### Content Path Best Practices\n\n1. **Be specific**: Don't use `\"./**/*\"` - it scans too many files\n2. **Include UI libraries**: Add paths to component libraries\n3. **Exclude tests**: Don't include test files in content paths\n\n---\n\n## CSS Optimization Techniques\n\n```html\n<!-- Use content-visibility for offscreen content -->\n<div class=\"content-visibility-auto\">\n  <div>Heavy content that's initially offscreen</div>\n</div>\n\n<!-- Optimize images with aspect-ratio -->\n<img class=\"aspect-video w-full object-cover\" src=\"video.jpg\" alt=\"Video thumbnail\" />\n\n<!-- Use contain for paint optimization -->\n<div class=\"contain-layout\">\n  Complex layout that doesn't affect outside elements\n</div>\n```\n\n---\n\n## Development Performance (v4.1+)\n\n```css\n/* Enable CSS-first configuration in v4.1 */\n@import \"tailwindcss\";\n\n@theme {\n  /* Define once, use everywhere */\n  --color-brand: #3b82f6;\n  --font-mono: \"Fira Code\", monospace;\n}\n\n/* Critical CSS for above-the-fold content */\n@layer critical {\n  .hero-title {\n    @apply text-4xl md:text-6xl font-bold;\n  }\n}\n```\n\n---\n\n## Production Build Optimization\n\n### PurgeCSS Configuration\n\n```javascript\n// tailwind.config.js\nmodule.exports = {\n  purge: {\n    enabled: process.env.NODE_ENV === 'production',\n    content: [\n      './src/**/*.html',\n      './src/**/*.jsx',\n      './src/**/*.tsx',\n    ],\n    options: {\n      safelist: [\n        'bg-red-500',\n        'text-center',\n        // Classes that shouldn't be purged\n      ],\n    },\n  },\n}\n```\n\n### Minification\n\n```bash\n# Use cssnano for minification\nnpm install -D cssnano\n\n# In postcss.config.js\nmodule.exports = {\n  plugins: [\n    require('tailwindcss'),\n    require('autoprefixer'),\n    ...(process.env.NODE_ENV === 'production' ? [require('cssnano')] : []),\n  ],\n}\n```\n\n---\n\n## Best Practices for Performance\n\n1. **Use the JIT engine**: Faster builds, smaller output\n2. **Configure content correctly**: Only include files that use Tailwind\n3. **Minimize custom CSS**: Use Tailwind utilities over custom CSS\n4. **Enable purging in production**: Removes unused styles\n5. **Use `@layer` for custom styles**: Helps with organization and purging\n6. **Avoid `@apply` in components**: Prefer composing utilities in markup\n",".agents/skills/tailwind-css-patterns/references/reference.md":"# Tailwind CSS Documentation\n\nTailwind CSS is a utility-first CSS framework that generates styles by scanning HTML, JavaScript, and template files for class names. It provides a comprehensive design system through CSS utility classes, enabling rapid UI development without writing custom CSS. The framework operates at build-time, analyzing source files and generating only the CSS classes actually used in the project, resulting in optimized production bundles with zero runtime overhead.\n\nThe framework includes an extensive default color palette (18 colors with 11 shades each), responsive breakpoint system, customizable design tokens via CSS custom properties, and support for dark mode, pseudo-classes, pseudo-elements, and media queries through variant prefixes. Tailwind CSS v4.1 introduces CSS-first configuration using the `@theme` directive, native support for custom utilities via `@utility`, seamless integration with modern build tools through Vite, PostCSS, and framework-specific plugins, and enhanced arbitrary value syntax for maximum flexibility.\n\n## Installation with Vite\n\nInstalling Tailwind CSS using the Vite plugin for modern JavaScript frameworks.\n\n```bash\n# Create a new Vite project\nnpm create vite@latest my-project\ncd my-project\n\n# Install Tailwind CSS and Vite plugin\nnpm install tailwindcss @tailwindcss/vite\n```\n\n```javascript\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n plugins: [\n tailwindcss(),\n ],\n})\n```\n\n```css\n/* src/style.css */\n@import \"tailwindcss\";\n```\n\n```html\n\n\n\n# Hello world!\n\n```\n\n## Utility Classes with Variants\n\nApplying conditional styles using variant prefixes for hover, focus, and responsive breakpoints.\n\n```html\n\n Save changes\n\nContent adapts to color scheme preference\n\n Submit\n\n```\n\n## Custom Theme Configuration\n\nDefining custom design tokens using the `@theme` directive in CSS.\n\n```css\n/* app.css */\n@import \"tailwindcss\";\n\n@theme {\n /* Custom fonts */\n --font-display: \"Satoshi\", \"sans-serif\";\n --font-body: \"Inter\", system-ui, sans-serif;\n\n /* Custom colors */\n --color-brand-50: oklch(0.98 0.02 264);\n --color-brand-100: oklch(0.95 0.05 264);\n --color-brand-500: oklch(0.55 0.22 264);\n --color-brand-900: oklch(0.25 0.12 264);\n\n /* Custom breakpoints */\n --breakpoint-3xl: 120rem;\n --breakpoint-4xl: 160rem;\n\n /* Custom spacing */\n --spacing-18: calc(var(--spacing) * 18);\n\n /* Custom animations */\n --ease-fluid: cubic-bezier(0.3, 0, 0, 1);\n --ease-snappy: cubic-bezier(0.2, 0, 0, 1);\n}\n```\n\n```html\n\nCustom design system\n\n```\n\n## Arbitrary Values\n\nUsing square bracket notation for one-off custom values without leaving HTML.\n\n```html\n\nPixel-perfect positioning\n\nCustom hex colors, font sizes, and content\n\nAny CSS property\n\nReference custom properties\n\nComplex grid layouts\n\nFont size from CSS variable\n\nColor from CSS variable\n\n```\n\n## Color System\n\nWorking with Tailwind's comprehensive color palette and opacity modifiers.\n\n```html\n\nColor utilities across all properties\n\nAlpha channel with percentage\n\nArbitrary opacity values\n\nOpacity from CSS variable\n\nAdapts to color scheme\n\n\n```\n\n## Dark Mode\n\nImplementing dark mode with CSS media queries or manual toggle.\n\n```html\n\nContent automatically adapts\n\n\n```\n\n```css\n/* Manual dark mode toggle with class selector */\n@import \"tailwindcss\";\n\n@custom-variant dark (&:where(.dark, .dark *));\n```\n\n```html\n\nControlled by .dark class\n\n\n\n\n```\n\n```javascript\n// Dark mode toggle logic\n// On page load or theme change\ndocument.documentElement.classList.toggle(\n \"dark\",\n localStorage.theme === \"dark\" ||\n (!(\"theme\" in localStorage) && window.matchMedia(\"(prefers-color-scheme: dark)\").matches)\n);\n\n// User chooses light mode\nlocalStorage.theme = \"light\";\n\n// User chooses dark mode\nlocalStorage.theme = \"dark\";\n\n// User chooses system preference\nlocalStorage.removeItem(\"theme\");\n```\n\n## State Variants\n\nStyling elements based on pseudo-classes and parent/sibling state.\n\n```html\n\n- Item content\n\n\n**Title**\nDescription\n\nPlease provide a valid email address.\n\n\n Option\n\n```\n\n## Responsive Design\n\nBuilding mobile-first responsive layouts with breakpoint variants.\n\n```html\n\n# Responsive heading\n\nText scales with viewport\n\n\nDesktop only\n\nMobile only\n\nCustom breakpoint\n\nBelow medium\n\n```\n\n## Custom Utilities\n\nCreating reusable custom utility classes with variant support.\n\n```css\n/* Simple custom utility */\n@utility content-auto {\n content-visibility: auto;\n}\n\n/* Complex utility with nesting */\n@utility scrollbar-hidden {\n &::-webkit-scrollbar {\n display: none;\n }\n}\n\n/* Functional utility with theme values */\n@theme {\n --tab-size-2: 2;\n --tab-size-4: 4;\n --tab-size-github: 8;\n}\n\n@utility tab-* {\n tab-size: --value(--tab-size-*);\n}\n\n/* Supporting arbitrary, bare, and theme values */\n@utility opacity-* {\n opacity: --value([percentage]);\n opacity: calc(--value(integer) * 1%);\n opacity: --value(--opacity-*);\n}\n\n/* Utility with modifiers */\n@utility text-* {\n font-size: --value(--text-*, [length]);\n line-height: --modifier(--leading-*, [length], [*]);\n}\n\n/* Negative value support */\n@utility inset-* {\n inset: --spacing(--value(integer));\n inset: --value([percentage], [length]);\n}\n\n@utility -inset-* {\n inset: --spacing(--value(integer) * -1);\n inset: calc(--value([percentage], [length]) * -1);\n}\n```\n\n```html\n\nCustom utilities work with variants\n\nVariants and arbitrary values supported\n\nUtility with modifier (font-size/line-height)\n\n```\n\n## Custom Variants\n\nRegistering custom conditional styles with the `@custom-variant` directive.\n\n```css\n/* Simple custom variant */\n@custom-variant theme-midnight (&:where([data-theme=\"midnight\"] *));\n\n/* Variant with media query */\n@custom-variant any-hover {\n @media (any-hover: hover) {\n &:hover {\n @slot;\n }\n }\n}\n\n/* ARIA state variant */\n@custom-variant aria-asc (&[aria-sort=\"ascending\"]);\n@custom-variant aria-desc (&[aria-sort=\"descending\"]);\n\n/* Data attribute variant */\n@custom-variant data-checked (&[data-ui~=\"checked\"]);\n```\n\n```html\n\n Midnight theme button\n\n\n Sortable column\n\nChecked state\n\nOne-off custom selectors\n\n```\n\n## Applying Variants in CSS\n\nUsing the `@variant` directive to apply variants within custom CSS.\n\n```css\n/* Single variant */\n.my-element {\n background: white;\n\n @variant dark {\n background: black;\n }\n}\n\n/* Nested variants */\n.my-button {\n background: white;\n\n @variant dark {\n background: gray;\n\n @variant hover {\n background: black;\n }\n }\n}\n\n/* Compiled output */\n.my-element {\n background: white;\n}\n\n@media (prefers-color-scheme: dark) {\n .my-element {\n background: black;\n }\n}\n```\n\n## Layer Organization\n\nOrganizing custom styles into Tailwind's cascade layers.\n\n```css\n@import \"tailwindcss\";\n\n/* Base styles for HTML elements */\n@layer base {\n h1 {\n font-size: var(--text-2xl);\n font-weight: bold;\n }\n\n h2 {\n font-size: var(--text-xl);\n font-weight: 600;\n }\n\n body {\n font-family: var(--font-body);\n }\n}\n\n/* Reusable component classes */\n@layer components {\n .btn {\n padding: --spacing(2) --spacing(4);\n border-radius: var(--radius);\n font-weight: 600;\n transition: all 150ms;\n }\n\n .btn-primary {\n background-color: var(--color-blue-500);\n color: white;\n }\n\n .card {\n background-color: var(--color-white);\n border-radius: var(--radius-lg);\n padding: --spacing(6);\n box-shadow: var(--shadow-xl);\n }\n\n /* Third-party component overrides */\n .select2-dropdown {\n border-radius: var(--radius);\n box-shadow: var(--shadow-lg);\n }\n}\n```\n\n```html\n\nSquare corners despite card class\n\n Component with utility overrides\n\n```\n\n## Functions and Directives\n\nUsing Tailwind's CSS functions for dynamic values and opacity adjustments.\n\n```css\n/* Alpha function for opacity */\n.my-element {\n color: --alpha(var(--color-lime-300) / 50%);\n background: --alpha(var(--color-blue-500) / 25%);\n}\n\n/* Spacing function */\n.my-element {\n margin: --spacing(4);\n padding: calc(--spacing(6) - 1px);\n}\n\n/* In arbitrary values */\n\n/* Source directive for additional content */\n@source \"../node_modules/@my-company/ui-lib\";\n\n/* Apply directive for inline utilities */\n.select2-dropdown {\n @apply rounded-b-lg shadow-md;\n}\n\n.select2-search {\n @apply rounded border border-gray-300;\n}\n\n.select2-results__group {\n @apply text-lg font-bold text-gray-900;\n}\n```\n\n## Pseudo-elements\n\nStyling ::before, ::after, ::placeholder, and other pseudo-elements.\n\n```html\n\n Email\n\n\n- First item\n- Second item\n\nSelect this text to see custom colors\n\nTypography with pseudo-elements\n\n```\n\n## Media Queries\n\nConditional styling based on user preferences and device capabilities.\n\n```html\n\n Respects user preference\n\n Only animates if motion allowed\n\n Adjusts for contrast needs\n\n\nHidden in portrait mode\n\nLayout adapts to orientation\n\n Not shown when printing\n\nOnly visible in print\n\nProgressive enhancement\n\n```\n\n## Summary\n\nTailwind CSS provides a complete utility-first design system that eliminates the need for writing custom CSS in most cases. The framework's primary use cases include rapid prototyping, building production applications with consistent design systems, creating responsive layouts, implementing dark mode, and maintaining design consistency across large teams. By using utility classes directly in markup, developers can iterate quickly, avoid naming conventions, and prevent CSS bloat since only used styles are generated.\n\nThe v4.1 release enhances the developer experience with CSS-first configuration, eliminating JavaScript configuration files for most projects. Integration patterns include using the Vite plugin for modern frameworks, PostCSS for custom build pipelines, the Tailwind CLI for simple projects, and CDN scripts for rapid prototyping. The framework excels at component-driven development when combined with React, Vue, Svelte, or other modern frameworks, where utility classes are co-located with component logic. Custom design systems can be fully defined in CSS using `@theme`, with project-specific utilities and variants extending the framework's capabilities without writing JavaScript plugins.",".agents/skills/tailwind-css-patterns/references/responsive-design.md":"# Tailwind CSS Responsive Design & Dark Mode\n\n## Responsive Design Patterns\n\n### Mobile-First Responsive Layout\n\n```html\n<div class=\"container mx-auto px-4\">\n  <!-- Hero Section -->\n  <div class=\"flex flex-col md:flex-row items-center gap-8 py-12\">\n    <div class=\"flex-1\">\n      <h1 class=\"text-3xl md:text-5xl font-bold mb-4\">\n        Welcome to Our Site\n      </h1>\n      <p class=\"text-lg text-gray-600 mb-6\">\n        Build amazing things with Tailwind CSS\n      </p>\n      <button class=\"bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700\">\n        Get Started\n      </button>\n    </div>\n    <div class=\"flex-1\">\n      <img src=\"hero.jpg\" class=\"w-full rounded-lg shadow-lg\" />\n    </div>\n  </div>\n</div>\n```\n\n### Responsive Grid Gallery\n\n```html\n<div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4\">\n  <div class=\"aspect-square bg-gray-200 rounded-lg overflow-hidden\">\n    <img src=\"image1.jpg\" class=\"w-full h-full object-cover hover:scale-105 transition\" />\n  </div>\n  <div class=\"aspect-square bg-gray-200 rounded-lg overflow-hidden\">\n    <img src=\"image2.jpg\" class=\"w-full h-full object-cover hover:scale-105 transition\" />\n  </div>\n  <!-- More items... -->\n</div>\n```\n\n### Responsive Card Component\n\n```tsx\nfunction ProductCard({ product }: { product: Product }) {\n  return (\n    <div className=\"bg-white rounded-lg shadow-lg overflow-hidden\n                    sm:flex sm:max-w-2xl\">\n      <img\n        className=\"h-48 w-full object-cover sm:h-auto sm:w-48\"\n        src={product.image}\n        alt={product.name}\n      />\n      <div className=\"p-6\">\n        <h3 className=\"text-lg font-semibold text-gray-900\">\n          {product.name}\n        </h3>\n        <p className=\"mt-2 text-gray-600\">\n          {product.description}\n        </p>\n        <button className=\"mt-4 px-4 py-2 bg-indigo-600 text-white\n                          rounded-lg hover:bg-indigo-700 transition\">\n          Add to Cart\n        </button>\n      </div>\n    </div>\n  );\n}\n```\n\n---\n\n## Dark Mode\n\n### Basic Dark Mode Support\n\n```html\n<div class=\"bg-white dark:bg-gray-900 text-gray-900 dark:text-white\">\n  <h1 class=\"text-gray-900 dark:text-white\">Title</h1>\n  <p class=\"text-gray-600 dark:text-gray-400\">Description</p>\n</div>\n```\n\nEnable dark mode in tailwind.config.js:\n\n```javascript\nmodule.exports = {\n  darkMode: 'class', // or 'media'\n  // ...\n}\n```\n\n### Dark Mode Toggle (React)\n\n```tsx\nfunction ThemeToggle() {\n  const [darkMode, setDarkMode] = useState(false);\n\n  useEffect(() => {\n    if (darkMode) {\n      document.documentElement.classList.add('dark');\n    } else {\n      document.documentElement.classList.remove('dark');\n    }\n  }, [darkMode]);\n\n  return (\n    <button\n      onClick={() => setDarkMode(!darkMode)}\n      className=\"p-2 rounded-lg bg-gray-200 dark:bg-gray-800\"\n    >\n      {darkMode ? '🌙' : '☀️'}\n    </button>\n  );\n}\n```\n\n### Dark Mode Best Practices\n\n1. **Use semantic color names**: Instead of `bg-white` use `bg-surface` custom color\n2. **Test with real content**: Some colors look good in light but not in dark\n3. **Respect system preference**: Use `darkMode: 'media'` for OS-level preference\n4. **Smooth transitions**: Add transition utilities for theme changes\n\n```css\n/* Global transition for theme changes */\n* {\n  @apply transition-colors duration-200;\n}\n```\n\n---\n\n## Container Queries (v4.1+)\n\nComponent that responds to its container size, not viewport:\n\n```html\n<div class=\"@container\">\n  <div class=\"@lg:text-xl @2xl:text-2xl\">\n    Text size based on container, not viewport\n  </div>\n</div>\n```\n\nUsage in a card component:\n\n```html\n<div class=\"@container w-full\">\n  <div class=\"flex flex-col @[400px]:flex-row gap-4\">\n    <img class=\"w-full @[400px]:w-32 h-32 object-cover\" src=\"image.jpg\" />\n    <div>\n      <h3 class=\"text-base @[400px]:text-lg font-bold\">Title</h3>\n      <p class=\"text-sm @[400px]:text-base\">Description</p>\n    </div>\n  </div>\n</div>\n```\n",".agents/skills/typescript-advanced-types/SKILL.md":"---\nname: typescript-advanced-types\ndescription: Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.\n---\n\n# TypeScript Advanced Types\n\nComprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.\n\n## When to Use This Skill\n\n- Building type-safe libraries or frameworks\n- Creating reusable generic components\n- Implementing complex type inference logic\n- Designing type-safe API clients\n- Building form validation systems\n- Creating strongly-typed configuration objects\n- Implementing type-safe state management\n- Migrating JavaScript codebases to TypeScript\n\n## Core Concepts\n\n### 1. Generics\n\n**Purpose:** Create reusable, type-flexible components while maintaining type safety.\n\n**Basic Generic Function:**\n\n```typescript\nfunction identity<T>(value: T): T {\n  return value;\n}\n\nconst num = identity<number>(42); // Type: number\nconst str = identity<string>(\"hello\"); // Type: string\nconst auto = identity(true); // Type inferred: boolean\n```\n\n**Generic Constraints:**\n\n```typescript\ninterface HasLength {\n  length: number;\n}\n\nfunction logLength<T extends HasLength>(item: T): T {\n  console.log(item.length);\n  return item;\n}\n\nlogLength(\"hello\"); // OK: string has length\nlogLength([1, 2, 3]); // OK: array has length\nlogLength({ length: 10 }); // OK: object has length\n// logLength(42);             // Error: number has no length\n```\n\n**Multiple Type Parameters:**\n\n```typescript\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n\nconst merged = merge({ name: \"John\" }, { age: 30 });\n// Type: { name: string } & { age: number }\n```\n\n### 2. Conditional Types\n\n**Purpose:** Create types that depend on conditions, enabling sophisticated type logic.\n\n**Basic Conditional Type:**\n\n```typescript\ntype IsString<T> = T extends string ? true : false;\n\ntype A = IsString<string>; // true\ntype B = IsString<number>; // false\n```\n\n**Extracting Return Types:**\n\n```typescript\ntype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;\n\nfunction getUser() {\n  return { id: 1, name: \"John\" };\n}\n\ntype User = ReturnType<typeof getUser>;\n// Type: { id: number; name: string; }\n```\n\n**Distributive Conditional Types:**\n\n```typescript\ntype ToArray<T> = T extends any ? T[] : never;\n\ntype StrOrNumArray = ToArray<string | number>;\n// Type: string[] | number[]\n```\n\n**Nested Conditions:**\n\n```typescript\ntype TypeName<T> = T extends string\n  ? \"string\"\n  : T extends number\n    ? \"number\"\n    : T extends boolean\n      ? \"boolean\"\n      : T extends undefined\n        ? \"undefined\"\n        : T extends Function\n          ? \"function\"\n          : \"object\";\n\ntype T1 = TypeName<string>; // \"string\"\ntype T2 = TypeName<() => void>; // \"function\"\n```\n\n### 3. Mapped Types\n\n**Purpose:** Transform existing types by iterating over their properties.\n\n**Basic Mapped Type:**\n\n```typescript\ntype Readonly<T> = {\n  readonly [P in keyof T]: T[P];\n};\n\ninterface User {\n  id: number;\n  name: string;\n}\n\ntype ReadonlyUser = Readonly<User>;\n// Type: { readonly id: number; readonly name: string; }\n```\n\n**Optional Properties:**\n\n```typescript\ntype Partial<T> = {\n  [P in keyof T]?: T[P];\n};\n\ntype PartialUser = Partial<User>;\n// Type: { id?: number; name?: string; }\n```\n\n**Key Remapping:**\n\n```typescript\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\ninterface Person {\n  name: string;\n  age: number;\n}\n\ntype PersonGetters = Getters<Person>;\n// Type: { getName: () => string; getAge: () => number; }\n```\n\n**Filtering Properties:**\n\n```typescript\ntype PickByType<T, U> = {\n  [K in keyof T as T[K] extends U ? K : never]: T[K];\n};\n\ninterface Mixed {\n  id: number;\n  name: string;\n  age: number;\n  active: boolean;\n}\n\ntype OnlyNumbers = PickByType<Mixed, number>;\n// Type: { id: number; age: number; }\n```\n\n### 4. Template Literal Types\n\n**Purpose:** Create string-based types with pattern matching and transformation.\n\n**Basic Template Literal:**\n\n```typescript\ntype EventName = \"click\" | \"focus\" | \"blur\";\ntype EventHandler = `on${Capitalize<EventName>}`;\n// Type: \"onClick\" | \"onFocus\" | \"onBlur\"\n```\n\n**String Manipulation:**\n\n```typescript\ntype UppercaseGreeting = Uppercase<\"hello\">; // \"HELLO\"\ntype LowercaseGreeting = Lowercase<\"HELLO\">; // \"hello\"\ntype CapitalizedName = Capitalize<\"john\">; // \"John\"\ntype UncapitalizedName = Uncapitalize<\"John\">; // \"john\"\n```\n\n**Path Building:**\n\n```typescript\ntype Path<T> = T extends object\n  ? {\n      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;\n    }[keyof T]\n  : never;\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n  };\n  database: {\n    url: string;\n  };\n}\n\ntype ConfigPath = Path<Config>;\n// Type: \"server\" | \"database\" | \"server.host\" | \"server.port\" | \"database.url\"\n```\n\n### 5. Utility Types\n\n**Built-in Utility Types:**\n\n```typescript\n// Partial<T> - Make all properties optional\ntype PartialUser = Partial<User>;\n\n// Required<T> - Make all properties required\ntype RequiredUser = Required<PartialUser>;\n\n// Readonly<T> - Make all properties readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick<T, K> - Select specific properties\ntype UserName = Pick<User, \"name\" | \"email\">;\n\n// Omit<T, K> - Remove specific properties\ntype UserWithoutPassword = Omit<User, \"password\">;\n\n// Exclude<T, U> - Exclude types from union\ntype T1 = Exclude<\"a\" | \"b\" | \"c\", \"a\">; // \"b\" | \"c\"\n\n// Extract<T, U> - Extract types from union\ntype T2 = Extract<\"a\" | \"b\" | \"c\", \"a\" | \"b\">; // \"a\" | \"b\"\n\n// NonNullable<T> - Exclude null and undefined\ntype T3 = NonNullable<string | null | undefined>; // string\n\n// Record<K, T> - Create object type with keys K and values T\ntype PageInfo = Record<\"home\" | \"about\", { title: string }>;\n```\n\n## Advanced Patterns\n\n### Pattern 1: Type-Safe Event Emitter\n\n```typescript\ntype EventMap = {\n  \"user:created\": { id: string; name: string };\n  \"user:updated\": { id: string };\n  \"user:deleted\": { id: string };\n};\n\nclass TypedEventEmitter<T extends Record<string, any>> {\n  private listeners: {\n    [K in keyof T]?: Array<(data: T[K]) => void>;\n  } = {};\n\n  on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {\n    if (!this.listeners[event]) {\n      this.listeners[event] = [];\n    }\n    this.listeners[event]!.push(callback);\n  }\n\n  emit<K extends keyof T>(event: K, data: T[K]): void {\n    const callbacks = this.listeners[event];\n    if (callbacks) {\n      callbacks.forEach((callback) => callback(data));\n    }\n  }\n}\n\nconst emitter = new TypedEventEmitter<EventMap>();\n\nemitter.on(\"user:created\", (data) => {\n  console.log(data.id, data.name); // Type-safe!\n});\n\nemitter.emit(\"user:created\", { id: \"1\", name: \"John\" });\n// emitter.emit(\"user:created\", { id: \"1\" });  // Error: missing 'name'\n```\n\n### Pattern 2: Type-Safe API Client\n\n```typescript\ntype HTTPMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\ntype EndpointConfig = {\n  \"/users\": {\n    GET: { response: User[] };\n    POST: { body: { name: string; email: string }; response: User };\n  };\n  \"/users/:id\": {\n    GET: { params: { id: string }; response: User };\n    PUT: { params: { id: string }; body: Partial<User>; response: User };\n    DELETE: { params: { id: string }; response: void };\n  };\n};\n\ntype ExtractParams<T> = T extends { params: infer P } ? P : never;\ntype ExtractBody<T> = T extends { body: infer B } ? B : never;\ntype ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\nclass APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {\n  async request<Path extends keyof Config, Method extends keyof Config[Path]>(\n    path: Path,\n    method: Method,\n    ...[options]: ExtractParams<Config[Path][Method]> extends never\n      ? ExtractBody<Config[Path][Method]> extends never\n        ? []\n        : [{ body: ExtractBody<Config[Path][Method]> }]\n      : [\n          {\n            params: ExtractParams<Config[Path][Method]>;\n            body?: ExtractBody<Config[Path][Method]>;\n          },\n        ]\n  ): Promise<ExtractResponse<Config[Path][Method]>> {\n    // Implementation here\n    return {} as any;\n  }\n}\n\nconst api = new APIClient<EndpointConfig>();\n\n// Type-safe API calls\nconst users = await api.request(\"/users\", \"GET\");\n// Type: User[]\n\nconst newUser = await api.request(\"/users\", \"POST\", {\n  body: { name: \"John\", email: \"john@example.com\" },\n});\n// Type: User\n\nconst user = await api.request(\"/users/:id\", \"GET\", {\n  params: { id: \"123\" },\n});\n// Type: User\n```\n\n### Pattern 3: Builder Pattern with Type Safety\n\n```typescript\ntype BuilderState<T> = {\n  [K in keyof T]: T[K] | undefined;\n};\n\ntype RequiredKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\ntype OptionalKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\n\ntype IsComplete<T, S> =\n  RequiredKeys<T> extends keyof S\n    ? S[RequiredKeys<T>] extends undefined\n      ? false\n      : true\n    : false;\n\nclass Builder<T, S extends BuilderState<T> = {}> {\n  private state: S = {} as S;\n\n  set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {\n    this.state[key] = value;\n    return this as any;\n  }\n\n  build(this: IsComplete<T, S> extends true ? this : never): T {\n    return this.state as T;\n  }\n}\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age?: number;\n}\n\nconst builder = new Builder<User>();\n\nconst user = builder\n  .set(\"id\", \"1\")\n  .set(\"name\", \"John\")\n  .set(\"email\", \"john@example.com\")\n  .build(); // OK: all required fields set\n\n// const incomplete = builder\n//   .set(\"id\", \"1\")\n//   .build();  // Error: missing required fields\n```\n\n### Pattern 4: Deep Readonly/Partial\n\n```typescript\ntype DeepReadonly<T> = {\n  readonly [P in keyof T]: T[P] extends object\n    ? T[P] extends Function\n      ? T[P]\n      : DeepReadonly<T[P]>\n    : T[P];\n};\n\ntype DeepPartial<T> = {\n  [P in keyof T]?: T[P] extends object\n    ? T[P] extends Array<infer U>\n      ? Array<DeepPartial<U>>\n      : DeepPartial<T[P]>\n    : T[P];\n};\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n    ssl: {\n      enabled: boolean;\n      cert: string;\n    };\n  };\n  database: {\n    url: string;\n    pool: {\n      min: number;\n      max: number;\n    };\n  };\n}\n\ntype ReadonlyConfig = DeepReadonly<Config>;\n// All nested properties are readonly\n\ntype PartialConfig = DeepPartial<Config>;\n// All nested properties are optional\n```\n\n### Pattern 5: Type-Safe Form Validation\n\n```typescript\ntype ValidationRule<T> = {\n  validate: (value: T) => boolean;\n  message: string;\n};\n\ntype FieldValidation<T> = {\n  [K in keyof T]?: ValidationRule<T[K]>[];\n};\n\ntype ValidationErrors<T> = {\n  [K in keyof T]?: string[];\n};\n\nclass FormValidator<T extends Record<string, any>> {\n  constructor(private rules: FieldValidation<T>) {}\n\n  validate(data: T): ValidationErrors<T> | null {\n    const errors: ValidationErrors<T> = {};\n    let hasErrors = false;\n\n    for (const key in this.rules) {\n      const fieldRules = this.rules[key];\n      const value = data[key];\n\n      if (fieldRules) {\n        const fieldErrors: string[] = [];\n\n        for (const rule of fieldRules) {\n          if (!rule.validate(value)) {\n            fieldErrors.push(rule.message);\n          }\n        }\n\n        if (fieldErrors.length > 0) {\n          errors[key] = fieldErrors;\n          hasErrors = true;\n        }\n      }\n    }\n\n    return hasErrors ? errors : null;\n  }\n}\n\ninterface LoginForm {\n  email: string;\n  password: string;\n}\n\nconst validator = new FormValidator<LoginForm>({\n  email: [\n    {\n      validate: (v) => v.includes(\"@\"),\n      message: \"Email must contain @\",\n    },\n    {\n      validate: (v) => v.length > 0,\n      message: \"Email is required\",\n    },\n  ],\n  password: [\n    {\n      validate: (v) => v.length >= 8,\n      message: \"Password must be at least 8 characters\",\n    },\n  ],\n});\n\nconst errors = validator.validate({\n  email: \"invalid\",\n  password: \"short\",\n});\n// Type: { email?: string[]; password?: string[]; } | null\n```\n\n### Pattern 6: Discriminated Unions\n\n```typescript\ntype Success<T> = {\n  status: \"success\";\n  data: T;\n};\n\ntype Error = {\n  status: \"error\";\n  error: string;\n};\n\ntype Loading = {\n  status: \"loading\";\n};\n\ntype AsyncState<T> = Success<T> | Error | Loading;\n\nfunction handleState<T>(state: AsyncState<T>): void {\n  switch (state.status) {\n    case \"success\":\n      console.log(state.data); // Type: T\n      break;\n    case \"error\":\n      console.log(state.error); // Type: string\n      break;\n    case \"loading\":\n      console.log(\"Loading...\");\n      break;\n  }\n}\n\n// Type-safe state machine\ntype State =\n  | { type: \"idle\" }\n  | { type: \"fetching\"; requestId: string }\n  | { type: \"success\"; data: any }\n  | { type: \"error\"; error: Error };\n\ntype Event =\n  | { type: \"FETCH\"; requestId: string }\n  | { type: \"SUCCESS\"; data: any }\n  | { type: \"ERROR\"; error: Error }\n  | { type: \"RESET\" };\n\nfunction reducer(state: State, event: Event): State {\n  switch (state.type) {\n    case \"idle\":\n      return event.type === \"FETCH\"\n        ? { type: \"fetching\", requestId: event.requestId }\n        : state;\n    case \"fetching\":\n      if (event.type === \"SUCCESS\") {\n        return { type: \"success\", data: event.data };\n      }\n      if (event.type === \"ERROR\") {\n        return { type: \"error\", error: event.error };\n      }\n      return state;\n    case \"success\":\n    case \"error\":\n      return event.type === \"RESET\" ? { type: \"idle\" } : state;\n  }\n}\n```\n\n## Type Inference Techniques\n\n### 1. Infer Keyword\n\n```typescript\n// Extract array element type\ntype ElementType<T> = T extends (infer U)[] ? U : never;\n\ntype NumArray = number[];\ntype Num = ElementType<NumArray>; // number\n\n// Extract promise type\ntype PromiseType<T> = T extends Promise<infer U> ? U : never;\n\ntype AsyncNum = PromiseType<Promise<number>>; // number\n\n// Extract function parameters\ntype Parameters<T> = T extends (...args: infer P) => any ? P : never;\n\nfunction foo(a: string, b: number) {}\ntype FooParams = Parameters<typeof foo>; // [string, number]\n```\n\n### 2. Type Guards\n\n```typescript\nfunction isString(value: unknown): value is string {\n  return typeof value === \"string\";\n}\n\nfunction isArrayOf<T>(\n  value: unknown,\n  guard: (item: unknown) => item is T,\n): value is T[] {\n  return Array.isArray(value) && value.every(guard);\n}\n\nconst data: unknown = [\"a\", \"b\", \"c\"];\n\nif (isArrayOf(data, isString)) {\n  data.forEach((s) => s.toUpperCase()); // Type: string[]\n}\n```\n\n### 3. Assertion Functions\n\n```typescript\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== \"string\") {\n    throw new Error(\"Not a string\");\n  }\n}\n\nfunction processValue(value: unknown) {\n  assertIsString(value);\n  // value is now typed as string\n  console.log(value.toUpperCase());\n}\n```\n\n## Best Practices\n\n1. **Use `unknown` over `any`**: Enforce type checking\n2. **Prefer `interface` for object shapes**: Better error messages\n3. **Use `type` for unions and complex types**: More flexible\n4. **Leverage type inference**: Let TypeScript infer when possible\n5. **Create helper types**: Build reusable type utilities\n6. **Use const assertions**: Preserve literal types\n7. **Avoid type assertions**: Use type guards instead\n8. **Document complex types**: Add JSDoc comments\n9. **Use strict mode**: Enable all strict compiler options\n10. **Test your types**: Use type tests to verify type behavior\n\n## Type Testing\n\n```typescript\n// Type assertion tests\ntype AssertEqual<T, U> = [T] extends [U]\n  ? [U] extends [T]\n    ? true\n    : false\n  : false;\n\ntype Test1 = AssertEqual<string, string>; // true\ntype Test2 = AssertEqual<string, number>; // false\ntype Test3 = AssertEqual<string | number, string>; // false\n\n// Expect error helper\ntype ExpectError<T extends never> = T;\n\n// Example usage\ntype ShouldError = ExpectError<AssertEqual<string, number>>;\n```\n\n## Common Pitfalls\n\n1. **Over-using `any`**: Defeats the purpose of TypeScript\n2. **Ignoring strict null checks**: Can lead to runtime errors\n3. **Too complex types**: Can slow down compilation\n4. **Not using discriminated unions**: Misses type narrowing opportunities\n5. **Forgetting readonly modifiers**: Allows unintended mutations\n6. **Circular type references**: Can cause compiler errors\n7. **Not handling edge cases**: Like empty arrays or null values\n\n## Performance Considerations\n\n- Avoid deeply nested conditional types\n- Use simple types when possible\n- Cache complex type computations\n- Limit recursion depth in recursive types\n- Use build tools to skip type checking in production\n",".agents/skills/vercel-composition-patterns/AGENTS.md":"# React Composition Patterns\n\n**Version 1.0.0**  \nEngineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React codebases using composition. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n---\n\n## Table of Contents\n\n1. [Component Architecture](#1-component-architecture) — **HIGH**\n   - 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation)\n   - 1.2 [Use Compound Components](#12-use-compound-components)\n2. [State Management](#2-state-management) — **MEDIUM**\n   - 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui)\n   - 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection)\n   - 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components)\n3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM**\n   - 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants)\n   - 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props)\n4. [React 19 APIs](#4-react-19-apis) — **MEDIUM**\n   - 4.1 [React 19 API Changes](#41-react-19-api-changes)\n\n---\n\n## 1. Component Architecture\n\n**Impact: HIGH**\n\nFundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n### 1.1 Avoid Boolean Prop Proliferation\n\n**Impact: CRITICAL (prevents unmaintainable component variants)**\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\n\ncomponent behavior. Each boolean doubles possible states and creates\n\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect: boolean props create exponential complexity**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct: composition eliminates conditionals**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\n\nsharing a single monolithic parent.\n\n### 1.2 Use Compound Components\n\n**Impact: HIGH (enables flexible composition without prop drilling)**\n\nStructure complex components as compound components with a shared context. Each\n\nsubcomponent accesses shared state via context, not props. Consumers compose the\n\npieces they need.\n\n**Incorrect: monolithic component with render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct: compound components with shared context**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n\n---\n\n## 2. State Management\n\n**Impact: MEDIUM**\n\nPatterns for lifting state and managing shared context across\ncomposed components.\n\n### 2.1 Decouple State Management from UI\n\n**Impact: MEDIUM (enables swapping state implementations without changing UI)**\n\nThe provider component should be the only place that knows how state is managed.\n\nUI components consume the context interface—they don't know if state comes from\n\nuseState, Zustand, or a server sync.\n\n**Incorrect: UI coupled to state implementation**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct: state management isolated in provider**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\n\ndepends on the context interface, not the implementation.\n\n### 2.2 Define Generic Context Interfaces for Dependency Injection\n\n**Impact: HIGH (enables dependency-injectable state across use-cases)**\n\nDefine a **generic interface** for your component context with three parts:\n\n`state`, `actions`, and `meta`. This interface is a contract that any provider\n\ncan implement—enabling the same UI components to work with completely different\n\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\n\ndependency-injectable.\n\n**Incorrect: UI coupled to specific state implementation**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct: generic interface enables dependency injection**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe provider boundary is what matters—not the visual nesting. Components that\n\nneed shared state don't have to be inside the `Composer.Frame`. They just need\n\nto be within the provider.\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\n\nbox, but they can still access its state and actions. This is the power of\n\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\n\nby the provider. Swap the provider, keep the UI.\n\n### 2.3 Lift State into Provider Components\n\n**Impact: HIGH (enables state sharing outside component boundaries)**\n\nMove state management into dedicated provider components. This allows sibling\n\ncomponents outside the main UI to access and modify state without prop drilling\n\nor awkward refs.\n\n**Incorrect: state trapped inside component**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect: useEffect to sync state up**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect: reading state from ref on submit**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct: state lifted to provider**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\n\nsubmit action because it's within the provider. Even though it's a one-off\n\ncomponent, it can still access the composer's state and actions from outside the\n\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\n\nnested inside each other—they just need to be within the same provider.\n\n---\n\n## 3. Implementation Patterns\n\n**Impact: MEDIUM**\n\nSpecific techniques for implementing compound components and\ncontext providers.\n\n### 3.1 Create Explicit Component Variants\n\n**Impact: MEDIUM (self-documenting code, no hidden conditionals)**\n\nInstead of one component with many boolean props, create explicit variant\n\ncomponents. Each variant composes the pieces it needs. The code documents\n\nitself.\n\n**Incorrect: one component, many modes**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct: explicit variants**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\n\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n\n- What UI elements it includes\n\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n\n### 3.2 Prefer Composing Children Over Render Props\n\n**Impact: MEDIUM (cleaner composition, better readability)**\n\nUse `children` for composition instead of `renderX` props. Children are more\n\nreadable, compose naturally, and don't require understanding callback\n\nsignatures.\n\n**Incorrect: render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct: compound components with children**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\n\nUse children when composing static structure.\n\n---\n\n## 4. React 19 APIs\n\n**Impact: MEDIUM**\n\nReact 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n\n### 4.1 React 19 API Changes\n\n**Impact: MEDIUM (cleaner component definitions and context usage)**\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect: forwardRef in React 19**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct: ref as a regular prop**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect: useContext in React 19**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct: use instead of useContext**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n\n---\n\n## References\n\n1. [https://react.dev](https://react.dev)\n2. [https://react.dev/learn/passing-data-deeply-with-context](https://react.dev/learn/passing-data-deeply-with-context)\n3. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)\n",".agents/skills/vercel-composition-patterns/README.md":"# React Composition Patterns\n\nA structured repository for React composition patterns that scale. These\npatterns help avoid boolean prop proliferation by using compound components,\nlifting state, and composing internals.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Rules\n\n### Component Architecture (CRITICAL)\n\n- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize\n  behavior\n- `architecture-compound-components.md` - Structure as compound components with\n  shared context\n\n### State Management (HIGH)\n\n- `state-lift-state.md` - Lift state into provider components\n- `state-context-interface.md` - Define clear context interfaces\n  (state/actions/meta)\n- `state-decouple-implementation.md` - Decouple state management from UI\n\n### Implementation Patterns (MEDIUM)\n\n- `patterns-children-over-render-props.md` - Prefer children over renderX props\n- `patterns-explicit-variants.md` - Create explicit component variants\n\n## Core Principles\n\n1. **Composition over configuration** — Instead of adding props, let consumers\n   compose\n2. **Lift your state** — State in providers, not trapped in components\n3. **Compose your internals** — Subcomponents access context, not props\n4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer\n   with isThread\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `architecture-` for Component Architecture\n   - `state-` for State Management\n   - `patterns-` for Implementation Patterns\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n\n## Impact Levels\n\n- `CRITICAL` - Foundational patterns, prevents unmaintainable code\n- `HIGH` - Significant maintainability improvements\n- `MEDIUM` - Good practices for cleaner code\n",".agents/skills/vercel-composition-patterns/SKILL.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2FSKILL.md -->",".agents/skills/vercel-composition-patterns/metadata.json":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://react.dev/learn/passing-data-deeply-with-context\",\n    \"https://react.dev/reference/react/use\"\n  ]\n}\n",".agents/skills/vercel-composition-patterns/rules/_sections.md":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Component Architecture (architecture)\n\n**Impact:** HIGH  \n**Description:** Fundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n## 2. State Management (state)\n\n**Impact:** MEDIUM  \n**Description:** Patterns for lifting state and managing shared context across\ncomposed components.\n\n## 3. Implementation Patterns (patterns)\n\n**Impact:** MEDIUM  \n**Description:** Specific techniques for implementing compound components and\ncontext providers.\n\n## 4. React 19 APIs (react19)\n\n**Impact:** MEDIUM  \n**Description:** React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n",".agents/skills/vercel-composition-patterns/rules/_template.md":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: brief description of impact\ntags: composition, components\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect:**\n\n```tsx\n// Bad code example\n```\n\n**Correct:**\n\n```tsx\n// Good code example\n```\n\nReference: [Link](https://example.com)\n",".agents/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Farchitecture-avoid-boolean-props.md -->",".agents/skills/vercel-composition-patterns/rules/architecture-compound-components.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Farchitecture-compound-components.md -->",".agents/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fpatterns-children-over-render-props.md -->",".agents/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fpatterns-explicit-variants.md -->",".agents/skills/vercel-composition-patterns/rules/react19-no-forwardref.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Freact19-no-forwardref.md -->",".agents/skills/vercel-composition-patterns/rules/state-context-interface.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-context-interface.md -->",".agents/skills/vercel-composition-patterns/rules/state-decouple-implementation.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-decouple-implementation.md -->",".agents/skills/vercel-composition-patterns/rules/state-lift-state.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-lift-state.md -->",".agents/skills/vercel-react-best-practices/AGENTS.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FAGENTS.md -->",".agents/skills/vercel-react-best-practices/README.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FREADME.md -->",".agents/skills/vercel-react-best-practices/SKILL.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FSKILL.md -->",".agents/skills/vercel-react-best-practices/metadata.json":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Fmetadata.json -->",".agents/skills/vercel-react-best-practices/rules/_sections.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2F_sections.md -->",".agents/skills/vercel-react-best-practices/rules/_template.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2F_template.md -->",".agents/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-effect-event-deps.md -->",".agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-event-handler-refs.md -->",".agents/skills/vercel-react-best-practices/rules/advanced-init-once.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-init-once.md -->",".agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-use-latest.md -->",".agents/skills/vercel-react-best-practices/rules/async-api-routes.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-api-routes.md -->",".agents/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-cheap-condition-before-await.md -->",".agents/skills/vercel-react-best-practices/rules/async-defer-await.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-defer-await.md -->",".agents/skills/vercel-react-best-practices/rules/async-dependencies.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-dependencies.md -->",".agents/skills/vercel-react-best-practices/rules/async-parallel.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-parallel.md -->",".agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-suspense-boundaries.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-barrel-imports.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-conditional.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-conditional.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-defer-third-party.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-dynamic-imports.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-preload.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-preload.md -->",".agents/skills/vercel-react-best-practices/rules/client-event-listeners.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-event-listeners.md -->",".agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-localstorage-schema.md -->",".agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-passive-event-listeners.md -->",".agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-swr-dedup.md -->",".agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-batch-dom-css.md -->",".agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-function-results.md -->",".agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-property-access.md -->",".agents/skills/vercel-react-best-practices/rules/js-cache-storage.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-storage.md -->",".agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-combine-iterations.md -->",".agents/skills/vercel-react-best-practices/rules/js-early-exit.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-early-exit.md -->",".agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-flatmap-filter.md -->",".agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-hoist-regexp.md -->",".agents/skills/vercel-react-best-practices/rules/js-index-maps.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-index-maps.md -->",".agents/skills/vercel-react-best-practices/rules/js-length-check-first.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-length-check-first.md -->",".agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-min-max-loop.md -->",".agents/skills/vercel-react-best-practices/rules/js-request-idle-callback.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-request-idle-callback.md -->",".agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-set-map-lookups.md -->",".agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-tosorted-immutable.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-activity.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-activity.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-animate-svg-wrapper.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-conditional-render.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-content-visibility.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hoist-jsx.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hydration-no-flicker.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hydration-suppress-warning.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-resource-hints.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-script-defer-async.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-svg-precision.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-usetransition-loading.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-defer-reads.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-dependencies.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-derived-state-no-effect.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-derived-state.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-functional-setstate.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-lazy-state-init.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-memo-with-default-value.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-memo.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-memo.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-move-effect-to-event.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-no-inline-components.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-simple-expression-in-memo.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-split-combined-hooks.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-transitions.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-transitions.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-use-deferred-value.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-use-ref-transient-values.md -->",".agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-after-nonblocking.md -->",".agents/skills/vercel-react-best-practices/rules/server-auth-actions.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-auth-actions.md -->",".agents/skills/vercel-react-best-practices/rules/server-cache-lru.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-cache-lru.md -->",".agents/skills/vercel-react-best-practices/rules/server-cache-react.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-cache-react.md -->",".agents/skills/vercel-react-best-practices/rules/server-dedup-props.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-dedup-props.md -->",".agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-hoist-static-io.md -->",".agents/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-no-shared-module-state.md -->",".agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-parallel-fetching.md -->",".agents/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-parallel-nested-fetching.md -->",".agents/skills/vercel-react-best-practices/rules/server-serialization.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-serialization.md -->",".agents/skills/web-design-guidelines/SKILL.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fweb-design-guidelines%2FSKILL.md -->",".agents/skills/web-quality-audit/SKILL.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fweb-quality-audit%2FSKILL.md -->","CLAUDE.md":"# CLAUDE.md\n\n<!-- autoskills:start -->\n\nSummary generated by `autoskills`. Check the full files inside `.claude/skills`.\n\n## Accessibility (a11y)\n\nAudit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".\n\n- `.claude/skills/accessibility/SKILL.md`\n- `.claude/skills/accessibility/references/WCAG.md`\n\n## Best practices\n\nApply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\n\n- `.claude/skills/best-practices/SKILL.md`\n\n## Core Web Vitals optimization\n\nOptimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\n\n- `.claude/skills/core-web-vitals/SKILL.md`\n- `.claude/skills/core-web-vitals/references/LCP.md`: Largest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:\n\n## Design Thinking\n\nCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beaut...\n\n- `.claude/skills/frontend-design/SKILL.md`\n\n## Next.js Best Practices\n\nNext.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling\n\n- `.claude/skills/next-best-practices/SKILL.md`\n- `.claude/skills/next-best-practices/async-patterns.md`: In Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.\n- `.claude/skills/next-best-practices/bundling.md`: Fix common bundling issues with third-party packages.\n- `.claude/skills/next-best-practices/data-patterns.md`: Choose the right data fetching pattern for each use case.\n- `.claude/skills/next-best-practices/debug-tricks.md`: Tricks to speed up debugging Next.js applications.\n- `.claude/skills/next-best-practices/directives.md`: These are React directives, not Next.js specific.\n- `.claude/skills/next-best-practices/error-handling.md`: Handle errors gracefully in Next.js applications.\n- `.claude/skills/next-best-practices/file-conventions.md`: Next.js App Router uses file-based routing with special file conventions.\n- `.claude/skills/next-best-practices/font.md`: Use `next/font` for automatic font optimization with zero layout shift.\n- `.claude/skills/next-best-practices/functions.md`: Next.js function APIs.\n- `.claude/skills/next-best-practices/hydration-error.md`: Diagnose and fix React hydration mismatch errors.\n- `.claude/skills/next-best-practices/image.md`: Use `next/image` for automatic image optimization.\n- `.claude/skills/next-best-practices/metadata.md`: Add SEO metadata to Next.js pages using the Metadata API.\n- `.claude/skills/next-best-practices/parallel-routes.md`: Parallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.\n- `.claude/skills/next-best-practices/route-handlers.md`: Create API endpoints with `route.ts` files.\n- `.claude/skills/next-best-practices/rsc-boundaries.md`: Detect and prevent invalid patterns when crossing Server/Client component boundaries.\n- `.claude/skills/next-best-practices/runtime-selection.md`: Use the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.\n- `.claude/skills/next-best-practices/scripts.md`: Loading third-party scripts in Next.js.\n- `.claude/skills/next-best-practices/self-hosting.md`: Deploy Next.js outside of Vercel with confidence.\n- `.claude/skills/next-best-practices/suspense-boundaries.md`: Client hooks that cause CSR bailout without Suspense boundaries.\n\n## Cache Components (Next.js 16+)\n\nNext.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag\n\n- `.claude/skills/next-cache-components/SKILL.md`\n\n## Upgrade Next.js\n\nUpgrade Next.js to the latest version following official migration guides and codemods\n\n- `.claude/skills/next-upgrade/SKILL.md`\n\n## Node.js Backend Patterns\n\nBuild production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.\n\n- `.claude/skills/nodejs-backend-patterns/SKILL.md`\n- `.claude/skills/nodejs-backend-patterns/references/advanced-patterns.md`: Advanced patterns for dependency injection, database integration, authentication, caching, and API response formatting.\n\n## Node.js Best Practices\n\nNode.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.\n\n- `.claude/skills/nodejs-best-practices/SKILL.md`\n\n## Performance optimization\n\nOptimize web performance for faster loading and better user experience. Use when asked to \"speed up my site\", \"optimize performance\", \"reduce load time\", \"fix slow loading\", \"improve page speed\", or \"performance audit\".\n\n- `.claude/skills/performance/SKILL.md`\n\n## SEO optimization\n\nOptimize for search engine visibility and ranking. Use when asked to \"improve SEO\", \"optimize for search\", \"fix meta tags\", \"add structured data\", \"sitemap optimization\", or \"search engine optimization\".\n\n- `.claude/skills/seo/SKILL.md`\n\n## Tailwind CSS Development Patterns\n\nProvides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or o...\n\n- `.claude/skills/tailwind-css-patterns/SKILL.md`\n- `.claude/skills/tailwind-css-patterns/references/accessibility.md`\n- `.claude/skills/tailwind-css-patterns/references/animations.md`: Usage:\n- `.claude/skills/tailwind-css-patterns/references/component-patterns.md`\n- `.claude/skills/tailwind-css-patterns/references/configuration.md`: Use the `@theme` directive for CSS-based configuration:\n- `.claude/skills/tailwind-css-patterns/references/layout-patterns.md`: Basic flex container:\n- `.claude/skills/tailwind-css-patterns/references/performance.md`: Configure content sources for optimal purging:\n- `.claude/skills/tailwind-css-patterns/references/reference.md`: Tailwind CSS is a utility-first CSS framework that generates styles by scanning HTML, JavaScript, and template files for class names. It provides a comprehensive design system through CSS utility classes, enabling rapid UI development without writing custom CSS. The framework operates at build-ti...\n- `.claude/skills/tailwind-css-patterns/references/responsive-design.md`: Enable dark mode in tailwind.config.js:\n\n## TypeScript Advanced Types\n\nMaster TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript pr...\n\n- `.claude/skills/typescript-advanced-types/SKILL.md`\n\n## React Composition Patterns\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n- `.claude/skills/vercel-composition-patterns/SKILL.md`\n- `.claude/skills/vercel-composition-patterns/AGENTS.md`: **Version 1.0.0** Engineering January 2026\n- `.claude/skills/vercel-composition-patterns/README.md`: A structured repository for React composition patterns that scale. These patterns help avoid boolean prop proliferation by using compound components, lifting state, and composing internals.\n- `.claude/skills/vercel-composition-patterns/rules/_sections.md`: This file defines all sections, their ordering, impact levels, and descriptions. The section ID (in parentheses) is the filename prefix used to group rules.\n- `.claude/skills/vercel-composition-patterns/rules/_template.md`: Brief explanation of the rule and why it matters.\n- `.claude/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md`: Don't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize component behavior. Each boolean doubles possible states and creates unmaintainable conditional logic. Use composition instead.\n- `.claude/skills/vercel-composition-patterns/rules/architecture-compound-components.md`: Structure complex components as compound components with a shared context. Each subcomponent accesses shared state via context, not props. Consumers compose the pieces they need.\n- `.claude/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md`: Use `children` for composition instead of `renderX` props. Children are more readable, compose naturally, and don't require understanding callback signatures.\n- `.claude/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md`: Instead of one component with many boolean props, create explicit variant components. Each variant composes the pieces it needs. The code documents itself.\n- `.claude/skills/vercel-composition-patterns/rules/react19-no-forwardref.md`: In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n- `.claude/skills/vercel-composition-patterns/rules/state-context-interface.md`: Define a **generic interface** for your component context with three parts: can implement—enabling the same UI components to work with completely different state implementations.\n- `.claude/skills/vercel-composition-patterns/rules/state-decouple-implementation.md`: The provider component should be the only place that knows how state is managed. UI components consume the context interface—they don't know if state comes from useState, Zustand, or a server sync.\n- `.claude/skills/vercel-composition-patterns/rules/state-lift-state.md`: Move state management into dedicated provider components. This allows sibling components outside the main UI to access and modify state without prop drilling or awkward refs.\n\n## Vercel React Best Practices\n\nReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimizati...\n\n- `.claude/skills/vercel-react-best-practices/SKILL.md`\n- `.claude/skills/vercel-react-best-practices/AGENTS.md`: **Version 1.0.0** Vercel Engineering January 2026\n- `.claude/skills/vercel-react-best-practices/README.md`: A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.\n- `.claude/skills/vercel-react-best-practices/rules/_sections.md`: This file defines all sections, their ordering, impact levels, and descriptions. The section ID (in parentheses) is the filename prefix used to group rules.\n- `.claude/skills/vercel-react-best-practices/rules/_template.md`: **Impact: MEDIUM (optional impact description)**\n- `.claude/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md`: Effect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect...\n- `.claude/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md`: Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.\n- `.claude/skills/vercel-react-best-practices/rules/advanced-init-once.md`: Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.\n- `.claude/skills/vercel-react-best-practices/rules/advanced-use-latest.md`: Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.\n- `.claude/skills/vercel-react-best-practices/rules/async-api-routes.md`: In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n- `.claude/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md`: When a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n- `.claude/skills/vercel-react-best-practices/rules/async-defer-await.md`: Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n- `.claude/skills/vercel-react-best-practices/rules/async-dependencies.md`: For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n- `.claude/skills/vercel-react-best-practices/rules/async-parallel.md`: When async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n- `.claude/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md`: Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md`: Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n- `.claude/skills/vercel-react-best-practices/rules/bundle-conditional.md`: Load large data or modules only when a feature is activated.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md`: Analytics, logging, and error tracking don't block user interaction. Load them after hydration.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md`: Use `next/dynamic` to lazy-load large components not needed on initial render.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-preload.md`: Preload heavy bundles before they're needed to reduce perceived latency.\n- `.claude/skills/vercel-react-best-practices/rules/client-event-listeners.md`: Use `useSWRSubscription()` to share global event listeners across component instances.\n- `.claude/skills/vercel-react-best-practices/rules/client-localstorage-schema.md`: Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n- `.claude/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md`: Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n- `.claude/skills/vercel-react-best-practices/rules/client-swr-dedup.md`: SWR enables request deduplication, caching, and revalidation across component instances.\n- `.claude/skills/vercel-react-best-practices/rules/js-batch-dom-css.md`: Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-function-results.md`: Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-property-access.md`: Cache object property lookups in hot paths.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-storage.md`: **Incorrect (reads storage on every call):**\n- `.claude/skills/vercel-react-best-practices/rules/js-combine-iterations.md`: Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.\n- `.claude/skills/vercel-react-best-practices/rules/js-early-exit.md`: Return early when result is determined to skip unnecessary processing.\n- `.claude/skills/vercel-react-best-practices/rules/js-flatmap-filter.md`: **Impact: LOW-MEDIUM (eliminates intermediate array)**\n- `.claude/skills/vercel-react-best-practices/rules/js-hoist-regexp.md`: Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.\n- `.claude/skills/vercel-react-best-practices/rules/js-index-maps.md`: Multiple `.find()` calls by the same key should use a Map.\n- `.claude/skills/vercel-react-best-practices/rules/js-length-check-first.md`: When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.\n- `.claude/skills/vercel-react-best-practices/rules/js-min-max-loop.md`: Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.\n- `.claude/skills/vercel-react-best-practices/rules/js-request-idle-callback.md`: **Impact: MEDIUM (keeps UI responsive during background tasks)**\n- `.claude/skills/vercel-react-best-practices/rules/js-set-map-lookups.md`: Convert arrays to Set/Map for repeated membership checks.\n- `.claude/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md`: **Incorrect (mutates original array):**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-activity.md`: Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md`: Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-conditional-render.md`: Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-content-visibility.md`: Apply `content-visibility: auto` to defer off-screen rendering.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md`: Extract static JSX outside components to avoid re-creation.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md`: When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md`: In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide r...\n- `.claude/skills/vercel-react-best-practices/rules/rendering-resource-hints.md`: **Impact: HIGH (reduces load time for critical resources)**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md`: **Impact: HIGH (eliminates render-blocking)**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-svg-precision.md`: Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md`: Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-defer-reads.md`: Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-dependencies.md`: Specify primitive dependencies instead of objects to minimize effect re-runs.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md`: If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-derived-state.md`: Subscribe to derived boolean state instead of continuous values to reduce re-render frequency.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md`: When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md`: Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md`: When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict...\n- `.claude/skills/vercel-react-best-practices/rules/rerender-memo.md`: Extract expensive work into memoized components to enable early returns before computation.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md`: If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md`: **Impact: HIGH (prevents remount on every render)**\n- `.claude/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md`: When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`. Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md`: When a hook contains multiple independent tasks with different dependencies, split them into separate hooks. A combined hook reruns all tasks when any dependency changes, even if some tasks don't use the changed value.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-transitions.md`: Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md`: When user input triggers expensive computations or renders, use `useDeferredValue` to keep the input responsive. The deferred value lags behind, allowing React to prioritize the input update and render the expensive result when idle.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md`: When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.\n- `.claude/skills/vercel-react-best-practices/rules/server-after-nonblocking.md`: Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.\n- `.claude/skills/vercel-react-best-practices/rules/server-auth-actions.md`: **Impact: CRITICAL (prevents unauthorized access to server mutations)**\n- `.claude/skills/vercel-react-best-practices/rules/server-cache-lru.md`: **Implementation:**\n- `.claude/skills/vercel-react-best-practices/rules/server-cache-react.md`: Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.\n- `.claude/skills/vercel-react-best-practices/rules/server-dedup-props.md`: **Impact: LOW (reduces network payload by avoiding duplicate serialization)**\n- `.claude/skills/vercel-react-best-practices/rules/server-hoist-static-io.md`: **Impact: HIGH (avoids repeated file/network I/O per request)**\n- `.claude/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md`: For React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race condit...\n- `.claude/skills/vercel-react-best-practices/rules/server-parallel-fetching.md`: React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.\n- `.claude/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md`: When fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.\n- `.claude/skills/vercel-react-best-practices/rules/server-serialization.md`: The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.\n\n## Web Interface Guidelines\n\nReview UI code for Web Interface Guidelines compliance. Use when asked to \"review my UI\", \"check accessibility\", \"audit design\", \"review UX\", or \"check my site against best practices\".\n\n- `.claude/skills/web-design-guidelines/SKILL.md`\n\n## Web quality audit\n\nComprehensive web quality audit covering performance, accessibility, SEO, and best practices. Use when asked to \"audit my site\", \"review web quality\", \"run lighthouse audit\", \"check page quality\", or \"optimize my website\".\n\n- `.claude/skills/web-quality-audit/SKILL.md`\n\n<!-- autoskills:end -->\n"},"files":{".agents/skills/accessibility/SKILL.md":"---\nname: accessibility\ndescription: Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Accessibility (a11y)\n\nComprehensive accessibility guidelines based on WCAG 2.1 and Lighthouse accessibility audits. Goal: make content usable by everyone, including people with disabilities.\n\n## WCAG Principles: POUR\n\n| Principle | Description |\n|-----------|-------------|\n| **P**erceivable | Content can be perceived through different senses |\n| **O**perable | Interface can be operated by all users |\n| **U**nderstandable | Content and interface are understandable |\n| **R**obust | Content works with assistive technologies |\n\n## Conformance levels\n\n| Level | Requirement | Target |\n|-------|-------------|--------|\n| **A** | Minimum accessibility | Must pass |\n| **AA** | Standard compliance | Should pass (legal requirement in many jurisdictions) |\n| **AAA** | Enhanced accessibility | Nice to have |\n\n---\n\n## Perceivable\n\n### Text alternatives (1.1)\n\n**Images require alt text:**\n```html\n<!-- ❌ Missing alt -->\n<img src=\"chart.png\">\n\n<!-- ✅ Descriptive alt -->\n<img src=\"chart.png\" alt=\"Bar chart showing 40% increase in Q3 sales\">\n\n<!-- ✅ Decorative image (empty alt) -->\n<img src=\"decorative-border.png\" alt=\"\" role=\"presentation\">\n\n<!-- ✅ Complex image with longer description -->\n<figure>\n  <img src=\"infographic.png\" alt=\"2024 market trends infographic\" \n       aria-describedby=\"infographic-desc\">\n  <figcaption id=\"infographic-desc\">\n    <!-- Detailed description -->\n  </figcaption>\n</figure>\n```\n\n**Icon buttons need accessible names:**\n```html\n<!-- ❌ No accessible name -->\n<button><svg><!-- menu icon --></svg></button>\n\n<!-- ✅ Using aria-label -->\n<button aria-label=\"Open menu\">\n  <svg aria-hidden=\"true\"><!-- menu icon --></svg>\n</button>\n\n<!-- ✅ Using visually hidden text -->\n<button>\n  <svg aria-hidden=\"true\"><!-- menu icon --></svg>\n  <span class=\"visually-hidden\">Open menu</span>\n</button>\n```\n\n**Visually hidden class:**\n```css\n.visually-hidden {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n```\n\n### Color contrast (1.4.3, 1.4.6)\n\n| Text Size | AA minimum | AAA enhanced |\n|-----------|------------|--------------|\n| Normal text (< 18px / < 14px bold) | 4.5:1 | 7:1 |\n| Large text (≥ 18px / ≥ 14px bold) | 3:1 | 4.5:1 |\n| UI components & graphics | 3:1 | 3:1 |\n\n```css\n/* ❌ Low contrast (2.5:1) */\n.low-contrast {\n  color: #999;\n  background: #fff;\n}\n\n/* ✅ Sufficient contrast (7:1) */\n.high-contrast {\n  color: #333;\n  background: #fff;\n}\n\n/* ✅ Focus states need contrast too */\n:focus-visible {\n  outline: 2px solid #005fcc;\n  outline-offset: 2px;\n}\n```\n\n**Don't rely on color alone:**\n```html\n<!-- ❌ Only color indicates error -->\n<input class=\"error-border\">\n<style>.error-border { border-color: red; }</style>\n\n<!-- ✅ Color + icon + text -->\n<div class=\"field-error\">\n  <input aria-invalid=\"true\" aria-describedby=\"email-error\">\n  <span id=\"email-error\" class=\"error-message\">\n    <svg aria-hidden=\"true\"><!-- error icon --></svg>\n    Please enter a valid email address\n  </span>\n</div>\n```\n\n### Media alternatives (1.2)\n\n```html\n<!-- Video with captions -->\n<video controls>\n  <source src=\"video.mp4\" type=\"video/mp4\">\n  <track kind=\"captions\" src=\"captions.vtt\" srclang=\"en\" label=\"English\" default>\n  <track kind=\"descriptions\" src=\"descriptions.vtt\" srclang=\"en\" label=\"Descriptions\">\n</video>\n\n<!-- Audio with transcript -->\n<audio controls>\n  <source src=\"podcast.mp3\" type=\"audio/mp3\">\n</audio>\n<details>\n  <summary>Transcript</summary>\n  <p>Full transcript text...</p>\n</details>\n```\n\n---\n\n## Operable\n\n### Keyboard accessible (2.1)\n\n**All functionality must be keyboard accessible:**\n```javascript\n// ❌ Only handles click\nelement.addEventListener('click', handleAction);\n\n// ✅ Handles both click and keyboard\nelement.addEventListener('click', handleAction);\nelement.addEventListener('keydown', (e) => {\n  if (e.key === 'Enter' || e.key === ' ') {\n    e.preventDefault();\n    handleAction();\n  }\n});\n```\n\n**No keyboard traps:**\n```javascript\n// Modal focus management\nfunction openModal(modal) {\n  const focusableElements = modal.querySelectorAll(\n    'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n  );\n  const firstElement = focusableElements[0];\n  const lastElement = focusableElements[focusableElements.length - 1];\n  \n  // Trap focus within modal\n  modal.addEventListener('keydown', (e) => {\n    if (e.key === 'Tab') {\n      if (e.shiftKey && document.activeElement === firstElement) {\n        e.preventDefault();\n        lastElement.focus();\n      } else if (!e.shiftKey && document.activeElement === lastElement) {\n        e.preventDefault();\n        firstElement.focus();\n      }\n    }\n    if (e.key === 'Escape') {\n      closeModal();\n    }\n  });\n  \n  firstElement.focus();\n}\n```\n\n### Focus visible (2.4.7)\n\n```css\n/* ❌ Never remove focus outlines */\n*:focus { outline: none; }\n\n/* ✅ Use :focus-visible for keyboard-only focus */\n:focus {\n  outline: none;\n}\n\n:focus-visible {\n  outline: 2px solid #005fcc;\n  outline-offset: 2px;\n}\n\n/* ✅ Or custom focus styles */\nbutton:focus-visible {\n  box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);\n}\n```\n\n### Skip links (2.4.1)\n\n```html\n<body>\n  <a href=\"#main-content\" class=\"skip-link\">Skip to main content</a>\n  <header><!-- navigation --></header>\n  <main id=\"main-content\" tabindex=\"-1\">\n    <!-- main content -->\n  </main>\n</body>\n```\n\n```css\n.skip-link {\n  position: absolute;\n  top: -40px;\n  left: 0;\n  background: #000;\n  color: #fff;\n  padding: 8px 16px;\n  z-index: 100;\n}\n\n.skip-link:focus {\n  top: 0;\n}\n```\n\n### Timing (2.2)\n\n```javascript\n// Allow users to extend time limits\nfunction showSessionWarning() {\n  const modal = createModal({\n    title: 'Session Expiring',\n    content: 'Your session will expire in 2 minutes.',\n    actions: [\n      { label: 'Extend session', action: extendSession },\n      { label: 'Log out', action: logout }\n    ],\n    timeout: 120000 // 2 minutes to respond\n  });\n}\n```\n\n### Motion (2.3)\n\n```css\n/* Respect reduced motion preference */\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important;\n  }\n}\n```\n\n---\n\n## Understandable\n\n### Page language (3.1.1)\n\n```html\n<!-- ❌ No language specified -->\n<html>\n\n<!-- ✅ Language specified -->\n<html lang=\"en\">\n\n<!-- ✅ Language changes within page -->\n<p>The French word for hello is <span lang=\"fr\">bonjour</span>.</p>\n```\n\n### Consistent navigation (3.2.3)\n\n```html\n<!-- Navigation should be consistent across pages -->\n<nav aria-label=\"Main\">\n  <ul>\n    <li><a href=\"/\" aria-current=\"page\">Home</a></li>\n    <li><a href=\"/products\">Products</a></li>\n    <li><a href=\"/about\">About</a></li>\n  </ul>\n</nav>\n```\n\n### Form labels (3.3.2)\n\n```html\n<!-- ❌ No label association -->\n<input type=\"email\" placeholder=\"Email\">\n\n<!-- ✅ Explicit label -->\n<label for=\"email\">Email address</label>\n<input type=\"email\" id=\"email\" name=\"email\" \n       autocomplete=\"email\" required>\n\n<!-- ✅ Implicit label -->\n<label>\n  Email address\n  <input type=\"email\" name=\"email\" autocomplete=\"email\" required>\n</label>\n\n<!-- ✅ With instructions -->\n<label for=\"password\">Password</label>\n<input type=\"password\" id=\"password\" \n       aria-describedby=\"password-requirements\">\n<p id=\"password-requirements\">\n  Must be at least 8 characters with one number.\n</p>\n```\n\n### Error handling (3.3.1, 3.3.3)\n\n```html\n<!-- Announce errors to screen readers -->\n<form novalidate>\n  <div class=\"field\" aria-live=\"polite\">\n    <label for=\"email\">Email</label>\n    <input type=\"email\" id=\"email\" \n           aria-invalid=\"true\"\n           aria-describedby=\"email-error\">\n    <p id=\"email-error\" class=\"error\" role=\"alert\">\n      Please enter a valid email address (e.g., name@example.com)\n    </p>\n  </div>\n</form>\n```\n\n```javascript\n// Focus first error on submit\nform.addEventListener('submit', (e) => {\n  const firstError = form.querySelector('[aria-invalid=\"true\"]');\n  if (firstError) {\n    e.preventDefault();\n    firstError.focus();\n    \n    // Announce error summary\n    const errorSummary = document.getElementById('error-summary');\n    errorSummary.textContent = `${errors.length} errors found. Please fix them and try again.`;\n    errorSummary.focus();\n  }\n});\n```\n\n---\n\n## Robust\n\n### Valid HTML (4.1.1)\n\n```html\n<!-- ❌ Duplicate IDs -->\n<div id=\"content\">...</div>\n<div id=\"content\">...</div>\n\n<!-- ❌ Invalid nesting -->\n<a href=\"/\"><button>Click</button></a>\n\n<!-- ✅ Unique IDs -->\n<div id=\"main-content\">...</div>\n<div id=\"sidebar-content\">...</div>\n\n<!-- ✅ Proper nesting -->\n<a href=\"/\" class=\"button-link\">Click</a>\n```\n\n### ARIA usage (4.1.2)\n\n**Prefer native elements:**\n```html\n<!-- ❌ ARIA role on div -->\n<div role=\"button\" tabindex=\"0\">Click me</div>\n\n<!-- ✅ Native button -->\n<button>Click me</button>\n\n<!-- ❌ ARIA checkbox -->\n<div role=\"checkbox\" aria-checked=\"false\">Option</div>\n\n<!-- ✅ Native checkbox -->\n<label><input type=\"checkbox\"> Option</label>\n```\n\n**When ARIA is needed:**\n```html\n<!-- Custom tabs component -->\n<div role=\"tablist\" aria-label=\"Product information\">\n  <button role=\"tab\" id=\"tab-1\" aria-selected=\"true\" \n          aria-controls=\"panel-1\">Description</button>\n  <button role=\"tab\" id=\"tab-2\" aria-selected=\"false\" \n          aria-controls=\"panel-2\" tabindex=\"-1\">Reviews</button>\n</div>\n<div role=\"tabpanel\" id=\"panel-1\" aria-labelledby=\"tab-1\">\n  <!-- Panel content -->\n</div>\n<div role=\"tabpanel\" id=\"panel-2\" aria-labelledby=\"tab-2\" hidden>\n  <!-- Panel content -->\n</div>\n```\n\n### Live regions (4.1.3)\n\n```html\n<!-- Status updates -->\n<div aria-live=\"polite\" aria-atomic=\"true\" class=\"status\">\n  <!-- Content updates announced to screen readers -->\n</div>\n\n<!-- Urgent alerts -->\n<div role=\"alert\" aria-live=\"assertive\">\n  <!-- Interrupts current announcement -->\n</div>\n```\n\n```javascript\n// Announce dynamic content changes\nfunction showNotification(message, type = 'polite') {\n  const container = document.getElementById(`${type}-announcer`);\n  container.textContent = ''; // Clear first\n  requestAnimationFrame(() => {\n    container.textContent = message;\n  });\n}\n```\n\n---\n\n## Testing checklist\n\n### Automated testing\n```bash\n# Lighthouse accessibility audit\nnpx lighthouse https://example.com --only-categories=accessibility\n\n# axe-core\nnpm install @axe-core/cli -g\naxe https://example.com\n```\n\n### Manual testing\n\n- [ ] **Keyboard navigation:** Tab through entire page, use Enter/Space to activate\n- [ ] **Screen reader:** Test with VoiceOver (Mac), NVDA (Windows), or TalkBack (Android)\n- [ ] **Zoom:** Content usable at 200% zoom\n- [ ] **High contrast:** Test with Windows High Contrast Mode\n- [ ] **Reduced motion:** Test with `prefers-reduced-motion: reduce`\n- [ ] **Focus order:** Logical and follows visual order\n\n### Screen reader commands\n\n| Action | VoiceOver (Mac) | NVDA (Windows) |\n|--------|-----------------|----------------|\n| Start/Stop | ⌘ + F5 | Ctrl + Alt + N |\n| Next item | VO + → | ↓ |\n| Previous item | VO + ← | ↑ |\n| Activate | VO + Space | Enter |\n| Headings list | VO + U, then arrows | H / Shift + H |\n| Links list | VO + U | K / Shift + K |\n\n---\n\n## Common issues by impact\n\n### Critical (fix immediately)\n1. Missing form labels\n2. Missing image alt text\n3. Insufficient color contrast\n4. Keyboard traps\n5. No focus indicators\n\n### Serious (fix before launch)\n1. Missing page language\n2. Missing heading structure\n3. Non-descriptive link text\n4. Auto-playing media\n5. Missing skip links\n\n### Moderate (fix soon)\n1. Missing ARIA labels on icons\n2. Inconsistent navigation\n3. Missing error identification\n4. Timing without controls\n5. Missing landmark regions\n\n## References\n\n- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/)\n- [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)\n- [Deque axe Rules](https://dequeuniversity.com/rules/axe/)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n",".agents/skills/accessibility/references/WCAG.md":"# WCAG 2.1 Quick Reference\n\n## Success criteria by level\n\n### Level A (minimum)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.1.1** Non-text Content | All images, icons have text alternatives |\n| **1.2.1** Audio-only/Video-only | Provide transcript or audio description |\n| **1.2.2** Captions | Video with audio has captions |\n| **1.2.3** Audio Description | Video has audio description |\n| **1.3.1** Info and Relationships | Information conveyed through presentation is available programmatically |\n| **1.3.2** Meaningful Sequence | Reading order is logical |\n| **1.3.3** Sensory Characteristics | Instructions don't rely solely on shape, color, size, location, orientation, or sound |\n| **1.4.1** Use of Color | Color is not the only visual means of conveying information |\n| **1.4.2** Audio Control | Audio playing automatically can be paused/stopped |\n| **2.1.1** Keyboard | All functionality available via keyboard |\n| **2.1.2** No Keyboard Trap | Keyboard focus can be moved away from any component |\n| **2.1.4** Character Key Shortcuts | Single-key shortcuts can be turned off or remapped |\n| **2.2.1** Timing Adjustable | Time limits can be extended |\n| **2.2.2** Pause, Stop, Hide | Moving/blinking content can be paused |\n| **2.3.1** Three Flashes | Nothing flashes more than 3 times per second |\n| **2.4.1** Bypass Blocks | Skip link or landmark navigation available |\n| **2.4.2** Page Titled | Pages have descriptive titles |\n| **2.4.3** Focus Order | Focus order preserves meaning |\n| **2.4.4** Link Purpose | Link purpose clear from link text or context |\n| **2.5.1** Pointer Gestures | Multi-point gestures have single-pointer alternatives |\n| **2.5.2** Pointer Cancellation | Down-event doesn't trigger action (use up-event or click) |\n| **2.5.3** Label in Name | Accessible name contains visible label text |\n| **2.5.4** Motion Actuation | Motion-triggered functions have alternatives |\n| **3.1.1** Language of Page | Default language specified in HTML |\n| **3.2.1** On Focus | Focus doesn't trigger unexpected changes |\n| **3.2.2** On Input | Input doesn't trigger unexpected changes |\n| **3.3.1** Error Identification | Input errors clearly described |\n| **3.3.2** Labels or Instructions | Form inputs have labels or instructions |\n| **4.1.1** Parsing | HTML is well-formed (no duplicate IDs, proper nesting) |\n| **4.1.2** Name, Role, Value | UI components have accessible names and correct roles |\n\n### Level AA (standard)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.2.4** Captions (Live) | Live audio has captions |\n| **1.2.5** Audio Description | Pre-recorded video has audio description |\n| **1.3.4** Orientation | Content doesn't restrict orientation |\n| **1.3.5** Identify Input Purpose | Input purpose can be programmatically determined |\n| **1.4.3** Contrast (Minimum) | 4.5:1 for normal text, 3:1 for large text |\n| **1.4.4** Resize Text | Text can be resized to 200% without loss of functionality |\n| **1.4.5** Images of Text | Text used instead of images of text |\n| **1.4.10** Reflow | Content reflows at 320px width without horizontal scroll |\n| **1.4.11** Non-text Contrast | UI components have 3:1 contrast |\n| **1.4.12** Text Spacing | Content adapts to text spacing changes |\n| **1.4.13** Content on Hover/Focus | Additional content is dismissible, hoverable, persistent |\n| **2.4.5** Multiple Ways | Multiple ways to find pages |\n| **2.4.6** Headings and Labels | Headings and labels are descriptive |\n| **2.4.7** Focus Visible | Focus indicator is visible |\n| **3.1.2** Language of Parts | Language changes are marked |\n| **3.2.3** Consistent Navigation | Navigation is consistent across pages |\n| **3.2.4** Consistent Identification | Same functionality uses same labels |\n| **3.3.3** Error Suggestion | Error corrections suggested when known |\n| **3.3.4** Error Prevention (Legal) | Actions can be reversed or confirmed |\n| **4.1.3** Status Messages | Status messages announced to screen readers |\n\n### Level AAA (enhanced)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.4.6** Contrast (Enhanced) | 7:1 for normal text, 4.5:1 for large text |\n| **1.4.8** Visual Presentation | Foreground/background colors can be selected |\n| **1.4.9** Images of Text (No Exception) | No images of text |\n| **2.1.3** Keyboard (No Exception) | All functionality keyboard accessible |\n| **2.2.3** No Timing | No time limits |\n| **2.2.4** Interruptions | Interruptions can be postponed |\n| **2.2.5** Re-authenticating | Data preserved on re-authentication |\n| **2.2.6** Timeouts | Users warned about data loss from inactivity |\n| **2.3.2** Three Flashes | No content flashes more than 3 times |\n| **2.3.3** Animation from Interactions | Motion animation can be disabled |\n| **2.4.8** Location | User location within site is available |\n| **2.4.9** Link Purpose (Link Only) | Link purpose clear from link text alone |\n| **2.4.10** Section Headings | Sections have headings |\n| **3.1.3** Unusual Words | Definitions available for unusual words |\n| **3.1.4** Abbreviations | Abbreviations expanded |\n| **3.1.5** Reading Level | Alternative content for complex text |\n| **3.1.6** Pronunciation | Pronunciation available where needed |\n| **3.2.5** Change on Request | Changes initiated only by user |\n| **3.3.5** Help | Context-sensitive help available |\n| **3.3.6** Error Prevention (All) | All form submissions can be reviewed |\n\n## Common ARIA patterns\n\n### Buttons\n```html\n<button>Label</button>\n<!-- or -->\n<button aria-label=\"Close dialog\">×</button>\n```\n\n### Links\n```html\n<a href=\"/page\">Descriptive link text</a>\n<!-- External links -->\n<a href=\"https://external.com\" target=\"_blank\" rel=\"noopener\">\n  External site\n  <span class=\"visually-hidden\">(opens in new tab)</span>\n</a>\n```\n\n### Form fields\n```html\n<label for=\"email\">Email address</label>\n<input type=\"email\" id=\"email\" aria-describedby=\"email-hint\">\n<p id=\"email-hint\">We'll never share your email.</p>\n```\n\n### Error states\n```html\n<label for=\"email\">Email</label>\n<input type=\"email\" id=\"email\" aria-invalid=\"true\" aria-describedby=\"email-error\">\n<p id=\"email-error\" role=\"alert\">Please enter a valid email address.</p>\n```\n\n### Navigation\n```html\n<nav aria-label=\"Main\">\n  <ul>\n    <li><a href=\"/\" aria-current=\"page\">Home</a></li>\n    <li><a href=\"/about\">About</a></li>\n  </ul>\n</nav>\n```\n\n### Modals\n```html\n<div role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"dialog-title\">\n  <h2 id=\"dialog-title\">Confirm Action</h2>\n  <!-- content -->\n</div>\n```\n\n### Live regions\n```html\n<!-- Polite (waits for pause in speech) -->\n<div aria-live=\"polite\">Status update here</div>\n\n<!-- Assertive (interrupts immediately) -->\n<div aria-live=\"assertive\" role=\"alert\">Error message here</div>\n\n<!-- Status (polite, implicit) -->\n<div role=\"status\">Loading complete</div>\n```\n\n## Testing tools\n\n| Tool | Type | URL |\n|------|------|-----|\n| axe DevTools | Browser extension | [deque.com/axe](https://www.deque.com/axe/) |\n| WAVE | Browser extension | [wave.webaim.org](https://wave.webaim.org/) |\n| Lighthouse | Built into Chrome | DevTools → Lighthouse |\n| NVDA | Screen reader (Windows) | [nvaccess.org](https://www.nvaccess.org/) |\n| VoiceOver | Screen reader (Mac) | Built into macOS |\n| Colour Contrast Analyser | Desktop app | [tpgi.com](https://www.tpgi.com/color-contrast-checker/) |\n",".agents/skills/best-practices/SKILL.md":"---\nname: best-practices\ndescription: Apply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Best practices\n\nModern web development standards based on Lighthouse best practices audits. Covers security, browser compatibility, and code quality patterns.\n\n## Security\n\n### HTTPS everywhere\n\n**Enforce HTTPS:**\n```html\n<!-- ❌ Mixed content -->\n<img src=\"http://example.com/image.jpg\">\n<script src=\"http://cdn.example.com/script.js\"></script>\n\n<!-- ✅ HTTPS only -->\n<img src=\"https://example.com/image.jpg\">\n<script src=\"https://cdn.example.com/script.js\"></script>\n\n<!-- ✅ Protocol-relative (will use page's protocol) -->\n<img src=\"//example.com/image.jpg\">\n```\n\n**HSTS Header:**\n```\nStrict-Transport-Security: max-age=31536000; includeSubDomains; preload\n```\n\n### Content Security Policy (CSP)\n\n```html\n<!-- Basic CSP via meta tag -->\n<meta http-equiv=\"Content-Security-Policy\" \n      content=\"default-src 'self'; \n               script-src 'self' https://trusted-cdn.com; \n               style-src 'self' 'unsafe-inline';\n               img-src 'self' data: https:;\n               connect-src 'self' https://api.example.com;\">\n\n<!-- Better: HTTP header -->\n```\n\n**CSP Header (recommended):**\n```\nContent-Security-Policy: \n  default-src 'self';\n  script-src 'self' 'nonce-abc123' https://trusted.com;\n  style-src 'self' 'nonce-abc123';\n  img-src 'self' data: https:;\n  connect-src 'self' https://api.example.com;\n  frame-ancestors 'self';\n  base-uri 'self';\n  form-action 'self';\n```\n\n**Using nonces for inline scripts:**\n```html\n<script nonce=\"abc123\">\n  // This inline script is allowed\n</script>\n```\n\n### Security headers\n\n```\n# Prevent clickjacking\nX-Frame-Options: DENY\n\n# Prevent MIME type sniffing\nX-Content-Type-Options: nosniff\n\n# Enable XSS filter (legacy browsers)\nX-XSS-Protection: 1; mode=block\n\n# Control referrer information\nReferrer-Policy: strict-origin-when-cross-origin\n\n# Permissions policy (formerly Feature-Policy)\nPermissions-Policy: geolocation=(), microphone=(), camera=()\n```\n\n### No vulnerable libraries\n\n```bash\n# Check for vulnerabilities\nnpm audit\nyarn audit\n\n# Auto-fix when possible\nnpm audit fix\n\n# Check specific package\nnpm ls lodash\n```\n\n**Keep dependencies updated:**\n```json\n// package.json\n{\n  \"scripts\": {\n    \"audit\": \"npm audit --audit-level=moderate\",\n    \"update\": \"npm update && npm audit fix\"\n  }\n}\n```\n\n**Known vulnerable patterns to avoid:**\n```javascript\n// ❌ Prototype pollution vulnerable patterns\nObject.assign(target, userInput);\n_.merge(target, userInput);\n\n// ✅ Safer alternatives\nconst safeData = JSON.parse(JSON.stringify(userInput));\n```\n\n### Input sanitization\n\n```javascript\n// ❌ XSS vulnerable\nelement.innerHTML = userInput;\ndocument.write(userInput);\n\n// ✅ Safe text content\nelement.textContent = userInput;\n\n// ✅ If HTML needed, sanitize\nimport DOMPurify from 'dompurify';\nelement.innerHTML = DOMPurify.sanitize(userInput);\n```\n\n### Secure cookies\n\n```javascript\n// ❌ Insecure cookie\ndocument.cookie = \"session=abc123\";\n\n// ✅ Secure cookie (server-side)\nSet-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/\n```\n\n---\n\n## Browser compatibility\n\n### Doctype declaration\n\n```html\n<!-- ❌ Missing or invalid doctype -->\n<HTML>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n\n<!-- ✅ HTML5 doctype -->\n<!DOCTYPE html>\n<html lang=\"en\">\n```\n\n### Character encoding\n\n```html\n<!-- ❌ Missing or late charset -->\n<html>\n<head>\n  <title>Page</title>\n  <meta charset=\"UTF-8\">\n</head>\n\n<!-- ✅ Charset as first element in head -->\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Page</title>\n</head>\n```\n\n### Viewport meta tag\n\n```html\n<!-- ❌ Missing viewport -->\n<head>\n  <title>Page</title>\n</head>\n\n<!-- ✅ Responsive viewport -->\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>Page</title>\n</head>\n```\n\n### Feature detection\n\n```javascript\n// ❌ Browser detection (brittle)\nif (navigator.userAgent.includes('Chrome')) {\n  // Chrome-specific code\n}\n\n// ✅ Feature detection\nif ('IntersectionObserver' in window) {\n  // Use IntersectionObserver\n} else {\n  // Fallback\n}\n\n// ✅ Using @supports in CSS\n@supports (display: grid) {\n  .container {\n    display: grid;\n  }\n}\n\n@supports not (display: grid) {\n  .container {\n    display: flex;\n  }\n}\n```\n\n### Polyfills (when needed)\n\n```html\n<!-- Load polyfills conditionally -->\n<script>\n  if (!('fetch' in window)) {\n    document.write('<script src=\"/polyfills/fetch.js\"><\\/script>');\n  }\n</script>\n\n<!-- Or use polyfill.io -->\n<script src=\"https://polyfill.io/v3/polyfill.min.js?features=fetch,IntersectionObserver\"></script>\n```\n\n---\n\n## Deprecated APIs\n\n### Avoid these\n\n```javascript\n// ❌ document.write (blocks parsing)\ndocument.write('<script src=\"...\"></script>');\n\n// ✅ Dynamic script loading\nconst script = document.createElement('script');\nscript.src = '...';\ndocument.head.appendChild(script);\n\n// ❌ Synchronous XHR (blocks main thread)\nconst xhr = new XMLHttpRequest();\nxhr.open('GET', url, false); // false = synchronous\n\n// ✅ Async fetch\nconst response = await fetch(url);\n\n// ❌ Application Cache (deprecated)\n<html manifest=\"cache.manifest\">\n\n// ✅ Service Workers\nif ('serviceWorker' in navigator) {\n  navigator.serviceWorker.register('/sw.js');\n}\n```\n\n### Event listener passive\n\n```javascript\n// ❌ Non-passive touch/wheel (may block scrolling)\nelement.addEventListener('touchstart', handler);\nelement.addEventListener('wheel', handler);\n\n// ✅ Passive listeners (allows smooth scrolling)\nelement.addEventListener('touchstart', handler, { passive: true });\nelement.addEventListener('wheel', handler, { passive: true });\n\n// ✅ If you need preventDefault, be explicit\nelement.addEventListener('touchstart', handler, { passive: false });\n```\n\n---\n\n## Console & errors\n\n### No console errors\n\n```javascript\n// ❌ Errors in production\nconsole.log('Debug info'); // Remove in production\nthrow new Error('Unhandled'); // Catch all errors\n\n// ✅ Proper error handling\ntry {\n  riskyOperation();\n} catch (error) {\n  // Log to error tracking service\n  errorTracker.captureException(error);\n  // Show user-friendly message\n  showErrorMessage('Something went wrong. Please try again.');\n}\n```\n\n### Error boundaries (React)\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n  \n  static getDerivedStateFromError(error) {\n    return { hasError: true };\n  }\n  \n  componentDidCatch(error, info) {\n    errorTracker.captureException(error, { extra: info });\n  }\n  \n  render() {\n    if (this.state.hasError) {\n      return <FallbackUI />;\n    }\n    return this.props.children;\n  }\n}\n\n// Usage\n<ErrorBoundary>\n  <App />\n</ErrorBoundary>\n```\n\n### Global error handler\n\n```javascript\n// Catch unhandled errors\nwindow.addEventListener('error', (event) => {\n  errorTracker.captureException(event.error);\n});\n\n// Catch unhandled promise rejections\nwindow.addEventListener('unhandledrejection', (event) => {\n  errorTracker.captureException(event.reason);\n});\n```\n\n---\n\n## Source maps\n\n### Production configuration\n\n```javascript\n// ❌ Source maps exposed in production\n// webpack.config.js\nmodule.exports = {\n  devtool: 'source-map', // Exposes source code\n};\n\n// ✅ Hidden source maps (uploaded to error tracker)\nmodule.exports = {\n  devtool: 'hidden-source-map',\n};\n\n// ✅ Or no source maps in production\nmodule.exports = {\n  devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',\n};\n```\n\n---\n\n## Performance best practices\n\n### Avoid blocking patterns\n\n```javascript\n// ❌ Blocking script\n<script src=\"heavy-library.js\"></script>\n\n// ✅ Deferred script\n<script defer src=\"heavy-library.js\"></script>\n\n// ❌ Blocking CSS import\n@import url('other-styles.css');\n\n// ✅ Link tags (parallel loading)\n<link rel=\"stylesheet\" href=\"styles.css\">\n<link rel=\"stylesheet\" href=\"other-styles.css\">\n```\n\n### Efficient event handlers\n\n```javascript\n// ❌ Handler on every element\nitems.forEach(item => {\n  item.addEventListener('click', handleClick);\n});\n\n// ✅ Event delegation\ncontainer.addEventListener('click', (e) => {\n  if (e.target.matches('.item')) {\n    handleClick(e);\n  }\n});\n```\n\n### Memory management\n\n```javascript\n// ❌ Memory leak (never removed)\nconst handler = () => { /* ... */ };\nwindow.addEventListener('resize', handler);\n\n// ✅ Cleanup when done\nconst handler = () => { /* ... */ };\nwindow.addEventListener('resize', handler);\n\n// Later, when component unmounts:\nwindow.removeEventListener('resize', handler);\n\n// ✅ Using AbortController\nconst controller = new AbortController();\nwindow.addEventListener('resize', handler, { signal: controller.signal });\n\n// Cleanup:\ncontroller.abort();\n```\n\n---\n\n## Code quality\n\n### Valid HTML\n\n```html\n<!-- ❌ Invalid HTML -->\n<div id=\"header\">\n<div id=\"header\"> <!-- Duplicate ID -->\n\n<ul>\n  <div>Item</div> <!-- Invalid child -->\n</ul>\n\n<a href=\"/\"><button>Click</button></a> <!-- Invalid nesting -->\n\n<!-- ✅ Valid HTML -->\n<header id=\"site-header\">\n</header>\n\n<ul>\n  <li>Item</li>\n</ul>\n\n<a href=\"/\" class=\"button\">Click</a>\n```\n\n### Semantic HTML\n\n```html\n<!-- ❌ Non-semantic -->\n<div class=\"header\">\n  <div class=\"nav\">\n    <div class=\"nav-item\">Home</div>\n  </div>\n</div>\n<div class=\"main\">\n  <div class=\"article\">\n    <div class=\"title\">Headline</div>\n  </div>\n</div>\n\n<!-- ✅ Semantic HTML5 -->\n<header>\n  <nav>\n    <a href=\"/\">Home</a>\n  </nav>\n</header>\n<main>\n  <article>\n    <h1>Headline</h1>\n  </article>\n</main>\n```\n\n### Image aspect ratios\n\n```html\n<!-- ❌ Distorted images -->\n<img src=\"photo.jpg\" width=\"300\" height=\"100\">\n<!-- If actual ratio is 4:3, this squishes the image -->\n\n<!-- ✅ Preserve aspect ratio -->\n<img src=\"photo.jpg\" width=\"300\" height=\"225\">\n<!-- Actual 4:3 dimensions -->\n\n<!-- ✅ CSS object-fit for flexibility -->\n<img src=\"photo.jpg\" style=\"width: 300px; height: 200px; object-fit: cover;\">\n```\n\n---\n\n## Permissions & privacy\n\n### Request permissions properly\n\n```javascript\n// ❌ Request on page load (bad UX, often denied)\nnavigator.geolocation.getCurrentPosition(success, error);\n\n// ✅ Request in context, after user action\nfindNearbyButton.addEventListener('click', async () => {\n  // Explain why you need it\n  if (await showPermissionExplanation()) {\n    navigator.geolocation.getCurrentPosition(success, error);\n  }\n});\n```\n\n### Permissions policy\n\n```html\n<!-- Restrict powerful features -->\n<meta http-equiv=\"Permissions-Policy\" \n      content=\"geolocation=(), camera=(), microphone=()\">\n\n<!-- Or allow for specific origins -->\n<meta http-equiv=\"Permissions-Policy\" \n      content=\"geolocation=(self 'https://maps.example.com')\">\n```\n\n---\n\n## Audit checklist\n\n### Security (critical)\n- [ ] HTTPS enabled, no mixed content\n- [ ] No vulnerable dependencies (`npm audit`)\n- [ ] CSP headers configured\n- [ ] Security headers present\n- [ ] No exposed source maps\n\n### Compatibility\n- [ ] Valid HTML5 doctype\n- [ ] Charset declared first in head\n- [ ] Viewport meta tag present\n- [ ] No deprecated APIs used\n- [ ] Passive event listeners for scroll/touch\n\n### Code quality\n- [ ] No console errors\n- [ ] Valid HTML (no duplicate IDs)\n- [ ] Semantic HTML elements used\n- [ ] Proper error handling\n- [ ] Memory cleanup in components\n\n### UX\n- [ ] No intrusive interstitials\n- [ ] Permission requests in context\n- [ ] Clear error messages\n- [ ] Appropriate image aspect ratios\n\n## Tools\n\n| Tool | Purpose |\n|------|---------|\n| `npm audit` | Dependency vulnerabilities |\n| [SecurityHeaders.com](https://securityheaders.com) | Header analysis |\n| [W3C Validator](https://validator.w3.org) | HTML validation |\n| Lighthouse | Best practices audit |\n| [Observatory](https://observatory.mozilla.org) | Security scan |\n\n## References\n\n- [MDN Web Security](https://developer.mozilla.org/en-US/docs/Web/Security)\n- [OWASP Top 10](https://owasp.org/www-project-top-ten/)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n",".agents/skills/core-web-vitals/SKILL.md":"---\nname: core-web-vitals\ndescription: Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Core Web Vitals optimization\n\nTargeted optimization for the three Core Web Vitals metrics that affect Google Search ranking and user experience.\n\n## The three metrics\n\n| Metric | Measures | Good | Needs work | Poor |\n|--------|----------|------|------------|------|\n| **LCP** | Loading | ≤ 2.5s | 2.5s – 4s | > 4s |\n| **INP** | Interactivity | ≤ 200ms | 200ms – 500ms | > 500ms |\n| **CLS** | Visual Stability | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |\n\nGoogle measures at the **75th percentile** — 75% of page visits must meet \"Good\" thresholds.\n\n---\n\n## LCP: Largest Contentful Paint\n\nLCP measures when the largest visible content element renders. Usually this is:\n- Hero image or video\n- Large text block\n- Background image\n- `<svg>` element\n\n### Common LCP issues\n\n**1. Slow server response (TTFB > 800ms)**\n```\nFix: CDN, caching, optimized backend, edge rendering\n```\n\n**2. Render-blocking resources**\n```html\n<!-- ❌ Blocks rendering -->\n<link rel=\"stylesheet\" href=\"/all-styles.css\">\n\n<!-- ✅ Critical CSS inlined, rest deferred -->\n<style>/* Critical above-fold CSS */</style>\n<link rel=\"preload\" href=\"/styles.css\" as=\"style\" \n      onload=\"this.onload=null;this.rel='stylesheet'\">\n```\n\n**3. Slow resource load times**\n```html\n<!-- ❌ No hints, discovered late -->\n<img src=\"/hero.jpg\" alt=\"Hero\">\n\n<!-- ✅ Preloaded with high priority -->\n<link rel=\"preload\" href=\"/hero.webp\" as=\"image\" fetchpriority=\"high\">\n<img src=\"/hero.webp\" alt=\"Hero\" fetchpriority=\"high\">\n```\n\n**4. Client-side rendering delays**\n```javascript\n// ❌ Content loads after JavaScript\nuseEffect(() => {\n  fetch('/api/hero-text').then(r => r.json()).then(setHeroText);\n}, []);\n\n// ✅ Server-side or static rendering\n// Use SSR, SSG, or streaming to send HTML with content\nexport async function getServerSideProps() {\n  const heroText = await fetchHeroText();\n  return { props: { heroText } };\n}\n```\n\n### LCP optimization checklist\n\n```markdown\n- [ ] TTFB < 800ms (use CDN, edge caching)\n- [ ] LCP image preloaded with fetchpriority=\"high\"\n- [ ] LCP image optimized (WebP/AVIF, correct size)\n- [ ] Critical CSS inlined (< 14KB)\n- [ ] No render-blocking JavaScript in <head>\n- [ ] Fonts don't block text rendering (font-display: swap)\n- [ ] LCP element in initial HTML (not JS-rendered)\n```\n\n### LCP element identification\n```javascript\n// Find your LCP element\nnew PerformanceObserver((list) => {\n  const entries = list.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  console.log('LCP element:', lastEntry.element);\n  console.log('LCP time:', lastEntry.startTime);\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n---\n\n## INP: Interaction to Next Paint\n\nINP measures responsiveness across ALL interactions (clicks, taps, key presses) during a page visit. It reports the worst interaction (at 98th percentile for high-traffic pages).\n\n### INP breakdown\n\nTotal INP = **Input Delay** + **Processing Time** + **Presentation Delay**\n\n| Phase | Target | Optimization |\n|-------|--------|--------------|\n| Input Delay | < 50ms | Reduce main thread blocking |\n| Processing | < 100ms | Optimize event handlers |\n| Presentation | < 50ms | Minimize rendering work |\n\n### Common INP issues\n\n**1. Long tasks blocking main thread**\n```javascript\n// ❌ Long synchronous task\nfunction processLargeArray(items) {\n  items.forEach(item => expensiveOperation(item));\n}\n\n// ✅ Break into chunks with yielding\nasync function processLargeArray(items) {\n  const CHUNK_SIZE = 100;\n  for (let i = 0; i < items.length; i += CHUNK_SIZE) {\n    const chunk = items.slice(i, i + CHUNK_SIZE);\n    chunk.forEach(item => expensiveOperation(item));\n    \n    // Yield to main thread\n    await new Promise(r => setTimeout(r, 0));\n    // Or use scheduler.yield() when available\n  }\n}\n```\n\n**2. Heavy event handlers**\n```javascript\n// ❌ All work in handler\nbutton.addEventListener('click', () => {\n  // Heavy computation\n  const result = calculateComplexThing();\n  // DOM updates\n  updateUI(result);\n  // Analytics\n  trackEvent('click');\n});\n\n// ✅ Prioritize visual feedback\nbutton.addEventListener('click', () => {\n  // Immediate visual feedback\n  button.classList.add('loading');\n  \n  // Defer non-critical work\n  requestAnimationFrame(() => {\n    const result = calculateComplexThing();\n    updateUI(result);\n  });\n  \n  // Use requestIdleCallback for analytics\n  requestIdleCallback(() => trackEvent('click'));\n});\n```\n\n**3. Third-party scripts**\n```javascript\n// ❌ Eagerly loaded, blocks interactions\n<script src=\"https://heavy-widget.com/widget.js\"></script>\n\n// ✅ Lazy loaded on interaction or visibility\nconst loadWidget = () => {\n  import('https://heavy-widget.com/widget.js')\n    .then(widget => widget.init());\n};\nbutton.addEventListener('click', loadWidget, { once: true });\n```\n\n**4. Excessive re-renders (React/Vue)**\n```javascript\n// ❌ Re-renders entire tree\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Counter count={count} />\n      <ExpensiveComponent /> {/* Re-renders on every count change */}\n    </div>\n  );\n}\n\n// ✅ Memoized expensive components\nconst MemoizedExpensive = React.memo(ExpensiveComponent);\n\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Counter count={count} />\n      <MemoizedExpensive />\n    </div>\n  );\n}\n```\n\n### INP optimization checklist\n\n```markdown\n- [ ] No tasks > 50ms on main thread\n- [ ] Event handlers complete quickly (< 100ms)\n- [ ] Visual feedback provided immediately\n- [ ] Heavy work deferred with requestIdleCallback\n- [ ] Third-party scripts don't block interactions\n- [ ] Debounced input handlers where appropriate\n- [ ] Web Workers for CPU-intensive operations\n```\n\n### INP debugging\n```javascript\n// Identify slow interactions\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (entry.duration > 200) {\n      console.warn('Slow interaction:', {\n        type: entry.name,\n        duration: entry.duration,\n        processingStart: entry.processingStart,\n        processingEnd: entry.processingEnd,\n        target: entry.target\n      });\n    }\n  }\n}).observe({ type: 'event', buffered: true, durationThreshold: 16 });\n```\n\n---\n\n## CLS: Cumulative Layout Shift\n\nCLS measures unexpected layout shifts. A shift occurs when a visible element changes position between frames without user interaction.\n\n**CLS Formula:** `impact fraction × distance fraction`\n\n### Common CLS causes\n\n**1. Images without dimensions**\n```html\n<!-- ❌ Causes layout shift when loaded -->\n<img src=\"photo.jpg\" alt=\"Photo\">\n\n<!-- ✅ Space reserved -->\n<img src=\"photo.jpg\" alt=\"Photo\" width=\"800\" height=\"600\">\n\n<!-- ✅ Or use aspect-ratio -->\n<img src=\"photo.jpg\" alt=\"Photo\" style=\"aspect-ratio: 4/3; width: 100%;\">\n```\n\n**2. Ads, embeds, and iframes**\n```html\n<!-- ❌ Unknown size until loaded -->\n<iframe src=\"https://ad-network.com/ad\"></iframe>\n\n<!-- ✅ Reserve space with min-height -->\n<div style=\"min-height: 250px;\">\n  <iframe src=\"https://ad-network.com/ad\" height=\"250\"></iframe>\n</div>\n\n<!-- ✅ Or use aspect-ratio container -->\n<div style=\"aspect-ratio: 16/9;\">\n  <iframe src=\"https://youtube.com/embed/...\" \n          style=\"width: 100%; height: 100%;\"></iframe>\n</div>\n```\n\n**3. Dynamically injected content**\n```javascript\n// ❌ Inserts content above viewport\nnotifications.prepend(newNotification);\n\n// ✅ Insert below viewport or use transform\nconst insertBelow = viewport.bottom < newNotification.top;\nif (insertBelow) {\n  notifications.prepend(newNotification);\n} else {\n  // Animate in without shifting\n  newNotification.style.transform = 'translateY(-100%)';\n  notifications.prepend(newNotification);\n  requestAnimationFrame(() => {\n    newNotification.style.transform = '';\n  });\n}\n```\n\n**4. Web fonts causing FOUT**\n```css\n/* ❌ Font swap shifts text */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n}\n\n/* ✅ Optional font (no shift if slow) */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: optional;\n}\n\n/* ✅ Or match fallback metrics */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: swap;\n  size-adjust: 105%; /* Match fallback size */\n  ascent-override: 95%;\n  descent-override: 20%;\n}\n```\n\n**5. Animations triggering layout**\n```css\n/* ❌ Animates layout properties */\n.animate {\n  transition: height 0.3s, width 0.3s;\n}\n\n/* ✅ Use transform instead */\n.animate {\n  transition: transform 0.3s;\n}\n.animate.expanded {\n  transform: scale(1.2);\n}\n```\n\n### CLS optimization checklist\n\n```markdown\n- [ ] All images have width/height or aspect-ratio\n- [ ] All videos/embeds have reserved space\n- [ ] Ads have min-height containers\n- [ ] Fonts use font-display: optional or matched metrics\n- [ ] Dynamic content inserted below viewport\n- [ ] Animations use transform/opacity only\n- [ ] No content injected above existing content\n```\n\n### CLS debugging\n```javascript\n// Track layout shifts\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (!entry.hadRecentInput) {\n      console.log('Layout shift:', entry.value);\n      entry.sources?.forEach(source => {\n        console.log('  Shifted element:', source.node);\n        console.log('  Previous rect:', source.previousRect);\n        console.log('  Current rect:', source.currentRect);\n      });\n    }\n  }\n}).observe({ type: 'layout-shift', buffered: true });\n```\n\n---\n\n## Measurement tools\n\n### Lab testing\n- **Chrome DevTools** → Performance panel, Lighthouse\n- **WebPageTest** → Detailed waterfall, filmstrip\n- **Lighthouse CLI** → `npx lighthouse <url>`\n\n### Field data (real users)\n- **Chrome User Experience Report (CrUX)** → BigQuery or API\n- **Search Console** → Core Web Vitals report\n- **web-vitals library** → Send to your analytics\n\n```javascript\nimport {onLCP, onINP, onCLS} from 'web-vitals';\n\nfunction sendToAnalytics({name, value, rating}) {\n  gtag('event', name, {\n    event_category: 'Web Vitals',\n    value: Math.round(name === 'CLS' ? value * 1000 : value),\n    event_label: rating\n  });\n}\n\nonLCP(sendToAnalytics);\nonINP(sendToAnalytics);\nonCLS(sendToAnalytics);\n```\n\n---\n\n## Framework quick fixes\n\n### Next.js\n```jsx\n// LCP: Use next/image with priority\nimport Image from 'next/image';\n<Image src=\"/hero.jpg\" priority fill alt=\"Hero\" />\n\n// INP: Use dynamic imports\nconst HeavyComponent = dynamic(() => import('./Heavy'), { ssr: false });\n\n// CLS: Image component handles dimensions automatically\n```\n\n### React\n```jsx\n// LCP: Preload in head\n<link rel=\"preload\" href=\"/hero.jpg\" as=\"image\" fetchpriority=\"high\" />\n\n// INP: Memoize and useTransition\nconst [isPending, startTransition] = useTransition();\nstartTransition(() => setExpensiveState(newValue));\n\n// CLS: Always specify dimensions in img tags\n```\n\n### Vue/Nuxt\n```vue\n<!-- LCP: Use nuxt/image with preload -->\n<NuxtImg src=\"/hero.jpg\" preload loading=\"eager\" />\n\n<!-- INP: Use async components -->\n<component :is=\"() => import('./Heavy.vue')\" />\n\n<!-- CLS: Use aspect-ratio CSS -->\n<img :style=\"{ aspectRatio: '16/9' }\" />\n```\n\n## References\n\n- [web.dev LCP](https://web.dev/articles/lcp)\n- [web.dev INP](https://web.dev/articles/inp)\n- [web.dev CLS](https://web.dev/articles/cls)\n- [Performance skill](../performance/SKILL.md)\n",".agents/skills/core-web-vitals/references/LCP.md":"# LCP optimization reference\n\n## What is LCP?\n\nLargest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:\n\n- An `<img>` element\n- An `<image>` element inside `<svg>`\n- A `<video>` element with poster image\n- An element with a background image via `url()`\n- A block-level element containing text nodes\n\n## LCP timeline\n\n```\n[  Server Response  ][  Resource Load  ][  Render  ]\n       TTFB              Download         Paint\n       └─────────────────────────────────────┘\n                         LCP Time\n```\n\n## Detailed optimizations\n\n### 1. Server response time (TTFB)\n\nTarget: < 800ms\n\n**Causes:**\n- Slow server/database queries\n- No CDN/edge caching\n- Inefficient backend code\n- Cold starts (serverless)\n\n**Solutions:**\n```javascript\n// Use edge functions for dynamic content\n// Vercel example\nexport const config = { runtime: 'edge' };\n\n// Use stale-while-revalidate caching\n// Cache-Control header\nres.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate=300');\n```\n\n### 2. Resource load time\n\n**For images:**\n```html\n<!-- Preload LCP image -->\n<link rel=\"preload\" as=\"image\" href=\"/hero.webp\" \n      imagesrcset=\"/hero-400.webp 400w, /hero-800.webp 800w\"\n      imagesizes=\"100vw\"\n      fetchpriority=\"high\">\n\n<!-- Modern format with fallback -->\n<picture>\n  <source srcset=\"/hero.avif\" type=\"image/avif\">\n  <source srcset=\"/hero.webp\" type=\"image/webp\">\n  <img src=\"/hero.jpg\" width=\"1200\" height=\"600\" \n       fetchpriority=\"high\" alt=\"Hero\">\n</picture>\n```\n\n**For text (web fonts):**\n```css\n@font-face {\n  font-family: 'Heading';\n  src: url('/fonts/heading.woff2') format('woff2');\n  font-display: swap; /* Show fallback immediately */\n}\n```\n\n### 3. Render blocking resources\n\n**Critical CSS pattern:**\n```html\n<head>\n  <!-- Inline critical CSS -->\n  <style>\n    /* Only above-fold styles, < 14KB */\n    .hero { /* ... */ }\n    .nav { /* ... */ }\n  </style>\n  \n  <!-- Defer non-critical CSS -->\n  <link rel=\"preload\" href=\"/styles.css\" as=\"style\" \n        onload=\"this.onload=null;this.rel='stylesheet'\">\n</head>\n```\n\n**Defer JavaScript:**\n```html\n<!-- ❌ Blocks parsing -->\n<script src=\"/app.js\"></script>\n\n<!-- ✅ Deferred (runs after HTML parsed) -->\n<script defer src=\"/app.js\"></script>\n\n<!-- ✅ Module (deferred by default) -->\n<script type=\"module\" src=\"/app.mjs\"></script>\n```\n\n### 4. Client-side rendering\n\n**Problem:** Content not in initial HTML.\n\n**Solutions:**\n\n**Server-side rendering (SSR):**\n```javascript\n// Next.js\nexport async function getServerSideProps() {\n  const data = await fetchHeroContent();\n  return { props: { hero: data } };\n}\n```\n\n**Static site generation (SSG):**\n```javascript\n// Next.js\nexport async function getStaticProps() {\n  const data = await fetchHeroContent();\n  return { props: { hero: data }, revalidate: 3600 };\n}\n```\n\n**Streaming SSR:**\n```jsx\n// React 18+\nimport { Suspense } from 'react';\n\nfunction Page() {\n  return (\n    <Suspense fallback={<HeroSkeleton />}>\n      <Hero />\n    </Suspense>\n  );\n}\n```\n\n## Framework-specific tips\n\n### Next.js\n```jsx\nimport Image from 'next/image';\n\n// LCP image with priority\n<Image \n  src=\"/hero.jpg\"\n  priority\n  fill\n  sizes=\"100vw\"\n  alt=\"Hero\"\n/>\n```\n\n### Nuxt\n```vue\n<NuxtImg\n  src=\"/hero.jpg\"\n  preload\n  loading=\"eager\"\n  sizes=\"100vw\"\n/>\n```\n\n### Astro\n```astro\n---\nimport { Image } from 'astro:assets';\nimport hero from '../assets/hero.jpg';\n---\n<Image \n  src={hero} \n  loading=\"eager\" \n  decoding=\"sync\"\n  alt=\"Hero\" \n/>\n```\n\n## Debugging LCP\n\n```javascript\n// Identify LCP element\nnew PerformanceObserver((entryList) => {\n  const entries = entryList.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  \n  console.log('LCP:', {\n    element: lastEntry.element,\n    time: lastEntry.startTime,\n    size: lastEntry.size,\n    url: lastEntry.url,\n    renderTime: lastEntry.renderTime,\n    loadTime: lastEntry.loadTime\n  });\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n## Common issues\n\n| Issue | Impact | Fix |\n|-------|--------|-----|\n| No preload for LCP image | +500-1000ms | Add `<link rel=\"preload\">` |\n| Large unoptimized image | +300-800ms | Compress, use WebP/AVIF |\n| Render-blocking CSS | +200-500ms | Inline critical CSS |\n| Slow TTFB | +300-2000ms | CDN, edge caching |\n| Client-rendered content | +500-2000ms | SSR/SSG |\n",".agents/skills/frontend-design/SKILL.md":"---\nname: frontend-design\ndescription: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.\nlicense: Complete terms in LICENSE.txt\n---\n\nThis skill guides creation of distinctive, production-grade frontend interfaces that avoid generic \"AI slop\" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.\n\nThe user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.\n\n## Design Thinking\n\nBefore coding, understand the context and commit to a BOLD aesthetic direction:\n- **Purpose**: What problem does this interface solve? Who uses it?\n- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.\n- **Constraints**: Technical requirements (framework, performance, accessibility).\n- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?\n\n**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.\n\nThen implement working code (HTML/CSS/JS, React, Vue, etc.) that is:\n- Production-grade and functional\n- Visually striking and memorable\n- Cohesive with a clear aesthetic point-of-view\n- Meticulously refined in every detail\n\n## Frontend Aesthetics Guidelines\n\nFocus on:\n- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.\n- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.\n- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.\n- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.\n- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.\n\nNEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.\n\nInterpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.\n\n**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.\n\nRemember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.\n",".agents/skills/migrate-oxlint/SKILL.md":"---\nname: migrate-oxlint\ndescription: Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint.\n---\n\nThis skill guides you through migrating a JavaScript/TypeScript project from ESLint to [Oxlint](https://oxc.rs/docs/guide/usage/linter/).\n\n## Overview\n\nOxlint is a high-performance linter that implements many popular ESLint rules natively in Rust. It can be used alongside ESLint or as a full replacement.\n\nAn official migration tool is available, and will be used by this skill: [`@oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate)\n\n## Step 1: Run Automated Migration\n\nRun the migration tool in the project root:\n\n```bash\nnpx @oxlint/migrate\n```\n\nThis reads your ESLint flat config (`eslint.config.js` for example) and generates a `.oxlintrc.json` file from it. It will find your ESLint config file automatically in most cases.\n\nSee options below for more info.\n\n### Key Options\n\n| Option                      | Description                                                                                                                     |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `--type-aware`              | Include type-aware rules from `@typescript-eslint` (will require the `oxlint-tsgolint` package to be installed after migrating) |\n| `--with-nursery`            | Include experimental rules still under development, may not be fully stable or consistent with ESLint equivalents               |\n| `--js-plugins [bool]`       | Enable/disable ESLint plugin migration via `jsPlugins` (default: enabled)                                                       |\n| `--details`                 | List rules that could not be migrated                                                                                           |\n| `--replace-eslint-comments` | Convert all `// eslint-disable` comments to `// oxlint-disable`                                                                 |\n| `--output-file <file>`      | Specify a different output path (default: `.oxlintrc.json`)                                                                     |\n\nIf your ESLint config is not at the default location, pass the path explicitly:\n\n```bash\nnpx @oxlint/migrate ./path/to/eslint.config.js\n```\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxlintrc.json`.\n\n### Plugin Mapping\n\nThe migration tool automatically maps ESLint plugins to oxlint's built-in equivalents. The following table is for reference when reviewing the generated config:\n\n| ESLint Plugin                                       | Oxlint Plugin Name |\n| --------------------------------------------------- | ------------------ |\n| `@typescript-eslint/eslint-plugin`                  | `typescript`       |\n| `eslint-plugin-react` / `eslint-plugin-react-hooks` | `react`            |\n| `eslint-plugin-import` / `eslint-plugin-import-x`   | `import`           |\n| `eslint-plugin-unicorn`                             | `unicorn`          |\n| `eslint-plugin-jsx-a11y`                            | `jsx-a11y`         |\n| `eslint-plugin-react-perf`                          | `react-perf`       |\n| `eslint-plugin-promise`                             | `promise`          |\n| `eslint-plugin-jest`                                | `jest`             |\n| `@vitest/eslint-plugin`                             | `vitest`           |\n| `eslint-plugin-jsdoc`                               | `jsdoc`            |\n| `eslint-plugin-next`                                | `nextjs`           |\n| `eslint-plugin-node`                                | `node`             |\n| `eslint-plugin-vue`                                 | `vue`              |\n\nDefault plugins (enabled when `plugins` field is omitted): `unicorn`, `typescript`, `oxc`.\nSetting the `plugins` array explicitly overrides these defaults.\n\nESLint core rules are usable in oxlint without needing to configure a plugin in the config file.\n\n### Rule Categories\n\nOxlint groups rules into categories for bulk configuration, though only `correctness` is enabled by default:\n\n```json\n{\n  \"categories\": {\n    \"correctness\": \"error\",\n    \"suspicious\": \"warn\"\n  }\n}\n```\n\nAvailable categories: `correctness` (default: enabled), `suspicious`, `pedantic`, `perf`, `style`, `restriction`, `nursery`.\n\nIndividual rule settings in `rules` override category settings.\n\n`@oxlint/migrate` will turn `correctness` off to avoid enabling additional rules that weren't enabled by your ESLint config. You can choose to enable additional categories after migration if desired.\n\n### Check Unmigrated Rules\n\nRun with `--details` to see which ESLint rules could not be migrated:\n\n```bash\nnpx @oxlint/migrate --details\n```\n\nReview the output and decide whether to keep ESLint for those rules or not. Some rules may be mentioned in the output from `--details` as having equivalents in oxlint that were not automatically mapped by the migration tool. In those cases, consider enabling the equivalent oxlint rule manually after migration.\n\n## Step 3: Install Oxlint\n\nInstall the core oxlint package (use `yarn install`, `pnpm install`, `vp install`, `bun install`, etc. depending on your package manager):\n\n```bash\nnpm install -D oxlint\n```\n\nIf you want to add the `oxlint-tsgolint` package, if you intend to use type-aware rules that require TypeScript type information:\n\n```bash\nnpm install -D oxlint-tsgolint\n```\n\nNo other packages besides the above are needed by default, though you will need to keep/install any additional ESLint plugins that were migrated into `jsPlugins`. Do not add `@oxlint/migrate` to the package.json, it is meant for one-off usage.\n\n## Step 4: Handle Unsupported Features\n\nSome features require manual attention:\n\n- Local plugins (relative path imports): Must be migrated manually to `jsPlugins`\n- `eslint-plugin-prettier`: Supported, but very slow. It is recommended to use [oxfmt](https://oxc.rs/docs/guide/usage/formatter) instead, or switch to `prettier --check` as a separate step alongside oxlint.\n- `settings` in override configs: Oxlint does not support `settings` inside `overrides` blocks.\n- ESLint v9+ plugins: Not all work with oxlint's JS Plugins API, but the majority will.\n\n### Local Plugins\n\nIf you have any custom ESLint rules in the project repo itself, you can migrate them manually after running the migration tool by adding them to the `jsPlugins` field in `.oxlintrc.json`:\n\n```json\n{\n  \"jsPlugins\": [\"./path/to/my-plugin.js\"],\n  \"rules\": {\n    \"local-plugin/rule-name\": \"error\"\n  }\n}\n```\n\n### External ESLint Plugins\n\nFor ESLint plugins without a built-in oxlint equivalent, use the `jsPlugins` field to load them:\n\n```json\n{\n  \"jsPlugins\": [\"eslint-plugin-custom\"],\n  \"rules\": {\n    \"custom/my-rule\": \"warn\"\n  }\n}\n```\n\n## Step 5: Update CI and Scripts\n\nReplace ESLint commands with oxlint. Path arguments are optional; oxlint defaults to the current working directory.\n\n```bash\n# Before\nnpx eslint src/\nnpx eslint --fix src/\n\n# After\nnpx oxlint src/\nnpx oxlint --fix src/\n```\n\n### Common CLI Options\n\n| ESLint                    | oxlint equivalent                              |\n| ------------------------- | ---------------------------------------------- |\n| `eslint .`                | `oxlint` (default: lints the cwd)              |\n| `eslint src/`             | `oxlint src/`                                  |\n| `eslint --fix`            | `oxlint --fix`                                 |\n| `eslint --max-warnings 0` | `oxlint --deny-warnings` or `--max-warnings 0` |\n| `eslint --format json`    | `oxlint --format json`                         |\n\nAdditional oxlint options:\n\n- `--tsconfig <path>`: Specify tsconfig.json path, likely unnecessary unless you have a non-standard name for `tsconfig.json`.\n\n## Tips\n\n- You can run alongside ESLint if necessary: Oxlint is designed to complement ESLint during migration, but with JS Plugins many projects can switch over fully without losing many rules.\n- Disable comments work: `// eslint-disable` and `// eslint-disable-next-line` comments are supported by oxlint. Use `--replace-eslint-comments` when running @oxlint/migrate to convert them to `// oxlint-disable` equivalents if desired.\n- List available rules: Run `npx oxlint --rules` to see all supported rules, or refer to the [rule documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).\n- Schema support: Add `\"$schema\": \"./node_modules/oxlint/configuration_schema.json\"` to `.oxlintrc.json` for editor autocompletion if the migration tool didn't do it automatically.\n- Output formats: `default`, `stylish`, `json`, `github`, `gitlab`, `junit`, `checkstyle`, `unix`\n- Ignore files: `.eslintignore` is supported by oxlint if you have it, but it's recommended to move any ignore patterns into the `ignorePatterns` field in `.oxlintrc.json` for consistency and simplicity. All files and paths ignored via a `.gitignore` file will be ignored by oxlint by default as well.\n- If you ran the migration tool multiple times, remove the `.oxlintrc.json.bak` backup file created by the migration tool once you've finished migrating.\n- If you are not using any JS Plugins and have replaced your ESLint configuration, you can remove all ESLint packages from your project dependencies.\n- Ensure your editor is configured to use oxlint instead of ESLint for linting and error reporting. You may want to install the Oxc extension for your preferred editor. See https://oxc.rs/docs/guide/usage/linter/editors.html for more details.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/linter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html)\n- [Complete Oxlint rule list and docs](https://oxc.rs/docs/guide/usage/linter/rules.html)\n",".agents/skills/next-best-practices/SKILL.md":"---\nname: next-best-practices\ndescription: Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling\nuser-invocable: false\n---\n\n# Next.js Best Practices\n\nApply these rules when writing or reviewing Next.js code.\n\n## File Conventions\n\nSee [file-conventions.md](./file-conventions.md) for:\n- Project structure and special files\n- Route segments (dynamic, catch-all, groups)\n- Parallel and intercepting routes\n- Middleware rename in v16 (middleware → proxy)\n\n## RSC Boundaries\n\nDetect invalid React Server Component patterns.\n\nSee [rsc-boundaries.md](./rsc-boundaries.md) for:\n- Async client component detection (invalid)\n- Non-serializable props detection\n- Server Action exceptions\n\n## Async Patterns\n\nNext.js 15+ async API changes.\n\nSee [async-patterns.md](./async-patterns.md) for:\n- Async `params` and `searchParams`\n- Async `cookies()` and `headers()`\n- Migration codemod\n\n## Runtime Selection\n\nSee [runtime-selection.md](./runtime-selection.md) for:\n- Default to Node.js runtime\n- When Edge runtime is appropriate\n\n## Directives\n\nSee [directives.md](./directives.md) for:\n- `'use client'`, `'use server'` (React)\n- `'use cache'` (Next.js)\n\n## Functions\n\nSee [functions.md](./functions.md) for:\n- Navigation hooks: `useRouter`, `usePathname`, `useSearchParams`, `useParams`\n- Server functions: `cookies`, `headers`, `draftMode`, `after`\n- Generate functions: `generateStaticParams`, `generateMetadata`\n\n## Error Handling\n\nSee [error-handling.md](./error-handling.md) for:\n- `error.tsx`, `global-error.tsx`, `not-found.tsx`\n- `redirect`, `permanentRedirect`, `notFound`\n- `forbidden`, `unauthorized` (auth errors)\n- `unstable_rethrow` for catch blocks\n\n## Data Patterns\n\nSee [data-patterns.md](./data-patterns.md) for:\n- Server Components vs Server Actions vs Route Handlers\n- Avoiding data waterfalls (`Promise.all`, Suspense, preload)\n- Client component data fetching\n\n## Route Handlers\n\nSee [route-handlers.md](./route-handlers.md) for:\n- `route.ts` basics\n- GET handler conflicts with `page.tsx`\n- Environment behavior (no React DOM)\n- When to use vs Server Actions\n\n## Metadata & OG Images\n\nSee [metadata.md](./metadata.md) for:\n- Static and dynamic metadata\n- `generateMetadata` function\n- OG image generation with `next/og`\n- File-based metadata conventions\n\n## Image Optimization\n\nSee [image.md](./image.md) for:\n- Always use `next/image` over `<img>`\n- Remote images configuration\n- Responsive `sizes` attribute\n- Blur placeholders\n- Priority loading for LCP\n\n## Font Optimization\n\nSee [font.md](./font.md) for:\n- `next/font` setup\n- Google Fonts, local fonts\n- Tailwind CSS integration\n- Preloading subsets\n\n## Bundling\n\nSee [bundling.md](./bundling.md) for:\n- Server-incompatible packages\n- CSS imports (not link tags)\n- Polyfills (already included)\n- ESM/CommonJS issues\n- Bundle analysis\n\n## Scripts\n\nSee [scripts.md](./scripts.md) for:\n- `next/script` vs native script tags\n- Inline scripts need `id`\n- Loading strategies\n- Google Analytics with `@next/third-parties`\n\n## Hydration Errors\n\nSee [hydration-error.md](./hydration-error.md) for:\n- Common causes (browser APIs, dates, invalid HTML)\n- Debugging with error overlay\n- Fixes for each cause\n\n## Suspense Boundaries\n\nSee [suspense-boundaries.md](./suspense-boundaries.md) for:\n- CSR bailout with `useSearchParams` and `usePathname`\n- Which hooks require Suspense boundaries\n\n## Parallel & Intercepting Routes\n\nSee [parallel-routes.md](./parallel-routes.md) for:\n- Modal patterns with `@slot` and `(.)` interceptors\n- `default.tsx` for fallbacks\n- Closing modals correctly with `router.back()`\n\n## Self-Hosting\n\nSee [self-hosting.md](./self-hosting.md) for:\n- `output: 'standalone'` for Docker\n- Cache handlers for multi-instance ISR\n- What works vs needs extra setup\n\n## Debug Tricks\n\nSee [debug-tricks.md](./debug-tricks.md) for:\n- MCP endpoint for AI-assisted debugging\n- Rebuild specific routes with `--debug-build-paths`\n\n",".agents/skills/next-best-practices/async-patterns.md":"# Async Patterns\n\nIn Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.\n\n## Async Params and SearchParams\n\nAlways type them as `Promise<...>` and await them.\n\n### Pages and Layouts\n\n```tsx\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default async function Page({ params }: Props) {\n  const { slug } = await params\n}\n```\n\n### Route Handlers\n\n```tsx\nexport async function GET(\n  request: Request,\n  { params }: { params: Promise<{ id: string }> }\n) {\n  const { id } = await params\n}\n```\n\n### SearchParams\n\n```tsx\ntype Props = {\n  params: Promise<{ slug: string }>\n  searchParams: Promise<{ query?: string }>\n}\n\nexport default async function Page({ params, searchParams }: Props) {\n  const { slug } = await params\n  const { query } = await searchParams\n}\n```\n\n### Synchronous Components\n\nUse `React.use()` for non-async components:\n\n```tsx\nimport { use } from 'react'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default function Page({ params }: Props) {\n  const { slug } = use(params)\n}\n```\n\n### generateMetadata\n\n```tsx\ntype Props = { params: Promise<{ slug: string }> }\n\nexport async function generateMetadata({ params }: Props): Promise<Metadata> {\n  const { slug } = await params\n  return { title: slug }\n}\n```\n\n## Async Cookies and Headers\n\n```tsx\nimport { cookies, headers } from 'next/headers'\n\nexport default async function Page() {\n  const cookieStore = await cookies()\n  const headersList = await headers()\n\n  const theme = cookieStore.get('theme')\n  const userAgent = headersList.get('user-agent')\n}\n```\n\n## Migration Codemod\n\n```bash\nnpx @next/codemod@latest next-async-request-api .\n```\n",".agents/skills/next-best-practices/bundling.md":"# Bundling\n\nFix common bundling issues with third-party packages.\n\n## Server-Incompatible Packages\n\nSome packages use browser APIs (`window`, `document`, `localStorage`) and fail in Server Components.\n\n### Error Signs\n\n```\nReferenceError: window is not defined\nReferenceError: document is not defined\nReferenceError: localStorage is not defined\nModule not found: Can't resolve 'fs'\n```\n\n### Solution 1: Mark as Client-Only\n\nIf the package is only needed on client:\n\n```tsx\n// Bad: Fails - package uses window\nimport SomeChart from 'some-chart-library'\n\nexport default function Page() {\n  return <SomeChart />\n}\n\n// Good: Use dynamic import with ssr: false\nimport dynamic from 'next/dynamic'\n\nconst SomeChart = dynamic(() => import('some-chart-library'), {\n  ssr: false,\n})\n\nexport default function Page() {\n  return <SomeChart />\n}\n```\n\n### Solution 2: Externalize from Server Bundle\n\nFor packages that should run on server but have bundling issues:\n\n```js\n// next.config.js\nmodule.exports = {\n  serverExternalPackages: ['problematic-package'],\n}\n```\n\nUse this for:\n- Packages with native bindings (sharp, bcrypt)\n- Packages that don't bundle well (some ORMs)\n- Packages with circular dependencies\n\n### Solution 3: Client Component Wrapper\n\nWrap the entire usage in a client component:\n\n```tsx\n// components/ChartWrapper.tsx\n'use client'\n\nimport { Chart } from 'chart-library'\n\nexport function ChartWrapper(props) {\n  return <Chart {...props} />\n}\n\n// app/page.tsx (server component)\nimport { ChartWrapper } from '@/components/ChartWrapper'\n\nexport default function Page() {\n  return <ChartWrapper data={data} />\n}\n```\n\n## CSS Imports\n\nImport CSS files instead of using `<link>` tags. Next.js handles bundling and optimization.\n\n```tsx\n// Bad: Manual link tag\n<link rel=\"stylesheet\" href=\"/styles.css\" />\n\n// Good: Import CSS\nimport './styles.css'\n\n// Good: CSS Modules\nimport styles from './Button.module.css'\n```\n\n## Polyfills\n\nNext.js includes common polyfills automatically. Don't load redundant ones from polyfill.io or similar CDNs.\n\nAlready included: `Array.from`, `Object.assign`, `Promise`, `fetch`, `Map`, `Set`, `Symbol`, `URLSearchParams`, and 50+ others.\n\n```tsx\n// Bad: Redundant polyfills\n<script src=\"https://polyfill.io/v3/polyfill.min.js?features=fetch,Promise,Array.from\" />\n\n// Good: Next.js includes these automatically\n```\n\n## ESM/CommonJS Issues\n\n### Error Signs\n\n```\nSyntaxError: Cannot use import statement outside a module\nError: require() of ES Module\nModule not found: ESM packages need to be imported\n```\n\n### Solution: Transpile Package\n\n```js\n// next.config.js\nmodule.exports = {\n  transpilePackages: ['some-esm-package', 'another-package'],\n}\n```\n\n## Common Problematic Packages\n\n| Package | Issue | Solution |\n|---------|-------|----------|\n| `sharp` | Native bindings | `serverExternalPackages: ['sharp']` |\n| `bcrypt` | Native bindings | `serverExternalPackages: ['bcrypt']` or use `bcryptjs` |\n| `canvas` | Native bindings | `serverExternalPackages: ['canvas']` |\n| `recharts` | Uses window | `dynamic(() => import('recharts'), { ssr: false })` |\n| `react-quill` | Uses document | `dynamic(() => import('react-quill'), { ssr: false })` |\n| `mapbox-gl` | Uses window | `dynamic(() => import('mapbox-gl'), { ssr: false })` |\n| `monaco-editor` | Uses window | `dynamic(() => import('@monaco-editor/react'), { ssr: false })` |\n| `lottie-web` | Uses document | `dynamic(() => import('lottie-react'), { ssr: false })` |\n\n## Bundle Analysis\n\nAnalyze bundle size with the built-in analyzer (Next.js 16.1+):\n\n```bash\nnext experimental-analyze\n```\n\nThis opens an interactive UI to:\n- Filter by route, environment (client/server), and type\n- Inspect module sizes and import chains\n- View treemap visualization\n\nSave output for comparison:\n\n```bash\nnext experimental-analyze --output\n# Output saved to .next/diagnostics/analyze\n```\n\nReference: https://nextjs.org/docs/app/guides/package-bundling\n\n## Migrating from Webpack to Turbopack\n\nTurbopack is the default bundler in Next.js 15+. If you have custom webpack config, migrate to Turbopack-compatible alternatives:\n\n```js\n// next.config.js\nmodule.exports = {\n  // Good: Works with Turbopack\n  serverExternalPackages: ['package'],\n  transpilePackages: ['package'],\n\n  // Bad: Webpack-only - migrate away from this\n  webpack: (config) => {\n    // custom webpack config\n  },\n}\n```\n\nReference: https://nextjs.org/docs/app/building-your-application/upgrading/from-webpack-to-turbopack\n",".agents/skills/next-best-practices/data-patterns.md":"# Data Patterns\n\nChoose the right data fetching pattern for each use case.\n\n## Decision Tree\n\n```\nNeed to fetch data?\n├── From a Server Component?\n│   └── Use: Fetch directly (no API needed)\n│\n├── From a Client Component?\n│   ├── Is it a mutation (POST/PUT/DELETE)?\n│   │   └── Use: Server Action\n│   └── Is it a read (GET)?\n│       └── Use: Route Handler OR pass from Server Component\n│\n├── Need external API access (webhooks, third parties)?\n│   └── Use: Route Handler\n│\n└── Need REST API for mobile app / external clients?\n    └── Use: Route Handler\n```\n\n## Pattern 1: Server Components (Preferred for Reads)\n\nFetch data directly in Server Components - no API layer needed.\n\n```tsx\n// app/users/page.tsx\nasync function UsersPage() {\n  // Direct database access - no API round-trip\n  const users = await db.user.findMany();\n\n  // Or fetch from external API\n  const posts = await fetch('https://api.example.com/posts').then(r => r.json());\n\n  return (\n    <ul>\n      {users.map(user => <li key={user.id}>{user.name}</li>)}\n    </ul>\n  );\n}\n```\n\n**Benefits**:\n- No API to maintain\n- No client-server waterfall\n- Secrets stay on server\n- Direct database access\n\n## Pattern 2: Server Actions (Preferred for Mutations)\n\nServer Actions are the recommended way to handle mutations.\n\n```tsx\n// app/actions.ts\n'use server';\n\nimport { revalidatePath } from 'next/cache';\n\nexport async function createPost(formData: FormData) {\n  const title = formData.get('title') as string;\n\n  await db.post.create({ data: { title } });\n\n  revalidatePath('/posts');\n}\n\nexport async function deletePost(id: string) {\n  await db.post.delete({ where: { id } });\n\n  revalidateTag('posts');\n}\n```\n\n```tsx\n// app/posts/new/page.tsx\nimport { createPost } from '@/app/actions';\n\nexport default function NewPost() {\n  return (\n    <form action={createPost}>\n      <input name=\"title\" required />\n      <button type=\"submit\">Create</button>\n    </form>\n  );\n}\n```\n\n**Benefits**:\n- End-to-end type safety\n- Progressive enhancement (works without JS)\n- Automatic request handling\n- Integrated with React transitions\n\n**Constraints**:\n- POST only (no GET caching semantics)\n- Internal use only (no external access)\n- Cannot return non-serializable data\n\n## Pattern 3: Route Handlers (APIs)\n\nUse Route Handlers when you need a REST API.\n\n```tsx\n// app/api/posts/route.ts\nimport { NextRequest, NextResponse } from 'next/server';\n\n// GET is cacheable\nexport async function GET(request: NextRequest) {\n  const posts = await db.post.findMany();\n  return NextResponse.json(posts);\n}\n\n// POST for mutations\nexport async function POST(request: NextRequest) {\n  const body = await request.json();\n  const post = await db.post.create({ data: body });\n  return NextResponse.json(post, { status: 201 });\n}\n```\n\n**When to use**:\n- External API access (mobile apps, third parties)\n- Webhooks from external services\n- GET endpoints that need HTTP caching\n- OpenAPI/Swagger documentation needed\n\n**When NOT to use**:\n- Internal data fetching (use Server Components)\n- Mutations from your UI (use Server Actions)\n\n## Avoiding Data Waterfalls\n\n### Problem: Sequential Fetches\n\n```tsx\n// Bad: Sequential waterfalls\nasync function Dashboard() {\n  const user = await getUser();        // Wait...\n  const posts = await getPosts();      // Then wait...\n  const comments = await getComments(); // Then wait...\n\n  return <div>...</div>;\n}\n```\n\n### Solution 1: Parallel Fetching with Promise.all\n\n```tsx\n// Good: Parallel fetching\nasync function Dashboard() {\n  const [user, posts, comments] = await Promise.all([\n    getUser(),\n    getPosts(),\n    getComments(),\n  ]);\n\n  return <div>...</div>;\n}\n```\n\n### Solution 2: Streaming with Suspense\n\n```tsx\n// Good: Show content progressively\nimport { Suspense } from 'react';\n\nasync function Dashboard() {\n  return (\n    <div>\n      <Suspense fallback={<UserSkeleton />}>\n        <UserSection />\n      </Suspense>\n      <Suspense fallback={<PostsSkeleton />}>\n        <PostsSection />\n      </Suspense>\n    </div>\n  );\n}\n\nasync function UserSection() {\n  const user = await getUser(); // Fetches independently\n  return <div>{user.name}</div>;\n}\n\nasync function PostsSection() {\n  const posts = await getPosts(); // Fetches independently\n  return <PostList posts={posts} />;\n}\n```\n\n### Solution 3: Preload Pattern\n\n```tsx\n// lib/data.ts\nimport { cache } from 'react';\n\nexport const getUser = cache(async (id: string) => {\n  return db.user.findUnique({ where: { id } });\n});\n\nexport const preloadUser = (id: string) => {\n  void getUser(id); // Fire and forget\n};\n```\n\n```tsx\n// app/user/[id]/page.tsx\nimport { getUser, preloadUser } from '@/lib/data';\n\nexport default async function UserPage({ params }) {\n  const { id } = await params;\n\n  // Start fetching early\n  preloadUser(id);\n\n  // Do other work...\n\n  // Data likely ready by now\n  const user = await getUser(id);\n  return <div>{user.name}</div>;\n}\n```\n\n## Client Component Data Fetching\n\nWhen Client Components need data:\n\n### Option 1: Pass from Server Component (Preferred)\n\n```tsx\n// Server Component\nasync function Page() {\n  const data = await fetchData();\n  return <ClientComponent initialData={data} />;\n}\n\n// Client Component\n'use client';\nfunction ClientComponent({ initialData }) {\n  const [data, setData] = useState(initialData);\n  // ...\n}\n```\n\n### Option 2: Fetch on Mount (When Necessary)\n\n```tsx\n'use client';\nimport { useEffect, useState } from 'react';\n\nfunction ClientComponent() {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    fetch('/api/data')\n      .then(r => r.json())\n      .then(setData);\n  }, []);\n\n  if (!data) return <Loading />;\n  return <div>{data.value}</div>;\n}\n```\n\n### Option 3: Server Action for Reads (Works But Not Ideal)\n\nServer Actions can be called from Client Components for reads, but this is not their intended purpose:\n\n```tsx\n'use client';\nimport { getData } from './actions';\nimport { useEffect, useState } from 'react';\n\nfunction ClientComponent() {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    getData().then(setData);\n  }, []);\n\n  return <div>{data?.value}</div>;\n}\n```\n\n**Note**: Server Actions always use POST, so no HTTP caching. Prefer Route Handlers for cacheable reads.\n\n## Quick Reference\n\n| Pattern | Use Case | HTTP Method | Caching |\n|---------|----------|-------------|---------|\n| Server Component fetch | Internal reads | Any | Full Next.js caching |\n| Server Action | Mutations, form submissions | POST only | No |\n| Route Handler | External APIs, webhooks | Any | GET can be cached |\n| Client fetch to API | Client-side reads | Any | HTTP cache headers |\n",".agents/skills/next-best-practices/debug-tricks.md":"# Debug Tricks\n\nTricks to speed up debugging Next.js applications.\n\n## MCP Endpoint (Dev Server)\n\nNext.js exposes a `/_next/mcp` endpoint in development for AI-assisted debugging via MCP (Model Context Protocol).\n\n- **Next.js 16+**: Enabled by default, use `next-devtools-mcp`\n- **Next.js < 16**: Requires `experimental.mcpServer: true` in next.config.js\n\nReference: https://nextjs.org/docs/app/guides/mcp\n\n**Important**: Find the actual port of the running Next.js dev server (check terminal output or `package.json` scripts). Don't assume port 3000.\n\n### Request Format\n\nThe endpoint uses JSON-RPC 2.0 over HTTP POST:\n\n```bash\ncurl -X POST http://localhost:<port>/_next/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": \"1\",\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"<tool-name>\",\n      \"arguments\": {}\n    }\n  }'\n```\n\n### Available Tools\n\n#### `get_errors`\nGet current errors from dev server (build errors, runtime errors with source-mapped stacks):\n```json\n{ \"name\": \"get_errors\", \"arguments\": {} }\n```\n\n#### `get_routes`\nDiscover all routes by scanning filesystem:\n```json\n{ \"name\": \"get_routes\", \"arguments\": {} }\n// Optional: { \"name\": \"get_routes\", \"arguments\": { \"routerType\": \"app\" } }\n```\nReturns: `{ \"appRouter\": [\"/\", \"/api/users/[id]\", ...], \"pagesRouter\": [...] }`\n\n#### `get_project_metadata`\nGet project path and dev server URL:\n```json\n{ \"name\": \"get_project_metadata\", \"arguments\": {} }\n```\nReturns: `{ \"projectPath\": \"/path/to/project\", \"devServerUrl\": \"http://localhost:3000\" }`\n\n#### `get_page_metadata`\nGet runtime metadata about current page render (requires active browser session):\n```json\n{ \"name\": \"get_page_metadata\", \"arguments\": {} }\n```\nReturns segment trie data showing layouts, boundaries, and page components.\n\n#### `get_logs`\nGet path to Next.js development log file:\n```json\n{ \"name\": \"get_logs\", \"arguments\": {} }\n```\nReturns path to `<distDir>/logs/next-development.log`\n\n#### `get_server_action_by_id`\nLocate a Server Action by ID:\n```json\n{ \"name\": \"get_server_action_by_id\", \"arguments\": { \"actionId\": \"<action-id>\" } }\n```\n\n### Example: Get Errors\n\n```bash\ncurl -X POST http://localhost:<port>/_next/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"method\":\"tools/call\",\"params\":{\"name\":\"get_errors\",\"arguments\":{}}}'\n```\n\n## Rebuild Specific Routes (Next.js 16+)\n\nUse `--debug-build-paths` to rebuild only specific routes instead of the entire app:\n\n```bash\n# Rebuild a specific route\nnext build --debug-build-paths \"/dashboard\"\n\n# Rebuild routes matching a glob\nnext build --debug-build-paths \"/api/*\"\n\n# Dynamic routes\nnext build --debug-build-paths \"/blog/[slug]\"\n```\n\nUse this to:\n- Quickly verify a build fix without full rebuild\n- Debug static generation issues for specific pages\n- Iterate faster on build errors\n",".agents/skills/next-best-practices/directives.md":"# Directives\n\n## React Directives\n\nThese are React directives, not Next.js specific.\n\n### `'use client'`\n\nMarks a component as a Client Component. Required for:\n- React hooks (`useState`, `useEffect`, etc.)\n- Event handlers (`onClick`, `onChange`)\n- Browser APIs (`window`, `localStorage`)\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\n\nexport function Counter() {\n  const [count, setCount] = useState(0)\n  return <button onClick={() => setCount(count + 1)}>{count}</button>\n}\n```\n\nReference: https://react.dev/reference/rsc/use-client\n\n### `'use server'`\n\nMarks a function as a Server Action. Can be passed to Client Components.\n\n```tsx\n'use server'\n\nexport async function submitForm(formData: FormData) {\n  // Runs on server\n}\n```\n\nOr inline within a Server Component:\n\n```tsx\nexport default function Page() {\n  async function submit() {\n    'use server'\n    // Runs on server\n  }\n  return <form action={submit}>...</form>\n}\n```\n\nReference: https://react.dev/reference/rsc/use-server\n\n---\n\n## Next.js Directive\n\n### `'use cache'`\n\nMarks a function or component for caching. Part of Next.js Cache Components.\n\n```tsx\n'use cache'\n\nexport async function getCachedData() {\n  return await fetchData()\n}\n```\n\nRequires `cacheComponents: true` in `next.config.ts`.\n\nFor detailed usage including cache profiles, `cacheLife()`, `cacheTag()`, and `updateTag()`, see the `next-cache-components` skill.\n\nReference: https://nextjs.org/docs/app/api-reference/directives/use-cache\n",".agents/skills/next-best-practices/error-handling.md":"# Error Handling\n\nHandle errors gracefully in Next.js applications.\n\nReference: https://nextjs.org/docs/app/getting-started/error-handling\n\n## Error Boundaries\n\n### `error.tsx`\n\nCatches errors in a route segment and its children:\n\n```tsx\n'use client'\n\nexport default function Error({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  return (\n    <div>\n      <h2>Something went wrong!</h2>\n      <button onClick={() => reset()}>Try again</button>\n    </div>\n  )\n}\n```\n\n**Important:** `error.tsx` must be a Client Component.\n\n### `global-error.tsx`\n\nCatches errors in root layout:\n\n```tsx\n'use client'\n\nexport default function GlobalError({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  return (\n    <html>\n      <body>\n        <h2>Something went wrong!</h2>\n        <button onClick={() => reset()}>Try again</button>\n      </body>\n    </html>\n  )\n}\n```\n\n**Important:** Must include `<html>` and `<body>` tags.\n\n## Server Actions: Navigation API Gotcha\n\n**Do NOT wrap navigation APIs in try-catch.** They throw special errors that Next.js handles internally.\n\nReference: https://nextjs.org/docs/app/api-reference/functions/redirect#behavior\n\n```tsx\n'use server'\n\nimport { redirect } from 'next/navigation'\nimport { notFound } from 'next/navigation'\n\n// Bad: try-catch catches the navigation \"error\"\nasync function createPost(formData: FormData) {\n  try {\n    const post = await db.post.create({ ... })\n    redirect(`/posts/${post.id}`)  // This throws!\n  } catch (error) {\n    // redirect() throw is caught here - navigation fails!\n    return { error: 'Failed to create post' }\n  }\n}\n\n// Good: Call navigation APIs outside try-catch\nasync function createPost(formData: FormData) {\n  let post\n  try {\n    post = await db.post.create({ ... })\n  } catch (error) {\n    return { error: 'Failed to create post' }\n  }\n  redirect(`/posts/${post.id}`)  // Outside try-catch\n}\n\n// Good: Re-throw navigation errors\nasync function createPost(formData: FormData) {\n  try {\n    const post = await db.post.create({ ... })\n    redirect(`/posts/${post.id}`)\n  } catch (error) {\n    if (error instanceof Error && error.message === 'NEXT_REDIRECT') {\n      throw error  // Re-throw navigation errors\n    }\n    return { error: 'Failed to create post' }\n  }\n}\n```\n\nSame applies to:\n- `redirect()` - 307 temporary redirect\n- `permanentRedirect()` - 308 permanent redirect\n- `notFound()` - 404 not found\n- `forbidden()` - 403 forbidden\n- `unauthorized()` - 401 unauthorized\n\nUse `unstable_rethrow()` to re-throw these errors in catch blocks:\n\n```tsx\nimport { unstable_rethrow } from 'next/navigation'\n\nasync function action() {\n  try {\n    // ...\n    redirect('/success')\n  } catch (error) {\n    unstable_rethrow(error) // Re-throws Next.js internal errors\n    return { error: 'Something went wrong' }\n  }\n}\n```\n\n## Redirects\n\n```tsx\nimport { redirect, permanentRedirect } from 'next/navigation'\n\n// 307 Temporary - use for most cases\nredirect('/new-path')\n\n// 308 Permanent - use for URL migrations (cached by browsers)\npermanentRedirect('/new-url')\n```\n\n## Auth Errors\n\nTrigger auth-related error pages:\n\n```tsx\nimport { forbidden, unauthorized } from 'next/navigation'\n\nasync function Page() {\n  const session = await getSession()\n\n  if (!session) {\n    unauthorized() // Renders unauthorized.tsx (401)\n  }\n\n  if (!session.hasAccess) {\n    forbidden() // Renders forbidden.tsx (403)\n  }\n\n  return <Dashboard />\n}\n```\n\nCreate corresponding error pages:\n\n```tsx\n// app/forbidden.tsx\nexport default function Forbidden() {\n  return <div>You don't have access to this resource</div>\n}\n\n// app/unauthorized.tsx\nexport default function Unauthorized() {\n  return <div>Please log in to continue</div>\n}\n```\n\n## Not Found\n\n### `not-found.tsx`\n\nCustom 404 page for a route segment:\n\n```tsx\nexport default function NotFound() {\n  return (\n    <div>\n      <h2>Not Found</h2>\n      <p>Could not find the requested resource</p>\n    </div>\n  )\n}\n```\n\n### Triggering Not Found\n\n```tsx\nimport { notFound } from 'next/navigation'\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const post = await getPost(id)\n\n  if (!post) {\n    notFound()  // Renders closest not-found.tsx\n  }\n\n  return <div>{post.title}</div>\n}\n```\n\n## Error Hierarchy\n\nErrors bubble up to the nearest error boundary:\n\n```\napp/\n├── error.tsx           # Catches errors from all children\n├── blog/\n│   ├── error.tsx       # Catches errors in /blog/*\n│   └── [slug]/\n│       ├── error.tsx   # Catches errors in /blog/[slug]\n│       └── page.tsx\n└── layout.tsx          # Errors here go to global-error.tsx\n```\n",".agents/skills/next-best-practices/file-conventions.md":"# File Conventions\n\nNext.js App Router uses file-based routing with special file conventions.\n\n## Project Structure\n\nReference: https://nextjs.org/docs/app/getting-started/project-structure\n\n```\napp/\n├── layout.tsx          # Root layout (required)\n├── page.tsx            # Home page (/)\n├── loading.tsx         # Loading UI\n├── error.tsx           # Error UI\n├── not-found.tsx       # 404 UI\n├── global-error.tsx    # Global error UI\n├── route.ts            # API endpoint\n├── template.tsx        # Re-rendered layout\n├── default.tsx         # Parallel route fallback\n├── blog/\n│   ├── page.tsx        # /blog\n│   └── [slug]/\n│       └── page.tsx    # /blog/:slug\n└── (group)/            # Route group (no URL impact)\n    └── page.tsx\n```\n\n## Special Files\n\n| File | Purpose |\n|------|---------|\n| `page.tsx` | UI for a route segment |\n| `layout.tsx` | Shared UI for segment and children |\n| `loading.tsx` | Loading UI (Suspense boundary) |\n| `error.tsx` | Error UI (Error boundary) |\n| `not-found.tsx` | 404 UI |\n| `route.ts` | API endpoint |\n| `template.tsx` | Like layout but re-renders on navigation |\n| `default.tsx` | Fallback for parallel routes |\n\n## Route Segments\n\n```\napp/\n├── blog/               # Static segment: /blog\n├── [slug]/             # Dynamic segment: /:slug\n├── [...slug]/          # Catch-all: /a/b/c\n├── [[...slug]]/        # Optional catch-all: / or /a/b/c\n└── (marketing)/        # Route group (ignored in URL)\n```\n\n## Parallel Routes\n\n```\napp/\n├── @analytics/\n│   └── page.tsx\n├── @sidebar/\n│   └── page.tsx\n└── layout.tsx          # Receives { analytics, sidebar } as props\n```\n\n## Intercepting Routes\n\n```\napp/\n├── feed/\n│   └── page.tsx\n├── @modal/\n│   └── (.)photo/[id]/  # Intercepts /photo/[id] from /feed\n│       └── page.tsx\n└── photo/[id]/\n    └── page.tsx\n```\n\nConventions:\n- `(.)` - same level\n- `(..)` - one level up\n- `(..)(..)` - two levels up\n- `(...)` - from root\n\n## Private Folders\n\n```\napp/\n├── _components/        # Private folder (not a route)\n│   └── Button.tsx\n└── page.tsx\n```\n\nPrefix with `_` to exclude from routing.\n\n## Middleware / Proxy\n\n### Next.js 14-15: `middleware.ts`\n\n```ts\n// middleware.ts (root of project)\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\n\nexport function middleware(request: NextRequest) {\n  // Auth, redirects, rewrites, etc.\n  return NextResponse.next();\n}\n\nexport const config = {\n  matcher: ['/dashboard/:path*', '/api/:path*'],\n};\n```\n\n### Next.js 16+: `proxy.ts`\n\nRenamed for clarity - same capabilities, different names:\n\n```ts\n// proxy.ts (root of project)\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\n\nexport function proxy(request: NextRequest) {\n  // Same logic as middleware\n  return NextResponse.next();\n}\n\nexport const proxyConfig = {\n  matcher: ['/dashboard/:path*', '/api/:path*'],\n};\n```\n\n| Version | File | Export | Config |\n|---------|------|--------|--------|\n| v14-15 | `middleware.ts` | `middleware()` | `config` |\n| v16+ | `proxy.ts` | `proxy()` | `proxyConfig` |\n\n**Migration**: Run `npx @next/codemod@latest upgrade` to auto-rename.\n\n## File Conventions Reference\n\nReference: https://nextjs.org/docs/app/api-reference/file-conventions\n",".agents/skills/next-best-practices/font.md":"# Font Optimization\n\nUse `next/font` for automatic font optimization with zero layout shift.\n\n## Google Fonts\n\n```tsx\n// app/layout.tsx\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({ subsets: ['latin'] })\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\" className={inter.className}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n## Multiple Fonts\n\n```tsx\nimport { Inter, Roboto_Mono } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  variable: '--font-inter',\n})\n\nconst robotoMono = Roboto_Mono({\n  subsets: ['latin'],\n  variable: '--font-roboto-mono',\n})\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\" className={`${inter.variable} ${robotoMono.variable}`}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\nUse in CSS:\n```css\nbody {\n  font-family: var(--font-inter);\n}\n\ncode {\n  font-family: var(--font-roboto-mono);\n}\n```\n\n## Font Weights and Styles\n\n```tsx\n// Single weight\nconst inter = Inter({\n  subsets: ['latin'],\n  weight: '400',\n})\n\n// Multiple weights\nconst inter = Inter({\n  subsets: ['latin'],\n  weight: ['400', '500', '700'],\n})\n\n// Variable font (recommended) - includes all weights\nconst inter = Inter({\n  subsets: ['latin'],\n  // No weight needed - variable fonts support all weights\n})\n\n// With italic\nconst inter = Inter({\n  subsets: ['latin'],\n  style: ['normal', 'italic'],\n})\n```\n\n## Local Fonts\n\n```tsx\nimport localFont from 'next/font/local'\n\nconst myFont = localFont({\n  src: './fonts/MyFont.woff2',\n})\n\n// Multiple files for different weights\nconst myFont = localFont({\n  src: [\n    {\n      path: './fonts/MyFont-Regular.woff2',\n      weight: '400',\n      style: 'normal',\n    },\n    {\n      path: './fonts/MyFont-Bold.woff2',\n      weight: '700',\n      style: 'normal',\n    },\n  ],\n})\n\n// Variable font\nconst myFont = localFont({\n  src: './fonts/MyFont-Variable.woff2',\n  variable: '--font-my-font',\n})\n```\n\n## Tailwind CSS Integration\n\n```tsx\n// app/layout.tsx\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  variable: '--font-inter',\n})\n\nexport default function RootLayout({ children }) {\n  return (\n    <html lang=\"en\" className={inter.variable}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    extend: {\n      fontFamily: {\n        sans: ['var(--font-inter)'],\n      },\n    },\n  },\n}\n```\n\n## Preloading Subsets\n\nOnly load needed character subsets:\n\n```tsx\n// Latin only (most common)\nconst inter = Inter({ subsets: ['latin'] })\n\n// Multiple subsets\nconst inter = Inter({ subsets: ['latin', 'latin-ext', 'cyrillic'] })\n```\n\n## Display Strategy\n\nControl font loading behavior:\n\n```tsx\nconst inter = Inter({\n  subsets: ['latin'],\n  display: 'swap', // Default - shows fallback, swaps when loaded\n})\n\n// Options:\n// 'auto' - browser decides\n// 'block' - short block period, then swap\n// 'swap' - immediate fallback, swap when ready (recommended)\n// 'fallback' - short block, short swap, then fallback\n// 'optional' - short block, no swap (use if font is optional)\n```\n\n## Don't Use Manual Font Links\n\nAlways use `next/font` instead of `<link>` tags for Google Fonts.\n\n```tsx\n// Bad: Manual link tag (blocks rendering, no optimization)\n<link href=\"https://fonts.googleapis.com/css2?family=Inter\" rel=\"stylesheet\" />\n\n// Bad: Missing display and preconnect\n<link href=\"https://fonts.googleapis.com/css2?family=Inter\" rel=\"stylesheet\" />\n\n// Good: Use next/font (self-hosted, zero layout shift)\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({ subsets: ['latin'] })\n```\n\n## Common Mistakes\n\n```tsx\n// Bad: Importing font in every component\n// components/Button.tsx\nimport { Inter } from 'next/font/google'\nconst inter = Inter({ subsets: ['latin'] }) // Creates new instance each time!\n\n// Good: Import once in layout, use CSS variable\n// app/layout.tsx\nconst inter = Inter({ subsets: ['latin'], variable: '--font-inter' })\n\n// Bad: Using @import in CSS (blocks rendering)\n/* globals.css */\n@import url('https://fonts.googleapis.com/css2?family=Inter');\n\n// Good: Use next/font (self-hosted, no network request)\nimport { Inter } from 'next/font/google'\n\n// Bad: Loading all weights when only using a few\nconst inter = Inter({ subsets: ['latin'] }) // Loads all weights\n\n// Good: Specify only needed weights (for non-variable fonts)\nconst inter = Inter({ subsets: ['latin'], weight: ['400', '700'] })\n\n// Bad: Missing subset - loads all characters\nconst inter = Inter({})\n\n// Good: Always specify subset\nconst inter = Inter({ subsets: ['latin'] })\n```\n\n## Font in Specific Components\n\n```tsx\n// For component-specific fonts, export from a shared file\n// lib/fonts.ts\nimport { Inter, Playfair_Display } from 'next/font/google'\n\nexport const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })\nexport const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-playfair' })\n\n// components/Heading.tsx\nimport { playfair } from '@/lib/fonts'\n\nexport function Heading({ children }) {\n  return <h1 className={playfair.className}>{children}</h1>\n}\n```\n",".agents/skills/next-best-practices/functions.md":"# Functions\n\nNext.js function APIs.\n\nReference: https://nextjs.org/docs/app/api-reference/functions\n\n## Navigation Hooks (Client)\n\n| Hook | Purpose | Reference |\n|------|---------|-----------|\n| `useRouter` | Programmatic navigation (`push`, `replace`, `back`, `refresh`) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-router) |\n| `usePathname` | Get current pathname | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-pathname) |\n| `useSearchParams` | Read URL search parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-search-params) |\n| `useParams` | Access dynamic route parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-params) |\n| `useSelectedLayoutSegment` | Active child segment (one level) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment) |\n| `useSelectedLayoutSegments` | All active segments below layout | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segments) |\n| `useLinkStatus` | Check link prefetch status | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-link-status) |\n| `useReportWebVitals` | Report Core Web Vitals metrics | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals) |\n\n## Server Functions\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `cookies` | Read/write cookies | [Docs](https://nextjs.org/docs/app/api-reference/functions/cookies) |\n| `headers` | Read request headers | [Docs](https://nextjs.org/docs/app/api-reference/functions/headers) |\n| `draftMode` | Enable preview of unpublished CMS content | [Docs](https://nextjs.org/docs/app/api-reference/functions/draft-mode) |\n| `after` | Run code after response finishes streaming | [Docs](https://nextjs.org/docs/app/api-reference/functions/after) |\n| `connection` | Wait for connection before dynamic rendering | [Docs](https://nextjs.org/docs/app/api-reference/functions/connection) |\n| `userAgent` | Parse User-Agent header | [Docs](https://nextjs.org/docs/app/api-reference/functions/userAgent) |\n\n## Generate Functions\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `generateStaticParams` | Pre-render dynamic routes at build time | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) |\n| `generateMetadata` | Dynamic metadata | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) |\n| `generateViewport` | Dynamic viewport config | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-viewport) |\n| `generateSitemaps` | Multiple sitemaps for large sites | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-sitemaps) |\n| `generateImageMetadata` | Multiple OG images per route | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-image-metadata) |\n\n## Request/Response\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `NextRequest` | Extended Request with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-request) |\n| `NextResponse` | Extended Response with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-response) |\n| `ImageResponse` | Generate OG images | [Docs](https://nextjs.org/docs/app/api-reference/functions/image-response) |\n\n## Common Examples\n\n### Navigation\n\nUse `next/link` for internal navigation instead of `<a>` tags.\n\n```tsx\n// Bad: Plain anchor tag\n<a href=\"/about\">About</a>\n\n// Good: Next.js Link\nimport Link from 'next/link'\n\n<Link href=\"/about\">About</Link>\n```\n\nActive link styling:\n\n```tsx\n'use client'\n\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\n\nexport function NavLink({ href, children }) {\n  const pathname = usePathname()\n\n  return (\n    <Link href={href} className={pathname === href ? 'active' : ''}>\n      {children}\n    </Link>\n  )\n}\n```\n\n### Static Generation\n\n```tsx\n// app/blog/[slug]/page.tsx\nexport async function generateStaticParams() {\n  const posts = await getPosts()\n  return posts.map((post) => ({ slug: post.slug }))\n}\n```\n\n### After Response\n\n```tsx\nimport { after } from 'next/server'\n\nexport async function POST(request: Request) {\n  const data = await processRequest(request)\n\n  after(async () => {\n    await logAnalytics(data)\n  })\n\n  return Response.json({ success: true })\n}\n```\n",".agents/skills/next-best-practices/hydration-error.md":"# Hydration Errors\n\nDiagnose and fix React hydration mismatch errors.\n\n## Error Signs\n\n- \"Hydration failed because the initial UI does not match\"\n- \"Text content does not match server-rendered HTML\"\n\n## Debugging\n\nIn development, click the hydration error to see the server/client diff.\n\n## Common Causes and Fixes\n\n### Browser-only APIs\n\n```tsx\n// Bad: Causes mismatch - window doesn't exist on server\n<div>{window.innerWidth}</div>\n\n// Good: Use client component with mounted check\n'use client'\nimport { useState, useEffect } from 'react'\n\nexport function ClientOnly({ children }: { children: React.ReactNode }) {\n  const [mounted, setMounted] = useState(false)\n  useEffect(() => setMounted(true), [])\n  return mounted ? children : null\n}\n```\n\n### Date/Time Rendering\n\nServer and client may be in different timezones:\n\n```tsx\n// Bad: Causes mismatch\n<span>{new Date().toLocaleString()}</span>\n\n// Good: Render on client only\n'use client'\nconst [time, setTime] = useState<string>()\nuseEffect(() => setTime(new Date().toLocaleString()), [])\n```\n\n### Random Values or IDs\n\n```tsx\n// Bad: Random values differ between server and client\n<div id={Math.random().toString()}>\n\n// Good: Use useId hook\nimport { useId } from 'react'\n\nfunction Input() {\n  const id = useId()\n  return <input id={id} />\n}\n```\n\n### Invalid HTML Nesting\n\n```tsx\n// Bad: Invalid - div inside p\n<p><div>Content</div></p>\n\n// Bad: Invalid - p inside p\n<p><p>Nested</p></p>\n\n// Good: Valid nesting\n<div><p>Content</p></div>\n```\n\n### Third-party Scripts\n\nScripts that modify DOM during hydration.\n\n```tsx\n// Good: Use next/script with afterInteractive\nimport Script from 'next/script'\n\nexport default function Page() {\n  return (\n    <Script\n      src=\"https://example.com/script.js\"\n      strategy=\"afterInteractive\"\n    />\n  )\n}\n```\n",".agents/skills/next-best-practices/image.md":"# Image Optimization\n\nUse `next/image` for automatic image optimization.\n\n## Always Use next/image\n\n```tsx\n// Bad: Avoid native img\n<img src=\"/hero.png\" alt=\"Hero\" />\n\n// Good: Use next/image\nimport Image from 'next/image'\n<Image src=\"/hero.png\" alt=\"Hero\" width={800} height={400} />\n```\n\n## Required Props\n\nImages need explicit dimensions to prevent layout shift:\n\n```tsx\n// Local images - dimensions inferred automatically\nimport heroImage from './hero.png'\n<Image src={heroImage} alt=\"Hero\" />\n\n// Remote images - must specify width/height\n<Image src=\"https://example.com/image.jpg\" alt=\"Hero\" width={800} height={400} />\n\n// Or use fill for parent-relative sizing\n<div style={{ position: 'relative', width: '100%', height: 400 }}>\n  <Image src=\"/hero.png\" alt=\"Hero\" fill style={{ objectFit: 'cover' }} />\n</div>\n```\n\n## Remote Images Configuration\n\nRemote domains must be configured in `next.config.js`:\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: 'https',\n        hostname: 'example.com',\n        pathname: '/images/**',\n      },\n      {\n        protocol: 'https',\n        hostname: '*.cdn.com', // Wildcard subdomain\n      },\n    ],\n  },\n}\n```\n\n## Responsive Images\n\nUse `sizes` to tell the browser which size to download:\n\n```tsx\n// Full-width hero\n<Image\n  src=\"/hero.png\"\n  alt=\"Hero\"\n  fill\n  sizes=\"100vw\"\n/>\n\n// Responsive grid (3 columns on desktop, 1 on mobile)\n<Image\n  src=\"/card.png\"\n  alt=\"Card\"\n  fill\n  sizes=\"(max-width: 768px) 100vw, 33vw\"\n/>\n\n// Fixed sidebar image\n<Image\n  src=\"/avatar.png\"\n  alt=\"Avatar\"\n  width={200}\n  height={200}\n  sizes=\"200px\"\n/>\n```\n\n## Blur Placeholder\n\nPrevent layout shift with placeholders:\n\n```tsx\n// Local images - automatic blur hash\nimport heroImage from './hero.png'\n<Image src={heroImage} alt=\"Hero\" placeholder=\"blur\" />\n\n// Remote images - provide blurDataURL\n<Image\n  src=\"https://example.com/image.jpg\"\n  alt=\"Hero\"\n  width={800}\n  height={400}\n  placeholder=\"blur\"\n  blurDataURL=\"data:image/jpeg;base64,/9j/4AAQSkZJRg...\"\n/>\n\n// Or use color placeholder\n<Image\n  src=\"https://example.com/image.jpg\"\n  alt=\"Hero\"\n  width={800}\n  height={400}\n  placeholder=\"empty\"\n  style={{ backgroundColor: '#e0e0e0' }}\n/>\n```\n\n## Priority Loading\n\nUse `priority` for above-the-fold images (LCP):\n\n```tsx\n// Hero image - loads immediately\n<Image src=\"/hero.png\" alt=\"Hero\" fill priority />\n\n// Below-fold images - lazy loaded by default (no priority needed)\n<Image src=\"/card.png\" alt=\"Card\" width={400} height={300} />\n```\n\n## Common Mistakes\n\n```tsx\n// Bad: Missing sizes with fill - downloads largest image\n<Image src=\"/hero.png\" alt=\"Hero\" fill />\n\n// Good: Add sizes for proper responsive behavior\n<Image src=\"/hero.png\" alt=\"Hero\" fill sizes=\"100vw\" />\n\n// Bad: Using width/height for aspect ratio only\n<Image src=\"/hero.png\" alt=\"Hero\" width={16} height={9} />\n\n// Good: Use actual display dimensions or fill with sizes\n<Image src=\"/hero.png\" alt=\"Hero\" fill sizes=\"100vw\" style={{ objectFit: 'cover' }} />\n\n// Bad: Remote image without config\n<Image src=\"https://untrusted.com/image.jpg\" alt=\"Image\" width={400} height={300} />\n// Error: Invalid src prop, hostname not configured\n\n// Good: Add hostname to next.config.js remotePatterns\n```\n\n## Static Export\n\nWhen using `output: 'export'`, use `unoptimized` or custom loader:\n\n```tsx\n// Option 1: Disable optimization\n<Image src=\"/hero.png\" alt=\"Hero\" width={800} height={400} unoptimized />\n\n// Option 2: Global config\n// next.config.js\nmodule.exports = {\n  output: 'export',\n  images: { unoptimized: true },\n}\n\n// Option 3: Custom loader (Cloudinary, Imgix, etc.)\nconst cloudinaryLoader = ({ src, width, quality }) => {\n  return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`\n}\n\n<Image loader={cloudinaryLoader} src=\"sample.jpg\" alt=\"Sample\" width={800} height={400} />\n```\n",".agents/skills/next-best-practices/metadata.md":"# Metadata\n\nAdd SEO metadata to Next.js pages using the Metadata API.\n\n## Important: Server Components Only\n\nThe `metadata` object and `generateMetadata` function are **only supported in Server Components**. They cannot be used in Client Components.\n\nIf the target page has `'use client'`:\n1. Remove `'use client'` if possible, move client logic to child components\n2. Or extract metadata to a parent Server Component layout\n3. Or split the file: Server Component with metadata imports Client Components\n\n## Static Metadata\n\n```tsx\nimport type { Metadata } from 'next'\n\nexport const metadata: Metadata = {\n  title: 'Page Title',\n  description: 'Page description for search engines',\n}\n```\n\n## Dynamic Metadata\n\n```tsx\nimport type { Metadata } from 'next'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport async function generateMetadata({ params }: Props): Promise<Metadata> {\n  const { slug } = await params\n  const post = await getPost(slug)\n  return { title: post.title, description: post.description }\n}\n```\n\n## Avoid Duplicate Fetches\n\nUse React `cache()` when the same data is needed for both metadata and page:\n\n```tsx\nimport { cache } from 'react'\n\nexport const getPost = cache(async (slug: string) => {\n  return await db.posts.findFirst({ where: { slug } })\n})\n```\n\n## Viewport\n\nSeparate from metadata for streaming support:\n\n```tsx\nimport type { Viewport } from 'next'\n\nexport const viewport: Viewport = {\n  width: 'device-width',\n  initialScale: 1,\n  themeColor: '#000000',\n}\n\n// Or dynamic\nexport function generateViewport({ params }): Viewport {\n  return { themeColor: getThemeColor(params) }\n}\n```\n\n## Title Templates\n\nIn root layout for consistent naming:\n\n```tsx\nexport const metadata: Metadata = {\n  title: { default: 'Site Name', template: '%s | Site Name' },\n}\n```\n\n## Metadata File Conventions\n\nReference: https://nextjs.org/docs/app/getting-started/project-structure#metadata-file-conventions\n\nPlace these files in `app/` directory (or route segments):\n\n| File | Purpose |\n|------|---------|\n| `favicon.ico` | Favicon |\n| `icon.png` / `icon.svg` | App icon |\n| `apple-icon.png` | Apple app icon |\n| `opengraph-image.png` | OG image |\n| `twitter-image.png` | Twitter card image |\n| `sitemap.ts` / `sitemap.xml` | Sitemap (use `generateSitemaps` for multiple) |\n| `robots.ts` / `robots.txt` | Robots directives |\n| `manifest.ts` / `manifest.json` | Web app manifest |\n\n## SEO Best Practice: Static Files Are Often Enough\n\nFor most sites, **static metadata files provide excellent SEO coverage**:\n\n```\napp/\n├── favicon.ico\n├── opengraph-image.png     # Works for both OG and Twitter\n├── sitemap.ts\n├── robots.ts\n└── layout.tsx              # With title/description metadata\n```\n\n**Tips:**\n- A single `opengraph-image.png` covers both Open Graph and Twitter (Twitter falls back to OG)\n- Static `title` and `description` in layout metadata is sufficient for most pages\n- Only use dynamic `generateMetadata` when content varies per page\n\n---\n\n# OG Image Generation\n\nGenerate dynamic Open Graph images using `next/og`.\n\n## Important Rules\n\n1. **Use `next/og`** - not `@vercel/og` (it's built into Next.js)\n2. **No searchParams** - OG images can't access search params, use route params instead\n3. **Avoid Edge runtime** - Use default Node.js runtime\n\n```tsx\n// Good\nimport { ImageResponse } from 'next/og'\n\n// Bad\n// import { ImageResponse } from '@vercel/og'\n// export const runtime = 'edge'\n```\n\n## Basic OG Image\n\n```tsx\n// app/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport const alt = 'Site Name'\nexport const size = { width: 1200, height: 630 }\nexport const contentType = 'image/png'\n\nexport default function Image() {\n  return new ImageResponse(\n    (\n      <div\n        style={{\n          fontSize: 128,\n          background: 'white',\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n        }}\n      >\n        Hello World\n      </div>\n    ),\n    { ...size }\n  )\n}\n```\n\n## Dynamic OG Image\n\n```tsx\n// app/blog/[slug]/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport const alt = 'Blog Post'\nexport const size = { width: 1200, height: 630 }\nexport const contentType = 'image/png'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default async function Image({ params }: Props) {\n  const { slug } = await params\n  const post = await getPost(slug)\n\n  return new ImageResponse(\n    (\n      <div\n        style={{\n          fontSize: 48,\n          background: 'linear-gradient(to bottom, #1a1a1a, #333)',\n          color: 'white',\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          flexDirection: 'column',\n          alignItems: 'center',\n          justifyContent: 'center',\n          padding: 48,\n        }}\n      >\n        <div style={{ fontSize: 64, fontWeight: 'bold' }}>{post.title}</div>\n        <div style={{ marginTop: 24, opacity: 0.8 }}>{post.description}</div>\n      </div>\n    ),\n    { ...size }\n  )\n}\n```\n\n## Custom Fonts\n\n```tsx\nimport { ImageResponse } from 'next/og'\nimport { join } from 'path'\nimport { readFile } from 'fs/promises'\n\nexport default async function Image() {\n  const fontPath = join(process.cwd(), 'assets/fonts/Inter-Bold.ttf')\n  const fontData = await readFile(fontPath)\n\n  return new ImageResponse(\n    (\n      <div style={{ fontFamily: 'Inter', fontSize: 64 }}>\n        Custom Font Text\n      </div>\n    ),\n    {\n      width: 1200,\n      height: 630,\n      fonts: [{ name: 'Inter', data: fontData, style: 'normal' }],\n    }\n  )\n}\n```\n\n## File Naming\n\n- `opengraph-image.tsx` - Open Graph (Facebook, LinkedIn)\n- `twitter-image.tsx` - Twitter/X cards (optional, falls back to OG)\n\n## Styling Notes\n\nImageResponse uses Flexbox layout:\n- Use `display: 'flex'`\n- No CSS Grid support\n- Styles must be inline objects\n\n## Multiple OG Images\n\nUse `generateImageMetadata` for multiple images per route:\n\n```tsx\n// app/blog/[slug]/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport async function generateImageMetadata({ params }) {\n  const images = await getPostImages(params.slug)\n  return images.map((img, idx) => ({\n    id: idx,\n    alt: img.alt,\n    size: { width: 1200, height: 630 },\n    contentType: 'image/png',\n  }))\n}\n\nexport default async function Image({ params, id }) {\n  const images = await getPostImages(params.slug)\n  const image = images[id]\n  return new ImageResponse(/* ... */)\n}\n```\n\n## Multiple Sitemaps\n\nUse `generateSitemaps` for large sites:\n\n```tsx\n// app/sitemap.ts\nimport type { MetadataRoute } from 'next'\n\nexport async function generateSitemaps() {\n  // Return array of sitemap IDs\n  return [{ id: 0 }, { id: 1 }, { id: 2 }]\n}\n\nexport default async function sitemap({\n  id,\n}: {\n  id: number\n}): Promise<MetadataRoute.Sitemap> {\n  const start = id * 50000\n  const end = start + 50000\n  const products = await getProducts(start, end)\n\n  return products.map((product) => ({\n    url: `https://example.com/product/${product.id}`,\n    lastModified: product.updatedAt,\n  }))\n}\n```\n\nGenerates `/sitemap/0.xml`, `/sitemap/1.xml`, etc.\n",".agents/skills/next-best-practices/parallel-routes.md":"# Parallel & Intercepting Routes\n\nParallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.\n\n## File Structure\n\n```\napp/\n├── @modal/                    # Parallel route slot\n│   ├── default.tsx            # Required! Returns null\n│   ├── (.)photos/             # Intercepts /photos/*\n│   │   └── [id]/\n│   │       └── page.tsx       # Modal content\n│   └── [...]catchall/         # Optional: catch unmatched\n│       └── page.tsx\n├── photos/\n│   └── [id]/\n│       └── page.tsx           # Full page (direct access)\n├── layout.tsx                 # Renders both children and @modal\n└── page.tsx\n```\n\n## Step 1: Root Layout with Slot\n\n```tsx\n// app/layout.tsx\nexport default function RootLayout({\n  children,\n  modal,\n}: {\n  children: React.ReactNode;\n  modal: React.ReactNode;\n}) {\n  return (\n    <html>\n      <body>\n        {children}\n        {modal}\n      </body>\n    </html>\n  );\n}\n```\n\n## Step 2: Default File (Critical!)\n\n**Every parallel route slot MUST have a `default.tsx`** to prevent 404s on hard navigation.\n\n```tsx\n// app/@modal/default.tsx\nexport default function Default() {\n  return null;\n}\n```\n\nWithout this file, refreshing any page will 404 because Next.js can't determine what to render in the `@modal` slot.\n\n## Step 3: Intercepting Route (Modal)\n\nThe `(.)` prefix intercepts routes at the same level.\n\n```tsx\n// app/@modal/(.)photos/[id]/page.tsx\nimport { Modal } from '@/components/modal';\n\nexport default async function PhotoModal({\n  params\n}: {\n  params: Promise<{ id: string }>\n}) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  return (\n    <Modal>\n      <img src={photo.url} alt={photo.title} />\n    </Modal>\n  );\n}\n```\n\n## Step 4: Full Page (Direct Access)\n\n```tsx\n// app/photos/[id]/page.tsx\nexport default async function PhotoPage({\n  params\n}: {\n  params: Promise<{ id: string }>\n}) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  return (\n    <div className=\"full-page\">\n      <img src={photo.url} alt={photo.title} />\n      <h1>{photo.title}</h1>\n    </div>\n  );\n}\n```\n\n## Step 5: Modal Component with Correct Closing\n\n**Critical: Use `router.back()` to close modals, NOT `router.push()` or `<Link>`.**\n\n```tsx\n// components/modal.tsx\n'use client';\n\nimport { useRouter } from 'next/navigation';\nimport { useCallback, useEffect, useRef } from 'react';\n\nexport function Modal({ children }: { children: React.ReactNode }) {\n  const router = useRouter();\n  const overlayRef = useRef<HTMLDivElement>(null);\n\n  // Close on escape key\n  useEffect(() => {\n    function onKeyDown(e: KeyboardEvent) {\n      if (e.key === 'Escape') {\n        router.back(); // Correct\n      }\n    }\n    document.addEventListener('keydown', onKeyDown);\n    return () => document.removeEventListener('keydown', onKeyDown);\n  }, [router]);\n\n  // Close on overlay click\n  const handleOverlayClick = useCallback((e: React.MouseEvent) => {\n    if (e.target === overlayRef.current) {\n      router.back(); // Correct\n    }\n  }, [router]);\n\n  return (\n    <div\n      ref={overlayRef}\n      onClick={handleOverlayClick}\n      className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\"\n    >\n      <div className=\"bg-white rounded-lg p-6 max-w-2xl w-full mx-4\">\n        <button\n          onClick={() => router.back()} // Correct!\n          className=\"absolute top-4 right-4\"\n        >\n          Close\n        </button>\n        {children}\n      </div>\n    </div>\n  );\n}\n```\n\n### Why NOT `router.push('/')` or `<Link href=\"/\">`?\n\nUsing `push` or `Link` to \"close\" a modal:\n1. Adds a new history entry (back button shows modal again)\n2. Doesn't properly clear the intercepted route\n3. Can cause the modal to flash or persist unexpectedly\n\n`router.back()` correctly:\n1. Removes the intercepted route from history\n2. Returns to the previous page\n3. Properly unmounts the modal\n\n## Route Matcher Reference\n\nMatchers match **route segments**, not filesystem paths:\n\n| Matcher | Matches | Example |\n|---------|---------|---------|\n| `(.)` | Same level | `@modal/(.)photos` intercepts `/photos` |\n| `(..)` | One level up | `@modal/(..)settings` from `/dashboard/@modal` intercepts `/settings` |\n| `(..)(..)` | Two levels up | Rarely used |\n| `(...)` | From root | `@modal/(...)photos` intercepts `/photos` from anywhere |\n\n**Common mistake**: Thinking `(..)` means \"parent folder\" - it means \"parent route segment\".\n\n## Handling Hard Navigation\n\nWhen users directly visit `/photos/123` (bookmark, refresh, shared link):\n- The intercepting route is bypassed\n- The full `photos/[id]/page.tsx` renders\n- Modal doesn't appear (expected behavior)\n\nIf you want the modal to appear on direct access too, you need additional logic:\n\n```tsx\n// app/photos/[id]/page.tsx\nimport { Modal } from '@/components/modal';\n\nexport default async function PhotoPage({ params }) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  // Option: Render as modal on direct access too\n  return (\n    <Modal>\n      <img src={photo.url} alt={photo.title} />\n    </Modal>\n  );\n}\n```\n\n## Common Gotchas\n\n### 1. Missing `default.tsx` → 404 on Refresh\n\nEvery `@slot` folder needs a `default.tsx` that returns `null` (or appropriate content).\n\n### 2. Modal Persists After Navigation\n\nYou're using `router.push()` instead of `router.back()`.\n\n### 3. Nested Parallel Routes Need Defaults Too\n\nIf you have `@modal` inside a route group, each level needs its own `default.tsx`:\n\n```\napp/\n├── (marketing)/\n│   ├── @modal/\n│   │   └── default.tsx     # Needed!\n│   └── layout.tsx\n└── layout.tsx\n```\n\n### 4. Intercepted Route Shows Wrong Content\n\nCheck your matcher:\n- `(.)photos` intercepts `/photos` from the same route level\n- If your `@modal` is in `app/dashboard/@modal`, use `(.)photos` to intercept `/dashboard/photos`, not `/photos`\n\n### 5. TypeScript Errors with `params`\n\nIn Next.js 15+, `params` is a Promise:\n\n```tsx\n// Correct\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params;\n}\n```\n\n## Complete Example: Photo Gallery Modal\n\n```\napp/\n├── @modal/\n│   ├── default.tsx\n│   └── (.)photos/\n│       └── [id]/\n│           └── page.tsx\n├── photos/\n│   ├── page.tsx           # Gallery grid\n│   └── [id]/\n│       └── page.tsx       # Full photo page\n├── layout.tsx\n└── page.tsx\n```\n\nLinks in the gallery:\n\n```tsx\n// app/photos/page.tsx\nimport Link from 'next/link';\n\nexport default async function Gallery() {\n  const photos = await getPhotos();\n\n  return (\n    <div className=\"grid grid-cols-3 gap-4\">\n      {photos.map(photo => (\n        <Link key={photo.id} href={`/photos/${photo.id}`}>\n          <img src={photo.thumbnail} alt={photo.title} />\n        </Link>\n      ))}\n    </div>\n  );\n}\n```\n\nClicking a photo → Modal opens (intercepted)\nDirect URL → Full page renders\nRefresh while modal open → Full page renders\n",".agents/skills/next-best-practices/route-handlers.md":"# Route Handlers\n\nCreate API endpoints with `route.ts` files.\n\n## Basic Usage\n\n```tsx\n// app/api/users/route.ts\nexport async function GET() {\n  const users = await getUsers()\n  return Response.json(users)\n}\n\nexport async function POST(request: Request) {\n  const body = await request.json()\n  const user = await createUser(body)\n  return Response.json(user, { status: 201 })\n}\n```\n\n## Supported Methods\n\n`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`\n\n## GET Handler Conflicts with page.tsx\n\n**A `route.ts` and `page.tsx` cannot coexist in the same folder.**\n\n```\napp/\n├── api/\n│   └── users/\n│       └── route.ts    # /api/users\n└── users/\n    ├── page.tsx        # /users (page)\n    └── route.ts        # Warning: Conflicts with page.tsx!\n```\n\nIf you need both a page and an API at the same path, use different paths:\n\n```\napp/\n├── users/\n│   └── page.tsx        # /users (page)\n└── api/\n    └── users/\n        └── route.ts    # /api/users (API)\n```\n\n## Environment Behavior\n\nRoute handlers run in a **Server Component-like environment**:\n\n- Yes: Can use `async/await`\n- Yes: Can access `cookies()`, `headers()`\n- Yes: Can use Node.js APIs\n- No: Cannot use React hooks\n- No: Cannot use React DOM APIs\n- No: Cannot use browser APIs\n\n```tsx\n// Bad: This won't work - no React DOM in route handlers\nimport { renderToString } from 'react-dom/server'\n\nexport async function GET() {\n  const html = renderToString(<Component />)  // Error!\n  return new Response(html)\n}\n```\n\n## Dynamic Route Handlers\n\n```tsx\n// app/api/users/[id]/route.ts\nexport async function GET(\n  request: Request,\n  { params }: { params: Promise<{ id: string }> }\n) {\n  const { id } = await params\n  const user = await getUser(id)\n\n  if (!user) {\n    return Response.json({ error: 'Not found' }, { status: 404 })\n  }\n\n  return Response.json(user)\n}\n```\n\n## Request Helpers\n\n```tsx\nexport async function GET(request: Request) {\n  // URL and search params\n  const { searchParams } = new URL(request.url)\n  const query = searchParams.get('q')\n\n  // Headers\n  const authHeader = request.headers.get('authorization')\n\n  // Cookies (Next.js helper)\n  const cookieStore = await cookies()\n  const token = cookieStore.get('token')\n\n  return Response.json({ query, token })\n}\n```\n\n## Response Helpers\n\n```tsx\n// JSON response\nreturn Response.json({ data })\n\n// With status\nreturn Response.json({ error: 'Not found' }, { status: 404 })\n\n// With headers\nreturn Response.json(data, {\n  headers: {\n    'Cache-Control': 'max-age=3600',\n  },\n})\n\n// Redirect\nreturn Response.redirect(new URL('/login', request.url))\n\n// Stream\nreturn new Response(stream, {\n  headers: { 'Content-Type': 'text/event-stream' },\n})\n```\n\n## When to Use Route Handlers vs Server Actions\n\n| Use Case | Route Handlers | Server Actions |\n|----------|----------------|----------------|\n| Form submissions | No | Yes |\n| Data mutations from UI | No | Yes |\n| Third-party webhooks | Yes | No |\n| External API consumption | Yes | No |\n| Public REST API | Yes | No |\n| File uploads | Both work | Both work |\n\n**Prefer Server Actions** for mutations triggered from your UI.\n**Use Route Handlers** for external integrations and public APIs.\n",".agents/skills/next-best-practices/rsc-boundaries.md":"# RSC Boundaries\n\nDetect and prevent invalid patterns when crossing Server/Client component boundaries.\n\n## Detection Rules\n\n### 1. Async Client Components Are Invalid\n\nClient components **cannot** be async functions. Only Server Components can be async.\n\n**Detect:** File has `'use client'` AND component is `async function` or returns `Promise`\n\n```tsx\n// Bad: async client component\n'use client'\nexport default async function UserProfile() {\n  const user = await getUser() // Cannot await in client component\n  return <div>{user.name}</div>\n}\n\n// Good: Remove async, fetch data in parent server component\n// page.tsx (server component - no 'use client')\nexport default async function Page() {\n  const user = await getUser()\n  return <UserProfile user={user} />\n}\n\n// UserProfile.tsx (client component)\n'use client'\nexport function UserProfile({ user }: { user: User }) {\n  return <div>{user.name}</div>\n}\n```\n\n```tsx\n// Bad: async arrow function client component\n'use client'\nconst Dashboard = async () => {\n  const data = await fetchDashboard()\n  return <div>{data}</div>\n}\n\n// Good: Fetch in server component, pass data down\n```\n\n### 2. Non-Serializable Props to Client Components\n\nProps passed from Server → Client must be JSON-serializable.\n\n**Detect:** Server component passes these to a client component:\n- Functions (except Server Actions with `'use server'`)\n- `Date` objects\n- `Map`, `Set`, `WeakMap`, `WeakSet`\n- Class instances\n- `Symbol` (unless globally registered)\n- Circular references\n\n```tsx\n// Bad: Function prop\n// page.tsx (server)\nexport default function Page() {\n  const handleClick = () => console.log('clicked')\n  return <ClientButton onClick={handleClick} />\n}\n\n// Good: Define function inside client component\n// ClientButton.tsx\n'use client'\nexport function ClientButton() {\n  const handleClick = () => console.log('clicked')\n  return <button onClick={handleClick}>Click</button>\n}\n```\n\n```tsx\n// Bad: Date object (silently becomes string, then crashes)\n// page.tsx (server)\nexport default async function Page() {\n  const post = await getPost()\n  return <PostCard createdAt={post.createdAt} /> // Date object\n}\n\n// PostCard.tsx (client) - will crash on .getFullYear()\n'use client'\nexport function PostCard({ createdAt }: { createdAt: Date }) {\n  return <span>{createdAt.getFullYear()}</span> // Runtime error!\n}\n\n// Good: Serialize to string on server\n// page.tsx (server)\nexport default async function Page() {\n  const post = await getPost()\n  return <PostCard createdAt={post.createdAt.toISOString()} />\n}\n\n// PostCard.tsx (client)\n'use client'\nexport function PostCard({ createdAt }: { createdAt: string }) {\n  const date = new Date(createdAt)\n  return <span>{date.getFullYear()}</span>\n}\n```\n\n```tsx\n// Bad: Class instance\nconst user = new UserModel(data)\n<ClientProfile user={user} /> // Methods will be stripped\n\n// Good: Pass plain object\nconst user = await getUser()\n<ClientProfile user={{ id: user.id, name: user.name }} />\n```\n\n```tsx\n// Bad: Map/Set\n<ClientComponent items={new Map([['a', 1]])} />\n\n// Good: Convert to array/object\n<ClientComponent items={Object.fromEntries(map)} />\n<ClientComponent items={Array.from(set)} />\n```\n\n### 3. Server Actions Are the Exception\n\nFunctions marked with `'use server'` CAN be passed to client components.\n\n```tsx\n// Valid: Server Action can be passed\n// actions.ts\n'use server'\nexport async function submitForm(formData: FormData) {\n  // server-side logic\n}\n\n// page.tsx (server)\nimport { submitForm } from './actions'\nexport default function Page() {\n  return <ClientForm onSubmit={submitForm} /> // OK!\n}\n\n// ClientForm.tsx (client)\n'use client'\nexport function ClientForm({ onSubmit }: { onSubmit: (data: FormData) => Promise<void> }) {\n  return <form action={onSubmit}>...</form>\n}\n```\n\n## Quick Reference\n\n| Pattern | Valid? | Fix |\n|---------|--------|-----|\n| `'use client'` + `async function` | No | Fetch in server parent, pass data |\n| Pass `() => {}` to client | No | Define in client or use server action |\n| Pass `new Date()` to client | No | Use `.toISOString()` |\n| Pass `new Map()` to client | No | Convert to object/array |\n| Pass class instance to client | No | Pass plain object |\n| Pass server action to client | Yes | - |\n| Pass `string/number/boolean` | Yes | - |\n| Pass plain object/array | Yes | - |\n",".agents/skills/next-best-practices/runtime-selection.md":"# Runtime Selection\n\n## Use Node.js Runtime by Default\n\nUse the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.\n\n```tsx\n// Good: Default - no runtime config needed (uses Node.js)\nexport default function Page() { ... }\n\n// Caution: Only if already used in project or specifically required\nexport const runtime = 'edge'\n```\n\n## When to Use Each\n\n### Node.js Runtime (Default)\n\n- Full Node.js API support\n- File system access (`fs`)\n- Full `crypto` support\n- Database connections\n- Most npm packages work\n\n### Edge Runtime\n\n- Only for specific edge-location latency requirements\n- Limited API (no `fs`, limited `crypto`)\n- Smaller cold start\n- Geographic distribution needs\n\n## Detection\n\n**Before adding `runtime = 'edge'`**, check:\n1. Does the project already use Edge runtime?\n2. Is there a specific latency requirement?\n3. Are all dependencies Edge-compatible?\n\nIf unsure, use Node.js runtime.\n",".agents/skills/next-best-practices/scripts.md":"# Scripts\n\nLoading third-party scripts in Next.js.\n\n## Use next/script\n\nAlways use `next/script` instead of native `<script>` tags for better performance.\n\n```tsx\n// Bad: Native script tag\n<script src=\"https://example.com/script.js\"></script>\n\n// Good: Next.js Script component\nimport Script from 'next/script'\n\n<Script src=\"https://example.com/script.js\" />\n```\n\n## Inline Scripts Need ID\n\nInline scripts require an `id` attribute for Next.js to track them.\n\n```tsx\n// Bad: Missing id\n<Script dangerouslySetInnerHTML={{ __html: 'console.log(\"hi\")' }} />\n\n// Good: Has id\n<Script id=\"my-script\" dangerouslySetInnerHTML={{ __html: 'console.log(\"hi\")' }} />\n\n// Good: Inline with id\n<Script id=\"show-banner\">\n  {`document.getElementById('banner').classList.remove('hidden')`}\n</Script>\n```\n\n## Don't Put Script in Head\n\n`next/script` should not be placed inside `next/head`. It handles its own positioning.\n\n```tsx\n// Bad: Script inside Head\nimport Head from 'next/head'\nimport Script from 'next/script'\n\n<Head>\n  <Script src=\"/analytics.js\" />\n</Head>\n\n// Good: Script outside Head\n<Head>\n  <title>Page</title>\n</Head>\n<Script src=\"/analytics.js\" />\n```\n\n## Loading Strategies\n\n```tsx\n// afterInteractive (default) - Load after page is interactive\n<Script src=\"/analytics.js\" strategy=\"afterInteractive\" />\n\n// lazyOnload - Load during idle time\n<Script src=\"/widget.js\" strategy=\"lazyOnload\" />\n\n// beforeInteractive - Load before page is interactive (use sparingly)\n// Only works in app/layout.tsx or pages/_document.js\n<Script src=\"/critical.js\" strategy=\"beforeInteractive\" />\n\n// worker - Load in web worker (experimental)\n<Script src=\"/heavy.js\" strategy=\"worker\" />\n```\n\n## Google Analytics\n\nUse `@next/third-parties` instead of inline GA scripts.\n\n```tsx\n// Bad: Inline GA script\n<Script src=\"https://www.googletagmanager.com/gtag/js?id=G-XXXXX\" />\n<Script id=\"ga-init\">\n  {`window.dataLayer = window.dataLayer || [];\n    function gtag(){dataLayer.push(arguments);}\n    gtag('js', new Date());\n    gtag('config', 'G-XXXXX');`}\n</Script>\n\n// Good: Next.js component\nimport { GoogleAnalytics } from '@next/third-parties/google'\n\nexport default function Layout({ children }) {\n  return (\n    <html>\n      <body>{children}</body>\n      <GoogleAnalytics gaId=\"G-XXXXX\" />\n    </html>\n  )\n}\n```\n\n## Google Tag Manager\n\n```tsx\nimport { GoogleTagManager } from '@next/third-parties/google'\n\nexport default function Layout({ children }) {\n  return (\n    <html>\n      <GoogleTagManager gtmId=\"GTM-XXXXX\" />\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n## Other Third-Party Scripts\n\n```tsx\n// YouTube embed\nimport { YouTubeEmbed } from '@next/third-parties/google'\n\n<YouTubeEmbed videoid=\"dQw4w9WgXcQ\" />\n\n// Google Maps\nimport { GoogleMapsEmbed } from '@next/third-parties/google'\n\n<GoogleMapsEmbed\n  apiKey=\"YOUR_API_KEY\"\n  mode=\"place\"\n  q=\"Brooklyn+Bridge,New+York,NY\"\n/>\n```\n\n## Quick Reference\n\n| Pattern | Issue | Fix |\n|---------|-------|-----|\n| `<script src=\"...\">` | No optimization | Use `next/script` |\n| `<Script>` without id | Can't track inline scripts | Add `id` attribute |\n| `<Script>` inside `<Head>` | Wrong placement | Move outside Head |\n| Inline GA/GTM scripts | No optimization | Use `@next/third-parties` |\n| `strategy=\"beforeInteractive\"` outside layout | Won't work | Only use in root layout |\n",".agents/skills/next-best-practices/self-hosting.md":"# Self-Hosting Next.js\n\nDeploy Next.js outside of Vercel with confidence.\n\n## Quick Start: Standalone Output\n\nFor Docker or any containerized deployment, use standalone output:\n\n```js\n// next.config.js\nmodule.exports = {\n  output: 'standalone',\n};\n```\n\nThis creates a minimal `standalone` folder with only production dependencies:\n\n```\n.next/\n├── standalone/\n│   ├── server.js          # Entry point\n│   ├── node_modules/      # Only production deps\n│   └── .next/             # Build output\n└── static/                # Must be copied separately\n```\n\n## Docker Deployment\n\n### Dockerfile\n\n```dockerfile\nFROM node:20-alpine AS base\n\n# Install dependencies\nFROM base AS deps\nWORKDIR /app\nCOPY package.json package-lock.json* ./\nRUN npm ci\n\n# Build\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\nRUN npm run build\n\n# Production\nFROM base AS runner\nWORKDIR /app\n\nENV NODE_ENV=production\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\n# Copy standalone output\nCOPY --from=builder /app/.next/standalone ./\nCOPY --from=builder /app/.next/static ./.next/static\nCOPY --from=builder /app/public ./public\n\nUSER nextjs\n\nEXPOSE 3000\nENV PORT=3000\nENV HOSTNAME=\"0.0.0.0\"\n\nCMD [\"node\", \"server.js\"]\n```\n\n### Docker Compose\n\n```yaml\nversion: '3.8'\n\nservices:\n  web:\n    build: .\n    ports:\n      - \"3000:3000\"\n    environment:\n      - NODE_ENV=production\n    restart: unless-stopped\n    healthcheck:\n      test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:3000/api/health\"]\n      interval: 30s\n      timeout: 10s\n      retries: 3\n```\n\n## PM2 Deployment\n\nFor traditional server deployments:\n\n```js\n// ecosystem.config.js\nmodule.exports = {\n  apps: [{\n    name: 'nextjs',\n    script: '.next/standalone/server.js',\n    instances: 'max',\n    exec_mode: 'cluster',\n    env: {\n      NODE_ENV: 'production',\n      PORT: 3000,\n    },\n  }],\n};\n```\n\n```bash\nnpm run build\npm2 start ecosystem.config.js\n```\n\n## ISR and Cache Handlers\n\n### The Problem\n\nISR (Incremental Static Regeneration) uses filesystem caching by default. This **breaks with multiple instances**:\n\n- Instance A regenerates page → saves to its local disk\n- Instance B serves stale page → doesn't see Instance A's cache\n- Load balancer sends users to random instances → inconsistent content\n\n### Solution: Custom Cache Handler\n\nNext.js 14+ supports custom cache handlers for shared storage:\n\n```js\n// next.config.js\nmodule.exports = {\n  cacheHandler: require.resolve('./cache-handler.js'),\n  cacheMaxMemorySize: 0, // Disable in-memory cache\n};\n```\n\n#### Redis Cache Handler Example\n\n```js\n// cache-handler.js\nconst Redis = require('ioredis');\n\nconst redis = new Redis(process.env.REDIS_URL);\nconst CACHE_PREFIX = 'nextjs:';\n\nmodule.exports = class CacheHandler {\n  constructor(options) {\n    this.options = options;\n  }\n\n  async get(key) {\n    const data = await redis.get(CACHE_PREFIX + key);\n    if (!data) return null;\n\n    const parsed = JSON.parse(data);\n    return {\n      value: parsed.value,\n      lastModified: parsed.lastModified,\n    };\n  }\n\n  async set(key, data, ctx) {\n    const cacheData = {\n      value: data,\n      lastModified: Date.now(),\n    };\n\n    // Set TTL based on revalidate option\n    if (ctx?.revalidate) {\n      await redis.setex(\n        CACHE_PREFIX + key,\n        ctx.revalidate,\n        JSON.stringify(cacheData)\n      );\n    } else {\n      await redis.set(CACHE_PREFIX + key, JSON.stringify(cacheData));\n    }\n  }\n\n  async revalidateTag(tags) {\n    // Implement tag-based invalidation\n    // This requires tracking which keys have which tags\n  }\n};\n```\n\n#### S3 Cache Handler Example\n\n```js\n// cache-handler.js\nconst { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3');\n\nconst s3 = new S3Client({ region: process.env.AWS_REGION });\nconst BUCKET = process.env.CACHE_BUCKET;\n\nmodule.exports = class CacheHandler {\n  async get(key) {\n    try {\n      const response = await s3.send(new GetObjectCommand({\n        Bucket: BUCKET,\n        Key: `cache/${key}`,\n      }));\n      const body = await response.Body.transformToString();\n      return JSON.parse(body);\n    } catch (err) {\n      if (err.name === 'NoSuchKey') return null;\n      throw err;\n    }\n  }\n\n  async set(key, data, ctx) {\n    await s3.send(new PutObjectCommand({\n      Bucket: BUCKET,\n      Key: `cache/${key}`,\n      Body: JSON.stringify({\n        value: data,\n        lastModified: Date.now(),\n      }),\n      ContentType: 'application/json',\n    }));\n  }\n};\n```\n\n## What Works vs What Needs Setup\n\n| Feature | Single Instance | Multi-Instance | Notes |\n|---------|----------------|----------------|-------|\n| SSR | Yes | Yes | No special setup |\n| SSG | Yes | Yes | Built at deploy time |\n| ISR | Yes | Needs cache handler | Filesystem cache breaks |\n| Image Optimization | Yes | Yes | CPU-intensive, consider CDN |\n| Middleware | Yes | Yes | Runs on Node.js |\n| Edge Runtime | Limited | Limited | Some features Node-only |\n| `revalidatePath/Tag` | Yes | Needs cache handler | Must share cache |\n| `next/font` | Yes | Yes | Fonts bundled at build |\n| Draft Mode | Yes | Yes | Cookie-based |\n\n## Image Optimization\n\nNext.js Image Optimization works out of the box but is CPU-intensive.\n\n### Option 1: Built-in (Simple)\n\nWorks automatically, but consider:\n- Set `deviceSizes` and `imageSizes` in config to limit variants\n- Use `minimumCacheTTL` to reduce regeneration\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    minimumCacheTTL: 60 * 60 * 24, // 24 hours\n    deviceSizes: [640, 750, 1080, 1920], // Limit sizes\n  },\n};\n```\n\n### Option 2: External Loader (Recommended for Scale)\n\nOffload to Cloudinary, Imgix, or similar:\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    loader: 'custom',\n    loaderFile: './lib/image-loader.js',\n  },\n};\n```\n\n```js\n// lib/image-loader.js\nexport default function cloudinaryLoader({ src, width, quality }) {\n  const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`];\n  return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}${src}`;\n}\n```\n\n## Environment Variables\n\n### Build-time vs Runtime\n\n```js\n// Available at build time only (baked into bundle)\nNEXT_PUBLIC_API_URL=https://api.example.com\n\n// Available at runtime (server-side only)\nDATABASE_URL=postgresql://...\nAPI_SECRET=...\n```\n\n### Runtime Configuration\n\nFor truly dynamic config, don't use `NEXT_PUBLIC_*`. Instead:\n\n```tsx\n// app/api/config/route.ts\nexport async function GET() {\n  return Response.json({\n    apiUrl: process.env.API_URL,\n    features: process.env.FEATURES?.split(','),\n  });\n}\n```\n\n## OpenNext: Serverless Without Vercel\n\n[OpenNext](https://open-next.js.org/) adapts Next.js for AWS Lambda, Cloudflare Workers, etc.\n\n```bash\nnpx create-sst@latest\n# or\nnpx @opennextjs/aws build\n```\n\nSupports:\n- AWS Lambda + CloudFront\n- Cloudflare Workers\n- Netlify Functions\n- Deno Deploy\n\n## Health Check Endpoint\n\nAlways include a health check for load balancers:\n\n```tsx\n// app/api/health/route.ts\nexport async function GET() {\n  try {\n    // Optional: check database connection\n    // await db.$queryRaw`SELECT 1`;\n\n    return Response.json({ status: 'healthy' }, { status: 200 });\n  } catch (error) {\n    return Response.json({ status: 'unhealthy' }, { status: 503 });\n  }\n}\n```\n\n## Pre-Deployment Checklist\n\n1. **Build locally first**: `npm run build` - catch errors before deploy\n2. **Test standalone output**: `node .next/standalone/server.js`\n3. **Set `output: 'standalone'`** for Docker\n4. **Configure cache handler** for multi-instance ISR\n5. **Set `HOSTNAME=\"0.0.0.0\"`** for containers\n6. **Copy `public/` and `.next/static/`** - not included in standalone\n7. **Add health check endpoint**\n8. **Test ISR revalidation** after deployment\n9. **Monitor memory usage** - Node.js defaults may need tuning\n\n## Testing Cache Handler\n\n**Critical**: Test your cache handler on every Next.js upgrade:\n\n```bash\n# Start multiple instances\nPORT=3001 node .next/standalone/server.js &\nPORT=3002 node .next/standalone/server.js &\n\n# Trigger ISR revalidation\ncurl http://localhost:3001/api/revalidate?path=/posts\n\n# Verify both instances see the update\ncurl http://localhost:3001/posts\ncurl http://localhost:3002/posts\n# Should return identical content\n```\n",".agents/skills/next-best-practices/suspense-boundaries.md":"# Suspense Boundaries\n\nClient hooks that cause CSR bailout without Suspense boundaries.\n\n## useSearchParams\n\nAlways requires Suspense boundary in static routes. Without it, the entire page becomes client-side rendered.\n\n```tsx\n// Bad: Entire page becomes CSR\n'use client'\n\nimport { useSearchParams } from 'next/navigation'\n\nexport default function SearchBar() {\n  const searchParams = useSearchParams()\n  return <div>Query: {searchParams.get('q')}</div>\n}\n```\n\n```tsx\n// Good: Wrap in Suspense\nimport { Suspense } from 'react'\nimport SearchBar from './search-bar'\n\nexport default function Page() {\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <SearchBar />\n    </Suspense>\n  )\n}\n```\n\n## usePathname\n\nRequires Suspense boundary when route has dynamic parameters.\n\n```tsx\n// In dynamic route [slug]\n// Bad: No Suspense\n'use client'\nimport { usePathname } from 'next/navigation'\n\nexport function Breadcrumb() {\n  const pathname = usePathname()\n  return <nav>{pathname}</nav>\n}\n```\n\n```tsx\n// Good: Wrap in Suspense\n<Suspense fallback={<BreadcrumbSkeleton />}>\n  <Breadcrumb />\n</Suspense>\n```\n\nIf you use `generateStaticParams`, Suspense is optional.\n\n## Quick Reference\n\n| Hook | Suspense Required |\n|------|-------------------|\n| `useSearchParams()` | Yes |\n| `usePathname()` | Yes (dynamic routes) |\n| `useParams()` | No |\n| `useRouter()` | No |\n",".agents/skills/next-cache-components/SKILL.md":"---\nname: next-cache-components\ndescription: Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag\n---\n\n# Cache Components (Next.js 16+)\n\nCache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route.\n\n## Enable Cache Components\n\n```ts\n// next.config.ts\nimport type { NextConfig } from 'next'\n\nconst nextConfig: NextConfig = {\n  cacheComponents: true,\n}\n\nexport default nextConfig\n```\n\nThis replaces the old `experimental.ppr` flag.\n\n---\n\n## Three Content Types\n\nWith Cache Components enabled, content falls into three categories:\n\n### 1. Static (Auto-Prerendered)\n\nSynchronous code, imports, pure computations - prerendered at build time:\n\n```tsx\nexport default function Page() {\n  return (\n    <header>\n      <h1>Our Blog</h1>  {/* Static - instant */}\n      <nav>...</nav>\n    </header>\n  )\n}\n```\n\n### 2. Cached (`use cache`)\n\nAsync data that doesn't need fresh fetches every request:\n\n```tsx\nasync function BlogPosts() {\n  'use cache'\n  cacheLife('hours')\n\n  const posts = await db.posts.findMany()\n  return <PostList posts={posts} />\n}\n```\n\n### 3. Dynamic (Suspense)\n\nRuntime data that must be fresh - wrap in Suspense:\n\n```tsx\nimport { Suspense } from 'react'\n\nexport default function Page() {\n  return (\n    <>\n      <BlogPosts />  {/* Cached */}\n\n      <Suspense fallback={<p>Loading...</p>}>\n        <UserPreferences />  {/* Dynamic - streams in */}\n      </Suspense>\n    </>\n  )\n}\n\nasync function UserPreferences() {\n  const theme = (await cookies()).get('theme')?.value\n  return <p>Theme: {theme}</p>\n}\n```\n\n---\n\n## `use cache` Directive\n\n### File Level\n\n```tsx\n'use cache'\n\nexport default async function Page() {\n  // Entire page is cached\n  const data = await fetchData()\n  return <div>{data}</div>\n}\n```\n\n### Component Level\n\n```tsx\nexport async function CachedComponent() {\n  'use cache'\n  const data = await fetchData()\n  return <div>{data}</div>\n}\n```\n\n### Function Level\n\n```tsx\nexport async function getData() {\n  'use cache'\n  return db.query('SELECT * FROM posts')\n}\n```\n\n---\n\n## Cache Profiles\n\n### Built-in Profiles\n\n```tsx\n'use cache'                    // Default: 5m stale, 15m revalidate\n```\n\n```tsx\n'use cache: remote'           // Platform-provided cache (Redis, KV)\n```\n\n```tsx\n'use cache: private'          // For compliance, allows runtime APIs\n```\n\n### `cacheLife()` - Custom Lifetime\n\n```tsx\nimport { cacheLife } from 'next/cache'\n\nasync function getData() {\n  'use cache'\n  cacheLife('hours')  // Built-in profile\n  return fetch('/api/data')\n}\n```\n\nBuilt-in profiles: `'default'`, `'minutes'`, `'hours'`, `'days'`, `'weeks'`, `'max'`\n\n### Inline Configuration\n\n```tsx\nasync function getData() {\n  'use cache'\n  cacheLife({\n    stale: 3600,      // 1 hour - serve stale while revalidating\n    revalidate: 7200, // 2 hours - background revalidation interval\n    expire: 86400,    // 1 day - hard expiration\n  })\n  return fetch('/api/data')\n}\n```\n\n---\n\n## Cache Invalidation\n\n### `cacheTag()` - Tag Cached Content\n\n```tsx\nimport { cacheTag } from 'next/cache'\n\nasync function getProducts() {\n  'use cache'\n  cacheTag('products')\n  return db.products.findMany()\n}\n\nasync function getProduct(id: string) {\n  'use cache'\n  cacheTag('products', `product-${id}`)\n  return db.products.findUnique({ where: { id } })\n}\n```\n\n### `updateTag()` - Immediate Invalidation\n\nUse when you need the cache refreshed within the same request:\n\n```tsx\n'use server'\n\nimport { updateTag } from 'next/cache'\n\nexport async function updateProduct(id: string, data: FormData) {\n  await db.products.update({ where: { id }, data })\n  updateTag(`product-${id}`)  // Immediate - same request sees fresh data\n}\n```\n\n### `revalidateTag()` - Background Revalidation\n\nUse for stale-while-revalidate behavior:\n\n```tsx\n'use server'\n\nimport { revalidateTag } from 'next/cache'\n\nexport async function createPost(data: FormData) {\n  await db.posts.create({ data })\n  revalidateTag('posts')  // Background - next request sees fresh data\n}\n```\n\n---\n\n## Runtime Data Constraint\n\n**Cannot** access `cookies()`, `headers()`, or `searchParams` inside `use cache`.\n\n### Solution: Pass as Arguments\n\n```tsx\n// Wrong - runtime API inside use cache\nasync function CachedProfile() {\n  'use cache'\n  const session = (await cookies()).get('session')?.value  // Error!\n  return <div>{session}</div>\n}\n\n// Correct - extract outside, pass as argument\nasync function ProfilePage() {\n  const session = (await cookies()).get('session')?.value\n  return <CachedProfile sessionId={session} />\n}\n\nasync function CachedProfile({ sessionId }: { sessionId: string }) {\n  'use cache'\n  // sessionId becomes part of cache key automatically\n  const data = await fetchUserData(sessionId)\n  return <div>{data.name}</div>\n}\n```\n\n### Exception: `use cache: private`\n\nFor compliance requirements when you can't refactor:\n\n```tsx\nasync function getData() {\n  'use cache: private'\n  const session = (await cookies()).get('session')?.value  // Allowed\n  return fetchData(session)\n}\n```\n\n---\n\n## Cache Key Generation\n\nCache keys are automatic based on:\n- **Build ID** - invalidates all caches on deploy\n- **Function ID** - hash of function location\n- **Serializable arguments** - props become part of key\n- **Closure variables** - outer scope values included\n\n```tsx\nasync function Component({ userId }: { userId: string }) {\n  const getData = async (filter: string) => {\n    'use cache'\n    // Cache key = userId (closure) + filter (argument)\n    return fetch(`/api/users/${userId}?filter=${filter}`)\n  }\n  return getData('active')\n}\n```\n\n---\n\n## Complete Example\n\n```tsx\nimport { Suspense } from 'react'\nimport { cookies } from 'next/headers'\nimport { cacheLife, cacheTag } from 'next/cache'\n\nexport default function DashboardPage() {\n  return (\n    <>\n      {/* Static shell - instant from CDN */}\n      <header><h1>Dashboard</h1></header>\n      <nav>...</nav>\n\n      {/* Cached - fast, revalidates hourly */}\n      <Stats />\n\n      {/* Dynamic - streams in with fresh data */}\n      <Suspense fallback={<NotificationsSkeleton />}>\n        <Notifications />\n      </Suspense>\n    </>\n  )\n}\n\nasync function Stats() {\n  'use cache'\n  cacheLife('hours')\n  cacheTag('dashboard-stats')\n\n  const stats = await db.stats.aggregate()\n  return <StatsDisplay stats={stats} />\n}\n\nasync function Notifications() {\n  const userId = (await cookies()).get('userId')?.value\n  const notifications = await db.notifications.findMany({\n    where: { userId, read: false }\n  })\n  return <NotificationList items={notifications} />\n}\n```\n\n---\n\n## Migration from Previous Versions\n\n| Old Config | Replacement |\n|-----------|-------------|\n| `experimental.ppr` | `cacheComponents: true` |\n| `dynamic = 'force-dynamic'` | Remove (default behavior) |\n| `dynamic = 'force-static'` | `'use cache'` + `cacheLife('max')` |\n| `revalidate = N` | `cacheLife({ revalidate: N })` |\n| `unstable_cache()` | `'use cache'` directive |\n\n### Migrating `unstable_cache` to `use cache`\n\n`unstable_cache` has been replaced by the `use cache` directive in Next.js 16. When `cacheComponents` is enabled, convert `unstable_cache` calls to `use cache` functions:\n\n**Before (`unstable_cache`):**\n\n```tsx\nimport { unstable_cache } from 'next/cache'\n\nconst getCachedUser = unstable_cache(\n  async (id) => getUser(id),\n  ['my-app-user'],\n  {\n    tags: ['users'],\n    revalidate: 60,\n  }\n)\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const user = await getCachedUser(id)\n  return <div>{user.name}</div>\n}\n```\n\n**After (`use cache`):**\n\n```tsx\nimport { cacheLife, cacheTag } from 'next/cache'\n\nasync function getCachedUser(id: string) {\n  'use cache'\n  cacheTag('users')\n  cacheLife({ revalidate: 60 })\n  return getUser(id)\n}\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const user = await getCachedUser(id)\n  return <div>{user.name}</div>\n}\n```\n\nKey differences:\n- **No manual cache keys** - `use cache` generates keys automatically from function arguments and closures. The `keyParts` array from `unstable_cache` is no longer needed.\n- **Tags** - Replace `options.tags` with `cacheTag()` calls inside the function.\n- **Revalidation** - Replace `options.revalidate` with `cacheLife({ revalidate: N })` or a built-in profile like `cacheLife('minutes')`.\n- **Dynamic data** - `unstable_cache` did not support `cookies()` or `headers()` inside the callback. The same restriction applies to `use cache`, but you can use `'use cache: private'` if needed.\n\n---\n\n## Limitations\n\n- **Edge runtime not supported** - requires Node.js\n- **Static export not supported** - needs server\n- **Non-deterministic values** (`Math.random()`, `Date.now()`) execute once at build time inside `use cache`\n\nFor request-time randomness outside cache:\n\n```tsx\nimport { connection } from 'next/server'\n\nasync function DynamicContent() {\n  await connection()  // Defer to request time\n  const id = crypto.randomUUID()  // Different per request\n  return <div>{id}</div>\n}\n```\n\nSources:\n- [Cache Components Guide](https://nextjs.org/docs/app/getting-started/cache-components)\n- [use cache Directive](https://nextjs.org/docs/app/api-reference/directives/use-cache)\n- [unstable_cache (legacy)](https://nextjs.org/docs/app/api-reference/functions/unstable_cache)\n",".agents/skills/next-upgrade/SKILL.md":"---\nname: next-upgrade\ndescription: Upgrade Next.js to the latest version following official migration guides and codemods\nargument-hint: \"[target-version]\"\n---\n\n# Upgrade Next.js\n\nUpgrade the current project to the latest Next.js version following official migration guides.\n\n## Instructions\n\n1. **Detect current version**: Read `package.json` to identify the current Next.js version and related dependencies (React, React DOM, etc.)\n\n2. **Fetch the latest upgrade guide**: Use WebFetch to get the official upgrade documentation:\n   - Codemods: https://nextjs.org/docs/app/guides/upgrading/codemods\n   - Version-specific guides (adjust version as needed):\n     - https://nextjs.org/docs/app/guides/upgrading/version-16 \n     - https://nextjs.org/docs/app/guides/upgrading/version-15\n     - https://nextjs.org/docs/app/guides/upgrading/version-14\n\n3. **Determine upgrade path**: Based on current version, identify which migration steps apply. For major version jumps, upgrade incrementally (e.g., 13 → 14 → 15).\n\n4. **Run codemods first**: Next.js provides codemods to automate breaking changes:\n   ```bash\n   npx @next/codemod@latest <transform> <path>\n   ```\n   Common transforms:\n   - `next-async-request-api` - Updates async Request APIs (v15)\n   - `next-request-geo-ip` - Migrates geo/ip properties (v15)\n   - `next-dynamic-access-named-export` - Transforms dynamic imports (v15)\n\n5. **Update dependencies**: Upgrade Next.js and peer dependencies together:\n   ```bash\n   npm install next@latest react@latest react-dom@latest\n   ```\n\n6. **Review breaking changes**: Check the upgrade guide for manual changes needed:\n   - API changes (e.g., async params in v15)\n   - Configuration changes in `next.config.js`\n   - Deprecated features being removed\n\n7. **Update TypeScript types** (if applicable):\n   ```bash\n   npm install @types/react@latest @types/react-dom@latest\n   ```\n\n8. **Test the upgrade**:\n   - Run `npm run build` to check for build errors\n   - Run `npm run dev` and test key functionality\n",".agents/skills/nodejs-backend-patterns/SKILL.md":"---\nname: nodejs-backend-patterns\ndescription: Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.\n---\n\n# Node.js Backend Patterns\n\nComprehensive guidance for building scalable, maintainable, and production-ready Node.js backend applications with modern frameworks, architectural patterns, and best practices.\n\n## When to Use This Skill\n\n- Building REST APIs or GraphQL servers\n- Creating microservices with Node.js\n- Implementing authentication and authorization\n- Designing scalable backend architectures\n- Setting up middleware and error handling\n- Integrating databases (SQL and NoSQL)\n- Building real-time applications with WebSockets\n- Implementing background job processing\n\n## Core Frameworks\n\n### Express.js - Minimalist Framework\n\n**Basic Setup:**\n\n```typescript\nimport express, { Request, Response, NextFunction } from \"express\";\nimport helmet from \"helmet\";\nimport cors from \"cors\";\nimport compression from \"compression\";\n\nconst app = express();\n\n// Security middleware\napp.use(helmet());\napp.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(\",\") }));\napp.use(compression());\n\n// Body parsing\napp.use(express.json({ limit: \"10mb\" }));\napp.use(express.urlencoded({ extended: true, limit: \"10mb\" }));\n\n// Request logging\napp.use((req: Request, res: Response, next: NextFunction) => {\n  console.log(`${req.method} ${req.path}`);\n  next();\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => {\n  console.log(`Server running on port ${PORT}`);\n});\n```\n\n### Fastify - High Performance Framework\n\n**Basic Setup:**\n\n```typescript\nimport Fastify from \"fastify\";\nimport helmet from \"@fastify/helmet\";\nimport cors from \"@fastify/cors\";\nimport compress from \"@fastify/compress\";\n\nconst fastify = Fastify({\n  logger: {\n    level: process.env.LOG_LEVEL || \"info\",\n    transport: {\n      target: \"pino-pretty\",\n      options: { colorize: true },\n    },\n  },\n});\n\n// Plugins\nawait fastify.register(helmet);\nawait fastify.register(cors, { origin: true });\nawait fastify.register(compress);\n\n// Type-safe routes with schema validation\nfastify.post<{\n  Body: { name: string; email: string };\n  Reply: { id: string; name: string };\n}>(\n  \"/users\",\n  {\n    schema: {\n      body: {\n        type: \"object\",\n        required: [\"name\", \"email\"],\n        properties: {\n          name: { type: \"string\", minLength: 1 },\n          email: { type: \"string\", format: \"email\" },\n        },\n      },\n    },\n  },\n  async (request, reply) => {\n    const { name, email } = request.body;\n    return { id: \"123\", name };\n  },\n);\n\nawait fastify.listen({ port: 3000, host: \"0.0.0.0\" });\n```\n\n## Architectural Patterns\n\n### Pattern 1: Layered Architecture\n\n**Structure:**\n\n```\nsrc/\n├── controllers/     # Handle HTTP requests/responses\n├── services/        # Business logic\n├── repositories/    # Data access layer\n├── models/          # Data models\n├── middleware/      # Express/Fastify middleware\n├── routes/          # Route definitions\n├── utils/           # Helper functions\n├── config/          # Configuration\n└── types/           # TypeScript types\n```\n\n**Controller Layer:**\n\n```typescript\n// controllers/user.controller.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { UserService } from \"../services/user.service\";\nimport { CreateUserDTO, UpdateUserDTO } from \"../types/user.types\";\n\nexport class UserController {\n  constructor(private userService: UserService) {}\n\n  async createUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const userData: CreateUserDTO = req.body;\n      const user = await this.userService.createUser(userData);\n      res.status(201).json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async getUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const user = await this.userService.getUserById(id);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async updateUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const updates: UpdateUserDTO = req.body;\n      const user = await this.userService.updateUser(id, updates);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async deleteUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      await this.userService.deleteUser(id);\n      res.status(204).send();\n    } catch (error) {\n      next(error);\n    }\n  }\n}\n```\n\n**Service Layer:**\n\n```typescript\n// services/user.service.ts\nimport { UserRepository } from \"../repositories/user.repository\";\nimport { CreateUserDTO, UpdateUserDTO, User } from \"../types/user.types\";\nimport { NotFoundError, ValidationError } from \"../utils/errors\";\nimport bcrypt from \"bcrypt\";\n\nexport class UserService {\n  constructor(private userRepository: UserRepository) {}\n\n  async createUser(userData: CreateUserDTO): Promise<User> {\n    // Validation\n    const existingUser = await this.userRepository.findByEmail(userData.email);\n    if (existingUser) {\n      throw new ValidationError(\"Email already exists\");\n    }\n\n    // Hash password\n    const hashedPassword = await bcrypt.hash(userData.password, 10);\n\n    // Create user\n    const user = await this.userRepository.create({\n      ...userData,\n      password: hashedPassword,\n    });\n\n    // Remove password from response\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async getUserById(id: string): Promise<User> {\n    const user = await this.userRepository.findById(id);\n    if (!user) {\n      throw new NotFoundError(\"User not found\");\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async updateUser(id: string, updates: UpdateUserDTO): Promise<User> {\n    const user = await this.userRepository.update(id, updates);\n    if (!user) {\n      throw new NotFoundError(\"User not found\");\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async deleteUser(id: string): Promise<void> {\n    const deleted = await this.userRepository.delete(id);\n    if (!deleted) {\n      throw new NotFoundError(\"User not found\");\n    }\n  }\n}\n```\n\n**Repository Layer:**\n\n```typescript\n// repositories/user.repository.ts\nimport { Pool } from \"pg\";\nimport { CreateUserDTO, UpdateUserDTO, UserEntity } from \"../types/user.types\";\n\nexport class UserRepository {\n  constructor(private db: Pool) {}\n\n  async create(\n    userData: CreateUserDTO & { password: string },\n  ): Promise<UserEntity> {\n    const query = `\n      INSERT INTO users (name, email, password)\n      VALUES ($1, $2, $3)\n      RETURNING id, name, email, password, created_at, updated_at\n    `;\n    const { rows } = await this.db.query(query, [\n      userData.name,\n      userData.email,\n      userData.password,\n    ]);\n    return rows[0];\n  }\n\n  async findById(id: string): Promise<UserEntity | null> {\n    const query = \"SELECT * FROM users WHERE id = $1\";\n    const { rows } = await this.db.query(query, [id]);\n    return rows[0] || null;\n  }\n\n  async findByEmail(email: string): Promise<UserEntity | null> {\n    const query = \"SELECT * FROM users WHERE email = $1\";\n    const { rows } = await this.db.query(query, [email]);\n    return rows[0] || null;\n  }\n\n  async update(id: string, updates: UpdateUserDTO): Promise<UserEntity | null> {\n    const fields = Object.keys(updates);\n    const values = Object.values(updates);\n\n    const setClause = fields\n      .map((field, idx) => `${field} = $${idx + 2}`)\n      .join(\", \");\n\n    const query = `\n      UPDATE users\n      SET ${setClause}, updated_at = CURRENT_TIMESTAMP\n      WHERE id = $1\n      RETURNING *\n    `;\n\n    const { rows } = await this.db.query(query, [id, ...values]);\n    return rows[0] || null;\n  }\n\n  async delete(id: string): Promise<boolean> {\n    const query = \"DELETE FROM users WHERE id = $1\";\n    const { rowCount } = await this.db.query(query, [id]);\n    return rowCount > 0;\n  }\n}\n```\n\n### Pattern 2: Dependency Injection\n\nUse a DI container to wire up repositories, services, and controllers. For a full container implementation, see [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Middleware Patterns\n\n### Authentication Middleware\n\n```typescript\n// middleware/auth.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport jwt from \"jsonwebtoken\";\nimport { UnauthorizedError } from \"../utils/errors\";\n\ninterface JWTPayload {\n  userId: string;\n  email: string;\n}\n\ndeclare global {\n  namespace Express {\n    interface Request {\n      user?: JWTPayload;\n    }\n  }\n}\n\nexport const authenticate = async (\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  try {\n    const token = req.headers.authorization?.replace(\"Bearer \", \"\");\n\n    if (!token) {\n      throw new UnauthorizedError(\"No token provided\");\n    }\n\n    const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;\n\n    req.user = payload;\n    next();\n  } catch (error) {\n    next(new UnauthorizedError(\"Invalid token\"));\n  }\n};\n\nexport const authorize = (...roles: string[]) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) {\n      return next(new UnauthorizedError(\"Not authenticated\"));\n    }\n\n    // Check if user has required role\n    const hasRole = roles.some((role) => req.user?.roles?.includes(role));\n\n    if (!hasRole) {\n      return next(new UnauthorizedError(\"Insufficient permissions\"));\n    }\n\n    next();\n  };\n};\n```\n\n### Validation Middleware\n\n```typescript\n// middleware/validation.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { AnyZodObject, ZodError } from \"zod\";\nimport { ValidationError } from \"../utils/errors\";\n\nexport const validate = (schema: AnyZodObject) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    try {\n      await schema.parseAsync({\n        body: req.body,\n        query: req.query,\n        params: req.params,\n      });\n      next();\n    } catch (error) {\n      if (error instanceof ZodError) {\n        const errors = error.errors.map((err) => ({\n          field: err.path.join(\".\"),\n          message: err.message,\n        }));\n        next(new ValidationError(\"Validation failed\", errors));\n      } else {\n        next(error);\n      }\n    }\n  };\n};\n\n// Usage with Zod\nimport { z } from \"zod\";\n\nconst createUserSchema = z.object({\n  body: z.object({\n    name: z.string().min(1),\n    email: z.string().email(),\n    password: z.string().min(8),\n  }),\n});\n\nrouter.post(\"/users\", validate(createUserSchema), userController.createUser);\n```\n\n### Rate Limiting Middleware\n\n```typescript\n// middleware/rate-limit.middleware.ts\nimport rateLimit from \"express-rate-limit\";\nimport RedisStore from \"rate-limit-redis\";\nimport Redis from \"ioredis\";\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || \"6379\"),\n});\n\nexport const apiLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: \"rl:\",\n  }),\n  windowMs: 15 * 60 * 1000, // 15 minutes\n  max: 100, // Limit each IP to 100 requests per windowMs\n  message: \"Too many requests from this IP, please try again later\",\n  standardHeaders: true,\n  legacyHeaders: false,\n});\n\nexport const authLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: \"rl:auth:\",\n  }),\n  windowMs: 15 * 60 * 1000,\n  max: 5, // Stricter limit for auth endpoints\n  skipSuccessfulRequests: true,\n});\n```\n\n### Request Logging Middleware\n\n```typescript\n// middleware/logger.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport pino from \"pino\";\n\nconst logger = pino({\n  level: process.env.LOG_LEVEL || \"info\",\n  transport: {\n    target: \"pino-pretty\",\n    options: { colorize: true },\n  },\n});\n\nexport const requestLogger = (\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  const start = Date.now();\n\n  // Log response when finished\n  res.on(\"finish\", () => {\n    const duration = Date.now() - start;\n    logger.info({\n      method: req.method,\n      url: req.url,\n      status: res.statusCode,\n      duration: `${duration}ms`,\n      userAgent: req.headers[\"user-agent\"],\n      ip: req.ip,\n    });\n  });\n\n  next();\n};\n\nexport { logger };\n```\n\n## Error Handling\n\n### Custom Error Classes\n\n```typescript\n// utils/errors.ts\nexport class AppError extends Error {\n  constructor(\n    public message: string,\n    public statusCode: number = 500,\n    public isOperational: boolean = true,\n  ) {\n    super(message);\n    Object.setPrototypeOf(this, AppError.prototype);\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(\n    message: string,\n    public errors?: any[],\n  ) {\n    super(message, 400);\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(message: string = \"Resource not found\") {\n    super(message, 404);\n  }\n}\n\nexport class UnauthorizedError extends AppError {\n  constructor(message: string = \"Unauthorized\") {\n    super(message, 401);\n  }\n}\n\nexport class ForbiddenError extends AppError {\n  constructor(message: string = \"Forbidden\") {\n    super(message, 403);\n  }\n}\n\nexport class ConflictError extends AppError {\n  constructor(message: string) {\n    super(message, 409);\n  }\n}\n```\n\n### Global Error Handler\n\n```typescript\n// middleware/error-handler.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { AppError } from \"../utils/errors\";\nimport { logger } from \"./logger.middleware\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  if (err instanceof AppError) {\n    return res.status(err.statusCode).json({\n      status: \"error\",\n      message: err.message,\n      ...(err instanceof ValidationError && { errors: err.errors }),\n    });\n  }\n\n  // Log unexpected errors\n  logger.error({\n    error: err.message,\n    stack: err.stack,\n    url: req.url,\n    method: req.method,\n  });\n\n  // Don't leak error details in production\n  const message =\n    process.env.NODE_ENV === \"production\"\n      ? \"Internal server error\"\n      : err.message;\n\n  res.status(500).json({\n    status: \"error\",\n    message,\n  });\n};\n\n// Async error wrapper\nexport const asyncHandler = (\n  fn: (req: Request, res: Response, next: NextFunction) => Promise<any>,\n) => {\n  return (req: Request, res: Response, next: NextFunction) => {\n    Promise.resolve(fn(req, res, next)).catch(next);\n  };\n};\n```\n\n## Database Patterns\n\nNode.js supports both SQL and NoSQL databases. Use connection pooling for all production databases.\n\nKey patterns covered in [references/advanced-patterns.md](references/advanced-patterns.md):\n- **PostgreSQL with connection pool** — `pg` Pool configuration and graceful shutdown\n- **MongoDB with Mongoose** — connection management and schema definition\n- **Transaction pattern** — `BEGIN`/`COMMIT`/`ROLLBACK` with `pg` client\n\n## Authentication & Authorization\n\nJWT-based auth with access tokens (short-lived, 15m) and refresh tokens (7d). Full `AuthService` implementation with `bcrypt` password comparison in [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Caching Strategies\n\nRedis-backed `CacheService` with get/set/delete/invalidatePattern, plus a `@Cacheable` decorator for method-level caching. See [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## API Response Format\n\nStandardized `ApiResponse` helper with `success`, `error`, and `paginated` static methods. See [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Best Practices\n\n1. **Use TypeScript**: Type safety prevents runtime errors\n2. **Implement proper error handling**: Use custom error classes\n3. **Validate input**: Use libraries like Zod or Joi\n4. **Use environment variables**: Never hardcode secrets\n5. **Implement logging**: Use structured logging (Pino, Winston)\n6. **Add rate limiting**: Prevent abuse\n7. **Use HTTPS**: Always in production\n8. **Implement CORS properly**: Don't use `*` in production\n9. **Use dependency injection**: Easier testing and maintenance\n10. **Write tests**: Unit, integration, and E2E tests\n11. **Handle graceful shutdown**: Clean up resources\n12. **Use connection pooling**: For databases\n13. **Implement health checks**: For monitoring\n14. **Use compression**: Reduce response size\n15. **Monitor performance**: Use APM tools\n\n## Testing Patterns\n\nSee `javascript-testing-patterns` skill for comprehensive testing guidance.\n",".agents/skills/nodejs-backend-patterns/references/advanced-patterns.md":"# Node.js Advanced Patterns\n\nAdvanced patterns for dependency injection, database integration, authentication, caching, and API response formatting.\n\n## Dependency Injection\n\n### DI Container\n\n```typescript\n// di-container.ts\nimport { Pool } from \"pg\";\nimport { UserRepository } from \"./repositories/user.repository\";\nimport { UserService } from \"./services/user.service\";\nimport { UserController } from \"./controllers/user.controller\";\nimport { AuthService } from \"./services/auth.service\";\n\nclass Container {\n  private instances = new Map<string, any>();\n\n  register<T>(key: string, factory: () => T): void {\n    this.instances.set(key, factory);\n  }\n\n  resolve<T>(key: string): T {\n    const factory = this.instances.get(key);\n    if (!factory) {\n      throw new Error(`No factory registered for ${key}`);\n    }\n    return factory();\n  }\n\n  singleton<T>(key: string, factory: () => T): void {\n    let instance: T;\n    this.instances.set(key, () => {\n      if (!instance) {\n        instance = factory();\n      }\n      return instance;\n    });\n  }\n}\n\nexport const container = new Container();\n\n// Register dependencies\ncontainer.singleton(\n  \"db\",\n  () =>\n    new Pool({\n      host: process.env.DB_HOST,\n      port: parseInt(process.env.DB_PORT || \"5432\"),\n      database: process.env.DB_NAME,\n      user: process.env.DB_USER,\n      password: process.env.DB_PASSWORD,\n      max: 20,\n      idleTimeoutMillis: 30000,\n      connectionTimeoutMillis: 2000,\n    }),\n);\n\ncontainer.singleton(\n  \"userRepository\",\n  () => new UserRepository(container.resolve(\"db\")),\n);\n\ncontainer.singleton(\n  \"userService\",\n  () => new UserService(container.resolve(\"userRepository\")),\n);\n\ncontainer.register(\n  \"userController\",\n  () => new UserController(container.resolve(\"userService\")),\n);\n\ncontainer.singleton(\n  \"authService\",\n  () => new AuthService(container.resolve(\"userRepository\")),\n);\n```\n\n## Database Patterns\n\n### PostgreSQL with Connection Pool\n\n```typescript\n// config/database.ts\nimport { Pool, PoolConfig } from \"pg\";\n\nconst poolConfig: PoolConfig = {\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT || \"5432\"),\n  database: process.env.DB_NAME,\n  user: process.env.DB_USER,\n  password: process.env.DB_PASSWORD,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n};\n\nexport const pool = new Pool(poolConfig);\n\n// Test connection\npool.on(\"connect\", () => {\n  console.log(\"Database connected\");\n});\n\npool.on(\"error\", (err) => {\n  console.error(\"Unexpected database error\", err);\n  process.exit(-1);\n});\n\n// Graceful shutdown\nexport const closeDatabase = async () => {\n  await pool.end();\n  console.log(\"Database connection closed\");\n};\n```\n\n### MongoDB with Mongoose\n\n```typescript\n// config/mongoose.ts\nimport mongoose from \"mongoose\";\n\nconst connectDB = async () => {\n  try {\n    await mongoose.connect(process.env.MONGODB_URI!, {\n      maxPoolSize: 10,\n      serverSelectionTimeoutMS: 5000,\n      socketTimeoutMS: 45000,\n    });\n\n    console.log(\"MongoDB connected\");\n  } catch (error) {\n    console.error(\"MongoDB connection error:\", error);\n    process.exit(1);\n  }\n};\n\nmongoose.connection.on(\"disconnected\", () => {\n  console.log(\"MongoDB disconnected\");\n});\n\nmongoose.connection.on(\"error\", (err) => {\n  console.error(\"MongoDB error:\", err);\n});\n\nexport { connectDB };\n\n// Model example\nimport { Schema, model, Document } from \"mongoose\";\n\ninterface IUser extends Document {\n  name: string;\n  email: string;\n  password: string;\n  createdAt: Date;\n  updatedAt: Date;\n}\n\nconst userSchema = new Schema<IUser>(\n  {\n    name: { type: String, required: true },\n    email: { type: String, required: true, unique: true },\n    password: { type: String, required: true },\n  },\n  {\n    timestamps: true,\n  },\n);\n\n// Indexes\nuserSchema.index({ email: 1 });\n\nexport const User = model<IUser>(\"User\", userSchema);\n```\n\n### Transaction Pattern\n\n```typescript\n// services/order.service.ts\nimport { Pool } from \"pg\";\n\nexport class OrderService {\n  constructor(private db: Pool) {}\n\n  async createOrder(userId: string, items: any[]) {\n    const client = await this.db.connect();\n\n    try {\n      await client.query(\"BEGIN\");\n\n      // Create order\n      const orderResult = await client.query(\n        \"INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id\",\n        [userId, calculateTotal(items)],\n      );\n      const orderId = orderResult.rows[0].id;\n\n      // Create order items\n      for (const item of items) {\n        await client.query(\n          \"INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)\",\n          [orderId, item.productId, item.quantity, item.price],\n        );\n\n        // Update inventory\n        await client.query(\n          \"UPDATE products SET stock = stock - $1 WHERE id = $2\",\n          [item.quantity, item.productId],\n        );\n      }\n\n      await client.query(\"COMMIT\");\n      return orderId;\n    } catch (error) {\n      await client.query(\"ROLLBACK\");\n      throw error;\n    } finally {\n      client.release();\n    }\n  }\n}\n```\n\n## Authentication & Authorization\n\n### JWT Authentication\n\n```typescript\n// services/auth.service.ts\nimport jwt from \"jsonwebtoken\";\nimport bcrypt from \"bcrypt\";\nimport { UserRepository } from \"../repositories/user.repository\";\nimport { UnauthorizedError } from \"../utils/errors\";\n\nexport class AuthService {\n  constructor(private userRepository: UserRepository) {}\n\n  async login(email: string, password: string) {\n    const user = await this.userRepository.findByEmail(email);\n\n    if (!user) {\n      throw new UnauthorizedError(\"Invalid credentials\");\n    }\n\n    const isValid = await bcrypt.compare(password, user.password);\n\n    if (!isValid) {\n      throw new UnauthorizedError(\"Invalid credentials\");\n    }\n\n    const token = this.generateToken({\n      userId: user.id,\n      email: user.email,\n    });\n\n    const refreshToken = this.generateRefreshToken({\n      userId: user.id,\n    });\n\n    return {\n      token,\n      refreshToken,\n      user: {\n        id: user.id,\n        name: user.name,\n        email: user.email,\n      },\n    };\n  }\n\n  async refreshToken(refreshToken: string) {\n    try {\n      const payload = jwt.verify(\n        refreshToken,\n        process.env.REFRESH_TOKEN_SECRET!,\n      ) as { userId: string };\n\n      const user = await this.userRepository.findById(payload.userId);\n\n      if (!user) {\n        throw new UnauthorizedError(\"User not found\");\n      }\n\n      const token = this.generateToken({\n        userId: user.id,\n        email: user.email,\n      });\n\n      return { token };\n    } catch (error) {\n      throw new UnauthorizedError(\"Invalid refresh token\");\n    }\n  }\n\n  private generateToken(payload: any): string {\n    return jwt.sign(payload, process.env.JWT_SECRET!, {\n      expiresIn: \"15m\",\n    });\n  }\n\n  private generateRefreshToken(payload: any): string {\n    return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {\n      expiresIn: \"7d\",\n    });\n  }\n}\n```\n\n## Caching Strategies\n\n```typescript\n// utils/cache.ts\nimport Redis from \"ioredis\";\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || \"6379\"),\n  retryStrategy: (times) => {\n    const delay = Math.min(times * 50, 2000);\n    return delay;\n  },\n});\n\nexport class CacheService {\n  async get<T>(key: string): Promise<T | null> {\n    const data = await redis.get(key);\n    return data ? JSON.parse(data) : null;\n  }\n\n  async set(key: string, value: any, ttl?: number): Promise<void> {\n    const serialized = JSON.stringify(value);\n    if (ttl) {\n      await redis.setex(key, ttl, serialized);\n    } else {\n      await redis.set(key, serialized);\n    }\n  }\n\n  async delete(key: string): Promise<void> {\n    await redis.del(key);\n  }\n\n  async invalidatePattern(pattern: string): Promise<void> {\n    const keys = await redis.keys(pattern);\n    if (keys.length > 0) {\n      await redis.del(...keys);\n    }\n  }\n}\n\n// Cache decorator\nexport function Cacheable(ttl: number = 300) {\n  return function (\n    target: any,\n    propertyKey: string,\n    descriptor: PropertyDescriptor,\n  ) {\n    const originalMethod = descriptor.value;\n\n    descriptor.value = async function (...args: any[]) {\n      const cache = new CacheService();\n      const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;\n\n      const cached = await cache.get(cacheKey);\n      if (cached) {\n        return cached;\n      }\n\n      const result = await originalMethod.apply(this, args);\n      await cache.set(cacheKey, result, ttl);\n\n      return result;\n    };\n\n    return descriptor;\n  };\n}\n```\n\n## API Response Format\n\n```typescript\n// utils/response.ts\nimport { Response } from \"express\";\n\nexport class ApiResponse {\n  static success<T>(\n    res: Response,\n    data: T,\n    message?: string,\n    statusCode = 200,\n  ) {\n    return res.status(statusCode).json({\n      status: \"success\",\n      message,\n      data,\n    });\n  }\n\n  static error(res: Response, message: string, statusCode = 500, errors?: any) {\n    return res.status(statusCode).json({\n      status: \"error\",\n      message,\n      ...(errors && { errors }),\n    });\n  }\n\n  static paginated<T>(\n    res: Response,\n    data: T[],\n    page: number,\n    limit: number,\n    total: number,\n  ) {\n    return res.json({\n      status: \"success\",\n      data,\n      pagination: {\n        page,\n        limit,\n        total,\n        pages: Math.ceil(total / limit),\n      },\n    });\n  }\n}\n```\n",".agents/skills/nodejs-best-practices/SKILL.md":"---\nname: nodejs-best-practices\ndescription: \"Node.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.\"\nrisk: unknown\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# Node.js Best Practices\n\n> Principles and decision-making for Node.js development in 2025.\n> **Learn to THINK, not memorize code patterns.**\n\n## When to Use\nUse this skill when making Node.js architecture decisions, choosing frameworks, designing async patterns, or applying security and deployment best practices.\n\n---\n\n## ⚠️ How to Use This Skill\n\nThis skill teaches **decision-making principles**, not fixed code to copy.\n\n- ASK user for preferences when unclear\n- Choose framework/pattern based on CONTEXT\n- Don't default to same solution every time\n\n---\n\n## 1. Framework Selection (2025)\n\n### Decision Tree\n\n```\nWhat are you building?\n│\n├── Edge/Serverless (Cloudflare, Vercel)\n│   └── Hono (zero-dependency, ultra-fast cold starts)\n│\n├── High Performance API\n│   └── Fastify (2-3x faster than Express)\n│\n├── Enterprise/Team familiarity\n│   └── NestJS (structured, DI, decorators)\n│\n├── Legacy/Stable/Maximum ecosystem\n│   └── Express (mature, most middleware)\n│\n└── Full-stack with frontend\n    └── Next.js API Routes or tRPC\n```\n\n### Comparison Principles\n\n| Factor | Hono | Fastify | Express |\n|--------|------|---------|---------|\n| **Best for** | Edge, serverless | Performance | Legacy, learning |\n| **Cold start** | Fastest | Fast | Moderate |\n| **Ecosystem** | Growing | Good | Largest |\n| **TypeScript** | Native | Excellent | Good |\n| **Learning curve** | Low | Medium | Low |\n\n### Selection Questions to Ask:\n1. What's the deployment target?\n2. Is cold start time critical?\n3. Does team have existing experience?\n4. Is there legacy code to maintain?\n\n---\n\n## 2. Runtime Considerations (2025)\n\n### Native TypeScript\n\n```\nNode.js 22+: --experimental-strip-types\n├── Run .ts files directly\n├── No build step needed for simple projects\n└── Consider for: scripts, simple APIs\n```\n\n### Module System Decision\n\n```\nESM (import/export)\n├── Modern standard\n├── Better tree-shaking\n├── Async module loading\n└── Use for: new projects\n\nCommonJS (require)\n├── Legacy compatibility\n├── More npm packages support\n└── Use for: existing codebases, some edge cases\n```\n\n### Runtime Selection\n\n| Runtime | Best For |\n|---------|----------|\n| **Node.js** | General purpose, largest ecosystem |\n| **Bun** | Performance, built-in bundler |\n| **Deno** | Security-first, built-in TypeScript |\n\n---\n\n## 3. Architecture Principles\n\n### Layered Structure Concept\n\n```\nRequest Flow:\n│\n├── Controller/Route Layer\n│   ├── Handles HTTP specifics\n│   ├── Input validation at boundary\n│   └── Calls service layer\n│\n├── Service Layer\n│   ├── Business logic\n│   ├── Framework-agnostic\n│   └── Calls repository layer\n│\n└── Repository Layer\n    ├── Data access only\n    ├── Database queries\n    └── ORM interactions\n```\n\n### Why This Matters:\n- **Testability**: Mock layers independently\n- **Flexibility**: Swap database without touching business logic\n- **Clarity**: Each layer has single responsibility\n\n### When to Simplify:\n- Small scripts → Single file OK\n- Prototypes → Less structure acceptable\n- Always ask: \"Will this grow?\"\n\n---\n\n## 4. Error Handling Principles\n\n### Centralized Error Handling\n\n```\nPattern:\n├── Create custom error classes\n├── Throw from any layer\n├── Catch at top level (middleware)\n└── Format consistent response\n```\n\n### Error Response Philosophy\n\n```\nClient gets:\n├── Appropriate HTTP status\n├── Error code for programmatic handling\n├── User-friendly message\n└── NO internal details (security!)\n\nLogs get:\n├── Full stack trace\n├── Request context\n├── User ID (if applicable)\n└── Timestamp\n```\n\n### Status Code Selection\n\n| Situation | Status | When |\n|-----------|--------|------|\n| Bad input | 400 | Client sent invalid data |\n| No auth | 401 | Missing or invalid credentials |\n| No permission | 403 | Valid auth, but not allowed |\n| Not found | 404 | Resource doesn't exist |\n| Conflict | 409 | Duplicate or state conflict |\n| Validation | 422 | Schema valid but business rules fail |\n| Server error | 500 | Our fault, log everything |\n\n---\n\n## 5. Async Patterns Principles\n\n### When to Use Each\n\n| Pattern | Use When |\n|---------|----------|\n| `async/await` | Sequential async operations |\n| `Promise.all` | Parallel independent operations |\n| `Promise.allSettled` | Parallel where some can fail |\n| `Promise.race` | Timeout or first response wins |\n\n### Event Loop Awareness\n\n```\nI/O-bound (async helps):\n├── Database queries\n├── HTTP requests\n├── File system\n└── Network operations\n\nCPU-bound (async doesn't help):\n├── Crypto operations\n├── Image processing\n├── Complex calculations\n└── → Use worker threads or offload\n```\n\n### Avoiding Event Loop Blocking\n\n- Never use sync methods in production (fs.readFileSync, etc.)\n- Offload CPU-intensive work\n- Use streaming for large data\n\n---\n\n## 6. Validation Principles\n\n### Validate at Boundaries\n\n```\nWhere to validate:\n├── API entry point (request body/params)\n├── Before database operations\n├── External data (API responses, file uploads)\n└── Environment variables (startup)\n```\n\n### Validation Library Selection\n\n| Library | Best For |\n|---------|----------|\n| **Zod** | TypeScript first, inference |\n| **Valibot** | Smaller bundle (tree-shakeable) |\n| **ArkType** | Performance critical |\n| **Yup** | Existing React Form usage |\n\n### Validation Philosophy\n\n- Fail fast: Validate early\n- Be specific: Clear error messages\n- Don't trust: Even \"internal\" data\n\n---\n\n## 7. Security Principles\n\n### Security Checklist (Not Code)\n\n- [ ] **Input validation**: All inputs validated\n- [ ] **Parameterized queries**: No string concatenation for SQL\n- [ ] **Password hashing**: bcrypt or argon2\n- [ ] **JWT verification**: Always verify signature and expiry\n- [ ] **Rate limiting**: Protect from abuse\n- [ ] **Security headers**: Helmet.js or equivalent\n- [ ] **HTTPS**: Everywhere in production\n- [ ] **CORS**: Properly configured\n- [ ] **Secrets**: Environment variables only\n- [ ] **Dependencies**: Regularly audited\n\n### Security Mindset\n\n```\nTrust nothing:\n├── Query params → validate\n├── Request body → validate\n├── Headers → verify\n├── Cookies → validate\n├── File uploads → scan\n└── External APIs → validate response\n```\n\n---\n\n## 8. Testing Principles\n\n### Test Strategy Selection\n\n| Type | Purpose | Tools |\n|------|---------|-------|\n| **Unit** | Business logic | node:test, Vitest |\n| **Integration** | API endpoints | Supertest |\n| **E2E** | Full flows | Playwright |\n\n### What to Test (Priorities)\n\n1. **Critical paths**: Auth, payments, core business\n2. **Edge cases**: Empty inputs, boundaries\n3. **Error handling**: What happens when things fail?\n4. **Not worth testing**: Framework code, trivial getters\n\n### Built-in Test Runner (Node.js 22+)\n\n```\nnode --test src/**/*.test.ts\n├── No external dependency\n├── Good coverage reporting\n└── Watch mode available\n```\n\n---\n\n## 10. Anti-Patterns to Avoid\n\n### ❌ DON'T:\n- Use Express for new edge projects (use Hono)\n- Use sync methods in production code\n- Put business logic in controllers\n- Skip input validation\n- Hardcode secrets\n- Trust external data without validation\n- Block event loop with CPU work\n\n### ✅ DO:\n- Choose framework based on context\n- Ask user for preferences when unclear\n- Use layered architecture for growing projects\n- Validate all inputs\n- Use environment variables for secrets\n- Profile before optimizing\n\n---\n\n## 11. Decision Checklist\n\nBefore implementing:\n\n- [ ] **Asked user about stack preference?**\n- [ ] **Chosen framework for THIS context?** (not just default)\n- [ ] **Considered deployment target?**\n- [ ] **Planned error handling strategy?**\n- [ ] **Identified validation points?**\n- [ ] **Considered security requirements?**\n\n---\n\n> **Remember**: Node.js best practices are about decision-making, not memorizing patterns. Every project deserves fresh consideration based on its requirements.\n",".agents/skills/performance/SKILL.md":"---\nname: performance\ndescription: Optimize web performance for faster loading and better user experience. Use when asked to \"speed up my site\", \"optimize performance\", \"reduce load time\", \"fix slow loading\", \"improve page speed\", or \"performance audit\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Performance optimization\n\nDeep performance optimization based on Lighthouse performance audits. Focuses on loading speed, runtime efficiency, and resource optimization.\n\n## How it works\n\n1. Identify performance bottlenecks in code and assets\n2. Prioritize by impact on Core Web Vitals\n3. Provide specific optimizations with code examples\n4. Measure improvement with before/after metrics\n\n## Performance budget\n\n| Resource | Budget | Rationale |\n|----------|--------|-----------|\n| Total page weight | < 1.5 MB | 3G loads in ~4s |\n| JavaScript (compressed) | < 300 KB | Parsing + execution time |\n| CSS (compressed) | < 100 KB | Render blocking |\n| Images (above-fold) | < 500 KB | LCP impact |\n| Fonts | < 100 KB | FOIT/FOUT prevention |\n| Third-party | < 200 KB | Uncontrolled latency |\n\n## Critical rendering path\n\n### Server response\n* **TTFB < 800ms.** Time to First Byte should be fast. Use CDN, caching, and efficient backends.\n* **Enable compression.** Gzip or Brotli for text assets. Brotli preferred (15-20% smaller).\n* **HTTP/2 or HTTP/3.** Multiplexing reduces connection overhead.\n* **Edge caching.** Cache HTML at CDN edge when possible.\n\n### Resource loading\n\n**Preconnect to required origins:**\n```html\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://cdn.example.com\" crossorigin>\n```\n\n**Preload critical resources:**\n```html\n<!-- LCP image -->\n<link rel=\"preload\" href=\"/hero.webp\" as=\"image\" fetchpriority=\"high\">\n\n<!-- Critical font -->\n<link rel=\"preload\" href=\"/font.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n```\n\n**Defer non-critical CSS:**\n```html\n<!-- Critical CSS inlined -->\n<style>/* Above-fold styles */</style>\n\n<!-- Non-critical CSS -->\n<link rel=\"preload\" href=\"/styles.css\" as=\"style\" onload=\"this.onload=null;this.rel='stylesheet'\">\n<noscript><link rel=\"stylesheet\" href=\"/styles.css\"></noscript>\n```\n\n### JavaScript optimization\n\n**Defer non-essential scripts:**\n```html\n<!-- Parser-blocking (avoid) -->\n<script src=\"/critical.js\"></script>\n\n<!-- Deferred (preferred) -->\n<script defer src=\"/app.js\"></script>\n\n<!-- Async (for independent scripts) -->\n<script async src=\"/analytics.js\"></script>\n\n<!-- Module (deferred by default) -->\n<script type=\"module\" src=\"/app.mjs\"></script>\n```\n\n**Code splitting patterns:**\n```javascript\n// Route-based splitting\nconst Dashboard = lazy(() => import('./Dashboard'));\n\n// Component-based splitting\nconst HeavyChart = lazy(() => import('./HeavyChart'));\n\n// Feature-based splitting\nif (user.isPremium) {\n  const PremiumFeatures = await import('./PremiumFeatures');\n}\n```\n\n**Tree shaking best practices:**\n```javascript\n// ❌ Imports entire library\nimport _ from 'lodash';\n_.debounce(fn, 300);\n\n// ✅ Imports only what's needed\nimport debounce from 'lodash/debounce';\ndebounce(fn, 300);\n```\n\n## Image optimization\n\n### Format selection\n| Format | Use case | Browser support |\n|--------|----------|-----------------|\n| AVIF | Photos, best compression | 92%+ |\n| WebP | Photos, good fallback | 97%+ |\n| PNG | Graphics with transparency | Universal |\n| SVG | Icons, logos, illustrations | Universal |\n\n### Responsive images\n```html\n<picture>\n  <!-- AVIF for modern browsers -->\n  <source \n    type=\"image/avif\"\n    srcset=\"hero-400.avif 400w,\n            hero-800.avif 800w,\n            hero-1200.avif 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\">\n  \n  <!-- WebP fallback -->\n  <source \n    type=\"image/webp\"\n    srcset=\"hero-400.webp 400w,\n            hero-800.webp 800w,\n            hero-1200.webp 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\">\n  \n  <!-- JPEG fallback -->\n  <img \n    src=\"hero-800.jpg\"\n    srcset=\"hero-400.jpg 400w,\n            hero-800.jpg 800w,\n            hero-1200.jpg 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\"\n    width=\"1200\" \n    height=\"600\"\n    alt=\"Hero image\"\n    loading=\"lazy\"\n    decoding=\"async\">\n</picture>\n```\n\n### LCP image priority\n```html\n<!-- Above-fold LCP image: eager loading, high priority -->\n<img \n  src=\"hero.webp\" \n  fetchpriority=\"high\"\n  loading=\"eager\"\n  decoding=\"sync\"\n  alt=\"Hero\">\n\n<!-- Below-fold images: lazy loading -->\n<img \n  src=\"product.webp\" \n  loading=\"lazy\"\n  decoding=\"async\"\n  alt=\"Product\">\n```\n\n## Font optimization\n\n### Loading strategy\n```css\n/* System font stack as fallback */\nbody {\n  font-family: 'Custom Font', -apple-system, BlinkMacSystemFont, \n               'Segoe UI', Roboto, sans-serif;\n}\n\n/* Prevent invisible text */\n@font-face {\n  font-family: 'Custom Font';\n  src: url('/fonts/custom.woff2') format('woff2');\n  font-display: swap; /* or optional for non-critical */\n  font-weight: 400;\n  font-style: normal;\n  unicode-range: U+0000-00FF; /* Subset to Latin */\n}\n```\n\n### Preloading critical fonts\n```html\n<link rel=\"preload\" href=\"/fonts/heading.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n```\n\n### Variable fonts\n```css\n/* One file instead of multiple weights */\n@font-face {\n  font-family: 'Inter';\n  src: url('/fonts/Inter-Variable.woff2') format('woff2-variations');\n  font-weight: 100 900;\n  font-display: swap;\n}\n```\n\n## Caching strategy\n\n### Cache-Control headers\n```\n# HTML (short or no cache)\nCache-Control: no-cache, must-revalidate\n\n# Static assets with hash (immutable)\nCache-Control: public, max-age=31536000, immutable\n\n# Static assets without hash\nCache-Control: public, max-age=86400, stale-while-revalidate=604800\n\n# API responses\nCache-Control: private, max-age=0, must-revalidate\n```\n\n### Service worker caching\n```javascript\n// Cache-first for static assets\nself.addEventListener('fetch', (event) => {\n  if (event.request.destination === 'image' ||\n      event.request.destination === 'style' ||\n      event.request.destination === 'script') {\n    event.respondWith(\n      caches.match(event.request).then((cached) => {\n        return cached || fetch(event.request).then((response) => {\n          const clone = response.clone();\n          caches.open('static-v1').then((cache) => cache.put(event.request, clone));\n          return response;\n        });\n      })\n    );\n  }\n});\n```\n\n## Runtime performance\n\n### Avoid layout thrashing\n```javascript\n// ❌ Forces multiple reflows\nelements.forEach(el => {\n  const height = el.offsetHeight; // Read\n  el.style.height = height + 10 + 'px'; // Write\n});\n\n// ✅ Batch reads, then batch writes\nconst heights = elements.map(el => el.offsetHeight); // All reads\nelements.forEach((el, i) => {\n  el.style.height = heights[i] + 10 + 'px'; // All writes\n});\n```\n\n### Debounce expensive operations\n```javascript\nfunction debounce(fn, delay) {\n  let timeout;\n  return (...args) => {\n    clearTimeout(timeout);\n    timeout = setTimeout(() => fn(...args), delay);\n  };\n}\n\n// Debounce scroll/resize handlers\nwindow.addEventListener('scroll', debounce(handleScroll, 100));\n```\n\n### Use requestAnimationFrame\n```javascript\n// ❌ May cause jank\nsetInterval(animate, 16);\n\n// ✅ Synced with display refresh\nfunction animate() {\n  // Animation logic\n  requestAnimationFrame(animate);\n}\nrequestAnimationFrame(animate);\n```\n\n### Virtualize long lists\n```javascript\n// For lists > 100 items, render only visible items\n// Use libraries like react-window, vue-virtual-scroller, or native CSS:\n.virtual-list {\n  content-visibility: auto;\n  contain-intrinsic-size: 0 50px; /* Estimated item height */\n}\n```\n\n## Third-party scripts\n\n### Load strategies\n```javascript\n// ❌ Blocks main thread\n<script src=\"https://analytics.example.com/script.js\"></script>\n\n// ✅ Async loading\n<script async src=\"https://analytics.example.com/script.js\"></script>\n\n// ✅ Delay until interaction\n<script>\ndocument.addEventListener('DOMContentLoaded', () => {\n  const observer = new IntersectionObserver((entries) => {\n    if (entries[0].isIntersecting) {\n      const script = document.createElement('script');\n      script.src = 'https://widget.example.com/embed.js';\n      document.body.appendChild(script);\n      observer.disconnect();\n    }\n  });\n  observer.observe(document.querySelector('#widget-container'));\n});\n</script>\n```\n\n### Facade pattern\n```html\n<!-- Show static placeholder until interaction -->\n<div class=\"youtube-facade\" \n     data-video-id=\"abc123\" \n     onclick=\"loadYouTube(this)\">\n  <img src=\"/thumbnails/abc123.jpg\" alt=\"Video title\">\n  <button aria-label=\"Play video\">▶</button>\n</div>\n```\n\n## Measurement\n\n### Key metrics\n| Metric | Target | Tool |\n|--------|--------|------|\n| LCP | < 2.5s | Lighthouse, CrUX |\n| FCP | < 1.8s | Lighthouse |\n| Speed Index | < 3.4s | Lighthouse |\n| TBT | < 200ms | Lighthouse |\n| TTI | < 3.8s | Lighthouse |\n\n### Testing commands\n```bash\n# Lighthouse CLI\nnpx lighthouse https://example.com --output html --output-path report.html\n\n# Web Vitals library\nimport {onLCP, onINP, onCLS} from 'web-vitals';\nonLCP(console.log);\nonINP(console.log);\nonCLS(console.log);\n```\n\n## References\n\nFor Core Web Vitals specific optimizations, see [Core Web Vitals](../core-web-vitals/SKILL.md).\n",".agents/skills/seo/SKILL.md":"---\nname: seo\ndescription: Optimize for search engine visibility and ranking. Use when asked to \"improve SEO\", \"optimize for search\", \"fix meta tags\", \"add structured data\", \"sitemap optimization\", or \"search engine optimization\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# SEO optimization\n\nSearch engine optimization based on Lighthouse SEO audits and Google Search guidelines. Focus on technical SEO, on-page optimization, and structured data.\n\n## SEO fundamentals\n\nSearch ranking factors (approximate influence):\n\n| Factor | Influence | This Skill |\n|--------|-----------|------------|\n| Content quality & relevance | ~40% | Partial (structure) |\n| Backlinks & authority | ~25% | ✗ |\n| Technical SEO | ~15% | ✓ |\n| Page experience (Core Web Vitals) | ~10% | See [Core Web Vitals](../core-web-vitals/SKILL.md) |\n| On-page SEO | ~10% | ✓ |\n\n---\n\n## Technical SEO\n\n### Crawlability\n\n**robots.txt:**\n```text\n# /robots.txt\nUser-agent: *\nAllow: /\n\n# Block admin/private areas\nDisallow: /admin/\nDisallow: /api/\nDisallow: /private/\n\n# Don't block resources needed for rendering\n# ❌ Disallow: /static/\n\nSitemap: https://example.com/sitemap.xml\n```\n\n**Meta robots:**\n```html\n<!-- Default: indexable, followable -->\n<meta name=\"robots\" content=\"index, follow\">\n\n<!-- Noindex specific pages -->\n<meta name=\"robots\" content=\"noindex, nofollow\">\n\n<!-- Indexable but don't follow links -->\n<meta name=\"robots\" content=\"index, nofollow\">\n\n<!-- Control snippets -->\n<meta name=\"robots\" content=\"max-snippet:150, max-image-preview:large\">\n```\n\n**Canonical URLs:**\n```html\n<!-- Prevent duplicate content issues -->\n<link rel=\"canonical\" href=\"https://example.com/page\">\n\n<!-- Self-referencing canonical (recommended) -->\n<link rel=\"canonical\" href=\"https://example.com/current-page\">\n\n<!-- For paginated content -->\n<link rel=\"canonical\" href=\"https://example.com/products\">\n<!-- Or use rel=\"prev\" / rel=\"next\" for explicit pagination -->\n```\n\n### XML sitemap\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n  <url>\n    <loc>https://example.com/</loc>\n    <lastmod>2024-01-15</lastmod>\n    <changefreq>daily</changefreq>\n    <priority>1.0</priority>\n  </url>\n  <url>\n    <loc>https://example.com/products</loc>\n    <lastmod>2024-01-14</lastmod>\n    <changefreq>weekly</changefreq>\n    <priority>0.8</priority>\n  </url>\n</urlset>\n```\n\n**Sitemap best practices:**\n- Maximum 50,000 URLs or 50MB per sitemap\n- Use sitemap index for larger sites\n- Include only canonical, indexable URLs\n- Update `lastmod` when content changes\n- Submit to Google Search Console\n\n### URL structure\n\n```\n✅ Good URLs:\nhttps://example.com/products/blue-widget\nhttps://example.com/blog/how-to-use-widgets\n\n❌ Poor URLs:\nhttps://example.com/p?id=12345\nhttps://example.com/products/item/category/subcategory/blue-widget-2024-sale-discount\n```\n\n**URL guidelines:**\n- Use hyphens, not underscores\n- Lowercase only\n- Keep short (< 75 characters)\n- Include target keywords naturally\n- Avoid parameters when possible\n- Use HTTPS always\n\n### HTTPS & security\n\n```html\n<!-- Ensure all resources use HTTPS -->\n<img src=\"https://example.com/image.jpg\">\n\n<!-- Not: -->\n<img src=\"http://example.com/image.jpg\">\n```\n\n**Security headers for SEO trust signals:**\n```\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff\nX-Frame-Options: DENY\n```\n\n---\n\n## On-page SEO\n\n### Title tags\n\n```html\n<!-- ❌ Missing or generic -->\n<title>Page</title>\n<title>Home</title>\n\n<!-- ✅ Descriptive with primary keyword -->\n<title>Blue Widgets for Sale | Premium Quality | Example Store</title>\n```\n\n**Title tag guidelines:**\n- 50-60 characters (Google truncates ~60)\n- Primary keyword near the beginning\n- Unique for every page\n- Brand name at end (unless homepage)\n- Action-oriented when appropriate\n\n### Meta descriptions\n\n```html\n<!-- ❌ Missing or duplicate -->\n<meta name=\"description\" content=\"\">\n\n<!-- ✅ Compelling and unique -->\n<meta name=\"description\" content=\"Shop premium blue widgets with free shipping. 30-day returns. Rated 4.9/5 by 10,000+ customers. Order today and save 20%.\">\n```\n\n**Meta description guidelines:**\n- 150-160 characters\n- Include primary keyword naturally\n- Compelling call-to-action\n- Unique for every page\n- Matches page content\n\n### Heading structure\n\n```html\n<!-- ❌ Poor structure -->\n<h2>Welcome to Our Store</h2>\n<h4>Products</h4>\n<h1>Contact Us</h1>\n\n<!-- ✅ Proper hierarchy -->\n<h1>Blue Widgets - Premium Quality</h1>\n  <h2>Product Features</h2>\n    <h3>Durability</h3>\n    <h3>Design</h3>\n  <h2>Customer Reviews</h2>\n  <h2>Pricing</h2>\n```\n\n**Heading guidelines:**\n- Single `<h1>` per page (the main topic)\n- Logical hierarchy (don't skip levels)\n- Include keywords naturally\n- Descriptive, not generic\n\n### Image SEO\n\n```html\n<!-- ❌ Poor image SEO -->\n<img src=\"IMG_12345.jpg\">\n\n<!-- ✅ Optimized image -->\n<img src=\"blue-widget-product-photo.webp\"\n     alt=\"Blue widget with chrome finish, side view showing control panel\"\n     width=\"800\"\n     height=\"600\"\n     loading=\"lazy\">\n```\n\n**Image guidelines:**\n- Descriptive filenames with keywords\n- Alt text describes the image content\n- Compressed and properly sized\n- WebP/AVIF with fallbacks\n- Lazy load below-fold images\n\n### Internal linking\n\n```html\n<!-- ❌ Non-descriptive -->\n<a href=\"/products\">Click here</a>\n<a href=\"/widgets\">Read more</a>\n\n<!-- ✅ Descriptive anchor text -->\n<a href=\"/products/blue-widgets\">Browse our blue widget collection</a>\n<a href=\"/guides/widget-maintenance\">Learn how to maintain your widgets</a>\n```\n\n**Linking guidelines:**\n- Descriptive anchor text with keywords\n- Link to relevant internal pages\n- Reasonable number of links per page\n- Fix broken links promptly\n- Use breadcrumbs for hierarchy\n\n---\n\n## Structured data (JSON-LD)\n\n### Organization\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Organization\",\n  \"name\": \"Example Company\",\n  \"url\": \"https://example.com\",\n  \"logo\": \"https://example.com/logo.png\",\n  \"sameAs\": [\n    \"https://twitter.com/example\",\n    \"https://linkedin.com/company/example\"\n  ],\n  \"contactPoint\": {\n    \"@type\": \"ContactPoint\",\n    \"telephone\": \"+1-555-123-4567\",\n    \"contactType\": \"customer service\"\n  }\n}\n</script>\n```\n\n### Article\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Article\",\n  \"headline\": \"How to Choose the Right Widget\",\n  \"description\": \"Complete guide to selecting widgets for your needs.\",\n  \"image\": \"https://example.com/article-image.jpg\",\n  \"author\": {\n    \"@type\": \"Person\",\n    \"name\": \"Jane Smith\",\n    \"url\": \"https://example.com/authors/jane-smith\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"Example Blog\",\n    \"logo\": {\n      \"@type\": \"ImageObject\",\n      \"url\": \"https://example.com/logo.png\"\n    }\n  },\n  \"datePublished\": \"2024-01-15\",\n  \"dateModified\": \"2024-01-20\"\n}\n</script>\n```\n\n### Product\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Product\",\n  \"name\": \"Blue Widget Pro\",\n  \"image\": \"https://example.com/blue-widget.jpg\",\n  \"description\": \"Premium blue widget with advanced features.\",\n  \"brand\": {\n    \"@type\": \"Brand\",\n    \"name\": \"WidgetCo\"\n  },\n  \"offers\": {\n    \"@type\": \"Offer\",\n    \"price\": \"49.99\",\n    \"priceCurrency\": \"USD\",\n    \"availability\": \"https://schema.org/InStock\",\n    \"url\": \"https://example.com/products/blue-widget\"\n  },\n  \"aggregateRating\": {\n    \"@type\": \"AggregateRating\",\n    \"ratingValue\": \"4.8\",\n    \"reviewCount\": \"1250\"\n  }\n}\n</script>\n```\n\n### FAQ\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What colors are available?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Our widgets come in blue, red, and green.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What is the warranty?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"All widgets include a 2-year warranty.\"\n      }\n    }\n  ]\n}\n</script>\n```\n\n### Breadcrumbs\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https://example.com\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Products\",\n      \"item\": \"https://example.com/products\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Blue Widgets\",\n      \"item\": \"https://example.com/products/blue-widgets\"\n    }\n  ]\n}\n</script>\n```\n\n### Validation\n\nTest structured data at:\n- [Google Rich Results Test](https://search.google.com/test/rich-results)\n- [Schema.org Validator](https://validator.schema.org/)\n\n---\n\n## Mobile SEO\n\n### Responsive design\n\n```html\n<!-- ❌ Not mobile-friendly -->\n<meta name=\"viewport\" content=\"width=1024\">\n\n<!-- ✅ Responsive viewport -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n```\n\n### Tap targets\n\n```css\n/* ❌ Too small for mobile */\n.small-link {\n  padding: 4px;\n  font-size: 12px;\n}\n\n/* ✅ Adequate tap target */\n.mobile-friendly-link {\n  padding: 12px;\n  font-size: 16px;\n  min-height: 48px;\n  min-width: 48px;\n}\n```\n\n### Font sizes\n\n```css\n/* ❌ Too small on mobile */\nbody {\n  font-size: 10px;\n}\n\n/* ✅ Readable without zooming */\nbody {\n  font-size: 16px;\n  line-height: 1.5;\n}\n```\n\n---\n\n## International SEO\n\n### Hreflang tags\n\n```html\n<!-- For multi-language sites -->\n<link rel=\"alternate\" hreflang=\"en\" href=\"https://example.com/page\">\n<link rel=\"alternate\" hreflang=\"es\" href=\"https://example.com/es/page\">\n<link rel=\"alternate\" hreflang=\"fr\" href=\"https://example.com/fr/page\">\n<link rel=\"alternate\" hreflang=\"x-default\" href=\"https://example.com/page\">\n```\n\n### Language declaration\n\n```html\n<html lang=\"en\">\n<!-- or -->\n<html lang=\"es-MX\">\n```\n\n---\n\n## SEO audit checklist\n\n### Critical\n- [ ] HTTPS enabled\n- [ ] robots.txt allows crawling\n- [ ] No `noindex` on important pages\n- [ ] Title tags present and unique\n- [ ] Single `<h1>` per page\n\n### High priority\n- [ ] Meta descriptions present\n- [ ] Sitemap submitted\n- [ ] Canonical URLs set\n- [ ] Mobile-responsive\n- [ ] Core Web Vitals passing\n\n### Medium priority\n- [ ] Structured data implemented\n- [ ] Internal linking strategy\n- [ ] Image alt text\n- [ ] Descriptive URLs\n- [ ] Breadcrumb navigation\n\n### Ongoing\n- [ ] Fix crawl errors in Search Console\n- [ ] Update sitemap when content changes\n- [ ] Monitor ranking changes\n- [ ] Check for broken links\n- [ ] Review Search Console insights\n\n---\n\n## Tools\n\n| Tool | Use |\n|------|-----|\n| Google Search Console | Monitor indexing, fix issues |\n| Google PageSpeed Insights | Performance + Core Web Vitals |\n| Rich Results Test | Validate structured data |\n| Lighthouse | Full SEO audit |\n| Screaming Frog | Crawl analysis |\n\n## References\n\n- [Google Search Central](https://developers.google.com/search)\n- [Schema.org](https://schema.org/)\n- [Core Web Vitals](../core-web-vitals/SKILL.md)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n",".agents/skills/tailwind-css-patterns/SKILL.md":"---\nname: tailwind-css-patterns\ndescription: Provides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or optimizing CSS workflow.\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash\n---\n\n# Tailwind CSS Development Patterns\n\nExpert guide for building modern, responsive user interfaces with Tailwind CSS utility-first framework. Covers v4.1+ features including CSS-first configuration, custom utilities, and enhanced developer experience.\n\n## Overview\n\nProvides actionable patterns for responsive, accessible UIs with Tailwind CSS v4.1+. Covers utility composition, dark mode, component patterns, and performance optimization.\n\n## When to Use\n\n- Styling React/Vue/Svelte components\n- Building responsive layouts and grids\n- Implementing design systems\n- Adding dark mode support\n- Optimizing CSS workflow\n\n## Quick Reference\n\n### Responsive Breakpoints\n\n| Prefix | Min Width | Description |\n|--------|-----------|-------------|\n| `sm:` | 640px | Small screens |\n| `md:` | 768px | Tablets |\n| `lg:` | 1024px | Desktops |\n| `xl:` | 1280px | Large screens |\n| `2xl:` | 1536px | Extra large |\n\n### Common Patterns\n\n```html\n<!-- Center content -->\n<div class=\"flex items-center justify-center min-h-screen\">\n  Content\n</div>\n\n<!-- Responsive grid -->\n<div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n  <!-- Items -->\n</div>\n\n<!-- Card component -->\n<div class=\"bg-white rounded-lg shadow-lg p-6\">\n  <h3 class=\"text-xl font-bold\">Title</h3>\n  <p class=\"text-gray-600\">Description</p>\n</div>\n```\n\n## Instructions\n\n1. **Start Mobile-First**: Write base styles for mobile, add responsive prefixes (`sm:`, `md:`, `lg:`) for larger screens\n2. **Use Design Tokens**: Leverage Tailwind's spacing, color, and typography scales\n3. **Compose Utilities**: Combine multiple utilities for complex styles\n4. **Extract Components**: Create reusable component classes for repeated patterns\n5. **Configure Theme**: Customize design tokens in `tailwind.config.js` or using `@theme`\n6. **Verify Changes**: Test at each breakpoint using DevTools responsive mode. Check for visual regressions and accessibility issues before committing.\n\n## Examples\n\n### Responsive Card Component\n\n```tsx\nfunction ProductCard({ product }: { product: Product }) {\n  return (\n    <div className=\"bg-white rounded-lg shadow-lg overflow-hidden sm:flex\">\n      <img className=\"h-48 w-full object-cover sm:h-auto sm:w-48\" src={product.image} />\n      <div className=\"p-6\">\n        <h3 className=\"text-lg font-semibold\">{product.name}</h3>\n        <button className=\"mt-4 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700\">\n          Add to Cart\n        </button>\n      </div>\n    </div>\n  );\n}\n```\n\n### Dark Mode Toggle\n\n```html\n<div class=\"bg-white dark:bg-gray-900 text-gray-900 dark:text-white\">\n  <h1 class=\"dark:text-white\">Title</h1>\n</div>\n```\n\n### Form Input\n\n```html\n<input\n  class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n  placeholder=\"you@example.com\"\n/>\n```\n\n## Best Practices\n\n1. **Consistent Spacing**: Use Tailwind's spacing scale (4, 8, 12, 16, etc.)\n2. **Color Palette**: Stick to Tailwind's color system for consistency\n3. **Component Extraction**: Extract repeated patterns into reusable components\n4. **Utility Composition**: Prefer utility classes over `@apply` for maintainability\n5. **Semantic HTML**: Use proper HTML elements with Tailwind classes\n6. **Performance**: Ensure content paths include all template files for optimal purging\n7. **Accessibility**: Include focus styles, ARIA labels, and respect user preferences (reduced-motion)\n\n## Troubleshooting\n\n### Classes Not Applying\n- **Check content paths**: Ensure all template files are included in `content: []` in config\n- **Verify build**: Run `npm run build` to regenerate purged CSS\n- **Dev mode**: Use `npx tailwindcss -o` with `--watch` flag for live updates\n\n### Responsive Styles Not Working\n- **Order matters**: Responsive prefixes must come before non-responsive (e.g., `md:flex` not `flex md:flex`)\n- **Check breakpoint values**: Verify breakpoints match your design requirements\n- **DevTools**: Use browser DevTools responsive mode to test at each breakpoint\n\n### Dark Mode Issues\n- **Verify config**: Ensure `darkMode: 'class'` or `'media'` is set correctly\n- **Toggle implementation**: Use `document.documentElement.classList.toggle('dark')` for class strategy\n- **Initial flash**: Add `dark` class to `<html>` before body renders\n\n## Constraints and Warnings\n\n- **Class Proliferation**: Long class strings reduce readability; extract into components\n- **Content Paths**: Misconfigured paths cause classes to be purged in production\n- **Arbitrary Values**: Use sparingly; prefer design tokens for consistency\n- **Specificity Issues**: Avoid `@apply` with complex selectors\n- **Dark Mode**: Requires correct configuration (`class` or `media` strategy)\n- **Browser Support**: Check Tailwind docs for compatibility notes\n\n## References\n\n- **[references/layout-patterns.md](references/layout-patterns.md)** — Flexbox, grid, spacing, typography, colors\n- **[references/component-patterns.md](references/component-patterns.md)** — Cards, navigation, forms, modals, React patterns\n- **[references/responsive-design.md](references/responsive-design.md)** — Responsive patterns, dark mode, container queries\n- **[references/animations.md](references/animations.md)** — Transitions, transforms, built-in animations, motion preferences\n- **[references/performance.md](references/performance.md)** — Bundle optimization, CSS optimization, production builds\n- **[references/accessibility.md](references/accessibility.md)** — Focus management, screen readers, color contrast, ARIA\n- **[references/configuration.md](references/configuration.md)** — CSS-first config, JavaScript config, plugins, presets\n- **[references/reference.md](references/reference.md)** — Additional reference materials\n\n## External Resources\n\n- [Tailwind CSS Docs](https://tailwindcss.com/docs)\n- [Tailwind UI](https://tailwindui.com)\n- [Tailwind Play](https://play.tailwindcss.com)\n",".agents/skills/tailwind-css-patterns/references/accessibility.md":"# Tailwind CSS Accessibility Guidelines\n\n## Focus Management\n\n```html\n<!-- Custom focus styles that meet WCAG AA -->\n<button class=\"focus:outline-none focus:ring-4 focus:ring-blue-500 focus:ring-offset-2\">\n  Accessible Button\n</button>\n\n<!-- Skip links for keyboard navigation -->\n<a href=\"#main-content\" class=\"sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4\">\n  Skip to main content\n</a>\n```\n\n### Focus Visible vs Focus\n\n```html\n<!-- Only show focus ring on keyboard navigation -->\n<button class=\"focus-visible:ring-2 focus-visible:ring-blue-500\">\n  Keyboard-only focus indicator\n</button>\n```\n\n---\n\n## Screen Reader Support\n\n```html\n<!-- Semantic buttons with ARIA labels -->\n<button aria-label=\"Close dialog\" class=\"p-2\">\n  <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\">\n    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n  </svg>\n</button>\n\n<!-- Descriptive links -->\n<a href=\"/docs\" aria-describedby=\"docs-description\">\n  Documentation\n</a>\n<p id=\"docs-description\" class=\"sr-only\">\n  Learn how to use our API and integration guides\n</p>\n\n<!-- Live regions for dynamic content -->\n<div aria-live=\"polite\" class=\"sr-only\">\n  <p>3 new notifications</p>\n</div>\n```\n\n### Screen Reader Only Content\n\n```html\n<span class=\"sr-only\">Opens in new window</span>\n```\n\n---\n\n## Color Contrast\n\n```html\n<!-- Ensure sufficient contrast ratios -->\n<div class=\"bg-gray-900 text-white\">\n  High contrast text (WCAG AAA)\n</div>\n\n<div class=\"bg-blue-500 text-blue-100\">\n  Good contrast on colored backgrounds\n</div>\n\n<!-- Use contrast utilities for testing -->\n<div class=\"bg-red-500 text-white contrast-more:bg-red-600 contrast-more:text-red-100\">\n  Adjusts for high contrast mode\n</div>\n```\n\n### Contrast Guidelines\n\n- **Normal text**: Minimum 4.5:1 contrast ratio\n- **Large text**: Minimum 3:1 contrast ratio\n- **UI components**: Minimum 3:1 contrast ratio\n\n---\n\n## Motion Preferences\n\n```html\n<!-- Respect prefers-reduced-motion -->\n<div class=\"transform transition-transform motion-reduce:transition-none\">\n  Doesn't animate when user prefers reduced motion\n</div>\n\n<!-- Conditional animations -->\n<div class=\"animate-pulse motion-safe:hover:animate-spin\">\n  Only animates when motion is preferred\n</div>\n```\n\n### Reduced Motion Media Query\n\n```css\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n  }\n}\n```\n\n---\n\n## ARIA Patterns with Tailwind\n\n### Toggle Button\n\n```html\n<button\n  type=\"button\"\n  role=\"switch\"\n  aria-checked=\"false\"\n  class=\"relative inline-flex h-6 w-11 items-center rounded-full bg-gray-200 aria-checked:bg-blue-600\"\n>\n  <span class=\"sr-only\">Enable notifications</span>\n  <span class=\"inline-block h-4 w-4 transform rounded-full bg-white transition translate-x-1 aria-checked:translate-x-6\"></span>\n</button>\n```\n\n### Alert Dialog\n\n```html\n<div\n  role=\"alertdialog\"\n  aria-labelledby=\"alert-title\"\n  aria-describedby=\"alert-description\"\n  class=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50\"\n>\n  <div class=\"max-w-md w-full bg-white rounded-lg shadow-xl p-6\">\n    <h2 id=\"alert-title\" class=\"text-lg font-bold mb-2\">\n      Are you sure?\n    </h2>\n    <p id=\"alert-description\" class=\"text-gray-600 mb-4\">\n      This action cannot be undone.\n    </p>\n    <div class=\"flex justify-end gap-2\">\n      <button class=\"px-4 py-2 text-gray-600\">Cancel</button>\n      <button class=\"px-4 py-2 bg-red-500 text-white rounded\">Delete</button>\n    </div>\n  </div>\n</div>\n```\n\n---\n\n## Accessibility Checklist\n\n- [ ] All interactive elements have visible focus indicators\n- [ ] Color contrast meets WCAG standards\n- [ ] Images have alt text\n- [ ] Form inputs have associated labels\n- [ ] ARIA labels used for icon-only buttons\n- [ ] Animations respect reduced motion preference\n- [ ] Skip links provided for keyboard navigation\n- [ ] Landmarks (header, main, nav, footer) used appropriately\n",".agents/skills/tailwind-css-patterns/references/animations.md":"# Tailwind CSS Animations & Transitions\n\n## Basic Transitions\n\n```html\n<button class=\"bg-blue-500 hover:bg-blue-700 transition duration-300\">\n  Smooth transition\n</button>\n```\n\n### Transition Properties\n\n| Class | Description |\n|-------|-------------|\n| `transition` | All properties |\n| `transition-colors` | Color properties only |\n| `transition-opacity` | Opacity only |\n| `transition-transform` | Transform only |\n| `duration-150` | 150ms duration |\n| `duration-300` | 300ms duration |\n| `ease-in` | Ease in timing |\n| `ease-out` | Ease out timing |\n| `ease-in-out` | Ease in-out timing |\n\n---\n\n## Transform Effects\n\n```html\n<!-- Scale on hover -->\n<div class=\"transform hover:scale-110 transition duration-300\">\n  Scale on hover\n</div>\n\n<!-- Rotate on hover -->\n<img class=\"transform hover:rotate-6 transition duration-300\" />\n\n<!-- Multiple transforms -->\n<div class=\"transform hover:scale-105 hover:-translate-y-1 transition\">\n  Scale up and move up\n</div>\n```\n\n---\n\n## Built-in Animations\n\n```html\n<div class=\"animate-spin\">Spinning</div>\n<div class=\"animate-pulse\">Pulsing</div>\n<div class=\"animate-bounce\">Bouncing</div>\n<div class=\"animate-ping\">Pinging (radar effect)</div>\n```\n\n### Common Use Cases\n\n```html\n<!-- Loading spinner -->\n<svg class=\"animate-spin h-5 w-5 text-blue-500\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n  <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n  <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n</svg>\n\n<!-- Skeleton loading -->\n<div class=\"animate-pulse space-y-4\">\n  <div class=\"h-4 bg-gray-200 rounded w-3/4\"></div>\n  <div class=\"h-4 bg-gray-200 rounded\"></div>\n  <div class=\"h-4 bg-gray-200 rounded w-5/6\"></div>\n</div>\n\n<!-- Notification badge -->\n<span class=\"absolute -top-1 -right-1 h-3 w-3 animate-ping rounded-full bg-red-400 opacity-75\"></span>\n<span class=\"absolute -top-1 -right-1 h-3 w-3 rounded-full bg-red-500\"></span>\n```\n\n---\n\n## Custom Animations (v4.1+)\n\n```css\n/* In your CSS with @theme */\n@theme {\n  --animate-fade-in: fadeIn 0.5s ease-in-out;\n  --animate-slide-up: slideUp 0.3s ease-out;\n}\n\n@keyframes fadeIn {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n@keyframes slideUp {\n  from {\n    opacity: 0;\n    transform: translateY(20px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n```\n\nUsage:\n\n```html\n<div class=\"animate-fade-in\">Fades in on load</div>\n<div class=\"animate-slide-up\">Slides up on load</div>\n```\n\n---\n\n## Motion Preferences\n\nRespect user's motion preferences:\n\n```html\n<!-- Disable animations for users who prefer reduced motion -->\n<div class=\"transform transition-transform motion-reduce:transition-none\">\n  Doesn't animate when user prefers reduced motion\n</div>\n\n<!-- Only animate when motion is preferred -->\n<div class=\"animate-pulse motion-safe:hover:animate-spin\">\n  Only animates when motion is preferred\n</div>\n```\n\n### Global Reduced Motion Support\n\n```css\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n  }\n}\n```\n",".agents/skills/tailwind-css-patterns/references/component-patterns.md":"# Tailwind CSS Component Patterns\n\n## Card Component\n\n```html\n<div class=\"bg-white rounded-lg shadow-lg overflow-hidden\">\n  <img class=\"w-full h-48 object-cover\" src=\"image.jpg\" alt=\"Card image\" />\n  <div class=\"p-6\">\n    <h3 class=\"text-xl font-bold mb-2\">Card Title</h3>\n    <p class=\"text-gray-700 mb-4\">Card description text goes here.</p>\n    <button class=\"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded\">\n      Action\n    </button>\n  </div>\n</div>\n```\n\n## Responsive User Card\n\n```html\n<div class=\"max-w-sm mx-auto bg-white rounded-xl shadow-lg overflow-hidden sm:flex sm:max-w-2xl\">\n  <img class=\"h-48 w-full object-cover sm:h-auto sm:w-48\"\n       src=\"profile.jpg\"\n       alt=\"Profile\" />\n  <div class=\"p-8\">\n    <div class=\"uppercase tracking-wide text-sm text-indigo-500 font-semibold\">\n      Product Engineer\n    </div>\n    <h2 class=\"mt-1 text-xl font-semibold text-gray-900\">\n      John Doe\n    </h2>\n    <p class=\"mt-2 text-gray-500\">\n      Building amazing products with modern technology.\n    </p>\n    <button class=\"mt-4 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition\">\n      Contact\n    </button>\n  </div>\n</div>\n```\n\n## Navigation Bar\n\n```html\n<nav class=\"bg-white shadow-lg\">\n  <div class=\"container mx-auto px-4\">\n    <div class=\"flex justify-between items-center h-16\">\n      <div class=\"flex items-center\">\n        <a href=\"#\" class=\"text-xl font-bold text-gray-800\">Logo</a>\n      </div>\n      <div class=\"hidden md:flex space-x-8\">\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Home</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">About</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Services</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Contact</a>\n      </div>\n      <button class=\"md:hidden\">\n        <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n          <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path>\n        </svg>\n      </button>\n    </div>\n  </div>\n</nav>\n```\n\n## Form Elements\n\n```html\n<form class=\"space-y-6 max-w-md mx-auto\">\n  <div>\n    <label class=\"block text-sm font-medium text-gray-700 mb-2\">\n      Email\n    </label>\n    <input\n      type=\"email\"\n      class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n      placeholder=\"you@example.com\"\n    />\n  </div>\n\n  <div>\n    <label class=\"block text-sm font-medium text-gray-700 mb-2\">\n      Password\n    </label>\n    <input\n      type=\"password\"\n      class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n    />\n  </div>\n\n  <div class=\"flex items-center\">\n    <input type=\"checkbox\" class=\"mr-2\" />\n    <label class=\"text-sm text-gray-600\">Remember me</label>\n  </div>\n\n  <button\n    type=\"submit\"\n    class=\"w-full bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition\"\n  >\n    Sign In\n  </button>\n</form>\n```\n\n## Modal/Dialog\n\n```html\n<div class=\"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4\">\n  <div class=\"bg-white rounded-lg shadow-xl max-w-md w-full p-6\">\n    <div class=\"flex justify-between items-center mb-4\">\n      <h3 class=\"text-xl font-bold\">Modal Title</h3>\n      <button class=\"text-gray-500 hover:text-gray-700\">\n        <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n          <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n        </svg>\n      </button>\n    </div>\n    <p class=\"text-gray-700 mb-6\">\n      Modal content goes here.\n    </p>\n    <div class=\"flex justify-end space-x-4\">\n      <button class=\"px-4 py-2 text-gray-600 hover:text-gray-800\">\n        Cancel\n      </button>\n      <button class=\"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700\">\n        Confirm\n      </button>\n    </div>\n  </div>\n</div>\n```\n\n## React Button Component with Variants\n\n```tsx\nimport { useState } from 'react';\n\nfunction Button({\n  variant = 'primary',\n  size = 'md',\n  children\n}: {\n  variant?: 'primary' | 'secondary';\n  size?: 'sm' | 'md' | 'lg';\n  children: React.ReactNode;\n}) {\n  const baseClasses = 'font-semibold rounded transition';\n\n  const variantClasses = {\n    primary: 'bg-blue-600 text-white hover:bg-blue-700',\n    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',\n  };\n\n  const sizeClasses = {\n    sm: 'px-3 py-1 text-sm',\n    md: 'px-4 py-2 text-base',\n    lg: 'px-6 py-3 text-lg',\n  };\n\n  return (\n    <button\n      className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}\n    >\n      {children}\n    </button>\n  );\n}\n```\n",".agents/skills/tailwind-css-patterns/references/configuration.md":"# Tailwind CSS Configuration\n\n## CSS-First Configuration (v4.1+)\n\nUse the `@theme` directive for CSS-based configuration:\n\n```css\n/* src/styles.css */\n@import \"tailwindcss\";\n\n@theme {\n  /* Custom colors */\n  --color-brand-50: #f0f9ff;\n  --color-brand-500: #3b82f6;\n  --color-brand-900: #1e3a8a;\n\n  /* Custom fonts */\n  --font-display: \"Inter\", system-ui, sans-serif;\n  --font-mono: \"Fira Code\", monospace;\n\n  /* Custom spacing */\n  --spacing-128: 32rem;\n\n  /* Custom animations */\n  --animate-fade-in: fadeIn 0.5s ease-in-out;\n\n  /* Custom breakpoints */\n  --breakpoint-3xl: 1920px;\n}\n\n/* Define custom animations */\n@keyframes fadeIn {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n/* Custom utilities */\n@utility content-auto {\n  content-visibility: auto;\n}\n```\n\n---\n\n## JavaScript Configuration (Legacy)\n\n```javascript\n/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\n    \"./index.html\",\n    \"./src/**/*.{js,ts,jsx,tsx,vue,svelte}\",\n  ],\n  theme: {\n    extend: {\n      colors: {\n        primary: {\n          50: '#f0f9ff',\n          500: '#3b82f6',\n          900: '#1e3a8a',\n        },\n      },\n      fontFamily: {\n        sans: ['Inter', 'system-ui', 'sans-serif'],\n      },\n      spacing: {\n        '128': '32rem',\n      },\n    },\n  },\n  plugins: [],\n}\n```\n\n---\n\n## Vite Integration (v4.1+)\n\n```javascript\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n  plugins: [\n    tailwindcss(),\n  ],\n})\n```\n\n---\n\n## Advanced v4.1 Features\n\n### Native CSS Custom Properties\n\n```html\n<div class=\"bg-[var(--color-brand-500)] text-[var(--color-white)]\">\n  Using CSS custom properties directly\n</div>\n```\n\n### Enhanced Arbitrary Values\n\n```html\n<!-- Complex grid with custom tracks -->\n<div class=\"grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4\">\n  Responsive grid without custom CSS\n</div>\n\n<!-- Custom animation timing -->\n<div class=\"animate-bounce ease-[cubic-bezier(0.68,-0.55,0.265,1.55)]\">\n  Bounce with custom easing\n</div>\n```\n\n### Custom Utilities\n\n```css\n@utility content-auto {\n  content-visibility: auto;\n}\n\n@utility text-shadow {\n  text-shadow: 0 2px 4px rgba(0,0,0,0.1);\n}\n```\n\n---\n\n## Plugins\n\n### Custom Plugin Example\n\n```javascript\n// tailwind.config.js\nconst plugin = require('tailwindcss/plugin')\n\nmodule.exports = {\n  plugins: [\n    plugin(function({ addUtilities, addComponents, e, config }) {\n      // Add custom utilities\n      addUtilities({\n        '.content-auto': {\n          contentVisibility: 'auto',\n        },\n      })\n\n      // Add custom components\n      addComponents({\n        '.btn': {\n          padding: '.5rem 1rem',\n          borderRadius: '.25rem',\n          fontWeight: '600',\n        },\n      })\n    }),\n  ],\n}\n```\n\n---\n\n## Presets\n\n### Creating a Reusable Preset\n\n```javascript\n// tailwind-preset.js\nmodule.exports = {\n  theme: {\n    extend: {\n      colors: {\n        brand: {\n          DEFAULT: '#3b82f6',\n          light: '#60a5fa',\n          dark: '#1d4ed8',\n        },\n      },\n    },\n  },\n  plugins: [\n    require('@tailwindcss/forms'),\n    require('@tailwindcss/typography'),\n  ],\n}\n```\n\n### Using a Preset\n\n```javascript\n// tailwind.config.js\nmodule.exports = {\n  presets: [\n    require('./tailwind-preset.js'),\n  ],\n}\n```\n",".agents/skills/tailwind-css-patterns/references/layout-patterns.md":"# Tailwind CSS Layout Patterns\n\n## Layout Utilities\n\n### Flexbox Layouts\n\nBasic flex container:\n\n```html\n<div class=\"flex items-center justify-between\">\n  <div>Left</div>\n  <div>Center</div>\n  <div>Right</div>\n</div>\n```\n\nResponsive flex direction:\n\n```html\n<div class=\"flex flex-col md:flex-row gap-4\">\n  <div class=\"flex-1\">Item 1</div>\n  <div class=\"flex-1\">Item 2</div>\n  <div class=\"flex-1\">Item 3</div>\n</div>\n```\n\nCommon flex patterns:\n\n```html\n<!-- Center content -->\n<div class=\"flex items-center justify-center min-h-screen\">\n  <div>Centered Content</div>\n</div>\n\n<!-- Space between items -->\n<div class=\"flex justify-between items-center\">\n  <span>Left</span>\n  <span>Right</span>\n</div>\n\n<!-- Vertical stack with gap -->\n<div class=\"flex flex-col gap-4\">\n  <div>Item 1</div>\n  <div>Item 2</div>\n</div>\n```\n\n### Grid Layouts\n\nBasic grid:\n\n```html\n<div class=\"grid grid-cols-3 gap-4\">\n  <div>Column 1</div>\n  <div>Column 2</div>\n  <div>Column 3</div>\n</div>\n```\n\nResponsive grid:\n\n```html\n<div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6\">\n  <!-- 1 column mobile, 2 tablet, 4 desktop -->\n  <div>Item 1</div>\n  <div>Item 2</div>\n  <div>Item 3</div>\n  <div>Item 4</div>\n</div>\n```\n\nAuto-fit columns:\n\n```html\n<div class=\"grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4\">\n  <!-- Automatically fit columns based on container width -->\n</div>\n```\n\n### Container & Max Width\n\nCentered container with max width:\n\n```html\n<div class=\"container mx-auto px-4 max-w-7xl\">\n  <!-- Centered content with padding -->\n</div>\n```\n\nResponsive max width:\n\n```html\n<div class=\"w-full max-w-md mx-auto\">\n  <!-- Max 448px width, centered -->\n</div>\n```\n\n---\n\n## Spacing\n\n### Padding & Margin\n\nUniform spacing:\n\n```html\n<div class=\"p-4\">Padding all sides</div>\n<div class=\"m-4\">Margin all sides</div>\n```\n\nIndividual sides:\n\n```html\n<div class=\"pt-4 pr-8 pb-4 pl-8\">\n  <!-- Top 1rem, Right 2rem, Bottom 1rem, Left 2rem -->\n</div>\n```\n\nAxis-based spacing:\n\n```html\n<div class=\"px-4 py-8\">\n  <!-- Horizontal padding 1rem, Vertical padding 2rem -->\n</div>\n```\n\nResponsive spacing:\n\n```html\n<div class=\"p-4 md:p-8 lg:p-12\">\n  <!-- Increases padding at larger breakpoints -->\n</div>\n```\n\nSpace between children:\n\n```html\n<div class=\"space-y-4\">\n  <div>Item 1</div>\n  <div>Item 2</div>\n  <div>Item 3</div>\n</div>\n```\n\n---\n\n## Typography\n\n### Font Size & Weight\n\n```html\n<h1 class=\"text-4xl font-bold\">Large Heading</h1>\n<h2 class=\"text-2xl font-semibold\">Subheading</h2>\n<p class=\"text-base font-normal\">Body text</p>\n<small class=\"text-sm text-gray-600\">Small text</small>\n```\n\nResponsive typography:\n\n```html\n<h1 class=\"text-2xl md:text-4xl lg:text-6xl font-bold\">\n  Responsive Heading\n</h1>\n```\n\n### Line Height & Letter Spacing\n\n```html\n<p class=\"leading-relaxed tracking-wide\">\n  Text with relaxed line height and wide letter spacing\n</p>\n```\n\n### Text Alignment\n\n```html\n<p class=\"text-left md:text-center\">\n  Left aligned on mobile, centered on tablet+\n</p>\n```\n\n---\n\n## Colors\n\n### Background Colors\n\n```html\n<div class=\"bg-blue-500\">Blue background</div>\n<div class=\"bg-gray-100\">Light gray background</div>\n<div class=\"bg-gradient-to-r from-blue-500 to-purple-600\">\n  Gradient background\n</div>\n```\n\n### Text Colors\n\n```html\n<p class=\"text-gray-900\">Dark text</p>\n<p class=\"text-blue-600\">Blue text</p>\n<p class=\"text-red-500\">Error text</p>\n```\n\n### Opacity\n\n```html\n<div class=\"bg-blue-500 bg-opacity-50\">\n  Semi-transparent blue\n</div>\n```\n\n---\n\n## Responsive Breakpoints Reference\n\n| Prefix | Min Width | CSS Equivalent |\n|--------|-----------|----------------|\n| `sm:` | 640px | `@media (min-width: 640px)` |\n| `md:` | 768px | `@media (min-width: 768px)` |\n| `lg:` | 1024px | `@media (min-width: 1024px)` |\n| `xl:` | 1280px | `@media (min-width: 1280px)` |\n| `2xl:` | 1536px | `@media (min-width: 1536px)` |\n",".agents/skills/tailwind-css-patterns/references/performance.md":"# Tailwind CSS Performance Optimization\n\n## Bundle Size Optimization\n\nConfigure content sources for optimal purging:\n\n```javascript\n// tailwind.config.js\nexport default {\n  content: [\n    \"./index.html\",\n    \"./src/**/*.{js,ts,jsx,tsx,vue,svelte}\",\n    \"./node_modules/@mycompany/ui-lib/**/*.{js,ts,jsx,tsx}\",\n  ],\n  // Enable JIT for faster builds\n  jit: true,\n}\n```\n\n### Content Path Best Practices\n\n1. **Be specific**: Don't use `\"./**/*\"` - it scans too many files\n2. **Include UI libraries**: Add paths to component libraries\n3. **Exclude tests**: Don't include test files in content paths\n\n---\n\n## CSS Optimization Techniques\n\n```html\n<!-- Use content-visibility for offscreen content -->\n<div class=\"content-visibility-auto\">\n  <div>Heavy content that's initially offscreen</div>\n</div>\n\n<!-- Optimize images with aspect-ratio -->\n<img class=\"aspect-video w-full object-cover\" src=\"video.jpg\" alt=\"Video thumbnail\" />\n\n<!-- Use contain for paint optimization -->\n<div class=\"contain-layout\">\n  Complex layout that doesn't affect outside elements\n</div>\n```\n\n---\n\n## Development Performance (v4.1+)\n\n```css\n/* Enable CSS-first configuration in v4.1 */\n@import \"tailwindcss\";\n\n@theme {\n  /* Define once, use everywhere */\n  --color-brand: #3b82f6;\n  --font-mono: \"Fira Code\", monospace;\n}\n\n/* Critical CSS for above-the-fold content */\n@layer critical {\n  .hero-title {\n    @apply text-4xl md:text-6xl font-bold;\n  }\n}\n```\n\n---\n\n## Production Build Optimization\n\n### PurgeCSS Configuration\n\n```javascript\n// tailwind.config.js\nmodule.exports = {\n  purge: {\n    enabled: process.env.NODE_ENV === 'production',\n    content: [\n      './src/**/*.html',\n      './src/**/*.jsx',\n      './src/**/*.tsx',\n    ],\n    options: {\n      safelist: [\n        'bg-red-500',\n        'text-center',\n        // Classes that shouldn't be purged\n      ],\n    },\n  },\n}\n```\n\n### Minification\n\n```bash\n# Use cssnano for minification\nnpm install -D cssnano\n\n# In postcss.config.js\nmodule.exports = {\n  plugins: [\n    require('tailwindcss'),\n    require('autoprefixer'),\n    ...(process.env.NODE_ENV === 'production' ? [require('cssnano')] : []),\n  ],\n}\n```\n\n---\n\n## Best Practices for Performance\n\n1. **Use the JIT engine**: Faster builds, smaller output\n2. **Configure content correctly**: Only include files that use Tailwind\n3. **Minimize custom CSS**: Use Tailwind utilities over custom CSS\n4. **Enable purging in production**: Removes unused styles\n5. **Use `@layer` for custom styles**: Helps with organization and purging\n6. **Avoid `@apply` in components**: Prefer composing utilities in markup\n",".agents/skills/tailwind-css-patterns/references/reference.md":"# Tailwind CSS Documentation\n\nTailwind CSS is a utility-first CSS framework that generates styles by scanning HTML, JavaScript, and template files for class names. It provides a comprehensive design system through CSS utility classes, enabling rapid UI development without writing custom CSS. The framework operates at build-time, analyzing source files and generating only the CSS classes actually used in the project, resulting in optimized production bundles with zero runtime overhead.\n\nThe framework includes an extensive default color palette (18 colors with 11 shades each), responsive breakpoint system, customizable design tokens via CSS custom properties, and support for dark mode, pseudo-classes, pseudo-elements, and media queries through variant prefixes. Tailwind CSS v4.1 introduces CSS-first configuration using the `@theme` directive, native support for custom utilities via `@utility`, seamless integration with modern build tools through Vite, PostCSS, and framework-specific plugins, and enhanced arbitrary value syntax for maximum flexibility.\n\n## Installation with Vite\n\nInstalling Tailwind CSS using the Vite plugin for modern JavaScript frameworks.\n\n```bash\n# Create a new Vite project\nnpm create vite@latest my-project\ncd my-project\n\n# Install Tailwind CSS and Vite plugin\nnpm install tailwindcss @tailwindcss/vite\n```\n\n```javascript\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n plugins: [\n tailwindcss(),\n ],\n})\n```\n\n```css\n/* src/style.css */\n@import \"tailwindcss\";\n```\n\n```html\n\n\n\n# Hello world!\n\n```\n\n## Utility Classes with Variants\n\nApplying conditional styles using variant prefixes for hover, focus, and responsive breakpoints.\n\n```html\n\n Save changes\n\nContent adapts to color scheme preference\n\n Submit\n\n```\n\n## Custom Theme Configuration\n\nDefining custom design tokens using the `@theme` directive in CSS.\n\n```css\n/* app.css */\n@import \"tailwindcss\";\n\n@theme {\n /* Custom fonts */\n --font-display: \"Satoshi\", \"sans-serif\";\n --font-body: \"Inter\", system-ui, sans-serif;\n\n /* Custom colors */\n --color-brand-50: oklch(0.98 0.02 264);\n --color-brand-100: oklch(0.95 0.05 264);\n --color-brand-500: oklch(0.55 0.22 264);\n --color-brand-900: oklch(0.25 0.12 264);\n\n /* Custom breakpoints */\n --breakpoint-3xl: 120rem;\n --breakpoint-4xl: 160rem;\n\n /* Custom spacing */\n --spacing-18: calc(var(--spacing) * 18);\n\n /* Custom animations */\n --ease-fluid: cubic-bezier(0.3, 0, 0, 1);\n --ease-snappy: cubic-bezier(0.2, 0, 0, 1);\n}\n```\n\n```html\n\nCustom design system\n\n```\n\n## Arbitrary Values\n\nUsing square bracket notation for one-off custom values without leaving HTML.\n\n```html\n\nPixel-perfect positioning\n\nCustom hex colors, font sizes, and content\n\nAny CSS property\n\nReference custom properties\n\nComplex grid layouts\n\nFont size from CSS variable\n\nColor from CSS variable\n\n```\n\n## Color System\n\nWorking with Tailwind's comprehensive color palette and opacity modifiers.\n\n```html\n\nColor utilities across all properties\n\nAlpha channel with percentage\n\nArbitrary opacity values\n\nOpacity from CSS variable\n\nAdapts to color scheme\n\n\n```\n\n## Dark Mode\n\nImplementing dark mode with CSS media queries or manual toggle.\n\n```html\n\nContent automatically adapts\n\n\n```\n\n```css\n/* Manual dark mode toggle with class selector */\n@import \"tailwindcss\";\n\n@custom-variant dark (&:where(.dark, .dark *));\n```\n\n```html\n\nControlled by .dark class\n\n\n\n\n```\n\n```javascript\n// Dark mode toggle logic\n// On page load or theme change\ndocument.documentElement.classList.toggle(\n \"dark\",\n localStorage.theme === \"dark\" ||\n (!(\"theme\" in localStorage) && window.matchMedia(\"(prefers-color-scheme: dark)\").matches)\n);\n\n// User chooses light mode\nlocalStorage.theme = \"light\";\n\n// User chooses dark mode\nlocalStorage.theme = \"dark\";\n\n// User chooses system preference\nlocalStorage.removeItem(\"theme\");\n```\n\n## State Variants\n\nStyling elements based on pseudo-classes and parent/sibling state.\n\n```html\n\n- Item content\n\n\n**Title**\nDescription\n\nPlease provide a valid email address.\n\n\n Option\n\n```\n\n## Responsive Design\n\nBuilding mobile-first responsive layouts with breakpoint variants.\n\n```html\n\n# Responsive heading\n\nText scales with viewport\n\n\nDesktop only\n\nMobile only\n\nCustom breakpoint\n\nBelow medium\n\n```\n\n## Custom Utilities\n\nCreating reusable custom utility classes with variant support.\n\n```css\n/* Simple custom utility */\n@utility content-auto {\n content-visibility: auto;\n}\n\n/* Complex utility with nesting */\n@utility scrollbar-hidden {\n &::-webkit-scrollbar {\n display: none;\n }\n}\n\n/* Functional utility with theme values */\n@theme {\n --tab-size-2: 2;\n --tab-size-4: 4;\n --tab-size-github: 8;\n}\n\n@utility tab-* {\n tab-size: --value(--tab-size-*);\n}\n\n/* Supporting arbitrary, bare, and theme values */\n@utility opacity-* {\n opacity: --value([percentage]);\n opacity: calc(--value(integer) * 1%);\n opacity: --value(--opacity-*);\n}\n\n/* Utility with modifiers */\n@utility text-* {\n font-size: --value(--text-*, [length]);\n line-height: --modifier(--leading-*, [length], [*]);\n}\n\n/* Negative value support */\n@utility inset-* {\n inset: --spacing(--value(integer));\n inset: --value([percentage], [length]);\n}\n\n@utility -inset-* {\n inset: --spacing(--value(integer) * -1);\n inset: calc(--value([percentage], [length]) * -1);\n}\n```\n\n```html\n\nCustom utilities work with variants\n\nVariants and arbitrary values supported\n\nUtility with modifier (font-size/line-height)\n\n```\n\n## Custom Variants\n\nRegistering custom conditional styles with the `@custom-variant` directive.\n\n```css\n/* Simple custom variant */\n@custom-variant theme-midnight (&:where([data-theme=\"midnight\"] *));\n\n/* Variant with media query */\n@custom-variant any-hover {\n @media (any-hover: hover) {\n &:hover {\n @slot;\n }\n }\n}\n\n/* ARIA state variant */\n@custom-variant aria-asc (&[aria-sort=\"ascending\"]);\n@custom-variant aria-desc (&[aria-sort=\"descending\"]);\n\n/* Data attribute variant */\n@custom-variant data-checked (&[data-ui~=\"checked\"]);\n```\n\n```html\n\n Midnight theme button\n\n\n Sortable column\n\nChecked state\n\nOne-off custom selectors\n\n```\n\n## Applying Variants in CSS\n\nUsing the `@variant` directive to apply variants within custom CSS.\n\n```css\n/* Single variant */\n.my-element {\n background: white;\n\n @variant dark {\n background: black;\n }\n}\n\n/* Nested variants */\n.my-button {\n background: white;\n\n @variant dark {\n background: gray;\n\n @variant hover {\n background: black;\n }\n }\n}\n\n/* Compiled output */\n.my-element {\n background: white;\n}\n\n@media (prefers-color-scheme: dark) {\n .my-element {\n background: black;\n }\n}\n```\n\n## Layer Organization\n\nOrganizing custom styles into Tailwind's cascade layers.\n\n```css\n@import \"tailwindcss\";\n\n/* Base styles for HTML elements */\n@layer base {\n h1 {\n font-size: var(--text-2xl);\n font-weight: bold;\n }\n\n h2 {\n font-size: var(--text-xl);\n font-weight: 600;\n }\n\n body {\n font-family: var(--font-body);\n }\n}\n\n/* Reusable component classes */\n@layer components {\n .btn {\n padding: --spacing(2) --spacing(4);\n border-radius: var(--radius);\n font-weight: 600;\n transition: all 150ms;\n }\n\n .btn-primary {\n background-color: var(--color-blue-500);\n color: white;\n }\n\n .card {\n background-color: var(--color-white);\n border-radius: var(--radius-lg);\n padding: --spacing(6);\n box-shadow: var(--shadow-xl);\n }\n\n /* Third-party component overrides */\n .select2-dropdown {\n border-radius: var(--radius);\n box-shadow: var(--shadow-lg);\n }\n}\n```\n\n```html\n\nSquare corners despite card class\n\n Component with utility overrides\n\n```\n\n## Functions and Directives\n\nUsing Tailwind's CSS functions for dynamic values and opacity adjustments.\n\n```css\n/* Alpha function for opacity */\n.my-element {\n color: --alpha(var(--color-lime-300) / 50%);\n background: --alpha(var(--color-blue-500) / 25%);\n}\n\n/* Spacing function */\n.my-element {\n margin: --spacing(4);\n padding: calc(--spacing(6) - 1px);\n}\n\n/* In arbitrary values */\n\n/* Source directive for additional content */\n@source \"../node_modules/@my-company/ui-lib\";\n\n/* Apply directive for inline utilities */\n.select2-dropdown {\n @apply rounded-b-lg shadow-md;\n}\n\n.select2-search {\n @apply rounded border border-gray-300;\n}\n\n.select2-results__group {\n @apply text-lg font-bold text-gray-900;\n}\n```\n\n## Pseudo-elements\n\nStyling ::before, ::after, ::placeholder, and other pseudo-elements.\n\n```html\n\n Email\n\n\n- First item\n- Second item\n\nSelect this text to see custom colors\n\nTypography with pseudo-elements\n\n```\n\n## Media Queries\n\nConditional styling based on user preferences and device capabilities.\n\n```html\n\n Respects user preference\n\n Only animates if motion allowed\n\n Adjusts for contrast needs\n\n\nHidden in portrait mode\n\nLayout adapts to orientation\n\n Not shown when printing\n\nOnly visible in print\n\nProgressive enhancement\n\n```\n\n## Summary\n\nTailwind CSS provides a complete utility-first design system that eliminates the need for writing custom CSS in most cases. The framework's primary use cases include rapid prototyping, building production applications with consistent design systems, creating responsive layouts, implementing dark mode, and maintaining design consistency across large teams. By using utility classes directly in markup, developers can iterate quickly, avoid naming conventions, and prevent CSS bloat since only used styles are generated.\n\nThe v4.1 release enhances the developer experience with CSS-first configuration, eliminating JavaScript configuration files for most projects. Integration patterns include using the Vite plugin for modern frameworks, PostCSS for custom build pipelines, the Tailwind CLI for simple projects, and CDN scripts for rapid prototyping. The framework excels at component-driven development when combined with React, Vue, Svelte, or other modern frameworks, where utility classes are co-located with component logic. Custom design systems can be fully defined in CSS using `@theme`, with project-specific utilities and variants extending the framework's capabilities without writing JavaScript plugins.",".agents/skills/tailwind-css-patterns/references/responsive-design.md":"# Tailwind CSS Responsive Design & Dark Mode\n\n## Responsive Design Patterns\n\n### Mobile-First Responsive Layout\n\n```html\n<div class=\"container mx-auto px-4\">\n  <!-- Hero Section -->\n  <div class=\"flex flex-col md:flex-row items-center gap-8 py-12\">\n    <div class=\"flex-1\">\n      <h1 class=\"text-3xl md:text-5xl font-bold mb-4\">\n        Welcome to Our Site\n      </h1>\n      <p class=\"text-lg text-gray-600 mb-6\">\n        Build amazing things with Tailwind CSS\n      </p>\n      <button class=\"bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700\">\n        Get Started\n      </button>\n    </div>\n    <div class=\"flex-1\">\n      <img src=\"hero.jpg\" class=\"w-full rounded-lg shadow-lg\" />\n    </div>\n  </div>\n</div>\n```\n\n### Responsive Grid Gallery\n\n```html\n<div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4\">\n  <div class=\"aspect-square bg-gray-200 rounded-lg overflow-hidden\">\n    <img src=\"image1.jpg\" class=\"w-full h-full object-cover hover:scale-105 transition\" />\n  </div>\n  <div class=\"aspect-square bg-gray-200 rounded-lg overflow-hidden\">\n    <img src=\"image2.jpg\" class=\"w-full h-full object-cover hover:scale-105 transition\" />\n  </div>\n  <!-- More items... -->\n</div>\n```\n\n### Responsive Card Component\n\n```tsx\nfunction ProductCard({ product }: { product: Product }) {\n  return (\n    <div className=\"bg-white rounded-lg shadow-lg overflow-hidden\n                    sm:flex sm:max-w-2xl\">\n      <img\n        className=\"h-48 w-full object-cover sm:h-auto sm:w-48\"\n        src={product.image}\n        alt={product.name}\n      />\n      <div className=\"p-6\">\n        <h3 className=\"text-lg font-semibold text-gray-900\">\n          {product.name}\n        </h3>\n        <p className=\"mt-2 text-gray-600\">\n          {product.description}\n        </p>\n        <button className=\"mt-4 px-4 py-2 bg-indigo-600 text-white\n                          rounded-lg hover:bg-indigo-700 transition\">\n          Add to Cart\n        </button>\n      </div>\n    </div>\n  );\n}\n```\n\n---\n\n## Dark Mode\n\n### Basic Dark Mode Support\n\n```html\n<div class=\"bg-white dark:bg-gray-900 text-gray-900 dark:text-white\">\n  <h1 class=\"text-gray-900 dark:text-white\">Title</h1>\n  <p class=\"text-gray-600 dark:text-gray-400\">Description</p>\n</div>\n```\n\nEnable dark mode in tailwind.config.js:\n\n```javascript\nmodule.exports = {\n  darkMode: 'class', // or 'media'\n  // ...\n}\n```\n\n### Dark Mode Toggle (React)\n\n```tsx\nfunction ThemeToggle() {\n  const [darkMode, setDarkMode] = useState(false);\n\n  useEffect(() => {\n    if (darkMode) {\n      document.documentElement.classList.add('dark');\n    } else {\n      document.documentElement.classList.remove('dark');\n    }\n  }, [darkMode]);\n\n  return (\n    <button\n      onClick={() => setDarkMode(!darkMode)}\n      className=\"p-2 rounded-lg bg-gray-200 dark:bg-gray-800\"\n    >\n      {darkMode ? '🌙' : '☀️'}\n    </button>\n  );\n}\n```\n\n### Dark Mode Best Practices\n\n1. **Use semantic color names**: Instead of `bg-white` use `bg-surface` custom color\n2. **Test with real content**: Some colors look good in light but not in dark\n3. **Respect system preference**: Use `darkMode: 'media'` for OS-level preference\n4. **Smooth transitions**: Add transition utilities for theme changes\n\n```css\n/* Global transition for theme changes */\n* {\n  @apply transition-colors duration-200;\n}\n```\n\n---\n\n## Container Queries (v4.1+)\n\nComponent that responds to its container size, not viewport:\n\n```html\n<div class=\"@container\">\n  <div class=\"@lg:text-xl @2xl:text-2xl\">\n    Text size based on container, not viewport\n  </div>\n</div>\n```\n\nUsage in a card component:\n\n```html\n<div class=\"@container w-full\">\n  <div class=\"flex flex-col @[400px]:flex-row gap-4\">\n    <img class=\"w-full @[400px]:w-32 h-32 object-cover\" src=\"image.jpg\" />\n    <div>\n      <h3 class=\"text-base @[400px]:text-lg font-bold\">Title</h3>\n      <p class=\"text-sm @[400px]:text-base\">Description</p>\n    </div>\n  </div>\n</div>\n```\n",".agents/skills/typescript-advanced-types/SKILL.md":"---\nname: typescript-advanced-types\ndescription: Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.\n---\n\n# TypeScript Advanced Types\n\nComprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.\n\n## When to Use This Skill\n\n- Building type-safe libraries or frameworks\n- Creating reusable generic components\n- Implementing complex type inference logic\n- Designing type-safe API clients\n- Building form validation systems\n- Creating strongly-typed configuration objects\n- Implementing type-safe state management\n- Migrating JavaScript codebases to TypeScript\n\n## Core Concepts\n\n### 1. Generics\n\n**Purpose:** Create reusable, type-flexible components while maintaining type safety.\n\n**Basic Generic Function:**\n\n```typescript\nfunction identity<T>(value: T): T {\n  return value;\n}\n\nconst num = identity<number>(42); // Type: number\nconst str = identity<string>(\"hello\"); // Type: string\nconst auto = identity(true); // Type inferred: boolean\n```\n\n**Generic Constraints:**\n\n```typescript\ninterface HasLength {\n  length: number;\n}\n\nfunction logLength<T extends HasLength>(item: T): T {\n  console.log(item.length);\n  return item;\n}\n\nlogLength(\"hello\"); // OK: string has length\nlogLength([1, 2, 3]); // OK: array has length\nlogLength({ length: 10 }); // OK: object has length\n// logLength(42);             // Error: number has no length\n```\n\n**Multiple Type Parameters:**\n\n```typescript\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n\nconst merged = merge({ name: \"John\" }, { age: 30 });\n// Type: { name: string } & { age: number }\n```\n\n### 2. Conditional Types\n\n**Purpose:** Create types that depend on conditions, enabling sophisticated type logic.\n\n**Basic Conditional Type:**\n\n```typescript\ntype IsString<T> = T extends string ? true : false;\n\ntype A = IsString<string>; // true\ntype B = IsString<number>; // false\n```\n\n**Extracting Return Types:**\n\n```typescript\ntype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;\n\nfunction getUser() {\n  return { id: 1, name: \"John\" };\n}\n\ntype User = ReturnType<typeof getUser>;\n// Type: { id: number; name: string; }\n```\n\n**Distributive Conditional Types:**\n\n```typescript\ntype ToArray<T> = T extends any ? T[] : never;\n\ntype StrOrNumArray = ToArray<string | number>;\n// Type: string[] | number[]\n```\n\n**Nested Conditions:**\n\n```typescript\ntype TypeName<T> = T extends string\n  ? \"string\"\n  : T extends number\n    ? \"number\"\n    : T extends boolean\n      ? \"boolean\"\n      : T extends undefined\n        ? \"undefined\"\n        : T extends Function\n          ? \"function\"\n          : \"object\";\n\ntype T1 = TypeName<string>; // \"string\"\ntype T2 = TypeName<() => void>; // \"function\"\n```\n\n### 3. Mapped Types\n\n**Purpose:** Transform existing types by iterating over their properties.\n\n**Basic Mapped Type:**\n\n```typescript\ntype Readonly<T> = {\n  readonly [P in keyof T]: T[P];\n};\n\ninterface User {\n  id: number;\n  name: string;\n}\n\ntype ReadonlyUser = Readonly<User>;\n// Type: { readonly id: number; readonly name: string; }\n```\n\n**Optional Properties:**\n\n```typescript\ntype Partial<T> = {\n  [P in keyof T]?: T[P];\n};\n\ntype PartialUser = Partial<User>;\n// Type: { id?: number; name?: string; }\n```\n\n**Key Remapping:**\n\n```typescript\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\ninterface Person {\n  name: string;\n  age: number;\n}\n\ntype PersonGetters = Getters<Person>;\n// Type: { getName: () => string; getAge: () => number; }\n```\n\n**Filtering Properties:**\n\n```typescript\ntype PickByType<T, U> = {\n  [K in keyof T as T[K] extends U ? K : never]: T[K];\n};\n\ninterface Mixed {\n  id: number;\n  name: string;\n  age: number;\n  active: boolean;\n}\n\ntype OnlyNumbers = PickByType<Mixed, number>;\n// Type: { id: number; age: number; }\n```\n\n### 4. Template Literal Types\n\n**Purpose:** Create string-based types with pattern matching and transformation.\n\n**Basic Template Literal:**\n\n```typescript\ntype EventName = \"click\" | \"focus\" | \"blur\";\ntype EventHandler = `on${Capitalize<EventName>}`;\n// Type: \"onClick\" | \"onFocus\" | \"onBlur\"\n```\n\n**String Manipulation:**\n\n```typescript\ntype UppercaseGreeting = Uppercase<\"hello\">; // \"HELLO\"\ntype LowercaseGreeting = Lowercase<\"HELLO\">; // \"hello\"\ntype CapitalizedName = Capitalize<\"john\">; // \"John\"\ntype UncapitalizedName = Uncapitalize<\"John\">; // \"john\"\n```\n\n**Path Building:**\n\n```typescript\ntype Path<T> = T extends object\n  ? {\n      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;\n    }[keyof T]\n  : never;\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n  };\n  database: {\n    url: string;\n  };\n}\n\ntype ConfigPath = Path<Config>;\n// Type: \"server\" | \"database\" | \"server.host\" | \"server.port\" | \"database.url\"\n```\n\n### 5. Utility Types\n\n**Built-in Utility Types:**\n\n```typescript\n// Partial<T> - Make all properties optional\ntype PartialUser = Partial<User>;\n\n// Required<T> - Make all properties required\ntype RequiredUser = Required<PartialUser>;\n\n// Readonly<T> - Make all properties readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick<T, K> - Select specific properties\ntype UserName = Pick<User, \"name\" | \"email\">;\n\n// Omit<T, K> - Remove specific properties\ntype UserWithoutPassword = Omit<User, \"password\">;\n\n// Exclude<T, U> - Exclude types from union\ntype T1 = Exclude<\"a\" | \"b\" | \"c\", \"a\">; // \"b\" | \"c\"\n\n// Extract<T, U> - Extract types from union\ntype T2 = Extract<\"a\" | \"b\" | \"c\", \"a\" | \"b\">; // \"a\" | \"b\"\n\n// NonNullable<T> - Exclude null and undefined\ntype T3 = NonNullable<string | null | undefined>; // string\n\n// Record<K, T> - Create object type with keys K and values T\ntype PageInfo = Record<\"home\" | \"about\", { title: string }>;\n```\n\n## Advanced Patterns\n\n### Pattern 1: Type-Safe Event Emitter\n\n```typescript\ntype EventMap = {\n  \"user:created\": { id: string; name: string };\n  \"user:updated\": { id: string };\n  \"user:deleted\": { id: string };\n};\n\nclass TypedEventEmitter<T extends Record<string, any>> {\n  private listeners: {\n    [K in keyof T]?: Array<(data: T[K]) => void>;\n  } = {};\n\n  on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {\n    if (!this.listeners[event]) {\n      this.listeners[event] = [];\n    }\n    this.listeners[event]!.push(callback);\n  }\n\n  emit<K extends keyof T>(event: K, data: T[K]): void {\n    const callbacks = this.listeners[event];\n    if (callbacks) {\n      callbacks.forEach((callback) => callback(data));\n    }\n  }\n}\n\nconst emitter = new TypedEventEmitter<EventMap>();\n\nemitter.on(\"user:created\", (data) => {\n  console.log(data.id, data.name); // Type-safe!\n});\n\nemitter.emit(\"user:created\", { id: \"1\", name: \"John\" });\n// emitter.emit(\"user:created\", { id: \"1\" });  // Error: missing 'name'\n```\n\n### Pattern 2: Type-Safe API Client\n\n```typescript\ntype HTTPMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\ntype EndpointConfig = {\n  \"/users\": {\n    GET: { response: User[] };\n    POST: { body: { name: string; email: string }; response: User };\n  };\n  \"/users/:id\": {\n    GET: { params: { id: string }; response: User };\n    PUT: { params: { id: string }; body: Partial<User>; response: User };\n    DELETE: { params: { id: string }; response: void };\n  };\n};\n\ntype ExtractParams<T> = T extends { params: infer P } ? P : never;\ntype ExtractBody<T> = T extends { body: infer B } ? B : never;\ntype ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\nclass APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {\n  async request<Path extends keyof Config, Method extends keyof Config[Path]>(\n    path: Path,\n    method: Method,\n    ...[options]: ExtractParams<Config[Path][Method]> extends never\n      ? ExtractBody<Config[Path][Method]> extends never\n        ? []\n        : [{ body: ExtractBody<Config[Path][Method]> }]\n      : [\n          {\n            params: ExtractParams<Config[Path][Method]>;\n            body?: ExtractBody<Config[Path][Method]>;\n          },\n        ]\n  ): Promise<ExtractResponse<Config[Path][Method]>> {\n    // Implementation here\n    return {} as any;\n  }\n}\n\nconst api = new APIClient<EndpointConfig>();\n\n// Type-safe API calls\nconst users = await api.request(\"/users\", \"GET\");\n// Type: User[]\n\nconst newUser = await api.request(\"/users\", \"POST\", {\n  body: { name: \"John\", email: \"john@example.com\" },\n});\n// Type: User\n\nconst user = await api.request(\"/users/:id\", \"GET\", {\n  params: { id: \"123\" },\n});\n// Type: User\n```\n\n### Pattern 3: Builder Pattern with Type Safety\n\n```typescript\ntype BuilderState<T> = {\n  [K in keyof T]: T[K] | undefined;\n};\n\ntype RequiredKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\ntype OptionalKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\n\ntype IsComplete<T, S> =\n  RequiredKeys<T> extends keyof S\n    ? S[RequiredKeys<T>] extends undefined\n      ? false\n      : true\n    : false;\n\nclass Builder<T, S extends BuilderState<T> = {}> {\n  private state: S = {} as S;\n\n  set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {\n    this.state[key] = value;\n    return this as any;\n  }\n\n  build(this: IsComplete<T, S> extends true ? this : never): T {\n    return this.state as T;\n  }\n}\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age?: number;\n}\n\nconst builder = new Builder<User>();\n\nconst user = builder\n  .set(\"id\", \"1\")\n  .set(\"name\", \"John\")\n  .set(\"email\", \"john@example.com\")\n  .build(); // OK: all required fields set\n\n// const incomplete = builder\n//   .set(\"id\", \"1\")\n//   .build();  // Error: missing required fields\n```\n\n### Pattern 4: Deep Readonly/Partial\n\n```typescript\ntype DeepReadonly<T> = {\n  readonly [P in keyof T]: T[P] extends object\n    ? T[P] extends Function\n      ? T[P]\n      : DeepReadonly<T[P]>\n    : T[P];\n};\n\ntype DeepPartial<T> = {\n  [P in keyof T]?: T[P] extends object\n    ? T[P] extends Array<infer U>\n      ? Array<DeepPartial<U>>\n      : DeepPartial<T[P]>\n    : T[P];\n};\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n    ssl: {\n      enabled: boolean;\n      cert: string;\n    };\n  };\n  database: {\n    url: string;\n    pool: {\n      min: number;\n      max: number;\n    };\n  };\n}\n\ntype ReadonlyConfig = DeepReadonly<Config>;\n// All nested properties are readonly\n\ntype PartialConfig = DeepPartial<Config>;\n// All nested properties are optional\n```\n\n### Pattern 5: Type-Safe Form Validation\n\n```typescript\ntype ValidationRule<T> = {\n  validate: (value: T) => boolean;\n  message: string;\n};\n\ntype FieldValidation<T> = {\n  [K in keyof T]?: ValidationRule<T[K]>[];\n};\n\ntype ValidationErrors<T> = {\n  [K in keyof T]?: string[];\n};\n\nclass FormValidator<T extends Record<string, any>> {\n  constructor(private rules: FieldValidation<T>) {}\n\n  validate(data: T): ValidationErrors<T> | null {\n    const errors: ValidationErrors<T> = {};\n    let hasErrors = false;\n\n    for (const key in this.rules) {\n      const fieldRules = this.rules[key];\n      const value = data[key];\n\n      if (fieldRules) {\n        const fieldErrors: string[] = [];\n\n        for (const rule of fieldRules) {\n          if (!rule.validate(value)) {\n            fieldErrors.push(rule.message);\n          }\n        }\n\n        if (fieldErrors.length > 0) {\n          errors[key] = fieldErrors;\n          hasErrors = true;\n        }\n      }\n    }\n\n    return hasErrors ? errors : null;\n  }\n}\n\ninterface LoginForm {\n  email: string;\n  password: string;\n}\n\nconst validator = new FormValidator<LoginForm>({\n  email: [\n    {\n      validate: (v) => v.includes(\"@\"),\n      message: \"Email must contain @\",\n    },\n    {\n      validate: (v) => v.length > 0,\n      message: \"Email is required\",\n    },\n  ],\n  password: [\n    {\n      validate: (v) => v.length >= 8,\n      message: \"Password must be at least 8 characters\",\n    },\n  ],\n});\n\nconst errors = validator.validate({\n  email: \"invalid\",\n  password: \"short\",\n});\n// Type: { email?: string[]; password?: string[]; } | null\n```\n\n### Pattern 6: Discriminated Unions\n\n```typescript\ntype Success<T> = {\n  status: \"success\";\n  data: T;\n};\n\ntype Error = {\n  status: \"error\";\n  error: string;\n};\n\ntype Loading = {\n  status: \"loading\";\n};\n\ntype AsyncState<T> = Success<T> | Error | Loading;\n\nfunction handleState<T>(state: AsyncState<T>): void {\n  switch (state.status) {\n    case \"success\":\n      console.log(state.data); // Type: T\n      break;\n    case \"error\":\n      console.log(state.error); // Type: string\n      break;\n    case \"loading\":\n      console.log(\"Loading...\");\n      break;\n  }\n}\n\n// Type-safe state machine\ntype State =\n  | { type: \"idle\" }\n  | { type: \"fetching\"; requestId: string }\n  | { type: \"success\"; data: any }\n  | { type: \"error\"; error: Error };\n\ntype Event =\n  | { type: \"FETCH\"; requestId: string }\n  | { type: \"SUCCESS\"; data: any }\n  | { type: \"ERROR\"; error: Error }\n  | { type: \"RESET\" };\n\nfunction reducer(state: State, event: Event): State {\n  switch (state.type) {\n    case \"idle\":\n      return event.type === \"FETCH\"\n        ? { type: \"fetching\", requestId: event.requestId }\n        : state;\n    case \"fetching\":\n      if (event.type === \"SUCCESS\") {\n        return { type: \"success\", data: event.data };\n      }\n      if (event.type === \"ERROR\") {\n        return { type: \"error\", error: event.error };\n      }\n      return state;\n    case \"success\":\n    case \"error\":\n      return event.type === \"RESET\" ? { type: \"idle\" } : state;\n  }\n}\n```\n\n## Type Inference Techniques\n\n### 1. Infer Keyword\n\n```typescript\n// Extract array element type\ntype ElementType<T> = T extends (infer U)[] ? U : never;\n\ntype NumArray = number[];\ntype Num = ElementType<NumArray>; // number\n\n// Extract promise type\ntype PromiseType<T> = T extends Promise<infer U> ? U : never;\n\ntype AsyncNum = PromiseType<Promise<number>>; // number\n\n// Extract function parameters\ntype Parameters<T> = T extends (...args: infer P) => any ? P : never;\n\nfunction foo(a: string, b: number) {}\ntype FooParams = Parameters<typeof foo>; // [string, number]\n```\n\n### 2. Type Guards\n\n```typescript\nfunction isString(value: unknown): value is string {\n  return typeof value === \"string\";\n}\n\nfunction isArrayOf<T>(\n  value: unknown,\n  guard: (item: unknown) => item is T,\n): value is T[] {\n  return Array.isArray(value) && value.every(guard);\n}\n\nconst data: unknown = [\"a\", \"b\", \"c\"];\n\nif (isArrayOf(data, isString)) {\n  data.forEach((s) => s.toUpperCase()); // Type: string[]\n}\n```\n\n### 3. Assertion Functions\n\n```typescript\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== \"string\") {\n    throw new Error(\"Not a string\");\n  }\n}\n\nfunction processValue(value: unknown) {\n  assertIsString(value);\n  // value is now typed as string\n  console.log(value.toUpperCase());\n}\n```\n\n## Best Practices\n\n1. **Use `unknown` over `any`**: Enforce type checking\n2. **Prefer `interface` for object shapes**: Better error messages\n3. **Use `type` for unions and complex types**: More flexible\n4. **Leverage type inference**: Let TypeScript infer when possible\n5. **Create helper types**: Build reusable type utilities\n6. **Use const assertions**: Preserve literal types\n7. **Avoid type assertions**: Use type guards instead\n8. **Document complex types**: Add JSDoc comments\n9. **Use strict mode**: Enable all strict compiler options\n10. **Test your types**: Use type tests to verify type behavior\n\n## Type Testing\n\n```typescript\n// Type assertion tests\ntype AssertEqual<T, U> = [T] extends [U]\n  ? [U] extends [T]\n    ? true\n    : false\n  : false;\n\ntype Test1 = AssertEqual<string, string>; // true\ntype Test2 = AssertEqual<string, number>; // false\ntype Test3 = AssertEqual<string | number, string>; // false\n\n// Expect error helper\ntype ExpectError<T extends never> = T;\n\n// Example usage\ntype ShouldError = ExpectError<AssertEqual<string, number>>;\n```\n\n## Common Pitfalls\n\n1. **Over-using `any`**: Defeats the purpose of TypeScript\n2. **Ignoring strict null checks**: Can lead to runtime errors\n3. **Too complex types**: Can slow down compilation\n4. **Not using discriminated unions**: Misses type narrowing opportunities\n5. **Forgetting readonly modifiers**: Allows unintended mutations\n6. **Circular type references**: Can cause compiler errors\n7. **Not handling edge cases**: Like empty arrays or null values\n\n## Performance Considerations\n\n- Avoid deeply nested conditional types\n- Use simple types when possible\n- Cache complex type computations\n- Limit recursion depth in recursive types\n- Use build tools to skip type checking in production\n",".agents/skills/vercel-composition-patterns/AGENTS.md":"# React Composition Patterns\n\n**Version 1.0.0**  \nEngineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React codebases using composition. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n---\n\n## Table of Contents\n\n1. [Component Architecture](#1-component-architecture) — **HIGH**\n   - 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation)\n   - 1.2 [Use Compound Components](#12-use-compound-components)\n2. [State Management](#2-state-management) — **MEDIUM**\n   - 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui)\n   - 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection)\n   - 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components)\n3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM**\n   - 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants)\n   - 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props)\n4. [React 19 APIs](#4-react-19-apis) — **MEDIUM**\n   - 4.1 [React 19 API Changes](#41-react-19-api-changes)\n\n---\n\n## 1. Component Architecture\n\n**Impact: HIGH**\n\nFundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n### 1.1 Avoid Boolean Prop Proliferation\n\n**Impact: CRITICAL (prevents unmaintainable component variants)**\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\n\ncomponent behavior. Each boolean doubles possible states and creates\n\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect: boolean props create exponential complexity**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct: composition eliminates conditionals**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\n\nsharing a single monolithic parent.\n\n### 1.2 Use Compound Components\n\n**Impact: HIGH (enables flexible composition without prop drilling)**\n\nStructure complex components as compound components with a shared context. Each\n\nsubcomponent accesses shared state via context, not props. Consumers compose the\n\npieces they need.\n\n**Incorrect: monolithic component with render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct: compound components with shared context**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n\n---\n\n## 2. State Management\n\n**Impact: MEDIUM**\n\nPatterns for lifting state and managing shared context across\ncomposed components.\n\n### 2.1 Decouple State Management from UI\n\n**Impact: MEDIUM (enables swapping state implementations without changing UI)**\n\nThe provider component should be the only place that knows how state is managed.\n\nUI components consume the context interface—they don't know if state comes from\n\nuseState, Zustand, or a server sync.\n\n**Incorrect: UI coupled to state implementation**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct: state management isolated in provider**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\n\ndepends on the context interface, not the implementation.\n\n### 2.2 Define Generic Context Interfaces for Dependency Injection\n\n**Impact: HIGH (enables dependency-injectable state across use-cases)**\n\nDefine a **generic interface** for your component context with three parts:\n\n`state`, `actions`, and `meta`. This interface is a contract that any provider\n\ncan implement—enabling the same UI components to work with completely different\n\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\n\ndependency-injectable.\n\n**Incorrect: UI coupled to specific state implementation**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct: generic interface enables dependency injection**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe provider boundary is what matters—not the visual nesting. Components that\n\nneed shared state don't have to be inside the `Composer.Frame`. They just need\n\nto be within the provider.\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\n\nbox, but they can still access its state and actions. This is the power of\n\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\n\nby the provider. Swap the provider, keep the UI.\n\n### 2.3 Lift State into Provider Components\n\n**Impact: HIGH (enables state sharing outside component boundaries)**\n\nMove state management into dedicated provider components. This allows sibling\n\ncomponents outside the main UI to access and modify state without prop drilling\n\nor awkward refs.\n\n**Incorrect: state trapped inside component**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect: useEffect to sync state up**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect: reading state from ref on submit**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct: state lifted to provider**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\n\nsubmit action because it's within the provider. Even though it's a one-off\n\ncomponent, it can still access the composer's state and actions from outside the\n\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\n\nnested inside each other—they just need to be within the same provider.\n\n---\n\n## 3. Implementation Patterns\n\n**Impact: MEDIUM**\n\nSpecific techniques for implementing compound components and\ncontext providers.\n\n### 3.1 Create Explicit Component Variants\n\n**Impact: MEDIUM (self-documenting code, no hidden conditionals)**\n\nInstead of one component with many boolean props, create explicit variant\n\ncomponents. Each variant composes the pieces it needs. The code documents\n\nitself.\n\n**Incorrect: one component, many modes**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct: explicit variants**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\n\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n\n- What UI elements it includes\n\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n\n### 3.2 Prefer Composing Children Over Render Props\n\n**Impact: MEDIUM (cleaner composition, better readability)**\n\nUse `children` for composition instead of `renderX` props. Children are more\n\nreadable, compose naturally, and don't require understanding callback\n\nsignatures.\n\n**Incorrect: render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct: compound components with children**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\n\nUse children when composing static structure.\n\n---\n\n## 4. React 19 APIs\n\n**Impact: MEDIUM**\n\nReact 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n\n### 4.1 React 19 API Changes\n\n**Impact: MEDIUM (cleaner component definitions and context usage)**\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect: forwardRef in React 19**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct: ref as a regular prop**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect: useContext in React 19**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct: use instead of useContext**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n\n---\n\n## References\n\n1. [https://react.dev](https://react.dev)\n2. [https://react.dev/learn/passing-data-deeply-with-context](https://react.dev/learn/passing-data-deeply-with-context)\n3. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)\n",".agents/skills/vercel-composition-patterns/README.md":"# React Composition Patterns\n\nA structured repository for React composition patterns that scale. These\npatterns help avoid boolean prop proliferation by using compound components,\nlifting state, and composing internals.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Rules\n\n### Component Architecture (CRITICAL)\n\n- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize\n  behavior\n- `architecture-compound-components.md` - Structure as compound components with\n  shared context\n\n### State Management (HIGH)\n\n- `state-lift-state.md` - Lift state into provider components\n- `state-context-interface.md` - Define clear context interfaces\n  (state/actions/meta)\n- `state-decouple-implementation.md` - Decouple state management from UI\n\n### Implementation Patterns (MEDIUM)\n\n- `patterns-children-over-render-props.md` - Prefer children over renderX props\n- `patterns-explicit-variants.md` - Create explicit component variants\n\n## Core Principles\n\n1. **Composition over configuration** — Instead of adding props, let consumers\n   compose\n2. **Lift your state** — State in providers, not trapped in components\n3. **Compose your internals** — Subcomponents access context, not props\n4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer\n   with isThread\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `architecture-` for Component Architecture\n   - `state-` for State Management\n   - `patterns-` for Implementation Patterns\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n\n## Impact Levels\n\n- `CRITICAL` - Foundational patterns, prevents unmaintainable code\n- `HIGH` - Significant maintainability improvements\n- `MEDIUM` - Good practices for cleaner code\n",".agents/skills/vercel-composition-patterns/SKILL.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2FSKILL.md -->",".agents/skills/vercel-composition-patterns/metadata.json":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://react.dev/learn/passing-data-deeply-with-context\",\n    \"https://react.dev/reference/react/use\"\n  ]\n}\n",".agents/skills/vercel-composition-patterns/rules/_sections.md":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Component Architecture (architecture)\n\n**Impact:** HIGH  \n**Description:** Fundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n## 2. State Management (state)\n\n**Impact:** MEDIUM  \n**Description:** Patterns for lifting state and managing shared context across\ncomposed components.\n\n## 3. Implementation Patterns (patterns)\n\n**Impact:** MEDIUM  \n**Description:** Specific techniques for implementing compound components and\ncontext providers.\n\n## 4. React 19 APIs (react19)\n\n**Impact:** MEDIUM  \n**Description:** React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n",".agents/skills/vercel-composition-patterns/rules/_template.md":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: brief description of impact\ntags: composition, components\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect:**\n\n```tsx\n// Bad code example\n```\n\n**Correct:**\n\n```tsx\n// Good code example\n```\n\nReference: [Link](https://example.com)\n",".agents/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Farchitecture-avoid-boolean-props.md -->",".agents/skills/vercel-composition-patterns/rules/architecture-compound-components.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Farchitecture-compound-components.md -->",".agents/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fpatterns-children-over-render-props.md -->",".agents/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fpatterns-explicit-variants.md -->",".agents/skills/vercel-composition-patterns/rules/react19-no-forwardref.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Freact19-no-forwardref.md -->",".agents/skills/vercel-composition-patterns/rules/state-context-interface.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-context-interface.md -->",".agents/skills/vercel-composition-patterns/rules/state-decouple-implementation.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-decouple-implementation.md -->",".agents/skills/vercel-composition-patterns/rules/state-lift-state.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-lift-state.md -->",".agents/skills/vercel-react-best-practices/AGENTS.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FAGENTS.md -->",".agents/skills/vercel-react-best-practices/README.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FREADME.md -->",".agents/skills/vercel-react-best-practices/SKILL.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FSKILL.md -->",".agents/skills/vercel-react-best-practices/metadata.json":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Fmetadata.json -->",".agents/skills/vercel-react-best-practices/rules/_sections.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2F_sections.md -->",".agents/skills/vercel-react-best-practices/rules/_template.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2F_template.md -->",".agents/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-effect-event-deps.md -->",".agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-event-handler-refs.md -->",".agents/skills/vercel-react-best-practices/rules/advanced-init-once.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-init-once.md -->",".agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-use-latest.md -->",".agents/skills/vercel-react-best-practices/rules/async-api-routes.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-api-routes.md -->",".agents/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-cheap-condition-before-await.md -->",".agents/skills/vercel-react-best-practices/rules/async-defer-await.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-defer-await.md -->",".agents/skills/vercel-react-best-practices/rules/async-dependencies.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-dependencies.md -->",".agents/skills/vercel-react-best-practices/rules/async-parallel.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-parallel.md -->",".agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-suspense-boundaries.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-barrel-imports.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-conditional.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-conditional.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-defer-third-party.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-dynamic-imports.md -->",".agents/skills/vercel-react-best-practices/rules/bundle-preload.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-preload.md -->",".agents/skills/vercel-react-best-practices/rules/client-event-listeners.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-event-listeners.md -->",".agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-localstorage-schema.md -->",".agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-passive-event-listeners.md -->",".agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-swr-dedup.md -->",".agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-batch-dom-css.md -->",".agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-function-results.md -->",".agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-property-access.md -->",".agents/skills/vercel-react-best-practices/rules/js-cache-storage.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-storage.md -->",".agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-combine-iterations.md -->",".agents/skills/vercel-react-best-practices/rules/js-early-exit.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-early-exit.md -->",".agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-flatmap-filter.md -->",".agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-hoist-regexp.md -->",".agents/skills/vercel-react-best-practices/rules/js-index-maps.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-index-maps.md -->",".agents/skills/vercel-react-best-practices/rules/js-length-check-first.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-length-check-first.md -->",".agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-min-max-loop.md -->",".agents/skills/vercel-react-best-practices/rules/js-request-idle-callback.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-request-idle-callback.md -->",".agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-set-map-lookups.md -->",".agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-tosorted-immutable.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-activity.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-activity.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-animate-svg-wrapper.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-conditional-render.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-content-visibility.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hoist-jsx.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hydration-no-flicker.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hydration-suppress-warning.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-resource-hints.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-script-defer-async.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-svg-precision.md -->",".agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-usetransition-loading.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-defer-reads.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-dependencies.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-derived-state-no-effect.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-derived-state.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-functional-setstate.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-lazy-state-init.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-memo-with-default-value.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-memo.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-memo.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-move-effect-to-event.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-no-inline-components.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-simple-expression-in-memo.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-split-combined-hooks.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-transitions.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-transitions.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-use-deferred-value.md -->",".agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-use-ref-transient-values.md -->",".agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-after-nonblocking.md -->",".agents/skills/vercel-react-best-practices/rules/server-auth-actions.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-auth-actions.md -->",".agents/skills/vercel-react-best-practices/rules/server-cache-lru.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-cache-lru.md -->",".agents/skills/vercel-react-best-practices/rules/server-cache-react.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-cache-react.md -->",".agents/skills/vercel-react-best-practices/rules/server-dedup-props.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-dedup-props.md -->",".agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-hoist-static-io.md -->",".agents/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-no-shared-module-state.md -->",".agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-parallel-fetching.md -->",".agents/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-parallel-nested-fetching.md -->",".agents/skills/vercel-react-best-practices/rules/server-serialization.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-serialization.md -->",".agents/skills/web-design-guidelines/SKILL.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fweb-design-guidelines%2FSKILL.md -->",".agents/skills/web-quality-audit/SKILL.md":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fweb-quality-audit%2FSKILL.md -->","CLAUDE.md":"# CLAUDE.md\n\n<!-- autoskills:start -->\n\nSummary generated by `autoskills`. Check the full files inside `.claude/skills`.\n\n## Accessibility (a11y)\n\nAudit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".\n\n- `.claude/skills/accessibility/SKILL.md`\n- `.claude/skills/accessibility/references/WCAG.md`\n\n## Best practices\n\nApply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\n\n- `.claude/skills/best-practices/SKILL.md`\n\n## Core Web Vitals optimization\n\nOptimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\n\n- `.claude/skills/core-web-vitals/SKILL.md`\n- `.claude/skills/core-web-vitals/references/LCP.md`: Largest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:\n\n## Design Thinking\n\nCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beaut...\n\n- `.claude/skills/frontend-design/SKILL.md`\n\n## Next.js Best Practices\n\nNext.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling\n\n- `.claude/skills/next-best-practices/SKILL.md`\n- `.claude/skills/next-best-practices/async-patterns.md`: In Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.\n- `.claude/skills/next-best-practices/bundling.md`: Fix common bundling issues with third-party packages.\n- `.claude/skills/next-best-practices/data-patterns.md`: Choose the right data fetching pattern for each use case.\n- `.claude/skills/next-best-practices/debug-tricks.md`: Tricks to speed up debugging Next.js applications.\n- `.claude/skills/next-best-practices/directives.md`: These are React directives, not Next.js specific.\n- `.claude/skills/next-best-practices/error-handling.md`: Handle errors gracefully in Next.js applications.\n- `.claude/skills/next-best-practices/file-conventions.md`: Next.js App Router uses file-based routing with special file conventions.\n- `.claude/skills/next-best-practices/font.md`: Use `next/font` for automatic font optimization with zero layout shift.\n- `.claude/skills/next-best-practices/functions.md`: Next.js function APIs.\n- `.claude/skills/next-best-practices/hydration-error.md`: Diagnose and fix React hydration mismatch errors.\n- `.claude/skills/next-best-practices/image.md`: Use `next/image` for automatic image optimization.\n- `.claude/skills/next-best-practices/metadata.md`: Add SEO metadata to Next.js pages using the Metadata API.\n- `.claude/skills/next-best-practices/parallel-routes.md`: Parallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.\n- `.claude/skills/next-best-practices/route-handlers.md`: Create API endpoints with `route.ts` files.\n- `.claude/skills/next-best-practices/rsc-boundaries.md`: Detect and prevent invalid patterns when crossing Server/Client component boundaries.\n- `.claude/skills/next-best-practices/runtime-selection.md`: Use the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.\n- `.claude/skills/next-best-practices/scripts.md`: Loading third-party scripts in Next.js.\n- `.claude/skills/next-best-practices/self-hosting.md`: Deploy Next.js outside of Vercel with confidence.\n- `.claude/skills/next-best-practices/suspense-boundaries.md`: Client hooks that cause CSR bailout without Suspense boundaries.\n\n## Cache Components (Next.js 16+)\n\nNext.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag\n\n- `.claude/skills/next-cache-components/SKILL.md`\n\n## Upgrade Next.js\n\nUpgrade Next.js to the latest version following official migration guides and codemods\n\n- `.claude/skills/next-upgrade/SKILL.md`\n\n## Node.js Backend Patterns\n\nBuild production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.\n\n- `.claude/skills/nodejs-backend-patterns/SKILL.md`\n- `.claude/skills/nodejs-backend-patterns/references/advanced-patterns.md`: Advanced patterns for dependency injection, database integration, authentication, caching, and API response formatting.\n\n## Node.js Best Practices\n\nNode.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.\n\n- `.claude/skills/nodejs-best-practices/SKILL.md`\n\n## Performance optimization\n\nOptimize web performance for faster loading and better user experience. Use when asked to \"speed up my site\", \"optimize performance\", \"reduce load time\", \"fix slow loading\", \"improve page speed\", or \"performance audit\".\n\n- `.claude/skills/performance/SKILL.md`\n\n## SEO optimization\n\nOptimize for search engine visibility and ranking. Use when asked to \"improve SEO\", \"optimize for search\", \"fix meta tags\", \"add structured data\", \"sitemap optimization\", or \"search engine optimization\".\n\n- `.claude/skills/seo/SKILL.md`\n\n## Tailwind CSS Development Patterns\n\nProvides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or o...\n\n- `.claude/skills/tailwind-css-patterns/SKILL.md`\n- `.claude/skills/tailwind-css-patterns/references/accessibility.md`\n- `.claude/skills/tailwind-css-patterns/references/animations.md`: Usage:\n- `.claude/skills/tailwind-css-patterns/references/component-patterns.md`\n- `.claude/skills/tailwind-css-patterns/references/configuration.md`: Use the `@theme` directive for CSS-based configuration:\n- `.claude/skills/tailwind-css-patterns/references/layout-patterns.md`: Basic flex container:\n- `.claude/skills/tailwind-css-patterns/references/performance.md`: Configure content sources for optimal purging:\n- `.claude/skills/tailwind-css-patterns/references/reference.md`: Tailwind CSS is a utility-first CSS framework that generates styles by scanning HTML, JavaScript, and template files for class names. It provides a comprehensive design system through CSS utility classes, enabling rapid UI development without writing custom CSS. The framework operates at build-ti...\n- `.claude/skills/tailwind-css-patterns/references/responsive-design.md`: Enable dark mode in tailwind.config.js:\n\n## TypeScript Advanced Types\n\nMaster TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript pr...\n\n- `.claude/skills/typescript-advanced-types/SKILL.md`\n\n## React Composition Patterns\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n- `.claude/skills/vercel-composition-patterns/SKILL.md`\n- `.claude/skills/vercel-composition-patterns/AGENTS.md`: **Version 1.0.0** Engineering January 2026\n- `.claude/skills/vercel-composition-patterns/README.md`: A structured repository for React composition patterns that scale. These patterns help avoid boolean prop proliferation by using compound components, lifting state, and composing internals.\n- `.claude/skills/vercel-composition-patterns/rules/_sections.md`: This file defines all sections, their ordering, impact levels, and descriptions. The section ID (in parentheses) is the filename prefix used to group rules.\n- `.claude/skills/vercel-composition-patterns/rules/_template.md`: Brief explanation of the rule and why it matters.\n- `.claude/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md`: Don't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize component behavior. Each boolean doubles possible states and creates unmaintainable conditional logic. Use composition instead.\n- `.claude/skills/vercel-composition-patterns/rules/architecture-compound-components.md`: Structure complex components as compound components with a shared context. Each subcomponent accesses shared state via context, not props. Consumers compose the pieces they need.\n- `.claude/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md`: Use `children` for composition instead of `renderX` props. Children are more readable, compose naturally, and don't require understanding callback signatures.\n- `.claude/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md`: Instead of one component with many boolean props, create explicit variant components. Each variant composes the pieces it needs. The code documents itself.\n- `.claude/skills/vercel-composition-patterns/rules/react19-no-forwardref.md`: In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n- `.claude/skills/vercel-composition-patterns/rules/state-context-interface.md`: Define a **generic interface** for your component context with three parts: can implement—enabling the same UI components to work with completely different state implementations.\n- `.claude/skills/vercel-composition-patterns/rules/state-decouple-implementation.md`: The provider component should be the only place that knows how state is managed. UI components consume the context interface—they don't know if state comes from useState, Zustand, or a server sync.\n- `.claude/skills/vercel-composition-patterns/rules/state-lift-state.md`: Move state management into dedicated provider components. This allows sibling components outside the main UI to access and modify state without prop drilling or awkward refs.\n\n## Vercel React Best Practices\n\nReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimizati...\n\n- `.claude/skills/vercel-react-best-practices/SKILL.md`\n- `.claude/skills/vercel-react-best-practices/AGENTS.md`: **Version 1.0.0** Vercel Engineering January 2026\n- `.claude/skills/vercel-react-best-practices/README.md`: A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.\n- `.claude/skills/vercel-react-best-practices/rules/_sections.md`: This file defines all sections, their ordering, impact levels, and descriptions. The section ID (in parentheses) is the filename prefix used to group rules.\n- `.claude/skills/vercel-react-best-practices/rules/_template.md`: **Impact: MEDIUM (optional impact description)**\n- `.claude/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md`: Effect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect...\n- `.claude/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md`: Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.\n- `.claude/skills/vercel-react-best-practices/rules/advanced-init-once.md`: Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.\n- `.claude/skills/vercel-react-best-practices/rules/advanced-use-latest.md`: Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.\n- `.claude/skills/vercel-react-best-practices/rules/async-api-routes.md`: In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n- `.claude/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md`: When a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n- `.claude/skills/vercel-react-best-practices/rules/async-defer-await.md`: Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n- `.claude/skills/vercel-react-best-practices/rules/async-dependencies.md`: For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n- `.claude/skills/vercel-react-best-practices/rules/async-parallel.md`: When async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n- `.claude/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md`: Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md`: Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n- `.claude/skills/vercel-react-best-practices/rules/bundle-conditional.md`: Load large data or modules only when a feature is activated.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md`: Analytics, logging, and error tracking don't block user interaction. Load them after hydration.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md`: Use `next/dynamic` to lazy-load large components not needed on initial render.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-preload.md`: Preload heavy bundles before they're needed to reduce perceived latency.\n- `.claude/skills/vercel-react-best-practices/rules/client-event-listeners.md`: Use `useSWRSubscription()` to share global event listeners across component instances.\n- `.claude/skills/vercel-react-best-practices/rules/client-localstorage-schema.md`: Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n- `.claude/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md`: Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n- `.claude/skills/vercel-react-best-practices/rules/client-swr-dedup.md`: SWR enables request deduplication, caching, and revalidation across component instances.\n- `.claude/skills/vercel-react-best-practices/rules/js-batch-dom-css.md`: Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-function-results.md`: Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-property-access.md`: Cache object property lookups in hot paths.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-storage.md`: **Incorrect (reads storage on every call):**\n- `.claude/skills/vercel-react-best-practices/rules/js-combine-iterations.md`: Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.\n- `.claude/skills/vercel-react-best-practices/rules/js-early-exit.md`: Return early when result is determined to skip unnecessary processing.\n- `.claude/skills/vercel-react-best-practices/rules/js-flatmap-filter.md`: **Impact: LOW-MEDIUM (eliminates intermediate array)**\n- `.claude/skills/vercel-react-best-practices/rules/js-hoist-regexp.md`: Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.\n- `.claude/skills/vercel-react-best-practices/rules/js-index-maps.md`: Multiple `.find()` calls by the same key should use a Map.\n- `.claude/skills/vercel-react-best-practices/rules/js-length-check-first.md`: When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.\n- `.claude/skills/vercel-react-best-practices/rules/js-min-max-loop.md`: Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.\n- `.claude/skills/vercel-react-best-practices/rules/js-request-idle-callback.md`: **Impact: MEDIUM (keeps UI responsive during background tasks)**\n- `.claude/skills/vercel-react-best-practices/rules/js-set-map-lookups.md`: Convert arrays to Set/Map for repeated membership checks.\n- `.claude/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md`: **Incorrect (mutates original array):**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-activity.md`: Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md`: Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-conditional-render.md`: Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-content-visibility.md`: Apply `content-visibility: auto` to defer off-screen rendering.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md`: Extract static JSX outside components to avoid re-creation.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md`: When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md`: In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide r...\n- `.claude/skills/vercel-react-best-practices/rules/rendering-resource-hints.md`: **Impact: HIGH (reduces load time for critical resources)**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md`: **Impact: HIGH (eliminates render-blocking)**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-svg-precision.md`: Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md`: Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-defer-reads.md`: Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-dependencies.md`: Specify primitive dependencies instead of objects to minimize effect re-runs.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md`: If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-derived-state.md`: Subscribe to derived boolean state instead of continuous values to reduce re-render frequency.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md`: When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md`: Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md`: When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict...\n- `.claude/skills/vercel-react-best-practices/rules/rerender-memo.md`: Extract expensive work into memoized components to enable early returns before computation.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md`: If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md`: **Impact: HIGH (prevents remount on every render)**\n- `.claude/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md`: When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`. Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md`: When a hook contains multiple independent tasks with different dependencies, split them into separate hooks. A combined hook reruns all tasks when any dependency changes, even if some tasks don't use the changed value.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-transitions.md`: Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md`: When user input triggers expensive computations or renders, use `useDeferredValue` to keep the input responsive. The deferred value lags behind, allowing React to prioritize the input update and render the expensive result when idle.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md`: When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.\n- `.claude/skills/vercel-react-best-practices/rules/server-after-nonblocking.md`: Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.\n- `.claude/skills/vercel-react-best-practices/rules/server-auth-actions.md`: **Impact: CRITICAL (prevents unauthorized access to server mutations)**\n- `.claude/skills/vercel-react-best-practices/rules/server-cache-lru.md`: **Implementation:**\n- `.claude/skills/vercel-react-best-practices/rules/server-cache-react.md`: Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.\n- `.claude/skills/vercel-react-best-practices/rules/server-dedup-props.md`: **Impact: LOW (reduces network payload by avoiding duplicate serialization)**\n- `.claude/skills/vercel-react-best-practices/rules/server-hoist-static-io.md`: **Impact: HIGH (avoids repeated file/network I/O per request)**\n- `.claude/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md`: For React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race condit...\n- `.claude/skills/vercel-react-best-practices/rules/server-parallel-fetching.md`: React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.\n- `.claude/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md`: When fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.\n- `.claude/skills/vercel-react-best-practices/rules/server-serialization.md`: The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.\n\n## Web Interface Guidelines\n\nReview UI code for Web Interface Guidelines compliance. Use when asked to \"review my UI\", \"check accessibility\", \"audit design\", \"review UX\", or \"check my site against best practices\".\n\n- `.claude/skills/web-design-guidelines/SKILL.md`\n\n## Web quality audit\n\nComprehensive web quality audit covering performance, accessibility, SEO, and best practices. Use when asked to \"audit my site\", \"review web quality\", \"run lighthouse audit\", \"check page quality\", or \"optimize my website\".\n\n- `.claude/skills/web-quality-audit/SKILL.md`\n\n<!-- autoskills:end -->\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\n<!-- autoskills:start -->\n\nSummary generated by `autoskills`. Check the full files inside `.claude/skills`.\n\n## Accessibility (a11y)\n\nAudit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".\n\n- `.claude/skills/accessibility/SKILL.md`\n- `.claude/skills/accessibility/references/WCAG.md`\n\n## Best practices\n\nApply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\n\n- `.claude/skills/best-practices/SKILL.md`\n\n## Core Web Vitals optimization\n\nOptimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\n\n- `.claude/skills/core-web-vitals/SKILL.md`\n- `.claude/skills/core-web-vitals/references/LCP.md`: Largest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:\n\n## Design Thinking\n\nCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beaut...\n\n- `.claude/skills/frontend-design/SKILL.md`\n\n## Next.js Best Practices\n\nNext.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling\n\n- `.claude/skills/next-best-practices/SKILL.md`\n- `.claude/skills/next-best-practices/async-patterns.md`: In Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.\n- `.claude/skills/next-best-practices/bundling.md`: Fix common bundling issues with third-party packages.\n- `.claude/skills/next-best-practices/data-patterns.md`: Choose the right data fetching pattern for each use case.\n- `.claude/skills/next-best-practices/debug-tricks.md`: Tricks to speed up debugging Next.js applications.\n- `.claude/skills/next-best-practices/directives.md`: These are React directives, not Next.js specific.\n- `.claude/skills/next-best-practices/error-handling.md`: Handle errors gracefully in Next.js applications.\n- `.claude/skills/next-best-practices/file-conventions.md`: Next.js App Router uses file-based routing with special file conventions.\n- `.claude/skills/next-best-practices/font.md`: Use `next/font` for automatic font optimization with zero layout shift.\n- `.claude/skills/next-best-practices/functions.md`: Next.js function APIs.\n- `.claude/skills/next-best-practices/hydration-error.md`: Diagnose and fix React hydration mismatch errors.\n- `.claude/skills/next-best-practices/image.md`: Use `next/image` for automatic image optimization.\n- `.claude/skills/next-best-practices/metadata.md`: Add SEO metadata to Next.js pages using the Metadata API.\n- `.claude/skills/next-best-practices/parallel-routes.md`: Parallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.\n- `.claude/skills/next-best-practices/route-handlers.md`: Create API endpoints with `route.ts` files.\n- `.claude/skills/next-best-practices/rsc-boundaries.md`: Detect and prevent invalid patterns when crossing Server/Client component boundaries.\n- `.claude/skills/next-best-practices/runtime-selection.md`: Use the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.\n- `.claude/skills/next-best-practices/scripts.md`: Loading third-party scripts in Next.js.\n- `.claude/skills/next-best-practices/self-hosting.md`: Deploy Next.js outside of Vercel with confidence.\n- `.claude/skills/next-best-practices/suspense-boundaries.md`: Client hooks that cause CSR bailout without Suspense boundaries.\n\n## Cache Components (Next.js 16+)\n\nNext.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag\n\n- `.claude/skills/next-cache-components/SKILL.md`\n\n## Upgrade Next.js\n\nUpgrade Next.js to the latest version following official migration guides and codemods\n\n- `.claude/skills/next-upgrade/SKILL.md`\n\n## Node.js Backend Patterns\n\nBuild production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.\n\n- `.claude/skills/nodejs-backend-patterns/SKILL.md`\n- `.claude/skills/nodejs-backend-patterns/references/advanced-patterns.md`: Advanced patterns for dependency injection, database integration, authentication, caching, and API response formatting.\n\n## Node.js Best Practices\n\nNode.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.\n\n- `.claude/skills/nodejs-best-practices/SKILL.md`\n\n## Performance optimization\n\nOptimize web performance for faster loading and better user experience. Use when asked to \"speed up my site\", \"optimize performance\", \"reduce load time\", \"fix slow loading\", \"improve page speed\", or \"performance audit\".\n\n- `.claude/skills/performance/SKILL.md`\n\n## SEO optimization\n\nOptimize for search engine visibility and ranking. Use when asked to \"improve SEO\", \"optimize for search\", \"fix meta tags\", \"add structured data\", \"sitemap optimization\", or \"search engine optimization\".\n\n- `.claude/skills/seo/SKILL.md`\n\n## Tailwind CSS Development Patterns\n\nProvides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or o...\n\n- `.claude/skills/tailwind-css-patterns/SKILL.md`\n- `.claude/skills/tailwind-css-patterns/references/accessibility.md`\n- `.claude/skills/tailwind-css-patterns/references/animations.md`: Usage:\n- `.claude/skills/tailwind-css-patterns/references/component-patterns.md`\n- `.claude/skills/tailwind-css-patterns/references/configuration.md`: Use the `@theme` directive for CSS-based configuration:\n- `.claude/skills/tailwind-css-patterns/references/layout-patterns.md`: Basic flex container:\n- `.claude/skills/tailwind-css-patterns/references/performance.md`: Configure content sources for optimal purging:\n- `.claude/skills/tailwind-css-patterns/references/reference.md`: Tailwind CSS is a utility-first CSS framework that generates styles by scanning HTML, JavaScript, and template files for class names. It provides a comprehensive design system through CSS utility classes, enabling rapid UI development without writing custom CSS. The framework operates at build-ti...\n- `.claude/skills/tailwind-css-patterns/references/responsive-design.md`: Enable dark mode in tailwind.config.js:\n\n## TypeScript Advanced Types\n\nMaster TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript pr...\n\n- `.claude/skills/typescript-advanced-types/SKILL.md`\n\n## React Composition Patterns\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n- `.claude/skills/vercel-composition-patterns/SKILL.md`\n- `.claude/skills/vercel-composition-patterns/AGENTS.md`: **Version 1.0.0** Engineering January 2026\n- `.claude/skills/vercel-composition-patterns/README.md`: A structured repository for React composition patterns that scale. These patterns help avoid boolean prop proliferation by using compound components, lifting state, and composing internals.\n- `.claude/skills/vercel-composition-patterns/rules/_sections.md`: This file defines all sections, their ordering, impact levels, and descriptions. The section ID (in parentheses) is the filename prefix used to group rules.\n- `.claude/skills/vercel-composition-patterns/rules/_template.md`: Brief explanation of the rule and why it matters.\n- `.claude/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md`: Don't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize component behavior. Each boolean doubles possible states and creates unmaintainable conditional logic. Use composition instead.\n- `.claude/skills/vercel-composition-patterns/rules/architecture-compound-components.md`: Structure complex components as compound components with a shared context. Each subcomponent accesses shared state via context, not props. Consumers compose the pieces they need.\n- `.claude/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md`: Use `children` for composition instead of `renderX` props. Children are more readable, compose naturally, and don't require understanding callback signatures.\n- `.claude/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md`: Instead of one component with many boolean props, create explicit variant components. Each variant composes the pieces it needs. The code documents itself.\n- `.claude/skills/vercel-composition-patterns/rules/react19-no-forwardref.md`: In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n- `.claude/skills/vercel-composition-patterns/rules/state-context-interface.md`: Define a **generic interface** for your component context with three parts: can implement—enabling the same UI components to work with completely different state implementations.\n- `.claude/skills/vercel-composition-patterns/rules/state-decouple-implementation.md`: The provider component should be the only place that knows how state is managed. UI components consume the context interface—they don't know if state comes from useState, Zustand, or a server sync.\n- `.claude/skills/vercel-composition-patterns/rules/state-lift-state.md`: Move state management into dedicated provider components. This allows sibling components outside the main UI to access and modify state without prop drilling or awkward refs.\n\n## Vercel React Best Practices\n\nReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimizati...\n\n- `.claude/skills/vercel-react-best-practices/SKILL.md`\n- `.claude/skills/vercel-react-best-practices/AGENTS.md`: **Version 1.0.0** Vercel Engineering January 2026\n- `.claude/skills/vercel-react-best-practices/README.md`: A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.\n- `.claude/skills/vercel-react-best-practices/rules/_sections.md`: This file defines all sections, their ordering, impact levels, and descriptions. The section ID (in parentheses) is the filename prefix used to group rules.\n- `.claude/skills/vercel-react-best-practices/rules/_template.md`: **Impact: MEDIUM (optional impact description)**\n- `.claude/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md`: Effect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect...\n- `.claude/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md`: Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.\n- `.claude/skills/vercel-react-best-practices/rules/advanced-init-once.md`: Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.\n- `.claude/skills/vercel-react-best-practices/rules/advanced-use-latest.md`: Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.\n- `.claude/skills/vercel-react-best-practices/rules/async-api-routes.md`: In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n- `.claude/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md`: When a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n- `.claude/skills/vercel-react-best-practices/rules/async-defer-await.md`: Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n- `.claude/skills/vercel-react-best-practices/rules/async-dependencies.md`: For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n- `.claude/skills/vercel-react-best-practices/rules/async-parallel.md`: When async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n- `.claude/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md`: Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md`: Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n- `.claude/skills/vercel-react-best-practices/rules/bundle-conditional.md`: Load large data or modules only when a feature is activated.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md`: Analytics, logging, and error tracking don't block user interaction. Load them after hydration.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md`: Use `next/dynamic` to lazy-load large components not needed on initial render.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-preload.md`: Preload heavy bundles before they're needed to reduce perceived latency.\n- `.claude/skills/vercel-react-best-practices/rules/client-event-listeners.md`: Use `useSWRSubscription()` to share global event listeners across component instances.\n- `.claude/skills/vercel-react-best-practices/rules/client-localstorage-schema.md`: Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n- `.claude/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md`: Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n- `.claude/skills/vercel-react-best-practices/rules/client-swr-dedup.md`: SWR enables request deduplication, caching, and revalidation across component instances.\n- `.claude/skills/vercel-react-best-practices/rules/js-batch-dom-css.md`: Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-function-results.md`: Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-property-access.md`: Cache object property lookups in hot paths.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-storage.md`: **Incorrect (reads storage on every call):**\n- `.claude/skills/vercel-react-best-practices/rules/js-combine-iterations.md`: Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.\n- `.claude/skills/vercel-react-best-practices/rules/js-early-exit.md`: Return early when result is determined to skip unnecessary processing.\n- `.claude/skills/vercel-react-best-practices/rules/js-flatmap-filter.md`: **Impact: LOW-MEDIUM (eliminates intermediate array)**\n- `.claude/skills/vercel-react-best-practices/rules/js-hoist-regexp.md`: Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.\n- `.claude/skills/vercel-react-best-practices/rules/js-index-maps.md`: Multiple `.find()` calls by the same key should use a Map.\n- `.claude/skills/vercel-react-best-practices/rules/js-length-check-first.md`: When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.\n- `.claude/skills/vercel-react-best-practices/rules/js-min-max-loop.md`: Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.\n- `.claude/skills/vercel-react-best-practices/rules/js-request-idle-callback.md`: **Impact: MEDIUM (keeps UI responsive during background tasks)**\n- `.claude/skills/vercel-react-best-practices/rules/js-set-map-lookups.md`: Convert arrays to Set/Map for repeated membership checks.\n- `.claude/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md`: **Incorrect (mutates original array):**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-activity.md`: Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md`: Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-conditional-render.md`: Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-content-visibility.md`: Apply `content-visibility: auto` to defer off-screen rendering.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md`: Extract static JSX outside components to avoid re-creation.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md`: When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md`: In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide r...\n- `.claude/skills/vercel-react-best-practices/rules/rendering-resource-hints.md`: **Impact: HIGH (reduces load time for critical resources)**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md`: **Impact: HIGH (eliminates render-blocking)**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-svg-precision.md`: Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md`: Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-defer-reads.md`: Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-dependencies.md`: Specify primitive dependencies instead of objects to minimize effect re-runs.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md`: If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-derived-state.md`: Subscribe to derived boolean state instead of continuous values to reduce re-render frequency.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md`: When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md`: Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md`: When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict...\n- `.claude/skills/vercel-react-best-practices/rules/rerender-memo.md`: Extract expensive work into memoized components to enable early returns before computation.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md`: If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md`: **Impact: HIGH (prevents remount on every render)**\n- `.claude/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md`: When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`. Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md`: When a hook contains multiple independent tasks with different dependencies, split them into separate hooks. A combined hook reruns all tasks when any dependency changes, even if some tasks don't use the changed value.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-transitions.md`: Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md`: When user input triggers expensive computations or renders, use `useDeferredValue` to keep the input responsive. The deferred value lags behind, allowing React to prioritize the input update and render the expensive result when idle.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md`: When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.\n- `.claude/skills/vercel-react-best-practices/rules/server-after-nonblocking.md`: Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.\n- `.claude/skills/vercel-react-best-practices/rules/server-auth-actions.md`: **Impact: CRITICAL (prevents unauthorized access to server mutations)**\n- `.claude/skills/vercel-react-best-practices/rules/server-cache-lru.md`: **Implementation:**\n- `.claude/skills/vercel-react-best-practices/rules/server-cache-react.md`: Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.\n- `.claude/skills/vercel-react-best-practices/rules/server-dedup-props.md`: **Impact: LOW (reduces network payload by avoiding duplicate serialization)**\n- `.claude/skills/vercel-react-best-practices/rules/server-hoist-static-io.md`: **Impact: HIGH (avoids repeated file/network I/O per request)**\n- `.claude/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md`: For React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race condit...\n- `.claude/skills/vercel-react-best-practices/rules/server-parallel-fetching.md`: React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.\n- `.claude/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md`: When fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.\n- `.claude/skills/vercel-react-best-practices/rules/server-serialization.md`: The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.\n\n## Web Interface Guidelines\n\nReview UI code for Web Interface Guidelines compliance. Use when asked to \"review my UI\", \"check accessibility\", \"audit design\", \"review UX\", or \"check my site against best practices\".\n\n- `.claude/skills/web-design-guidelines/SKILL.md`\n\n## Web quality audit\n\nComprehensive web quality audit covering performance, accessibility, SEO, and best practices. Use when asked to \"audit my site\", \"review web quality\", \"run lighthouse audit\", \"check page quality\", or \"optimize my website\".\n\n- `.claude/skills/web-quality-audit/SKILL.md`\n\n<!-- autoskills:end -->\n","isInternal":false,"tokens":6095,"sizeBytes":27444},{"name":"WCAG.md","path":".agents/skills/accessibility/references/WCAG.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/accessibility/references/WCAG.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# WCAG 2.1 Quick Reference\n\n## Success criteria by level\n\n### Level A (minimum)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.1.1** Non-text Content | All images, icons have text alternatives |\n| **1.2.1** Audio-only/Video-only | Provide transcript or audio description |\n| **1.2.2** Captions | Video with audio has captions |\n| **1.2.3** Audio Description | Video has audio description |\n| **1.3.1** Info and Relationships | Information conveyed through presentation is available programmatically |\n| **1.3.2** Meaningful Sequence | Reading order is logical |\n| **1.3.3** Sensory Characteristics | Instructions don't rely solely on shape, color, size, location, orientation, or sound |\n| **1.4.1** Use of Color | Color is not the only visual means of conveying information |\n| **1.4.2** Audio Control | Audio playing automatically can be paused/stopped |\n| **2.1.1** Keyboard | All functionality available via keyboard |\n| **2.1.2** No Keyboard Trap | Keyboard focus can be moved away from any component |\n| **2.1.4** Character Key Shortcuts | Single-key shortcuts can be turned off or remapped |\n| **2.2.1** Timing Adjustable | Time limits can be extended |\n| **2.2.2** Pause, Stop, Hide | Moving/blinking content can be paused |\n| **2.3.1** Three Flashes | Nothing flashes more than 3 times per second |\n| **2.4.1** Bypass Blocks | Skip link or landmark navigation available |\n| **2.4.2** Page Titled | Pages have descriptive titles |\n| **2.4.3** Focus Order | Focus order preserves meaning |\n| **2.4.4** Link Purpose | Link purpose clear from link text or context |\n| **2.5.1** Pointer Gestures | Multi-point gestures have single-pointer alternatives |\n| **2.5.2** Pointer Cancellation | Down-event doesn't trigger action (use up-event or click) |\n| **2.5.3** Label in Name | Accessible name contains visible label text |\n| **2.5.4** Motion Actuation | Motion-triggered functions have alternatives |\n| **3.1.1** Language of Page | Default language specified in HTML |\n| **3.2.1** On Focus | Focus doesn't trigger unexpected changes |\n| **3.2.2** On Input | Input doesn't trigger unexpected changes |\n| **3.3.1** Error Identification | Input errors clearly described |\n| **3.3.2** Labels or Instructions | Form inputs have labels or instructions |\n| **4.1.1** Parsing | HTML is well-formed (no duplicate IDs, proper nesting) |\n| **4.1.2** Name, Role, Value | UI components have accessible names and correct roles |\n\n### Level AA (standard)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.2.4** Captions (Live) | Live audio has captions |\n| **1.2.5** Audio Description | Pre-recorded video has audio description |\n| **1.3.4** Orientation | Content doesn't restrict orientation |\n| **1.3.5** Identify Input Purpose | Input purpose can be programmatically determined |\n| **1.4.3** Contrast (Minimum) | 4.5:1 for normal text, 3:1 for large text |\n| **1.4.4** Resize Text | Text can be resized to 200% without loss of functionality |\n| **1.4.5** Images of Text | Text used instead of images of text |\n| **1.4.10** Reflow | Content reflows at 320px width without horizontal scroll |\n| **1.4.11** Non-text Contrast | UI components have 3:1 contrast |\n| **1.4.12** Text Spacing | Content adapts to text spacing changes |\n| **1.4.13** Content on Hover/Focus | Additional content is dismissible, hoverable, persistent |\n| **2.4.5** Multiple Ways | Multiple ways to find pages |\n| **2.4.6** Headings and Labels | Headings and labels are descriptive |\n| **2.4.7** Focus Visible | Focus indicator is visible |\n| **3.1.2** Language of Parts | Language changes are marked |\n| **3.2.3** Consistent Navigation | Navigation is consistent across pages |\n| **3.2.4** Consistent Identification | Same functionality uses same labels |\n| **3.3.3** Error Suggestion | Error corrections suggested when known |\n| **3.3.4** Error Prevention (Legal) | Actions can be reversed or confirmed |\n| **4.1.3** Status Messages | Status messages announced to screen readers |\n\n### Level AAA (enhanced)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.4.6** Contrast (Enhanced) | 7:1 for normal text, 4.5:1 for large text |\n| **1.4.8** Visual Presentation | Foreground/background colors can be selected |\n| **1.4.9** Images of Text (No Exception) | No images of text |\n| **2.1.3** Keyboard (No Exception) | All functionality keyboard accessible |\n| **2.2.3** No Timing | No time limits |\n| **2.2.4** Interruptions | Interruptions can be postponed |\n| **2.2.5** Re-authenticating | Data preserved on re-authentication |\n| **2.2.6** Timeouts | Users warned about data loss from inactivity |\n| **2.3.2** Three Flashes | No content flashes more than 3 times |\n| **2.3.3** Animation from Interactions | Motion animation can be disabled |\n| **2.4.8** Location | User location within site is available |\n| **2.4.9** Link Purpose (Link Only) | Link purpose clear from link text alone |\n| **2.4.10** Section Headings | Sections have headings |\n| **3.1.3** Unusual Words | Definitions available for unusual words |\n| **3.1.4** Abbreviations | Abbreviations expanded |\n| **3.1.5** Reading Level | Alternative content for complex text |\n| **3.1.6** Pronunciation | Pronunciation available where needed |\n| **3.2.5** Change on Request | Changes initiated only by user |\n| **3.3.5** Help | Context-sensitive help available |\n| **3.3.6** Error Prevention (All) | All form submissions can be reviewed |\n\n## Common ARIA patterns\n\n### Buttons\n```html\n<button>Label</button>\n<!-- or -->\n<button aria-label=\"Close dialog\">×</button>\n```\n\n### Links\n```html\n<a href=\"/page\">Descriptive link text</a>\n<!-- External links -->\n<a href=\"https://external.com\" target=\"_blank\" rel=\"noopener\">\n  External site\n  <span class=\"visually-hidden\">(opens in new tab)</span>\n</a>\n```\n\n### Form fields\n```html\n<label for=\"email\">Email address</label>\n<input type=\"email\" id=\"email\" aria-describedby=\"email-hint\">\n<p id=\"email-hint\">We'll never share your email.</p>\n```\n\n### Error states\n```html\n<label for=\"email\">Email</label>\n<input type=\"email\" id=\"email\" aria-invalid=\"true\" aria-describedby=\"email-error\">\n<p id=\"email-error\" role=\"alert\">Please enter a valid email address.</p>\n```\n\n### Navigation\n```html\n<nav aria-label=\"Main\">\n  <ul>\n    <li><a href=\"/\" aria-current=\"page\">Home</a></li>\n    <li><a href=\"/about\">About</a></li>\n  </ul>\n</nav>\n```\n\n### Modals\n```html\n<div role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"dialog-title\">\n  <h2 id=\"dialog-title\">Confirm Action</h2>\n  <!-- content -->\n</div>\n```\n\n### Live regions\n```html\n<!-- Polite (waits for pause in speech) -->\n<div aria-live=\"polite\">Status update here</div>\n\n<!-- Assertive (interrupts immediately) -->\n<div aria-live=\"assertive\" role=\"alert\">Error message here</div>\n\n<!-- Status (polite, implicit) -->\n<div role=\"status\">Loading complete</div>\n```\n\n## Testing tools\n\n| Tool | Type | URL |\n|------|------|-----|\n| axe DevTools | Browser extension | [deque.com/axe](https://www.deque.com/axe/) |\n| WAVE | Browser extension | [wave.webaim.org](https://wave.webaim.org/) |\n| Lighthouse | Built into Chrome | DevTools → Lighthouse |\n| NVDA | Screen reader (Windows) | [nvaccess.org](https://www.nvaccess.org/) |\n| VoiceOver | Screen reader (Mac) | Built into macOS |\n| Colour Contrast Analyser | Desktop app | [tpgi.com](https://www.tpgi.com/color-contrast-checker/) |\n","isInternal":false,"tokens":1980,"sizeBytes":7324},{"name":"SKILL.md","path":".agents/skills/accessibility/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/accessibility/SKILL.md","title":"accessibility","category":"anthropic-skill","format":"markdown","content":"---\nname: accessibility\ndescription: Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Accessibility (a11y)\n\nComprehensive accessibility guidelines based on WCAG 2.1 and Lighthouse accessibility audits. Goal: make content usable by everyone, including people with disabilities.\n\n## WCAG Principles: POUR\n\n| Principle | Description |\n|-----------|-------------|\n| **P**erceivable | Content can be perceived through different senses |\n| **O**perable | Interface can be operated by all users |\n| **U**nderstandable | Content and interface are understandable |\n| **R**obust | Content works with assistive technologies |\n\n## Conformance levels\n\n| Level | Requirement | Target |\n|-------|-------------|--------|\n| **A** | Minimum accessibility | Must pass |\n| **AA** | Standard compliance | Should pass (legal requirement in many jurisdictions) |\n| **AAA** | Enhanced accessibility | Nice to have |\n\n---\n\n## Perceivable\n\n### Text alternatives (1.1)\n\n**Images require alt text:**\n```html\n<!-- ❌ Missing alt -->\n<img src=\"chart.png\">\n\n<!-- ✅ Descriptive alt -->\n<img src=\"chart.png\" alt=\"Bar chart showing 40% increase in Q3 sales\">\n\n<!-- ✅ Decorative image (empty alt) -->\n<img src=\"decorative-border.png\" alt=\"\" role=\"presentation\">\n\n<!-- ✅ Complex image with longer description -->\n<figure>\n  <img src=\"infographic.png\" alt=\"2024 market trends infographic\" \n       aria-describedby=\"infographic-desc\">\n  <figcaption id=\"infographic-desc\">\n    <!-- Detailed description -->\n  </figcaption>\n</figure>\n```\n\n**Icon buttons need accessible names:**\n```html\n<!-- ❌ No accessible name -->\n<button><svg><!-- menu icon --></svg></button>\n\n<!-- ✅ Using aria-label -->\n<button aria-label=\"Open menu\">\n  <svg aria-hidden=\"true\"><!-- menu icon --></svg>\n</button>\n\n<!-- ✅ Using visually hidden text -->\n<button>\n  <svg aria-hidden=\"true\"><!-- menu icon --></svg>\n  <span class=\"visually-hidden\">Open menu</span>\n</button>\n```\n\n**Visually hidden class:**\n```css\n.visually-hidden {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n```\n\n### Color contrast (1.4.3, 1.4.6)\n\n| Text Size | AA minimum | AAA enhanced |\n|-----------|------------|--------------|\n| Normal text (< 18px / < 14px bold) | 4.5:1 | 7:1 |\n| Large text (≥ 18px / ≥ 14px bold) | 3:1 | 4.5:1 |\n| UI components & graphics | 3:1 | 3:1 |\n\n```css\n/* ❌ Low contrast (2.5:1) */\n.low-contrast {\n  color: #999;\n  background: #fff;\n}\n\n/* ✅ Sufficient contrast (7:1) */\n.high-contrast {\n  color: #333;\n  background: #fff;\n}\n\n/* ✅ Focus states need contrast too */\n:focus-visible {\n  outline: 2px solid #005fcc;\n  outline-offset: 2px;\n}\n```\n\n**Don't rely on color alone:**\n```html\n<!-- ❌ Only color indicates error -->\n<input class=\"error-border\">\n<style>.error-border { border-color: red; }</style>\n\n<!-- ✅ Color + icon + text -->\n<div class=\"field-error\">\n  <input aria-invalid=\"true\" aria-describedby=\"email-error\">\n  <span id=\"email-error\" class=\"error-message\">\n    <svg aria-hidden=\"true\"><!-- error icon --></svg>\n    Please enter a valid email address\n  </span>\n</div>\n```\n\n### Media alternatives (1.2)\n\n```html\n<!-- Video with captions -->\n<video controls>\n  <source src=\"video.mp4\" type=\"video/mp4\">\n  <track kind=\"captions\" src=\"captions.vtt\" srclang=\"en\" label=\"English\" default>\n  <track kind=\"descriptions\" src=\"descriptions.vtt\" srclang=\"en\" label=\"Descriptions\">\n</video>\n\n<!-- Audio with transcript -->\n<audio controls>\n  <source src=\"podcast.mp3\" type=\"audio/mp3\">\n</audio>\n<details>\n  <summary>Transcript</summary>\n  <p>Full transcript text...</p>\n</details>\n```\n\n---\n\n## Operable\n\n### Keyboard accessible (2.1)\n\n**All functionality must be keyboard accessible:**\n```javascript\n// ❌ Only handles click\nelement.addEventListener('click', handleAction);\n\n// ✅ Handles both click and keyboard\nelement.addEventListener('click', handleAction);\nelement.addEventListener('keydown', (e) => {\n  if (e.key === 'Enter' || e.key === ' ') {\n    e.preventDefault();\n    handleAction();\n  }\n});\n```\n\n**No keyboard traps:**\n```javascript\n// Modal focus management\nfunction openModal(modal) {\n  const focusableElements = modal.querySelectorAll(\n    'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n  );\n  const firstElement = focusableElements[0];\n  const lastElement = focusableElements[focusableElements.length - 1];\n  \n  // Trap focus within modal\n  modal.addEventListener('keydown', (e) => {\n    if (e.key === 'Tab') {\n      if (e.shiftKey && document.activeElement === firstElement) {\n        e.preventDefault();\n        lastElement.focus();\n      } else if (!e.shiftKey && document.activeElement === lastElement) {\n        e.preventDefault();\n        firstElement.focus();\n      }\n    }\n    if (e.key === 'Escape') {\n      closeModal();\n    }\n  });\n  \n  firstElement.focus();\n}\n```\n\n### Focus visible (2.4.7)\n\n```css\n/* ❌ Never remove focus outlines */\n*:focus { outline: none; }\n\n/* ✅ Use :focus-visible for keyboard-only focus */\n:focus {\n  outline: none;\n}\n\n:focus-visible {\n  outline: 2px solid #005fcc;\n  outline-offset: 2px;\n}\n\n/* ✅ Or custom focus styles */\nbutton:focus-visible {\n  box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);\n}\n```\n\n### Skip links (2.4.1)\n\n```html\n<body>\n  <a href=\"#main-content\" class=\"skip-link\">Skip to main content</a>\n  <header><!-- navigation --></header>\n  <main id=\"main-content\" tabindex=\"-1\">\n    <!-- main content -->\n  </main>\n</body>\n```\n\n```css\n.skip-link {\n  position: absolute;\n  top: -40px;\n  left: 0;\n  background: #000;\n  color: #fff;\n  padding: 8px 16px;\n  z-index: 100;\n}\n\n.skip-link:focus {\n  top: 0;\n}\n```\n\n### Timing (2.2)\n\n```javascript\n// Allow users to extend time limits\nfunction showSessionWarning() {\n  const modal = createModal({\n    title: 'Session Expiring',\n    content: 'Your session will expire in 2 minutes.',\n    actions: [\n      { label: 'Extend session', action: extendSession },\n      { label: 'Log out', action: logout }\n    ],\n    timeout: 120000 // 2 minutes to respond\n  });\n}\n```\n\n### Motion (2.3)\n\n```css\n/* Respect reduced motion preference */\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important;\n  }\n}\n```\n\n---\n\n## Understandable\n\n### Page language (3.1.1)\n\n```html\n<!-- ❌ No language specified -->\n<html>\n\n<!-- ✅ Language specified -->\n<html lang=\"en\">\n\n<!-- ✅ Language changes within page -->\n<p>The French word for hello is <span lang=\"fr\">bonjour</span>.</p>\n```\n\n### Consistent navigation (3.2.3)\n\n```html\n<!-- Navigation should be consistent across pages -->\n<nav aria-label=\"Main\">\n  <ul>\n    <li><a href=\"/\" aria-current=\"page\">Home</a></li>\n    <li><a href=\"/products\">Products</a></li>\n    <li><a href=\"/about\">About</a></li>\n  </ul>\n</nav>\n```\n\n### Form labels (3.3.2)\n\n```html\n<!-- ❌ No label association -->\n<input type=\"email\" placeholder=\"Email\">\n\n<!-- ✅ Explicit label -->\n<label for=\"email\">Email address</label>\n<input type=\"email\" id=\"email\" name=\"email\" \n       autocomplete=\"email\" required>\n\n<!-- ✅ Implicit label -->\n<label>\n  Email address\n  <input type=\"email\" name=\"email\" autocomplete=\"email\" required>\n</label>\n\n<!-- ✅ With instructions -->\n<label for=\"password\">Password</label>\n<input type=\"password\" id=\"password\" \n       aria-describedby=\"password-requirements\">\n<p id=\"password-requirements\">\n  Must be at least 8 characters with one number.\n</p>\n```\n\n### Error handling (3.3.1, 3.3.3)\n\n```html\n<!-- Announce errors to screen readers -->\n<form novalidate>\n  <div class=\"field\" aria-live=\"polite\">\n    <label for=\"email\">Email</label>\n    <input type=\"email\" id=\"email\" \n           aria-invalid=\"true\"\n           aria-describedby=\"email-error\">\n    <p id=\"email-error\" class=\"error\" role=\"alert\">\n      Please enter a valid email address (e.g., name@example.com)\n    </p>\n  </div>\n</form>\n```\n\n```javascript\n// Focus first error on submit\nform.addEventListener('submit', (e) => {\n  const firstError = form.querySelector('[aria-invalid=\"true\"]');\n  if (firstError) {\n    e.preventDefault();\n    firstError.focus();\n    \n    // Announce error summary\n    const errorSummary = document.getElementById('error-summary');\n    errorSummary.textContent = `${errors.length} errors found. Please fix them and try again.`;\n    errorSummary.focus();\n  }\n});\n```\n\n---\n\n## Robust\n\n### Valid HTML (4.1.1)\n\n```html\n<!-- ❌ Duplicate IDs -->\n<div id=\"content\">...</div>\n<div id=\"content\">...</div>\n\n<!-- ❌ Invalid nesting -->\n<a href=\"/\"><button>Click</button></a>\n\n<!-- ✅ Unique IDs -->\n<div id=\"main-content\">...</div>\n<div id=\"sidebar-content\">...</div>\n\n<!-- ✅ Proper nesting -->\n<a href=\"/\" class=\"button-link\">Click</a>\n```\n\n### ARIA usage (4.1.2)\n\n**Prefer native elements:**\n```html\n<!-- ❌ ARIA role on div -->\n<div role=\"button\" tabindex=\"0\">Click me</div>\n\n<!-- ✅ Native button -->\n<button>Click me</button>\n\n<!-- ❌ ARIA checkbox -->\n<div role=\"checkbox\" aria-checked=\"false\">Option</div>\n\n<!-- ✅ Native checkbox -->\n<label><input type=\"checkbox\"> Option</label>\n```\n\n**When ARIA is needed:**\n```html\n<!-- Custom tabs component -->\n<div role=\"tablist\" aria-label=\"Product information\">\n  <button role=\"tab\" id=\"tab-1\" aria-selected=\"true\" \n          aria-controls=\"panel-1\">Description</button>\n  <button role=\"tab\" id=\"tab-2\" aria-selected=\"false\" \n          aria-controls=\"panel-2\" tabindex=\"-1\">Reviews</button>\n</div>\n<div role=\"tabpanel\" id=\"panel-1\" aria-labelledby=\"tab-1\">\n  <!-- Panel content -->\n</div>\n<div role=\"tabpanel\" id=\"panel-2\" aria-labelledby=\"tab-2\" hidden>\n  <!-- Panel content -->\n</div>\n```\n\n### Live regions (4.1.3)\n\n```html\n<!-- Status updates -->\n<div aria-live=\"polite\" aria-atomic=\"true\" class=\"status\">\n  <!-- Content updates announced to screen readers -->\n</div>\n\n<!-- Urgent alerts -->\n<div role=\"alert\" aria-live=\"assertive\">\n  <!-- Interrupts current announcement -->\n</div>\n```\n\n```javascript\n// Announce dynamic content changes\nfunction showNotification(message, type = 'polite') {\n  const container = document.getElementById(`${type}-announcer`);\n  container.textContent = ''; // Clear first\n  requestAnimationFrame(() => {\n    container.textContent = message;\n  });\n}\n```\n\n---\n\n## Testing checklist\n\n### Automated testing\n```bash\n# Lighthouse accessibility audit\nnpx lighthouse https://example.com --only-categories=accessibility\n\n# axe-core\nnpm install @axe-core/cli -g\naxe https://example.com\n```\n\n### Manual testing\n\n- [ ] **Keyboard navigation:** Tab through entire page, use Enter/Space to activate\n- [ ] **Screen reader:** Test with VoiceOver (Mac), NVDA (Windows), or TalkBack (Android)\n- [ ] **Zoom:** Content usable at 200% zoom\n- [ ] **High contrast:** Test with Windows High Contrast Mode\n- [ ] **Reduced motion:** Test with `prefers-reduced-motion: reduce`\n- [ ] **Focus order:** Logical and follows visual order\n\n### Screen reader commands\n\n| Action | VoiceOver (Mac) | NVDA (Windows) |\n|--------|-----------------|----------------|\n| Start/Stop | ⌘ + F5 | Ctrl + Alt + N |\n| Next item | VO + → | ↓ |\n| Previous item | VO + ← | ↑ |\n| Activate | VO + Space | Enter |\n| Headings list | VO + U, then arrows | H / Shift + H |\n| Links list | VO + U | K / Shift + K |\n\n---\n\n## Common issues by impact\n\n### Critical (fix immediately)\n1. Missing form labels\n2. Missing image alt text\n3. Insufficient color contrast\n4. Keyboard traps\n5. No focus indicators\n\n### Serious (fix before launch)\n1. Missing page language\n2. Missing heading structure\n3. Non-descriptive link text\n4. Auto-playing media\n5. Missing skip links\n\n### Moderate (fix soon)\n1. Missing ARIA labels on icons\n2. Inconsistent navigation\n3. Missing error identification\n4. Timing without controls\n5. Missing landmark regions\n\n## References\n\n- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/)\n- [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)\n- [Deque axe Rules](https://dequeuniversity.com/rules/axe/)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n","frontmatter":{"name":"accessibility","description":"Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".","license":"MIT","metadata":{"author":"web-quality-skills","version":"1.0"}},"isInternal":false,"tokens":3275,"sizeBytes":12388},{"name":"SKILL.md","path":".agents/skills/best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/best-practices/SKILL.md","title":"best-practices","category":"anthropic-skill","format":"markdown","content":"---\nname: best-practices\ndescription: Apply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Best practices\n\nModern web development standards based on Lighthouse best practices audits. Covers security, browser compatibility, and code quality patterns.\n\n## Security\n\n### HTTPS everywhere\n\n**Enforce HTTPS:**\n```html\n<!-- ❌ Mixed content -->\n<img src=\"http://example.com/image.jpg\">\n<script src=\"http://cdn.example.com/script.js\"></script>\n\n<!-- ✅ HTTPS only -->\n<img src=\"https://example.com/image.jpg\">\n<script src=\"https://cdn.example.com/script.js\"></script>\n\n<!-- ✅ Protocol-relative (will use page's protocol) -->\n<img src=\"//example.com/image.jpg\">\n```\n\n**HSTS Header:**\n```\nStrict-Transport-Security: max-age=31536000; includeSubDomains; preload\n```\n\n### Content Security Policy (CSP)\n\n```html\n<!-- Basic CSP via meta tag -->\n<meta http-equiv=\"Content-Security-Policy\" \n      content=\"default-src 'self'; \n               script-src 'self' https://trusted-cdn.com; \n               style-src 'self' 'unsafe-inline';\n               img-src 'self' data: https:;\n               connect-src 'self' https://api.example.com;\">\n\n<!-- Better: HTTP header -->\n```\n\n**CSP Header (recommended):**\n```\nContent-Security-Policy: \n  default-src 'self';\n  script-src 'self' 'nonce-abc123' https://trusted.com;\n  style-src 'self' 'nonce-abc123';\n  img-src 'self' data: https:;\n  connect-src 'self' https://api.example.com;\n  frame-ancestors 'self';\n  base-uri 'self';\n  form-action 'self';\n```\n\n**Using nonces for inline scripts:**\n```html\n<script nonce=\"abc123\">\n  // This inline script is allowed\n</script>\n```\n\n### Security headers\n\n```\n# Prevent clickjacking\nX-Frame-Options: DENY\n\n# Prevent MIME type sniffing\nX-Content-Type-Options: nosniff\n\n# Enable XSS filter (legacy browsers)\nX-XSS-Protection: 1; mode=block\n\n# Control referrer information\nReferrer-Policy: strict-origin-when-cross-origin\n\n# Permissions policy (formerly Feature-Policy)\nPermissions-Policy: geolocation=(), microphone=(), camera=()\n```\n\n### No vulnerable libraries\n\n```bash\n# Check for vulnerabilities\nnpm audit\nyarn audit\n\n# Auto-fix when possible\nnpm audit fix\n\n# Check specific package\nnpm ls lodash\n```\n\n**Keep dependencies updated:**\n```json\n// package.json\n{\n  \"scripts\": {\n    \"audit\": \"npm audit --audit-level=moderate\",\n    \"update\": \"npm update && npm audit fix\"\n  }\n}\n```\n\n**Known vulnerable patterns to avoid:**\n```javascript\n// ❌ Prototype pollution vulnerable patterns\nObject.assign(target, userInput);\n_.merge(target, userInput);\n\n// ✅ Safer alternatives\nconst safeData = JSON.parse(JSON.stringify(userInput));\n```\n\n### Input sanitization\n\n```javascript\n// ❌ XSS vulnerable\nelement.innerHTML = userInput;\ndocument.write(userInput);\n\n// ✅ Safe text content\nelement.textContent = userInput;\n\n// ✅ If HTML needed, sanitize\nimport DOMPurify from 'dompurify';\nelement.innerHTML = DOMPurify.sanitize(userInput);\n```\n\n### Secure cookies\n\n```javascript\n// ❌ Insecure cookie\ndocument.cookie = \"session=abc123\";\n\n// ✅ Secure cookie (server-side)\nSet-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/\n```\n\n---\n\n## Browser compatibility\n\n### Doctype declaration\n\n```html\n<!-- ❌ Missing or invalid doctype -->\n<HTML>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n\n<!-- ✅ HTML5 doctype -->\n<!DOCTYPE html>\n<html lang=\"en\">\n```\n\n### Character encoding\n\n```html\n<!-- ❌ Missing or late charset -->\n<html>\n<head>\n  <title>Page</title>\n  <meta charset=\"UTF-8\">\n</head>\n\n<!-- ✅ Charset as first element in head -->\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Page</title>\n</head>\n```\n\n### Viewport meta tag\n\n```html\n<!-- ❌ Missing viewport -->\n<head>\n  <title>Page</title>\n</head>\n\n<!-- ✅ Responsive viewport -->\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>Page</title>\n</head>\n```\n\n### Feature detection\n\n```javascript\n// ❌ Browser detection (brittle)\nif (navigator.userAgent.includes('Chrome')) {\n  // Chrome-specific code\n}\n\n// ✅ Feature detection\nif ('IntersectionObserver' in window) {\n  // Use IntersectionObserver\n} else {\n  // Fallback\n}\n\n// ✅ Using @supports in CSS\n@supports (display: grid) {\n  .container {\n    display: grid;\n  }\n}\n\n@supports not (display: grid) {\n  .container {\n    display: flex;\n  }\n}\n```\n\n### Polyfills (when needed)\n\n```html\n<!-- Load polyfills conditionally -->\n<script>\n  if (!('fetch' in window)) {\n    document.write('<script src=\"/polyfills/fetch.js\"><\\/script>');\n  }\n</script>\n\n<!-- Or use polyfill.io -->\n<script src=\"https://polyfill.io/v3/polyfill.min.js?features=fetch,IntersectionObserver\"></script>\n```\n\n---\n\n## Deprecated APIs\n\n### Avoid these\n\n```javascript\n// ❌ document.write (blocks parsing)\ndocument.write('<script src=\"...\"></script>');\n\n// ✅ Dynamic script loading\nconst script = document.createElement('script');\nscript.src = '...';\ndocument.head.appendChild(script);\n\n// ❌ Synchronous XHR (blocks main thread)\nconst xhr = new XMLHttpRequest();\nxhr.open('GET', url, false); // false = synchronous\n\n// ✅ Async fetch\nconst response = await fetch(url);\n\n// ❌ Application Cache (deprecated)\n<html manifest=\"cache.manifest\">\n\n// ✅ Service Workers\nif ('serviceWorker' in navigator) {\n  navigator.serviceWorker.register('/sw.js');\n}\n```\n\n### Event listener passive\n\n```javascript\n// ❌ Non-passive touch/wheel (may block scrolling)\nelement.addEventListener('touchstart', handler);\nelement.addEventListener('wheel', handler);\n\n// ✅ Passive listeners (allows smooth scrolling)\nelement.addEventListener('touchstart', handler, { passive: true });\nelement.addEventListener('wheel', handler, { passive: true });\n\n// ✅ If you need preventDefault, be explicit\nelement.addEventListener('touchstart', handler, { passive: false });\n```\n\n---\n\n## Console & errors\n\n### No console errors\n\n```javascript\n// ❌ Errors in production\nconsole.log('Debug info'); // Remove in production\nthrow new Error('Unhandled'); // Catch all errors\n\n// ✅ Proper error handling\ntry {\n  riskyOperation();\n} catch (error) {\n  // Log to error tracking service\n  errorTracker.captureException(error);\n  // Show user-friendly message\n  showErrorMessage('Something went wrong. Please try again.');\n}\n```\n\n### Error boundaries (React)\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n  \n  static getDerivedStateFromError(error) {\n    return { hasError: true };\n  }\n  \n  componentDidCatch(error, info) {\n    errorTracker.captureException(error, { extra: info });\n  }\n  \n  render() {\n    if (this.state.hasError) {\n      return <FallbackUI />;\n    }\n    return this.props.children;\n  }\n}\n\n// Usage\n<ErrorBoundary>\n  <App />\n</ErrorBoundary>\n```\n\n### Global error handler\n\n```javascript\n// Catch unhandled errors\nwindow.addEventListener('error', (event) => {\n  errorTracker.captureException(event.error);\n});\n\n// Catch unhandled promise rejections\nwindow.addEventListener('unhandledrejection', (event) => {\n  errorTracker.captureException(event.reason);\n});\n```\n\n---\n\n## Source maps\n\n### Production configuration\n\n```javascript\n// ❌ Source maps exposed in production\n// webpack.config.js\nmodule.exports = {\n  devtool: 'source-map', // Exposes source code\n};\n\n// ✅ Hidden source maps (uploaded to error tracker)\nmodule.exports = {\n  devtool: 'hidden-source-map',\n};\n\n// ✅ Or no source maps in production\nmodule.exports = {\n  devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',\n};\n```\n\n---\n\n## Performance best practices\n\n### Avoid blocking patterns\n\n```javascript\n// ❌ Blocking script\n<script src=\"heavy-library.js\"></script>\n\n// ✅ Deferred script\n<script defer src=\"heavy-library.js\"></script>\n\n// ❌ Blocking CSS import\n@import url('other-styles.css');\n\n// ✅ Link tags (parallel loading)\n<link rel=\"stylesheet\" href=\"styles.css\">\n<link rel=\"stylesheet\" href=\"other-styles.css\">\n```\n\n### Efficient event handlers\n\n```javascript\n// ❌ Handler on every element\nitems.forEach(item => {\n  item.addEventListener('click', handleClick);\n});\n\n// ✅ Event delegation\ncontainer.addEventListener('click', (e) => {\n  if (e.target.matches('.item')) {\n    handleClick(e);\n  }\n});\n```\n\n### Memory management\n\n```javascript\n// ❌ Memory leak (never removed)\nconst handler = () => { /* ... */ };\nwindow.addEventListener('resize', handler);\n\n// ✅ Cleanup when done\nconst handler = () => { /* ... */ };\nwindow.addEventListener('resize', handler);\n\n// Later, when component unmounts:\nwindow.removeEventListener('resize', handler);\n\n// ✅ Using AbortController\nconst controller = new AbortController();\nwindow.addEventListener('resize', handler, { signal: controller.signal });\n\n// Cleanup:\ncontroller.abort();\n```\n\n---\n\n## Code quality\n\n### Valid HTML\n\n```html\n<!-- ❌ Invalid HTML -->\n<div id=\"header\">\n<div id=\"header\"> <!-- Duplicate ID -->\n\n<ul>\n  <div>Item</div> <!-- Invalid child -->\n</ul>\n\n<a href=\"/\"><button>Click</button></a> <!-- Invalid nesting -->\n\n<!-- ✅ Valid HTML -->\n<header id=\"site-header\">\n</header>\n\n<ul>\n  <li>Item</li>\n</ul>\n\n<a href=\"/\" class=\"button\">Click</a>\n```\n\n### Semantic HTML\n\n```html\n<!-- ❌ Non-semantic -->\n<div class=\"header\">\n  <div class=\"nav\">\n    <div class=\"nav-item\">Home</div>\n  </div>\n</div>\n<div class=\"main\">\n  <div class=\"article\">\n    <div class=\"title\">Headline</div>\n  </div>\n</div>\n\n<!-- ✅ Semantic HTML5 -->\n<header>\n  <nav>\n    <a href=\"/\">Home</a>\n  </nav>\n</header>\n<main>\n  <article>\n    <h1>Headline</h1>\n  </article>\n</main>\n```\n\n### Image aspect ratios\n\n```html\n<!-- ❌ Distorted images -->\n<img src=\"photo.jpg\" width=\"300\" height=\"100\">\n<!-- If actual ratio is 4:3, this squishes the image -->\n\n<!-- ✅ Preserve aspect ratio -->\n<img src=\"photo.jpg\" width=\"300\" height=\"225\">\n<!-- Actual 4:3 dimensions -->\n\n<!-- ✅ CSS object-fit for flexibility -->\n<img src=\"photo.jpg\" style=\"width: 300px; height: 200px; object-fit: cover;\">\n```\n\n---\n\n## Permissions & privacy\n\n### Request permissions properly\n\n```javascript\n// ❌ Request on page load (bad UX, often denied)\nnavigator.geolocation.getCurrentPosition(success, error);\n\n// ✅ Request in context, after user action\nfindNearbyButton.addEventListener('click', async () => {\n  // Explain why you need it\n  if (await showPermissionExplanation()) {\n    navigator.geolocation.getCurrentPosition(success, error);\n  }\n});\n```\n\n### Permissions policy\n\n```html\n<!-- Restrict powerful features -->\n<meta http-equiv=\"Permissions-Policy\" \n      content=\"geolocation=(), camera=(), microphone=()\">\n\n<!-- Or allow for specific origins -->\n<meta http-equiv=\"Permissions-Policy\" \n      content=\"geolocation=(self 'https://maps.example.com')\">\n```\n\n---\n\n## Audit checklist\n\n### Security (critical)\n- [ ] HTTPS enabled, no mixed content\n- [ ] No vulnerable dependencies (`npm audit`)\n- [ ] CSP headers configured\n- [ ] Security headers present\n- [ ] No exposed source maps\n\n### Compatibility\n- [ ] Valid HTML5 doctype\n- [ ] Charset declared first in head\n- [ ] Viewport meta tag present\n- [ ] No deprecated APIs used\n- [ ] Passive event listeners for scroll/touch\n\n### Code quality\n- [ ] No console errors\n- [ ] Valid HTML (no duplicate IDs)\n- [ ] Semantic HTML elements used\n- [ ] Proper error handling\n- [ ] Memory cleanup in components\n\n### UX\n- [ ] No intrusive interstitials\n- [ ] Permission requests in context\n- [ ] Clear error messages\n- [ ] Appropriate image aspect ratios\n\n## Tools\n\n| Tool | Purpose |\n|------|---------|\n| `npm audit` | Dependency vulnerabilities |\n| [SecurityHeaders.com](https://securityheaders.com) | Header analysis |\n| [W3C Validator](https://validator.w3.org) | HTML validation |\n| Lighthouse | Best practices audit |\n| [Observatory](https://observatory.mozilla.org) | Security scan |\n\n## References\n\n- [MDN Web Security](https://developer.mozilla.org/en-US/docs/Web/Security)\n- [OWASP Top 10](https://owasp.org/www-project-top-ten/)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n","frontmatter":{"name":"best-practices","description":"Apply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".","license":"MIT","metadata":{"author":"web-quality-skills","version":"1.0"}},"isInternal":false,"tokens":2955,"sizeBytes":12138},{"name":"LCP.md","path":".agents/skills/core-web-vitals/references/LCP.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/core-web-vitals/references/LCP.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# LCP optimization reference\n\n## What is LCP?\n\nLargest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:\n\n- An `<img>` element\n- An `<image>` element inside `<svg>`\n- A `<video>` element with poster image\n- An element with a background image via `url()`\n- A block-level element containing text nodes\n\n## LCP timeline\n\n```\n[  Server Response  ][  Resource Load  ][  Render  ]\n       TTFB              Download         Paint\n       └─────────────────────────────────────┘\n                         LCP Time\n```\n\n## Detailed optimizations\n\n### 1. Server response time (TTFB)\n\nTarget: < 800ms\n\n**Causes:**\n- Slow server/database queries\n- No CDN/edge caching\n- Inefficient backend code\n- Cold starts (serverless)\n\n**Solutions:**\n```javascript\n// Use edge functions for dynamic content\n// Vercel example\nexport const config = { runtime: 'edge' };\n\n// Use stale-while-revalidate caching\n// Cache-Control header\nres.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate=300');\n```\n\n### 2. Resource load time\n\n**For images:**\n```html\n<!-- Preload LCP image -->\n<link rel=\"preload\" as=\"image\" href=\"/hero.webp\" \n      imagesrcset=\"/hero-400.webp 400w, /hero-800.webp 800w\"\n      imagesizes=\"100vw\"\n      fetchpriority=\"high\">\n\n<!-- Modern format with fallback -->\n<picture>\n  <source srcset=\"/hero.avif\" type=\"image/avif\">\n  <source srcset=\"/hero.webp\" type=\"image/webp\">\n  <img src=\"/hero.jpg\" width=\"1200\" height=\"600\" \n       fetchpriority=\"high\" alt=\"Hero\">\n</picture>\n```\n\n**For text (web fonts):**\n```css\n@font-face {\n  font-family: 'Heading';\n  src: url('/fonts/heading.woff2') format('woff2');\n  font-display: swap; /* Show fallback immediately */\n}\n```\n\n### 3. Render blocking resources\n\n**Critical CSS pattern:**\n```html\n<head>\n  <!-- Inline critical CSS -->\n  <style>\n    /* Only above-fold styles, < 14KB */\n    .hero { /* ... */ }\n    .nav { /* ... */ }\n  </style>\n  \n  <!-- Defer non-critical CSS -->\n  <link rel=\"preload\" href=\"/styles.css\" as=\"style\" \n        onload=\"this.onload=null;this.rel='stylesheet'\">\n</head>\n```\n\n**Defer JavaScript:**\n```html\n<!-- ❌ Blocks parsing -->\n<script src=\"/app.js\"></script>\n\n<!-- ✅ Deferred (runs after HTML parsed) -->\n<script defer src=\"/app.js\"></script>\n\n<!-- ✅ Module (deferred by default) -->\n<script type=\"module\" src=\"/app.mjs\"></script>\n```\n\n### 4. Client-side rendering\n\n**Problem:** Content not in initial HTML.\n\n**Solutions:**\n\n**Server-side rendering (SSR):**\n```javascript\n// Next.js\nexport async function getServerSideProps() {\n  const data = await fetchHeroContent();\n  return { props: { hero: data } };\n}\n```\n\n**Static site generation (SSG):**\n```javascript\n// Next.js\nexport async function getStaticProps() {\n  const data = await fetchHeroContent();\n  return { props: { hero: data }, revalidate: 3600 };\n}\n```\n\n**Streaming SSR:**\n```jsx\n// React 18+\nimport { Suspense } from 'react';\n\nfunction Page() {\n  return (\n    <Suspense fallback={<HeroSkeleton />}>\n      <Hero />\n    </Suspense>\n  );\n}\n```\n\n## Framework-specific tips\n\n### Next.js\n```jsx\nimport Image from 'next/image';\n\n// LCP image with priority\n<Image \n  src=\"/hero.jpg\"\n  priority\n  fill\n  sizes=\"100vw\"\n  alt=\"Hero\"\n/>\n```\n\n### Nuxt\n```vue\n<NuxtImg\n  src=\"/hero.jpg\"\n  preload\n  loading=\"eager\"\n  sizes=\"100vw\"\n/>\n```\n\n### Astro\n```astro\n---\nimport { Image } from 'astro:assets';\nimport hero from '../assets/hero.jpg';\n---\n<Image \n  src={hero} \n  loading=\"eager\" \n  decoding=\"sync\"\n  alt=\"Hero\" \n/>\n```\n\n## Debugging LCP\n\n```javascript\n// Identify LCP element\nnew PerformanceObserver((entryList) => {\n  const entries = entryList.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  \n  console.log('LCP:', {\n    element: lastEntry.element,\n    time: lastEntry.startTime,\n    size: lastEntry.size,\n    url: lastEntry.url,\n    renderTime: lastEntry.renderTime,\n    loadTime: lastEntry.loadTime\n  });\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n## Common issues\n\n| Issue | Impact | Fix |\n|-------|--------|-----|\n| No preload for LCP image | +500-1000ms | Add `<link rel=\"preload\">` |\n| Large unoptimized image | +300-800ms | Compress, use WebP/AVIF |\n| Render-blocking CSS | +200-500ms | Inline critical CSS |\n| Slow TTFB | +300-2000ms | CDN, edge caching |\n| Client-rendered content | +500-2000ms | SSR/SSG |\n","isInternal":false,"tokens":1196,"sizeBytes":4431},{"name":"SKILL.md","path":".agents/skills/core-web-vitals/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/core-web-vitals/SKILL.md","title":"core-web-vitals","category":"anthropic-skill","format":"markdown","content":"---\nname: core-web-vitals\ndescription: Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Core Web Vitals optimization\n\nTargeted optimization for the three Core Web Vitals metrics that affect Google Search ranking and user experience.\n\n## The three metrics\n\n| Metric | Measures | Good | Needs work | Poor |\n|--------|----------|------|------------|------|\n| **LCP** | Loading | ≤ 2.5s | 2.5s – 4s | > 4s |\n| **INP** | Interactivity | ≤ 200ms | 200ms – 500ms | > 500ms |\n| **CLS** | Visual Stability | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |\n\nGoogle measures at the **75th percentile** — 75% of page visits must meet \"Good\" thresholds.\n\n---\n\n## LCP: Largest Contentful Paint\n\nLCP measures when the largest visible content element renders. Usually this is:\n- Hero image or video\n- Large text block\n- Background image\n- `<svg>` element\n\n### Common LCP issues\n\n**1. Slow server response (TTFB > 800ms)**\n```\nFix: CDN, caching, optimized backend, edge rendering\n```\n\n**2. Render-blocking resources**\n```html\n<!-- ❌ Blocks rendering -->\n<link rel=\"stylesheet\" href=\"/all-styles.css\">\n\n<!-- ✅ Critical CSS inlined, rest deferred -->\n<style>/* Critical above-fold CSS */</style>\n<link rel=\"preload\" href=\"/styles.css\" as=\"style\" \n      onload=\"this.onload=null;this.rel='stylesheet'\">\n```\n\n**3. Slow resource load times**\n```html\n<!-- ❌ No hints, discovered late -->\n<img src=\"/hero.jpg\" alt=\"Hero\">\n\n<!-- ✅ Preloaded with high priority -->\n<link rel=\"preload\" href=\"/hero.webp\" as=\"image\" fetchpriority=\"high\">\n<img src=\"/hero.webp\" alt=\"Hero\" fetchpriority=\"high\">\n```\n\n**4. Client-side rendering delays**\n```javascript\n// ❌ Content loads after JavaScript\nuseEffect(() => {\n  fetch('/api/hero-text').then(r => r.json()).then(setHeroText);\n}, []);\n\n// ✅ Server-side or static rendering\n// Use SSR, SSG, or streaming to send HTML with content\nexport async function getServerSideProps() {\n  const heroText = await fetchHeroText();\n  return { props: { heroText } };\n}\n```\n\n### LCP optimization checklist\n\n```markdown\n- [ ] TTFB < 800ms (use CDN, edge caching)\n- [ ] LCP image preloaded with fetchpriority=\"high\"\n- [ ] LCP image optimized (WebP/AVIF, correct size)\n- [ ] Critical CSS inlined (< 14KB)\n- [ ] No render-blocking JavaScript in <head>\n- [ ] Fonts don't block text rendering (font-display: swap)\n- [ ] LCP element in initial HTML (not JS-rendered)\n```\n\n### LCP element identification\n```javascript\n// Find your LCP element\nnew PerformanceObserver((list) => {\n  const entries = list.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  console.log('LCP element:', lastEntry.element);\n  console.log('LCP time:', lastEntry.startTime);\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n---\n\n## INP: Interaction to Next Paint\n\nINP measures responsiveness across ALL interactions (clicks, taps, key presses) during a page visit. It reports the worst interaction (at 98th percentile for high-traffic pages).\n\n### INP breakdown\n\nTotal INP = **Input Delay** + **Processing Time** + **Presentation Delay**\n\n| Phase | Target | Optimization |\n|-------|--------|--------------|\n| Input Delay | < 50ms | Reduce main thread blocking |\n| Processing | < 100ms | Optimize event handlers |\n| Presentation | < 50ms | Minimize rendering work |\n\n### Common INP issues\n\n**1. Long tasks blocking main thread**\n```javascript\n// ❌ Long synchronous task\nfunction processLargeArray(items) {\n  items.forEach(item => expensiveOperation(item));\n}\n\n// ✅ Break into chunks with yielding\nasync function processLargeArray(items) {\n  const CHUNK_SIZE = 100;\n  for (let i = 0; i < items.length; i += CHUNK_SIZE) {\n    const chunk = items.slice(i, i + CHUNK_SIZE);\n    chunk.forEach(item => expensiveOperation(item));\n    \n    // Yield to main thread\n    await new Promise(r => setTimeout(r, 0));\n    // Or use scheduler.yield() when available\n  }\n}\n```\n\n**2. Heavy event handlers**\n```javascript\n// ❌ All work in handler\nbutton.addEventListener('click', () => {\n  // Heavy computation\n  const result = calculateComplexThing();\n  // DOM updates\n  updateUI(result);\n  // Analytics\n  trackEvent('click');\n});\n\n// ✅ Prioritize visual feedback\nbutton.addEventListener('click', () => {\n  // Immediate visual feedback\n  button.classList.add('loading');\n  \n  // Defer non-critical work\n  requestAnimationFrame(() => {\n    const result = calculateComplexThing();\n    updateUI(result);\n  });\n  \n  // Use requestIdleCallback for analytics\n  requestIdleCallback(() => trackEvent('click'));\n});\n```\n\n**3. Third-party scripts**\n```javascript\n// ❌ Eagerly loaded, blocks interactions\n<script src=\"https://heavy-widget.com/widget.js\"></script>\n\n// ✅ Lazy loaded on interaction or visibility\nconst loadWidget = () => {\n  import('https://heavy-widget.com/widget.js')\n    .then(widget => widget.init());\n};\nbutton.addEventListener('click', loadWidget, { once: true });\n```\n\n**4. Excessive re-renders (React/Vue)**\n```javascript\n// ❌ Re-renders entire tree\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Counter count={count} />\n      <ExpensiveComponent /> {/* Re-renders on every count change */}\n    </div>\n  );\n}\n\n// ✅ Memoized expensive components\nconst MemoizedExpensive = React.memo(ExpensiveComponent);\n\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Counter count={count} />\n      <MemoizedExpensive />\n    </div>\n  );\n}\n```\n\n### INP optimization checklist\n\n```markdown\n- [ ] No tasks > 50ms on main thread\n- [ ] Event handlers complete quickly (< 100ms)\n- [ ] Visual feedback provided immediately\n- [ ] Heavy work deferred with requestIdleCallback\n- [ ] Third-party scripts don't block interactions\n- [ ] Debounced input handlers where appropriate\n- [ ] Web Workers for CPU-intensive operations\n```\n\n### INP debugging\n```javascript\n// Identify slow interactions\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (entry.duration > 200) {\n      console.warn('Slow interaction:', {\n        type: entry.name,\n        duration: entry.duration,\n        processingStart: entry.processingStart,\n        processingEnd: entry.processingEnd,\n        target: entry.target\n      });\n    }\n  }\n}).observe({ type: 'event', buffered: true, durationThreshold: 16 });\n```\n\n---\n\n## CLS: Cumulative Layout Shift\n\nCLS measures unexpected layout shifts. A shift occurs when a visible element changes position between frames without user interaction.\n\n**CLS Formula:** `impact fraction × distance fraction`\n\n### Common CLS causes\n\n**1. Images without dimensions**\n```html\n<!-- ❌ Causes layout shift when loaded -->\n<img src=\"photo.jpg\" alt=\"Photo\">\n\n<!-- ✅ Space reserved -->\n<img src=\"photo.jpg\" alt=\"Photo\" width=\"800\" height=\"600\">\n\n<!-- ✅ Or use aspect-ratio -->\n<img src=\"photo.jpg\" alt=\"Photo\" style=\"aspect-ratio: 4/3; width: 100%;\">\n```\n\n**2. Ads, embeds, and iframes**\n```html\n<!-- ❌ Unknown size until loaded -->\n<iframe src=\"https://ad-network.com/ad\"></iframe>\n\n<!-- ✅ Reserve space with min-height -->\n<div style=\"min-height: 250px;\">\n  <iframe src=\"https://ad-network.com/ad\" height=\"250\"></iframe>\n</div>\n\n<!-- ✅ Or use aspect-ratio container -->\n<div style=\"aspect-ratio: 16/9;\">\n  <iframe src=\"https://youtube.com/embed/...\" \n          style=\"width: 100%; height: 100%;\"></iframe>\n</div>\n```\n\n**3. Dynamically injected content**\n```javascript\n// ❌ Inserts content above viewport\nnotifications.prepend(newNotification);\n\n// ✅ Insert below viewport or use transform\nconst insertBelow = viewport.bottom < newNotification.top;\nif (insertBelow) {\n  notifications.prepend(newNotification);\n} else {\n  // Animate in without shifting\n  newNotification.style.transform = 'translateY(-100%)';\n  notifications.prepend(newNotification);\n  requestAnimationFrame(() => {\n    newNotification.style.transform = '';\n  });\n}\n```\n\n**4. Web fonts causing FOUT**\n```css\n/* ❌ Font swap shifts text */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n}\n\n/* ✅ Optional font (no shift if slow) */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: optional;\n}\n\n/* ✅ Or match fallback metrics */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: swap;\n  size-adjust: 105%; /* Match fallback size */\n  ascent-override: 95%;\n  descent-override: 20%;\n}\n```\n\n**5. Animations triggering layout**\n```css\n/* ❌ Animates layout properties */\n.animate {\n  transition: height 0.3s, width 0.3s;\n}\n\n/* ✅ Use transform instead */\n.animate {\n  transition: transform 0.3s;\n}\n.animate.expanded {\n  transform: scale(1.2);\n}\n```\n\n### CLS optimization checklist\n\n```markdown\n- [ ] All images have width/height or aspect-ratio\n- [ ] All videos/embeds have reserved space\n- [ ] Ads have min-height containers\n- [ ] Fonts use font-display: optional or matched metrics\n- [ ] Dynamic content inserted below viewport\n- [ ] Animations use transform/opacity only\n- [ ] No content injected above existing content\n```\n\n### CLS debugging\n```javascript\n// Track layout shifts\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (!entry.hadRecentInput) {\n      console.log('Layout shift:', entry.value);\n      entry.sources?.forEach(source => {\n        console.log('  Shifted element:', source.node);\n        console.log('  Previous rect:', source.previousRect);\n        console.log('  Current rect:', source.currentRect);\n      });\n    }\n  }\n}).observe({ type: 'layout-shift', buffered: true });\n```\n\n---\n\n## Measurement tools\n\n### Lab testing\n- **Chrome DevTools** → Performance panel, Lighthouse\n- **WebPageTest** → Detailed waterfall, filmstrip\n- **Lighthouse CLI** → `npx lighthouse <url>`\n\n### Field data (real users)\n- **Chrome User Experience Report (CrUX)** → BigQuery or API\n- **Search Console** → Core Web Vitals report\n- **web-vitals library** → Send to your analytics\n\n```javascript\nimport {onLCP, onINP, onCLS} from 'web-vitals';\n\nfunction sendToAnalytics({name, value, rating}) {\n  gtag('event', name, {\n    event_category: 'Web Vitals',\n    value: Math.round(name === 'CLS' ? value * 1000 : value),\n    event_label: rating\n  });\n}\n\nonLCP(sendToAnalytics);\nonINP(sendToAnalytics);\nonCLS(sendToAnalytics);\n```\n\n---\n\n## Framework quick fixes\n\n### Next.js\n```jsx\n// LCP: Use next/image with priority\nimport Image from 'next/image';\n<Image src=\"/hero.jpg\" priority fill alt=\"Hero\" />\n\n// INP: Use dynamic imports\nconst HeavyComponent = dynamic(() => import('./Heavy'), { ssr: false });\n\n// CLS: Image component handles dimensions automatically\n```\n\n### React\n```jsx\n// LCP: Preload in head\n<link rel=\"preload\" href=\"/hero.jpg\" as=\"image\" fetchpriority=\"high\" />\n\n// INP: Memoize and useTransition\nconst [isPending, startTransition] = useTransition();\nstartTransition(() => setExpensiveState(newValue));\n\n// CLS: Always specify dimensions in img tags\n```\n\n### Vue/Nuxt\n```vue\n<!-- LCP: Use nuxt/image with preload -->\n<NuxtImg src=\"/hero.jpg\" preload loading=\"eager\" />\n\n<!-- INP: Use async components -->\n<component :is=\"() => import('./Heavy.vue')\" />\n\n<!-- CLS: Use aspect-ratio CSS -->\n<img :style=\"{ aspectRatio: '16/9' }\" />\n```\n\n## References\n\n- [web.dev LCP](https://web.dev/articles/lcp)\n- [web.dev INP](https://web.dev/articles/inp)\n- [web.dev CLS](https://web.dev/articles/cls)\n- [Performance skill](../performance/SKILL.md)\n","frontmatter":{"name":"core-web-vitals","description":"Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".","license":"MIT","metadata":{"author":"web-quality-skills","version":"1.0"}},"isInternal":false,"tokens":2973,"sizeBytes":11669},{"name":"SKILL.md","path":".agents/skills/frontend-design/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/frontend-design/SKILL.md","title":"frontend-design","category":"anthropic-skill","format":"markdown","content":"---\nname: frontend-design\ndescription: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.\nlicense: Complete terms in LICENSE.txt\n---\n\nThis skill guides creation of distinctive, production-grade frontend interfaces that avoid generic \"AI slop\" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.\n\nThe user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.\n\n## Design Thinking\n\nBefore coding, understand the context and commit to a BOLD aesthetic direction:\n- **Purpose**: What problem does this interface solve? Who uses it?\n- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.\n- **Constraints**: Technical requirements (framework, performance, accessibility).\n- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?\n\n**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.\n\nThen implement working code (HTML/CSS/JS, React, Vue, etc.) that is:\n- Production-grade and functional\n- Visually striking and memorable\n- Cohesive with a clear aesthetic point-of-view\n- Meticulously refined in every detail\n\n## Frontend Aesthetics Guidelines\n\nFocus on:\n- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.\n- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.\n- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.\n- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.\n- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.\n\nNEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.\n\nInterpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.\n\n**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.\n\nRemember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.\n","frontmatter":{"name":"frontend-design","description":"Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.","license":"Complete terms in LICENSE.txt"},"isInternal":false,"tokens":858,"sizeBytes":4440},{"name":"SKILL.md","path":".agents/skills/migrate-oxlint/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/migrate-oxlint/SKILL.md","title":"migrate-oxlint","category":"anthropic-skill","format":"markdown","content":"---\nname: migrate-oxlint\ndescription: Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint.\n---\n\nThis skill guides you through migrating a JavaScript/TypeScript project from ESLint to [Oxlint](https://oxc.rs/docs/guide/usage/linter/).\n\n## Overview\n\nOxlint is a high-performance linter that implements many popular ESLint rules natively in Rust. It can be used alongside ESLint or as a full replacement.\n\nAn official migration tool is available, and will be used by this skill: [`@oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate)\n\n## Step 1: Run Automated Migration\n\nRun the migration tool in the project root:\n\n```bash\nnpx @oxlint/migrate\n```\n\nThis reads your ESLint flat config (`eslint.config.js` for example) and generates a `.oxlintrc.json` file from it. It will find your ESLint config file automatically in most cases.\n\nSee options below for more info.\n\n### Key Options\n\n| Option                      | Description                                                                                                                     |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `--type-aware`              | Include type-aware rules from `@typescript-eslint` (will require the `oxlint-tsgolint` package to be installed after migrating) |\n| `--with-nursery`            | Include experimental rules still under development, may not be fully stable or consistent with ESLint equivalents               |\n| `--js-plugins [bool]`       | Enable/disable ESLint plugin migration via `jsPlugins` (default: enabled)                                                       |\n| `--details`                 | List rules that could not be migrated                                                                                           |\n| `--replace-eslint-comments` | Convert all `// eslint-disable` comments to `// oxlint-disable`                                                                 |\n| `--output-file <file>`      | Specify a different output path (default: `.oxlintrc.json`)                                                                     |\n\nIf your ESLint config is not at the default location, pass the path explicitly:\n\n```bash\nnpx @oxlint/migrate ./path/to/eslint.config.js\n```\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxlintrc.json`.\n\n### Plugin Mapping\n\nThe migration tool automatically maps ESLint plugins to oxlint's built-in equivalents. The following table is for reference when reviewing the generated config:\n\n| ESLint Plugin                                       | Oxlint Plugin Name |\n| --------------------------------------------------- | ------------------ |\n| `@typescript-eslint/eslint-plugin`                  | `typescript`       |\n| `eslint-plugin-react` / `eslint-plugin-react-hooks` | `react`            |\n| `eslint-plugin-import` / `eslint-plugin-import-x`   | `import`           |\n| `eslint-plugin-unicorn`                             | `unicorn`          |\n| `eslint-plugin-jsx-a11y`                            | `jsx-a11y`         |\n| `eslint-plugin-react-perf`                          | `react-perf`       |\n| `eslint-plugin-promise`                             | `promise`          |\n| `eslint-plugin-jest`                                | `jest`             |\n| `@vitest/eslint-plugin`                             | `vitest`           |\n| `eslint-plugin-jsdoc`                               | `jsdoc`            |\n| `eslint-plugin-next`                                | `nextjs`           |\n| `eslint-plugin-node`                                | `node`             |\n| `eslint-plugin-vue`                                 | `vue`              |\n\nDefault plugins (enabled when `plugins` field is omitted): `unicorn`, `typescript`, `oxc`.\nSetting the `plugins` array explicitly overrides these defaults.\n\nESLint core rules are usable in oxlint without needing to configure a plugin in the config file.\n\n### Rule Categories\n\nOxlint groups rules into categories for bulk configuration, though only `correctness` is enabled by default:\n\n```json\n{\n  \"categories\": {\n    \"correctness\": \"error\",\n    \"suspicious\": \"warn\"\n  }\n}\n```\n\nAvailable categories: `correctness` (default: enabled), `suspicious`, `pedantic`, `perf`, `style`, `restriction`, `nursery`.\n\nIndividual rule settings in `rules` override category settings.\n\n`@oxlint/migrate` will turn `correctness` off to avoid enabling additional rules that weren't enabled by your ESLint config. You can choose to enable additional categories after migration if desired.\n\n### Check Unmigrated Rules\n\nRun with `--details` to see which ESLint rules could not be migrated:\n\n```bash\nnpx @oxlint/migrate --details\n```\n\nReview the output and decide whether to keep ESLint for those rules or not. Some rules may be mentioned in the output from `--details` as having equivalents in oxlint that were not automatically mapped by the migration tool. In those cases, consider enabling the equivalent oxlint rule manually after migration.\n\n## Step 3: Install Oxlint\n\nInstall the core oxlint package (use `yarn install`, `pnpm install`, `vp install`, `bun install`, etc. depending on your package manager):\n\n```bash\nnpm install -D oxlint\n```\n\nIf you want to add the `oxlint-tsgolint` package, if you intend to use type-aware rules that require TypeScript type information:\n\n```bash\nnpm install -D oxlint-tsgolint\n```\n\nNo other packages besides the above are needed by default, though you will need to keep/install any additional ESLint plugins that were migrated into `jsPlugins`. Do not add `@oxlint/migrate` to the package.json, it is meant for one-off usage.\n\n## Step 4: Handle Unsupported Features\n\nSome features require manual attention:\n\n- Local plugins (relative path imports): Must be migrated manually to `jsPlugins`\n- `eslint-plugin-prettier`: Supported, but very slow. It is recommended to use [oxfmt](https://oxc.rs/docs/guide/usage/formatter) instead, or switch to `prettier --check` as a separate step alongside oxlint.\n- `settings` in override configs: Oxlint does not support `settings` inside `overrides` blocks.\n- ESLint v9+ plugins: Not all work with oxlint's JS Plugins API, but the majority will.\n\n### Local Plugins\n\nIf you have any custom ESLint rules in the project repo itself, you can migrate them manually after running the migration tool by adding them to the `jsPlugins` field in `.oxlintrc.json`:\n\n```json\n{\n  \"jsPlugins\": [\"./path/to/my-plugin.js\"],\n  \"rules\": {\n    \"local-plugin/rule-name\": \"error\"\n  }\n}\n```\n\n### External ESLint Plugins\n\nFor ESLint plugins without a built-in oxlint equivalent, use the `jsPlugins` field to load them:\n\n```json\n{\n  \"jsPlugins\": [\"eslint-plugin-custom\"],\n  \"rules\": {\n    \"custom/my-rule\": \"warn\"\n  }\n}\n```\n\n## Step 5: Update CI and Scripts\n\nReplace ESLint commands with oxlint. Path arguments are optional; oxlint defaults to the current working directory.\n\n```bash\n# Before\nnpx eslint src/\nnpx eslint --fix src/\n\n# After\nnpx oxlint src/\nnpx oxlint --fix src/\n```\n\n### Common CLI Options\n\n| ESLint                    | oxlint equivalent                              |\n| ------------------------- | ---------------------------------------------- |\n| `eslint .`                | `oxlint` (default: lints the cwd)              |\n| `eslint src/`             | `oxlint src/`                                  |\n| `eslint --fix`            | `oxlint --fix`                                 |\n| `eslint --max-warnings 0` | `oxlint --deny-warnings` or `--max-warnings 0` |\n| `eslint --format json`    | `oxlint --format json`                         |\n\nAdditional oxlint options:\n\n- `--tsconfig <path>`: Specify tsconfig.json path, likely unnecessary unless you have a non-standard name for `tsconfig.json`.\n\n## Tips\n\n- You can run alongside ESLint if necessary: Oxlint is designed to complement ESLint during migration, but with JS Plugins many projects can switch over fully without losing many rules.\n- Disable comments work: `// eslint-disable` and `// eslint-disable-next-line` comments are supported by oxlint. Use `--replace-eslint-comments` when running @oxlint/migrate to convert them to `// oxlint-disable` equivalents if desired.\n- List available rules: Run `npx oxlint --rules` to see all supported rules, or refer to the [rule documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).\n- Schema support: Add `\"$schema\": \"./node_modules/oxlint/configuration_schema.json\"` to `.oxlintrc.json` for editor autocompletion if the migration tool didn't do it automatically.\n- Output formats: `default`, `stylish`, `json`, `github`, `gitlab`, `junit`, `checkstyle`, `unix`\n- Ignore files: `.eslintignore` is supported by oxlint if you have it, but it's recommended to move any ignore patterns into the `ignorePatterns` field in `.oxlintrc.json` for consistency and simplicity. All files and paths ignored via a `.gitignore` file will be ignored by oxlint by default as well.\n- If you ran the migration tool multiple times, remove the `.oxlintrc.json.bak` backup file created by the migration tool once you've finished migrating.\n- If you are not using any JS Plugins and have replaced your ESLint configuration, you can remove all ESLint packages from your project dependencies.\n- Ensure your editor is configured to use oxlint instead of ESLint for linting and error reporting. You may want to install the Oxc extension for your preferred editor. See https://oxc.rs/docs/guide/usage/linter/editors.html for more details.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/linter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html)\n- [Complete Oxlint rule list and docs](https://oxc.rs/docs/guide/usage/linter/rules.html)\n","frontmatter":{"name":"migrate-oxlint","description":"Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint."},"isInternal":false,"tokens":2165,"sizeBytes":9911},{"name":"async-patterns.md","path":".agents/skills/next-best-practices/async-patterns.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/async-patterns.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Async Patterns\n\nIn Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.\n\n## Async Params and SearchParams\n\nAlways type them as `Promise<...>` and await them.\n\n### Pages and Layouts\n\n```tsx\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default async function Page({ params }: Props) {\n  const { slug } = await params\n}\n```\n\n### Route Handlers\n\n```tsx\nexport async function GET(\n  request: Request,\n  { params }: { params: Promise<{ id: string }> }\n) {\n  const { id } = await params\n}\n```\n\n### SearchParams\n\n```tsx\ntype Props = {\n  params: Promise<{ slug: string }>\n  searchParams: Promise<{ query?: string }>\n}\n\nexport default async function Page({ params, searchParams }: Props) {\n  const { slug } = await params\n  const { query } = await searchParams\n}\n```\n\n### Synchronous Components\n\nUse `React.use()` for non-async components:\n\n```tsx\nimport { use } from 'react'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default function Page({ params }: Props) {\n  const { slug } = use(params)\n}\n```\n\n### generateMetadata\n\n```tsx\ntype Props = { params: Promise<{ slug: string }> }\n\nexport async function generateMetadata({ params }: Props): Promise<Metadata> {\n  const { slug } = await params\n  return { title: slug }\n}\n```\n\n## Async Cookies and Headers\n\n```tsx\nimport { cookies, headers } from 'next/headers'\n\nexport default async function Page() {\n  const cookieStore = await cookies()\n  const headersList = await headers()\n\n  const theme = cookieStore.get('theme')\n  const userAgent = headersList.get('user-agent')\n}\n```\n\n## Migration Codemod\n\n```bash\nnpx @next/codemod@latest next-async-request-api .\n```\n","isInternal":false,"tokens":415,"sizeBytes":1664},{"name":"bundling.md","path":".agents/skills/next-best-practices/bundling.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/bundling.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Bundling\n\nFix common bundling issues with third-party packages.\n\n## Server-Incompatible Packages\n\nSome packages use browser APIs (`window`, `document`, `localStorage`) and fail in Server Components.\n\n### Error Signs\n\n```\nReferenceError: window is not defined\nReferenceError: document is not defined\nReferenceError: localStorage is not defined\nModule not found: Can't resolve 'fs'\n```\n\n### Solution 1: Mark as Client-Only\n\nIf the package is only needed on client:\n\n```tsx\n// Bad: Fails - package uses window\nimport SomeChart from 'some-chart-library'\n\nexport default function Page() {\n  return <SomeChart />\n}\n\n// Good: Use dynamic import with ssr: false\nimport dynamic from 'next/dynamic'\n\nconst SomeChart = dynamic(() => import('some-chart-library'), {\n  ssr: false,\n})\n\nexport default function Page() {\n  return <SomeChart />\n}\n```\n\n### Solution 2: Externalize from Server Bundle\n\nFor packages that should run on server but have bundling issues:\n\n```js\n// next.config.js\nmodule.exports = {\n  serverExternalPackages: ['problematic-package'],\n}\n```\n\nUse this for:\n- Packages with native bindings (sharp, bcrypt)\n- Packages that don't bundle well (some ORMs)\n- Packages with circular dependencies\n\n### Solution 3: Client Component Wrapper\n\nWrap the entire usage in a client component:\n\n```tsx\n// components/ChartWrapper.tsx\n'use client'\n\nimport { Chart } from 'chart-library'\n\nexport function ChartWrapper(props) {\n  return <Chart {...props} />\n}\n\n// app/page.tsx (server component)\nimport { ChartWrapper } from '@/components/ChartWrapper'\n\nexport default function Page() {\n  return <ChartWrapper data={data} />\n}\n```\n\n## CSS Imports\n\nImport CSS files instead of using `<link>` tags. Next.js handles bundling and optimization.\n\n```tsx\n// Bad: Manual link tag\n<link rel=\"stylesheet\" href=\"/styles.css\" />\n\n// Good: Import CSS\nimport './styles.css'\n\n// Good: CSS Modules\nimport styles from './Button.module.css'\n```\n\n## Polyfills\n\nNext.js includes common polyfills automatically. Don't load redundant ones from polyfill.io or similar CDNs.\n\nAlready included: `Array.from`, `Object.assign`, `Promise`, `fetch`, `Map`, `Set`, `Symbol`, `URLSearchParams`, and 50+ others.\n\n```tsx\n// Bad: Redundant polyfills\n<script src=\"https://polyfill.io/v3/polyfill.min.js?features=fetch,Promise,Array.from\" />\n\n// Good: Next.js includes these automatically\n```\n\n## ESM/CommonJS Issues\n\n### Error Signs\n\n```\nSyntaxError: Cannot use import statement outside a module\nError: require() of ES Module\nModule not found: ESM packages need to be imported\n```\n\n### Solution: Transpile Package\n\n```js\n// next.config.js\nmodule.exports = {\n  transpilePackages: ['some-esm-package', 'another-package'],\n}\n```\n\n## Common Problematic Packages\n\n| Package | Issue | Solution |\n|---------|-------|----------|\n| `sharp` | Native bindings | `serverExternalPackages: ['sharp']` |\n| `bcrypt` | Native bindings | `serverExternalPackages: ['bcrypt']` or use `bcryptjs` |\n| `canvas` | Native bindings | `serverExternalPackages: ['canvas']` |\n| `recharts` | Uses window | `dynamic(() => import('recharts'), { ssr: false })` |\n| `react-quill` | Uses document | `dynamic(() => import('react-quill'), { ssr: false })` |\n| `mapbox-gl` | Uses window | `dynamic(() => import('mapbox-gl'), { ssr: false })` |\n| `monaco-editor` | Uses window | `dynamic(() => import('@monaco-editor/react'), { ssr: false })` |\n| `lottie-web` | Uses document | `dynamic(() => import('lottie-react'), { ssr: false })` |\n\n## Bundle Analysis\n\nAnalyze bundle size with the built-in analyzer (Next.js 16.1+):\n\n```bash\nnext experimental-analyze\n```\n\nThis opens an interactive UI to:\n- Filter by route, environment (client/server), and type\n- Inspect module sizes and import chains\n- View treemap visualization\n\nSave output for comparison:\n\n```bash\nnext experimental-analyze --output\n# Output saved to .next/diagnostics/analyze\n```\n\nReference: https://nextjs.org/docs/app/guides/package-bundling\n\n## Migrating from Webpack to Turbopack\n\nTurbopack is the default bundler in Next.js 15+. If you have custom webpack config, migrate to Turbopack-compatible alternatives:\n\n```js\n// next.config.js\nmodule.exports = {\n  // Good: Works with Turbopack\n  serverExternalPackages: ['package'],\n  transpilePackages: ['package'],\n\n  // Bad: Webpack-only - migrate away from this\n  webpack: (config) => {\n    // custom webpack config\n  },\n}\n```\n\nReference: https://nextjs.org/docs/app/building-your-application/upgrading/from-webpack-to-turbopack\n","isInternal":false,"tokens":1105,"sizeBytes":4456},{"name":"data-patterns.md","path":".agents/skills/next-best-practices/data-patterns.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/data-patterns.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Data Patterns\n\nChoose the right data fetching pattern for each use case.\n\n## Decision Tree\n\n```\nNeed to fetch data?\n├── From a Server Component?\n│   └── Use: Fetch directly (no API needed)\n│\n├── From a Client Component?\n│   ├── Is it a mutation (POST/PUT/DELETE)?\n│   │   └── Use: Server Action\n│   └── Is it a read (GET)?\n│       └── Use: Route Handler OR pass from Server Component\n│\n├── Need external API access (webhooks, third parties)?\n│   └── Use: Route Handler\n│\n└── Need REST API for mobile app / external clients?\n    └── Use: Route Handler\n```\n\n## Pattern 1: Server Components (Preferred for Reads)\n\nFetch data directly in Server Components - no API layer needed.\n\n```tsx\n// app/users/page.tsx\nasync function UsersPage() {\n  // Direct database access - no API round-trip\n  const users = await db.user.findMany();\n\n  // Or fetch from external API\n  const posts = await fetch('https://api.example.com/posts').then(r => r.json());\n\n  return (\n    <ul>\n      {users.map(user => <li key={user.id}>{user.name}</li>)}\n    </ul>\n  );\n}\n```\n\n**Benefits**:\n- No API to maintain\n- No client-server waterfall\n- Secrets stay on server\n- Direct database access\n\n## Pattern 2: Server Actions (Preferred for Mutations)\n\nServer Actions are the recommended way to handle mutations.\n\n```tsx\n// app/actions.ts\n'use server';\n\nimport { revalidatePath } from 'next/cache';\n\nexport async function createPost(formData: FormData) {\n  const title = formData.get('title') as string;\n\n  await db.post.create({ data: { title } });\n\n  revalidatePath('/posts');\n}\n\nexport async function deletePost(id: string) {\n  await db.post.delete({ where: { id } });\n\n  revalidateTag('posts');\n}\n```\n\n```tsx\n// app/posts/new/page.tsx\nimport { createPost } from '@/app/actions';\n\nexport default function NewPost() {\n  return (\n    <form action={createPost}>\n      <input name=\"title\" required />\n      <button type=\"submit\">Create</button>\n    </form>\n  );\n}\n```\n\n**Benefits**:\n- End-to-end type safety\n- Progressive enhancement (works without JS)\n- Automatic request handling\n- Integrated with React transitions\n\n**Constraints**:\n- POST only (no GET caching semantics)\n- Internal use only (no external access)\n- Cannot return non-serializable data\n\n## Pattern 3: Route Handlers (APIs)\n\nUse Route Handlers when you need a REST API.\n\n```tsx\n// app/api/posts/route.ts\nimport { NextRequest, NextResponse } from 'next/server';\n\n// GET is cacheable\nexport async function GET(request: NextRequest) {\n  const posts = await db.post.findMany();\n  return NextResponse.json(posts);\n}\n\n// POST for mutations\nexport async function POST(request: NextRequest) {\n  const body = await request.json();\n  const post = await db.post.create({ data: body });\n  return NextResponse.json(post, { status: 201 });\n}\n```\n\n**When to use**:\n- External API access (mobile apps, third parties)\n- Webhooks from external services\n- GET endpoints that need HTTP caching\n- OpenAPI/Swagger documentation needed\n\n**When NOT to use**:\n- Internal data fetching (use Server Components)\n- Mutations from your UI (use Server Actions)\n\n## Avoiding Data Waterfalls\n\n### Problem: Sequential Fetches\n\n```tsx\n// Bad: Sequential waterfalls\nasync function Dashboard() {\n  const user = await getUser();        // Wait...\n  const posts = await getPosts();      // Then wait...\n  const comments = await getComments(); // Then wait...\n\n  return <div>...</div>;\n}\n```\n\n### Solution 1: Parallel Fetching with Promise.all\n\n```tsx\n// Good: Parallel fetching\nasync function Dashboard() {\n  const [user, posts, comments] = await Promise.all([\n    getUser(),\n    getPosts(),\n    getComments(),\n  ]);\n\n  return <div>...</div>;\n}\n```\n\n### Solution 2: Streaming with Suspense\n\n```tsx\n// Good: Show content progressively\nimport { Suspense } from 'react';\n\nasync function Dashboard() {\n  return (\n    <div>\n      <Suspense fallback={<UserSkeleton />}>\n        <UserSection />\n      </Suspense>\n      <Suspense fallback={<PostsSkeleton />}>\n        <PostsSection />\n      </Suspense>\n    </div>\n  );\n}\n\nasync function UserSection() {\n  const user = await getUser(); // Fetches independently\n  return <div>{user.name}</div>;\n}\n\nasync function PostsSection() {\n  const posts = await getPosts(); // Fetches independently\n  return <PostList posts={posts} />;\n}\n```\n\n### Solution 3: Preload Pattern\n\n```tsx\n// lib/data.ts\nimport { cache } from 'react';\n\nexport const getUser = cache(async (id: string) => {\n  return db.user.findUnique({ where: { id } });\n});\n\nexport const preloadUser = (id: string) => {\n  void getUser(id); // Fire and forget\n};\n```\n\n```tsx\n// app/user/[id]/page.tsx\nimport { getUser, preloadUser } from '@/lib/data';\n\nexport default async function UserPage({ params }) {\n  const { id } = await params;\n\n  // Start fetching early\n  preloadUser(id);\n\n  // Do other work...\n\n  // Data likely ready by now\n  const user = await getUser(id);\n  return <div>{user.name}</div>;\n}\n```\n\n## Client Component Data Fetching\n\nWhen Client Components need data:\n\n### Option 1: Pass from Server Component (Preferred)\n\n```tsx\n// Server Component\nasync function Page() {\n  const data = await fetchData();\n  return <ClientComponent initialData={data} />;\n}\n\n// Client Component\n'use client';\nfunction ClientComponent({ initialData }) {\n  const [data, setData] = useState(initialData);\n  // ...\n}\n```\n\n### Option 2: Fetch on Mount (When Necessary)\n\n```tsx\n'use client';\nimport { useEffect, useState } from 'react';\n\nfunction ClientComponent() {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    fetch('/api/data')\n      .then(r => r.json())\n      .then(setData);\n  }, []);\n\n  if (!data) return <Loading />;\n  return <div>{data.value}</div>;\n}\n```\n\n### Option 3: Server Action for Reads (Works But Not Ideal)\n\nServer Actions can be called from Client Components for reads, but this is not their intended purpose:\n\n```tsx\n'use client';\nimport { getData } from './actions';\nimport { useEffect, useState } from 'react';\n\nfunction ClientComponent() {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    getData().then(setData);\n  }, []);\n\n  return <div>{data?.value}</div>;\n}\n```\n\n**Note**: Server Actions always use POST, so no HTTP caching. Prefer Route Handlers for cacheable reads.\n\n## Quick Reference\n\n| Pattern | Use Case | HTTP Method | Caching |\n|---------|----------|-------------|---------|\n| Server Component fetch | Internal reads | Any | Full Next.js caching |\n| Server Action | Mutations, form submissions | POST only | No |\n| Route Handler | External APIs, webhooks | Any | GET can be cached |\n| Client fetch to API | Client-side reads | Any | HTTP cache headers |\n","isInternal":false,"tokens":1614,"sizeBytes":6687},{"name":"debug-tricks.md","path":".agents/skills/next-best-practices/debug-tricks.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/debug-tricks.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Debug Tricks\n\nTricks to speed up debugging Next.js applications.\n\n## MCP Endpoint (Dev Server)\n\nNext.js exposes a `/_next/mcp` endpoint in development for AI-assisted debugging via MCP (Model Context Protocol).\n\n- **Next.js 16+**: Enabled by default, use `next-devtools-mcp`\n- **Next.js < 16**: Requires `experimental.mcpServer: true` in next.config.js\n\nReference: https://nextjs.org/docs/app/guides/mcp\n\n**Important**: Find the actual port of the running Next.js dev server (check terminal output or `package.json` scripts). Don't assume port 3000.\n\n### Request Format\n\nThe endpoint uses JSON-RPC 2.0 over HTTP POST:\n\n```bash\ncurl -X POST http://localhost:<port>/_next/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": \"1\",\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"<tool-name>\",\n      \"arguments\": {}\n    }\n  }'\n```\n\n### Available Tools\n\n#### `get_errors`\nGet current errors from dev server (build errors, runtime errors with source-mapped stacks):\n```json\n{ \"name\": \"get_errors\", \"arguments\": {} }\n```\n\n#### `get_routes`\nDiscover all routes by scanning filesystem:\n```json\n{ \"name\": \"get_routes\", \"arguments\": {} }\n// Optional: { \"name\": \"get_routes\", \"arguments\": { \"routerType\": \"app\" } }\n```\nReturns: `{ \"appRouter\": [\"/\", \"/api/users/[id]\", ...], \"pagesRouter\": [...] }`\n\n#### `get_project_metadata`\nGet project path and dev server URL:\n```json\n{ \"name\": \"get_project_metadata\", \"arguments\": {} }\n```\nReturns: `{ \"projectPath\": \"/path/to/project\", \"devServerUrl\": \"http://localhost:3000\" }`\n\n#### `get_page_metadata`\nGet runtime metadata about current page render (requires active browser session):\n```json\n{ \"name\": \"get_page_metadata\", \"arguments\": {} }\n```\nReturns segment trie data showing layouts, boundaries, and page components.\n\n#### `get_logs`\nGet path to Next.js development log file:\n```json\n{ \"name\": \"get_logs\", \"arguments\": {} }\n```\nReturns path to `<distDir>/logs/next-development.log`\n\n#### `get_server_action_by_id`\nLocate a Server Action by ID:\n```json\n{ \"name\": \"get_server_action_by_id\", \"arguments\": { \"actionId\": \"<action-id>\" } }\n```\n\n### Example: Get Errors\n\n```bash\ncurl -X POST http://localhost:<port>/_next/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"method\":\"tools/call\",\"params\":{\"name\":\"get_errors\",\"arguments\":{}}}'\n```\n\n## Rebuild Specific Routes (Next.js 16+)\n\nUse `--debug-build-paths` to rebuild only specific routes instead of the entire app:\n\n```bash\n# Rebuild a specific route\nnext build --debug-build-paths \"/dashboard\"\n\n# Rebuild routes matching a glob\nnext build --debug-build-paths \"/api/*\"\n\n# Dynamic routes\nnext build --debug-build-paths \"/blog/[slug]\"\n```\n\nUse this to:\n- Quickly verify a build fix without full rebuild\n- Debug static generation issues for specific pages\n- Iterate faster on build errors\n","isInternal":false,"tokens":781,"sizeBytes":2953},{"name":"directives.md","path":".agents/skills/next-best-practices/directives.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/directives.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Directives\n\n## React Directives\n\nThese are React directives, not Next.js specific.\n\n### `'use client'`\n\nMarks a component as a Client Component. Required for:\n- React hooks (`useState`, `useEffect`, etc.)\n- Event handlers (`onClick`, `onChange`)\n- Browser APIs (`window`, `localStorage`)\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\n\nexport function Counter() {\n  const [count, setCount] = useState(0)\n  return <button onClick={() => setCount(count + 1)}>{count}</button>\n}\n```\n\nReference: https://react.dev/reference/rsc/use-client\n\n### `'use server'`\n\nMarks a function as a Server Action. Can be passed to Client Components.\n\n```tsx\n'use server'\n\nexport async function submitForm(formData: FormData) {\n  // Runs on server\n}\n```\n\nOr inline within a Server Component:\n\n```tsx\nexport default function Page() {\n  async function submit() {\n    'use server'\n    // Runs on server\n  }\n  return <form action={submit}>...</form>\n}\n```\n\nReference: https://react.dev/reference/rsc/use-server\n\n---\n\n## Next.js Directive\n\n### `'use cache'`\n\nMarks a function or component for caching. Part of Next.js Cache Components.\n\n```tsx\n'use cache'\n\nexport async function getCachedData() {\n  return await fetchData()\n}\n```\n\nRequires `cacheComponents: true` in `next.config.ts`.\n\nFor detailed usage including cache profiles, `cacheLife()`, `cacheTag()`, and `updateTag()`, see the `next-cache-components` skill.\n\nReference: https://nextjs.org/docs/app/api-reference/directives/use-cache\n","isInternal":false,"tokens":351,"sizeBytes":1476},{"name":"error-handling.md","path":".agents/skills/next-best-practices/error-handling.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/error-handling.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Error Handling\n\nHandle errors gracefully in Next.js applications.\n\nReference: https://nextjs.org/docs/app/getting-started/error-handling\n\n## Error Boundaries\n\n### `error.tsx`\n\nCatches errors in a route segment and its children:\n\n```tsx\n'use client'\n\nexport default function Error({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  return (\n    <div>\n      <h2>Something went wrong!</h2>\n      <button onClick={() => reset()}>Try again</button>\n    </div>\n  )\n}\n```\n\n**Important:** `error.tsx` must be a Client Component.\n\n### `global-error.tsx`\n\nCatches errors in root layout:\n\n```tsx\n'use client'\n\nexport default function GlobalError({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  return (\n    <html>\n      <body>\n        <h2>Something went wrong!</h2>\n        <button onClick={() => reset()}>Try again</button>\n      </body>\n    </html>\n  )\n}\n```\n\n**Important:** Must include `<html>` and `<body>` tags.\n\n## Server Actions: Navigation API Gotcha\n\n**Do NOT wrap navigation APIs in try-catch.** They throw special errors that Next.js handles internally.\n\nReference: https://nextjs.org/docs/app/api-reference/functions/redirect#behavior\n\n```tsx\n'use server'\n\nimport { redirect } from 'next/navigation'\nimport { notFound } from 'next/navigation'\n\n// Bad: try-catch catches the navigation \"error\"\nasync function createPost(formData: FormData) {\n  try {\n    const post = await db.post.create({ ... })\n    redirect(`/posts/${post.id}`)  // This throws!\n  } catch (error) {\n    // redirect() throw is caught here - navigation fails!\n    return { error: 'Failed to create post' }\n  }\n}\n\n// Good: Call navigation APIs outside try-catch\nasync function createPost(formData: FormData) {\n  let post\n  try {\n    post = await db.post.create({ ... })\n  } catch (error) {\n    return { error: 'Failed to create post' }\n  }\n  redirect(`/posts/${post.id}`)  // Outside try-catch\n}\n\n// Good: Re-throw navigation errors\nasync function createPost(formData: FormData) {\n  try {\n    const post = await db.post.create({ ... })\n    redirect(`/posts/${post.id}`)\n  } catch (error) {\n    if (error instanceof Error && error.message === 'NEXT_REDIRECT') {\n      throw error  // Re-throw navigation errors\n    }\n    return { error: 'Failed to create post' }\n  }\n}\n```\n\nSame applies to:\n- `redirect()` - 307 temporary redirect\n- `permanentRedirect()` - 308 permanent redirect\n- `notFound()` - 404 not found\n- `forbidden()` - 403 forbidden\n- `unauthorized()` - 401 unauthorized\n\nUse `unstable_rethrow()` to re-throw these errors in catch blocks:\n\n```tsx\nimport { unstable_rethrow } from 'next/navigation'\n\nasync function action() {\n  try {\n    // ...\n    redirect('/success')\n  } catch (error) {\n    unstable_rethrow(error) // Re-throws Next.js internal errors\n    return { error: 'Something went wrong' }\n  }\n}\n```\n\n## Redirects\n\n```tsx\nimport { redirect, permanentRedirect } from 'next/navigation'\n\n// 307 Temporary - use for most cases\nredirect('/new-path')\n\n// 308 Permanent - use for URL migrations (cached by browsers)\npermanentRedirect('/new-url')\n```\n\n## Auth Errors\n\nTrigger auth-related error pages:\n\n```tsx\nimport { forbidden, unauthorized } from 'next/navigation'\n\nasync function Page() {\n  const session = await getSession()\n\n  if (!session) {\n    unauthorized() // Renders unauthorized.tsx (401)\n  }\n\n  if (!session.hasAccess) {\n    forbidden() // Renders forbidden.tsx (403)\n  }\n\n  return <Dashboard />\n}\n```\n\nCreate corresponding error pages:\n\n```tsx\n// app/forbidden.tsx\nexport default function Forbidden() {\n  return <div>You don't have access to this resource</div>\n}\n\n// app/unauthorized.tsx\nexport default function Unauthorized() {\n  return <div>Please log in to continue</div>\n}\n```\n\n## Not Found\n\n### `not-found.tsx`\n\nCustom 404 page for a route segment:\n\n```tsx\nexport default function NotFound() {\n  return (\n    <div>\n      <h2>Not Found</h2>\n      <p>Could not find the requested resource</p>\n    </div>\n  )\n}\n```\n\n### Triggering Not Found\n\n```tsx\nimport { notFound } from 'next/navigation'\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const post = await getPost(id)\n\n  if (!post) {\n    notFound()  // Renders closest not-found.tsx\n  }\n\n  return <div>{post.title}</div>\n}\n```\n\n## Error Hierarchy\n\nErrors bubble up to the nearest error boundary:\n\n```\napp/\n├── error.tsx           # Catches errors from all children\n├── blog/\n│   ├── error.tsx       # Catches errors in /blog/*\n│   └── [slug]/\n│       ├── error.tsx   # Catches errors in /blog/[slug]\n│       └── page.tsx\n└── layout.tsx          # Errors here go to global-error.tsx\n```\n","isInternal":false,"tokens":1198,"sizeBytes":4736},{"name":"file-conventions.md","path":".agents/skills/next-best-practices/file-conventions.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/file-conventions.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# File Conventions\n\nNext.js App Router uses file-based routing with special file conventions.\n\n## Project Structure\n\nReference: https://nextjs.org/docs/app/getting-started/project-structure\n\n```\napp/\n├── layout.tsx          # Root layout (required)\n├── page.tsx            # Home page (/)\n├── loading.tsx         # Loading UI\n├── error.tsx           # Error UI\n├── not-found.tsx       # 404 UI\n├── global-error.tsx    # Global error UI\n├── route.ts            # API endpoint\n├── template.tsx        # Re-rendered layout\n├── default.tsx         # Parallel route fallback\n├── blog/\n│   ├── page.tsx        # /blog\n│   └── [slug]/\n│       └── page.tsx    # /blog/:slug\n└── (group)/            # Route group (no URL impact)\n    └── page.tsx\n```\n\n## Special Files\n\n| File | Purpose |\n|------|---------|\n| `page.tsx` | UI for a route segment |\n| `layout.tsx` | Shared UI for segment and children |\n| `loading.tsx` | Loading UI (Suspense boundary) |\n| `error.tsx` | Error UI (Error boundary) |\n| `not-found.tsx` | 404 UI |\n| `route.ts` | API endpoint |\n| `template.tsx` | Like layout but re-renders on navigation |\n| `default.tsx` | Fallback for parallel routes |\n\n## Route Segments\n\n```\napp/\n├── blog/               # Static segment: /blog\n├── [slug]/             # Dynamic segment: /:slug\n├── [...slug]/          # Catch-all: /a/b/c\n├── [[...slug]]/        # Optional catch-all: / or /a/b/c\n└── (marketing)/        # Route group (ignored in URL)\n```\n\n## Parallel Routes\n\n```\napp/\n├── @analytics/\n│   └── page.tsx\n├── @sidebar/\n│   └── page.tsx\n└── layout.tsx          # Receives { analytics, sidebar } as props\n```\n\n## Intercepting Routes\n\n```\napp/\n├── feed/\n│   └── page.tsx\n├── @modal/\n│   └── (.)photo/[id]/  # Intercepts /photo/[id] from /feed\n│       └── page.tsx\n└── photo/[id]/\n    └── page.tsx\n```\n\nConventions:\n- `(.)` - same level\n- `(..)` - one level up\n- `(..)(..)` - two levels up\n- `(...)` - from root\n\n## Private Folders\n\n```\napp/\n├── _components/        # Private folder (not a route)\n│   └── Button.tsx\n└── page.tsx\n```\n\nPrefix with `_` to exclude from routing.\n\n## Middleware / Proxy\n\n### Next.js 14-15: `middleware.ts`\n\n```ts\n// middleware.ts (root of project)\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\n\nexport function middleware(request: NextRequest) {\n  // Auth, redirects, rewrites, etc.\n  return NextResponse.next();\n}\n\nexport const config = {\n  matcher: ['/dashboard/:path*', '/api/:path*'],\n};\n```\n\n### Next.js 16+: `proxy.ts`\n\nRenamed for clarity - same capabilities, different names:\n\n```ts\n// proxy.ts (root of project)\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\n\nexport function proxy(request: NextRequest) {\n  // Same logic as middleware\n  return NextResponse.next();\n}\n\nexport const proxyConfig = {\n  matcher: ['/dashboard/:path*', '/api/:path*'],\n};\n```\n\n| Version | File | Export | Config |\n|---------|------|--------|--------|\n| v14-15 | `middleware.ts` | `middleware()` | `config` |\n| v16+ | `proxy.ts` | `proxy()` | `proxyConfig` |\n\n**Migration**: Run `npx @next/codemod@latest upgrade` to auto-rename.\n\n## File Conventions Reference\n\nReference: https://nextjs.org/docs/app/api-reference/file-conventions\n","isInternal":false,"tokens":951,"sizeBytes":3464},{"name":"font.md","path":".agents/skills/next-best-practices/font.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/font.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Font Optimization\n\nUse `next/font` for automatic font optimization with zero layout shift.\n\n## Google Fonts\n\n```tsx\n// app/layout.tsx\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({ subsets: ['latin'] })\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\" className={inter.className}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n## Multiple Fonts\n\n```tsx\nimport { Inter, Roboto_Mono } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  variable: '--font-inter',\n})\n\nconst robotoMono = Roboto_Mono({\n  subsets: ['latin'],\n  variable: '--font-roboto-mono',\n})\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\" className={`${inter.variable} ${robotoMono.variable}`}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\nUse in CSS:\n```css\nbody {\n  font-family: var(--font-inter);\n}\n\ncode {\n  font-family: var(--font-roboto-mono);\n}\n```\n\n## Font Weights and Styles\n\n```tsx\n// Single weight\nconst inter = Inter({\n  subsets: ['latin'],\n  weight: '400',\n})\n\n// Multiple weights\nconst inter = Inter({\n  subsets: ['latin'],\n  weight: ['400', '500', '700'],\n})\n\n// Variable font (recommended) - includes all weights\nconst inter = Inter({\n  subsets: ['latin'],\n  // No weight needed - variable fonts support all weights\n})\n\n// With italic\nconst inter = Inter({\n  subsets: ['latin'],\n  style: ['normal', 'italic'],\n})\n```\n\n## Local Fonts\n\n```tsx\nimport localFont from 'next/font/local'\n\nconst myFont = localFont({\n  src: './fonts/MyFont.woff2',\n})\n\n// Multiple files for different weights\nconst myFont = localFont({\n  src: [\n    {\n      path: './fonts/MyFont-Regular.woff2',\n      weight: '400',\n      style: 'normal',\n    },\n    {\n      path: './fonts/MyFont-Bold.woff2',\n      weight: '700',\n      style: 'normal',\n    },\n  ],\n})\n\n// Variable font\nconst myFont = localFont({\n  src: './fonts/MyFont-Variable.woff2',\n  variable: '--font-my-font',\n})\n```\n\n## Tailwind CSS Integration\n\n```tsx\n// app/layout.tsx\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  variable: '--font-inter',\n})\n\nexport default function RootLayout({ children }) {\n  return (\n    <html lang=\"en\" className={inter.variable}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    extend: {\n      fontFamily: {\n        sans: ['var(--font-inter)'],\n      },\n    },\n  },\n}\n```\n\n## Preloading Subsets\n\nOnly load needed character subsets:\n\n```tsx\n// Latin only (most common)\nconst inter = Inter({ subsets: ['latin'] })\n\n// Multiple subsets\nconst inter = Inter({ subsets: ['latin', 'latin-ext', 'cyrillic'] })\n```\n\n## Display Strategy\n\nControl font loading behavior:\n\n```tsx\nconst inter = Inter({\n  subsets: ['latin'],\n  display: 'swap', // Default - shows fallback, swaps when loaded\n})\n\n// Options:\n// 'auto' - browser decides\n// 'block' - short block period, then swap\n// 'swap' - immediate fallback, swap when ready (recommended)\n// 'fallback' - short block, short swap, then fallback\n// 'optional' - short block, no swap (use if font is optional)\n```\n\n## Don't Use Manual Font Links\n\nAlways use `next/font` instead of `<link>` tags for Google Fonts.\n\n```tsx\n// Bad: Manual link tag (blocks rendering, no optimization)\n<link href=\"https://fonts.googleapis.com/css2?family=Inter\" rel=\"stylesheet\" />\n\n// Bad: Missing display and preconnect\n<link href=\"https://fonts.googleapis.com/css2?family=Inter\" rel=\"stylesheet\" />\n\n// Good: Use next/font (self-hosted, zero layout shift)\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({ subsets: ['latin'] })\n```\n\n## Common Mistakes\n\n```tsx\n// Bad: Importing font in every component\n// components/Button.tsx\nimport { Inter } from 'next/font/google'\nconst inter = Inter({ subsets: ['latin'] }) // Creates new instance each time!\n\n// Good: Import once in layout, use CSS variable\n// app/layout.tsx\nconst inter = Inter({ subsets: ['latin'], variable: '--font-inter' })\n\n// Bad: Using @import in CSS (blocks rendering)\n/* globals.css */\n@import url('https://fonts.googleapis.com/css2?family=Inter');\n\n// Good: Use next/font (self-hosted, no network request)\nimport { Inter } from 'next/font/google'\n\n// Bad: Loading all weights when only using a few\nconst inter = Inter({ subsets: ['latin'] }) // Loads all weights\n\n// Good: Specify only needed weights (for non-variable fonts)\nconst inter = Inter({ subsets: ['latin'], weight: ['400', '700'] })\n\n// Bad: Missing subset - loads all characters\nconst inter = Inter({})\n\n// Good: Always specify subset\nconst inter = Inter({ subsets: ['latin'] })\n```\n\n## Font in Specific Components\n\n```tsx\n// For component-specific fonts, export from a shared file\n// lib/fonts.ts\nimport { Inter, Playfair_Display } from 'next/font/google'\n\nexport const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })\nexport const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-playfair' })\n\n// components/Heading.tsx\nimport { playfair } from '@/lib/fonts'\n\nexport function Heading({ children }) {\n  return <h1 className={playfair.className}>{children}</h1>\n}\n```\n","isInternal":false,"tokens":1316,"sizeBytes":5196},{"name":"functions.md","path":".agents/skills/next-best-practices/functions.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/functions.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Functions\n\nNext.js function APIs.\n\nReference: https://nextjs.org/docs/app/api-reference/functions\n\n## Navigation Hooks (Client)\n\n| Hook | Purpose | Reference |\n|------|---------|-----------|\n| `useRouter` | Programmatic navigation (`push`, `replace`, `back`, `refresh`) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-router) |\n| `usePathname` | Get current pathname | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-pathname) |\n| `useSearchParams` | Read URL search parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-search-params) |\n| `useParams` | Access dynamic route parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-params) |\n| `useSelectedLayoutSegment` | Active child segment (one level) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment) |\n| `useSelectedLayoutSegments` | All active segments below layout | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segments) |\n| `useLinkStatus` | Check link prefetch status | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-link-status) |\n| `useReportWebVitals` | Report Core Web Vitals metrics | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals) |\n\n## Server Functions\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `cookies` | Read/write cookies | [Docs](https://nextjs.org/docs/app/api-reference/functions/cookies) |\n| `headers` | Read request headers | [Docs](https://nextjs.org/docs/app/api-reference/functions/headers) |\n| `draftMode` | Enable preview of unpublished CMS content | [Docs](https://nextjs.org/docs/app/api-reference/functions/draft-mode) |\n| `after` | Run code after response finishes streaming | [Docs](https://nextjs.org/docs/app/api-reference/functions/after) |\n| `connection` | Wait for connection before dynamic rendering | [Docs](https://nextjs.org/docs/app/api-reference/functions/connection) |\n| `userAgent` | Parse User-Agent header | [Docs](https://nextjs.org/docs/app/api-reference/functions/userAgent) |\n\n## Generate Functions\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `generateStaticParams` | Pre-render dynamic routes at build time | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) |\n| `generateMetadata` | Dynamic metadata | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) |\n| `generateViewport` | Dynamic viewport config | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-viewport) |\n| `generateSitemaps` | Multiple sitemaps for large sites | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-sitemaps) |\n| `generateImageMetadata` | Multiple OG images per route | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-image-metadata) |\n\n## Request/Response\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `NextRequest` | Extended Request with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-request) |\n| `NextResponse` | Extended Response with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-response) |\n| `ImageResponse` | Generate OG images | [Docs](https://nextjs.org/docs/app/api-reference/functions/image-response) |\n\n## Common Examples\n\n### Navigation\n\nUse `next/link` for internal navigation instead of `<a>` tags.\n\n```tsx\n// Bad: Plain anchor tag\n<a href=\"/about\">About</a>\n\n// Good: Next.js Link\nimport Link from 'next/link'\n\n<Link href=\"/about\">About</Link>\n```\n\nActive link styling:\n\n```tsx\n'use client'\n\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\n\nexport function NavLink({ href, children }) {\n  const pathname = usePathname()\n\n  return (\n    <Link href={href} className={pathname === href ? 'active' : ''}>\n      {children}\n    </Link>\n  )\n}\n```\n\n### Static Generation\n\n```tsx\n// app/blog/[slug]/page.tsx\nexport async function generateStaticParams() {\n  const posts = await getPosts()\n  return posts.map((post) => ({ slug: post.slug }))\n}\n```\n\n### After Response\n\n```tsx\nimport { after } from 'next/server'\n\nexport async function POST(request: Request) {\n  const data = await processRequest(request)\n\n  after(async () => {\n    await logAnalytics(data)\n  })\n\n  return Response.json({ success: true })\n}\n```\n","isInternal":false,"tokens":1027,"sizeBytes":4388},{"name":"hydration-error.md","path":".agents/skills/next-best-practices/hydration-error.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/hydration-error.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Hydration Errors\n\nDiagnose and fix React hydration mismatch errors.\n\n## Error Signs\n\n- \"Hydration failed because the initial UI does not match\"\n- \"Text content does not match server-rendered HTML\"\n\n## Debugging\n\nIn development, click the hydration error to see the server/client diff.\n\n## Common Causes and Fixes\n\n### Browser-only APIs\n\n```tsx\n// Bad: Causes mismatch - window doesn't exist on server\n<div>{window.innerWidth}</div>\n\n// Good: Use client component with mounted check\n'use client'\nimport { useState, useEffect } from 'react'\n\nexport function ClientOnly({ children }: { children: React.ReactNode }) {\n  const [mounted, setMounted] = useState(false)\n  useEffect(() => setMounted(true), [])\n  return mounted ? children : null\n}\n```\n\n### Date/Time Rendering\n\nServer and client may be in different timezones:\n\n```tsx\n// Bad: Causes mismatch\n<span>{new Date().toLocaleString()}</span>\n\n// Good: Render on client only\n'use client'\nconst [time, setTime] = useState<string>()\nuseEffect(() => setTime(new Date().toLocaleString()), [])\n```\n\n### Random Values or IDs\n\n```tsx\n// Bad: Random values differ between server and client\n<div id={Math.random().toString()}>\n\n// Good: Use useId hook\nimport { useId } from 'react'\n\nfunction Input() {\n  const id = useId()\n  return <input id={id} />\n}\n```\n\n### Invalid HTML Nesting\n\n```tsx\n// Bad: Invalid - div inside p\n<p><div>Content</div></p>\n\n// Bad: Invalid - p inside p\n<p><p>Nested</p></p>\n\n// Good: Valid nesting\n<div><p>Content</p></div>\n```\n\n### Third-party Scripts\n\nScripts that modify DOM during hydration.\n\n```tsx\n// Good: Use next/script with afterInteractive\nimport Script from 'next/script'\n\nexport default function Page() {\n  return (\n    <Script\n      src=\"https://example.com/script.js\"\n      strategy=\"afterInteractive\"\n    />\n  )\n}\n```\n","isInternal":false,"tokens":437,"sizeBytes":1801},{"name":"image.md","path":".agents/skills/next-best-practices/image.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/image.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Image Optimization\n\nUse `next/image` for automatic image optimization.\n\n## Always Use next/image\n\n```tsx\n// Bad: Avoid native img\n<img src=\"/hero.png\" alt=\"Hero\" />\n\n// Good: Use next/image\nimport Image from 'next/image'\n<Image src=\"/hero.png\" alt=\"Hero\" width={800} height={400} />\n```\n\n## Required Props\n\nImages need explicit dimensions to prevent layout shift:\n\n```tsx\n// Local images - dimensions inferred automatically\nimport heroImage from './hero.png'\n<Image src={heroImage} alt=\"Hero\" />\n\n// Remote images - must specify width/height\n<Image src=\"https://example.com/image.jpg\" alt=\"Hero\" width={800} height={400} />\n\n// Or use fill for parent-relative sizing\n<div style={{ position: 'relative', width: '100%', height: 400 }}>\n  <Image src=\"/hero.png\" alt=\"Hero\" fill style={{ objectFit: 'cover' }} />\n</div>\n```\n\n## Remote Images Configuration\n\nRemote domains must be configured in `next.config.js`:\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: 'https',\n        hostname: 'example.com',\n        pathname: '/images/**',\n      },\n      {\n        protocol: 'https',\n        hostname: '*.cdn.com', // Wildcard subdomain\n      },\n    ],\n  },\n}\n```\n\n## Responsive Images\n\nUse `sizes` to tell the browser which size to download:\n\n```tsx\n// Full-width hero\n<Image\n  src=\"/hero.png\"\n  alt=\"Hero\"\n  fill\n  sizes=\"100vw\"\n/>\n\n// Responsive grid (3 columns on desktop, 1 on mobile)\n<Image\n  src=\"/card.png\"\n  alt=\"Card\"\n  fill\n  sizes=\"(max-width: 768px) 100vw, 33vw\"\n/>\n\n// Fixed sidebar image\n<Image\n  src=\"/avatar.png\"\n  alt=\"Avatar\"\n  width={200}\n  height={200}\n  sizes=\"200px\"\n/>\n```\n\n## Blur Placeholder\n\nPrevent layout shift with placeholders:\n\n```tsx\n// Local images - automatic blur hash\nimport heroImage from './hero.png'\n<Image src={heroImage} alt=\"Hero\" placeholder=\"blur\" />\n\n// Remote images - provide blurDataURL\n<Image\n  src=\"https://example.com/image.jpg\"\n  alt=\"Hero\"\n  width={800}\n  height={400}\n  placeholder=\"blur\"\n  blurDataURL=\"data:image/jpeg;base64,/9j/4AAQSkZJRg...\"\n/>\n\n// Or use color placeholder\n<Image\n  src=\"https://example.com/image.jpg\"\n  alt=\"Hero\"\n  width={800}\n  height={400}\n  placeholder=\"empty\"\n  style={{ backgroundColor: '#e0e0e0' }}\n/>\n```\n\n## Priority Loading\n\nUse `priority` for above-the-fold images (LCP):\n\n```tsx\n// Hero image - loads immediately\n<Image src=\"/hero.png\" alt=\"Hero\" fill priority />\n\n// Below-fold images - lazy loaded by default (no priority needed)\n<Image src=\"/card.png\" alt=\"Card\" width={400} height={300} />\n```\n\n## Common Mistakes\n\n```tsx\n// Bad: Missing sizes with fill - downloads largest image\n<Image src=\"/hero.png\" alt=\"Hero\" fill />\n\n// Good: Add sizes for proper responsive behavior\n<Image src=\"/hero.png\" alt=\"Hero\" fill sizes=\"100vw\" />\n\n// Bad: Using width/height for aspect ratio only\n<Image src=\"/hero.png\" alt=\"Hero\" width={16} height={9} />\n\n// Good: Use actual display dimensions or fill with sizes\n<Image src=\"/hero.png\" alt=\"Hero\" fill sizes=\"100vw\" style={{ objectFit: 'cover' }} />\n\n// Bad: Remote image without config\n<Image src=\"https://untrusted.com/image.jpg\" alt=\"Image\" width={400} height={300} />\n// Error: Invalid src prop, hostname not configured\n\n// Good: Add hostname to next.config.js remotePatterns\n```\n\n## Static Export\n\nWhen using `output: 'export'`, use `unoptimized` or custom loader:\n\n```tsx\n// Option 1: Disable optimization\n<Image src=\"/hero.png\" alt=\"Hero\" width={800} height={400} unoptimized />\n\n// Option 2: Global config\n// next.config.js\nmodule.exports = {\n  output: 'export',\n  images: { unoptimized: true },\n}\n\n// Option 3: Custom loader (Cloudinary, Imgix, etc.)\nconst cloudinaryLoader = ({ src, width, quality }) => {\n  return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`\n}\n\n<Image loader={cloudinaryLoader} src=\"sample.jpg\" alt=\"Sample\" width={800} height={400} />\n```\n","isInternal":false,"tokens":1025,"sizeBytes":3882},{"name":"metadata.md","path":".agents/skills/next-best-practices/metadata.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/metadata.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Metadata\n\nAdd SEO metadata to Next.js pages using the Metadata API.\n\n## Important: Server Components Only\n\nThe `metadata` object and `generateMetadata` function are **only supported in Server Components**. They cannot be used in Client Components.\n\nIf the target page has `'use client'`:\n1. Remove `'use client'` if possible, move client logic to child components\n2. Or extract metadata to a parent Server Component layout\n3. Or split the file: Server Component with metadata imports Client Components\n\n## Static Metadata\n\n```tsx\nimport type { Metadata } from 'next'\n\nexport const metadata: Metadata = {\n  title: 'Page Title',\n  description: 'Page description for search engines',\n}\n```\n\n## Dynamic Metadata\n\n```tsx\nimport type { Metadata } from 'next'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport async function generateMetadata({ params }: Props): Promise<Metadata> {\n  const { slug } = await params\n  const post = await getPost(slug)\n  return { title: post.title, description: post.description }\n}\n```\n\n## Avoid Duplicate Fetches\n\nUse React `cache()` when the same data is needed for both metadata and page:\n\n```tsx\nimport { cache } from 'react'\n\nexport const getPost = cache(async (slug: string) => {\n  return await db.posts.findFirst({ where: { slug } })\n})\n```\n\n## Viewport\n\nSeparate from metadata for streaming support:\n\n```tsx\nimport type { Viewport } from 'next'\n\nexport const viewport: Viewport = {\n  width: 'device-width',\n  initialScale: 1,\n  themeColor: '#000000',\n}\n\n// Or dynamic\nexport function generateViewport({ params }): Viewport {\n  return { themeColor: getThemeColor(params) }\n}\n```\n\n## Title Templates\n\nIn root layout for consistent naming:\n\n```tsx\nexport const metadata: Metadata = {\n  title: { default: 'Site Name', template: '%s | Site Name' },\n}\n```\n\n## Metadata File Conventions\n\nReference: https://nextjs.org/docs/app/getting-started/project-structure#metadata-file-conventions\n\nPlace these files in `app/` directory (or route segments):\n\n| File | Purpose |\n|------|---------|\n| `favicon.ico` | Favicon |\n| `icon.png` / `icon.svg` | App icon |\n| `apple-icon.png` | Apple app icon |\n| `opengraph-image.png` | OG image |\n| `twitter-image.png` | Twitter card image |\n| `sitemap.ts` / `sitemap.xml` | Sitemap (use `generateSitemaps` for multiple) |\n| `robots.ts` / `robots.txt` | Robots directives |\n| `manifest.ts` / `manifest.json` | Web app manifest |\n\n## SEO Best Practice: Static Files Are Often Enough\n\nFor most sites, **static metadata files provide excellent SEO coverage**:\n\n```\napp/\n├── favicon.ico\n├── opengraph-image.png     # Works for both OG and Twitter\n├── sitemap.ts\n├── robots.ts\n└── layout.tsx              # With title/description metadata\n```\n\n**Tips:**\n- A single `opengraph-image.png` covers both Open Graph and Twitter (Twitter falls back to OG)\n- Static `title` and `description` in layout metadata is sufficient for most pages\n- Only use dynamic `generateMetadata` when content varies per page\n\n---\n\n# OG Image Generation\n\nGenerate dynamic Open Graph images using `next/og`.\n\n## Important Rules\n\n1. **Use `next/og`** - not `@vercel/og` (it's built into Next.js)\n2. **No searchParams** - OG images can't access search params, use route params instead\n3. **Avoid Edge runtime** - Use default Node.js runtime\n\n```tsx\n// Good\nimport { ImageResponse } from 'next/og'\n\n// Bad\n// import { ImageResponse } from '@vercel/og'\n// export const runtime = 'edge'\n```\n\n## Basic OG Image\n\n```tsx\n// app/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport const alt = 'Site Name'\nexport const size = { width: 1200, height: 630 }\nexport const contentType = 'image/png'\n\nexport default function Image() {\n  return new ImageResponse(\n    (\n      <div\n        style={{\n          fontSize: 128,\n          background: 'white',\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n        }}\n      >\n        Hello World\n      </div>\n    ),\n    { ...size }\n  )\n}\n```\n\n## Dynamic OG Image\n\n```tsx\n// app/blog/[slug]/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport const alt = 'Blog Post'\nexport const size = { width: 1200, height: 630 }\nexport const contentType = 'image/png'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default async function Image({ params }: Props) {\n  const { slug } = await params\n  const post = await getPost(slug)\n\n  return new ImageResponse(\n    (\n      <div\n        style={{\n          fontSize: 48,\n          background: 'linear-gradient(to bottom, #1a1a1a, #333)',\n          color: 'white',\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          flexDirection: 'column',\n          alignItems: 'center',\n          justifyContent: 'center',\n          padding: 48,\n        }}\n      >\n        <div style={{ fontSize: 64, fontWeight: 'bold' }}>{post.title}</div>\n        <div style={{ marginTop: 24, opacity: 0.8 }}>{post.description}</div>\n      </div>\n    ),\n    { ...size }\n  )\n}\n```\n\n## Custom Fonts\n\n```tsx\nimport { ImageResponse } from 'next/og'\nimport { join } from 'path'\nimport { readFile } from 'fs/promises'\n\nexport default async function Image() {\n  const fontPath = join(process.cwd(), 'assets/fonts/Inter-Bold.ttf')\n  const fontData = await readFile(fontPath)\n\n  return new ImageResponse(\n    (\n      <div style={{ fontFamily: 'Inter', fontSize: 64 }}>\n        Custom Font Text\n      </div>\n    ),\n    {\n      width: 1200,\n      height: 630,\n      fonts: [{ name: 'Inter', data: fontData, style: 'normal' }],\n    }\n  )\n}\n```\n\n## File Naming\n\n- `opengraph-image.tsx` - Open Graph (Facebook, LinkedIn)\n- `twitter-image.tsx` - Twitter/X cards (optional, falls back to OG)\n\n## Styling Notes\n\nImageResponse uses Flexbox layout:\n- Use `display: 'flex'`\n- No CSS Grid support\n- Styles must be inline objects\n\n## Multiple OG Images\n\nUse `generateImageMetadata` for multiple images per route:\n\n```tsx\n// app/blog/[slug]/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport async function generateImageMetadata({ params }) {\n  const images = await getPostImages(params.slug)\n  return images.map((img, idx) => ({\n    id: idx,\n    alt: img.alt,\n    size: { width: 1200, height: 630 },\n    contentType: 'image/png',\n  }))\n}\n\nexport default async function Image({ params, id }) {\n  const images = await getPostImages(params.slug)\n  const image = images[id]\n  return new ImageResponse(/* ... */)\n}\n```\n\n## Multiple Sitemaps\n\nUse `generateSitemaps` for large sites:\n\n```tsx\n// app/sitemap.ts\nimport type { MetadataRoute } from 'next'\n\nexport async function generateSitemaps() {\n  // Return array of sitemap IDs\n  return [{ id: 0 }, { id: 1 }, { id: 2 }]\n}\n\nexport default async function sitemap({\n  id,\n}: {\n  id: number\n}): Promise<MetadataRoute.Sitemap> {\n  const start = id * 50000\n  const end = start + 50000\n  const products = await getProducts(start, end)\n\n  return products.map((product) => ({\n    url: `https://example.com/product/${product.id}`,\n    lastModified: product.updatedAt,\n  }))\n}\n```\n\nGenerates `/sitemap/0.xml`, `/sitemap/1.xml`, etc.\n","isInternal":false,"tokens":1810,"sizeBytes":7140},{"name":"parallel-routes.md","path":".agents/skills/next-best-practices/parallel-routes.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/parallel-routes.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Parallel & Intercepting Routes\n\nParallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.\n\n## File Structure\n\n```\napp/\n├── @modal/                    # Parallel route slot\n│   ├── default.tsx            # Required! Returns null\n│   ├── (.)photos/             # Intercepts /photos/*\n│   │   └── [id]/\n│   │       └── page.tsx       # Modal content\n│   └── [...]catchall/         # Optional: catch unmatched\n│       └── page.tsx\n├── photos/\n│   └── [id]/\n│       └── page.tsx           # Full page (direct access)\n├── layout.tsx                 # Renders both children and @modal\n└── page.tsx\n```\n\n## Step 1: Root Layout with Slot\n\n```tsx\n// app/layout.tsx\nexport default function RootLayout({\n  children,\n  modal,\n}: {\n  children: React.ReactNode;\n  modal: React.ReactNode;\n}) {\n  return (\n    <html>\n      <body>\n        {children}\n        {modal}\n      </body>\n    </html>\n  );\n}\n```\n\n## Step 2: Default File (Critical!)\n\n**Every parallel route slot MUST have a `default.tsx`** to prevent 404s on hard navigation.\n\n```tsx\n// app/@modal/default.tsx\nexport default function Default() {\n  return null;\n}\n```\n\nWithout this file, refreshing any page will 404 because Next.js can't determine what to render in the `@modal` slot.\n\n## Step 3: Intercepting Route (Modal)\n\nThe `(.)` prefix intercepts routes at the same level.\n\n```tsx\n// app/@modal/(.)photos/[id]/page.tsx\nimport { Modal } from '@/components/modal';\n\nexport default async function PhotoModal({\n  params\n}: {\n  params: Promise<{ id: string }>\n}) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  return (\n    <Modal>\n      <img src={photo.url} alt={photo.title} />\n    </Modal>\n  );\n}\n```\n\n## Step 4: Full Page (Direct Access)\n\n```tsx\n// app/photos/[id]/page.tsx\nexport default async function PhotoPage({\n  params\n}: {\n  params: Promise<{ id: string }>\n}) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  return (\n    <div className=\"full-page\">\n      <img src={photo.url} alt={photo.title} />\n      <h1>{photo.title}</h1>\n    </div>\n  );\n}\n```\n\n## Step 5: Modal Component with Correct Closing\n\n**Critical: Use `router.back()` to close modals, NOT `router.push()` or `<Link>`.**\n\n```tsx\n// components/modal.tsx\n'use client';\n\nimport { useRouter } from 'next/navigation';\nimport { useCallback, useEffect, useRef } from 'react';\n\nexport function Modal({ children }: { children: React.ReactNode }) {\n  const router = useRouter();\n  const overlayRef = useRef<HTMLDivElement>(null);\n\n  // Close on escape key\n  useEffect(() => {\n    function onKeyDown(e: KeyboardEvent) {\n      if (e.key === 'Escape') {\n        router.back(); // Correct\n      }\n    }\n    document.addEventListener('keydown', onKeyDown);\n    return () => document.removeEventListener('keydown', onKeyDown);\n  }, [router]);\n\n  // Close on overlay click\n  const handleOverlayClick = useCallback((e: React.MouseEvent) => {\n    if (e.target === overlayRef.current) {\n      router.back(); // Correct\n    }\n  }, [router]);\n\n  return (\n    <div\n      ref={overlayRef}\n      onClick={handleOverlayClick}\n      className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\"\n    >\n      <div className=\"bg-white rounded-lg p-6 max-w-2xl w-full mx-4\">\n        <button\n          onClick={() => router.back()} // Correct!\n          className=\"absolute top-4 right-4\"\n        >\n          Close\n        </button>\n        {children}\n      </div>\n    </div>\n  );\n}\n```\n\n### Why NOT `router.push('/')` or `<Link href=\"/\">`?\n\nUsing `push` or `Link` to \"close\" a modal:\n1. Adds a new history entry (back button shows modal again)\n2. Doesn't properly clear the intercepted route\n3. Can cause the modal to flash or persist unexpectedly\n\n`router.back()` correctly:\n1. Removes the intercepted route from history\n2. Returns to the previous page\n3. Properly unmounts the modal\n\n## Route Matcher Reference\n\nMatchers match **route segments**, not filesystem paths:\n\n| Matcher | Matches | Example |\n|---------|---------|---------|\n| `(.)` | Same level | `@modal/(.)photos` intercepts `/photos` |\n| `(..)` | One level up | `@modal/(..)settings` from `/dashboard/@modal` intercepts `/settings` |\n| `(..)(..)` | Two levels up | Rarely used |\n| `(...)` | From root | `@modal/(...)photos` intercepts `/photos` from anywhere |\n\n**Common mistake**: Thinking `(..)` means \"parent folder\" - it means \"parent route segment\".\n\n## Handling Hard Navigation\n\nWhen users directly visit `/photos/123` (bookmark, refresh, shared link):\n- The intercepting route is bypassed\n- The full `photos/[id]/page.tsx` renders\n- Modal doesn't appear (expected behavior)\n\nIf you want the modal to appear on direct access too, you need additional logic:\n\n```tsx\n// app/photos/[id]/page.tsx\nimport { Modal } from '@/components/modal';\n\nexport default async function PhotoPage({ params }) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  // Option: Render as modal on direct access too\n  return (\n    <Modal>\n      <img src={photo.url} alt={photo.title} />\n    </Modal>\n  );\n}\n```\n\n## Common Gotchas\n\n### 1. Missing `default.tsx` → 404 on Refresh\n\nEvery `@slot` folder needs a `default.tsx` that returns `null` (or appropriate content).\n\n### 2. Modal Persists After Navigation\n\nYou're using `router.push()` instead of `router.back()`.\n\n### 3. Nested Parallel Routes Need Defaults Too\n\nIf you have `@modal` inside a route group, each level needs its own `default.tsx`:\n\n```\napp/\n├── (marketing)/\n│   ├── @modal/\n│   │   └── default.tsx     # Needed!\n│   └── layout.tsx\n└── layout.tsx\n```\n\n### 4. Intercepted Route Shows Wrong Content\n\nCheck your matcher:\n- `(.)photos` intercepts `/photos` from the same route level\n- If your `@modal` is in `app/dashboard/@modal`, use `(.)photos` to intercept `/dashboard/photos`, not `/photos`\n\n### 5. TypeScript Errors with `params`\n\nIn Next.js 15+, `params` is a Promise:\n\n```tsx\n// Correct\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params;\n}\n```\n\n## Complete Example: Photo Gallery Modal\n\n```\napp/\n├── @modal/\n│   ├── default.tsx\n│   └── (.)photos/\n│       └── [id]/\n│           └── page.tsx\n├── photos/\n│   ├── page.tsx           # Gallery grid\n│   └── [id]/\n│       └── page.tsx       # Full photo page\n├── layout.tsx\n└── page.tsx\n```\n\nLinks in the gallery:\n\n```tsx\n// app/photos/page.tsx\nimport Link from 'next/link';\n\nexport default async function Gallery() {\n  const photos = await getPhotos();\n\n  return (\n    <div className=\"grid grid-cols-3 gap-4\">\n      {photos.map(photo => (\n        <Link key={photo.id} href={`/photos/${photo.id}`}>\n          <img src={photo.thumbnail} alt={photo.title} />\n        </Link>\n      ))}\n    </div>\n  );\n}\n```\n\nClicking a photo → Modal opens (intercepted)\nDirect URL → Full page renders\nRefresh while modal open → Full page renders\n","isInternal":false,"tokens":1826,"sizeBytes":7171},{"name":"route-handlers.md","path":".agents/skills/next-best-practices/route-handlers.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/route-handlers.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Route Handlers\n\nCreate API endpoints with `route.ts` files.\n\n## Basic Usage\n\n```tsx\n// app/api/users/route.ts\nexport async function GET() {\n  const users = await getUsers()\n  return Response.json(users)\n}\n\nexport async function POST(request: Request) {\n  const body = await request.json()\n  const user = await createUser(body)\n  return Response.json(user, { status: 201 })\n}\n```\n\n## Supported Methods\n\n`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`\n\n## GET Handler Conflicts with page.tsx\n\n**A `route.ts` and `page.tsx` cannot coexist in the same folder.**\n\n```\napp/\n├── api/\n│   └── users/\n│       └── route.ts    # /api/users\n└── users/\n    ├── page.tsx        # /users (page)\n    └── route.ts        # Warning: Conflicts with page.tsx!\n```\n\nIf you need both a page and an API at the same path, use different paths:\n\n```\napp/\n├── users/\n│   └── page.tsx        # /users (page)\n└── api/\n    └── users/\n        └── route.ts    # /api/users (API)\n```\n\n## Environment Behavior\n\nRoute handlers run in a **Server Component-like environment**:\n\n- Yes: Can use `async/await`\n- Yes: Can access `cookies()`, `headers()`\n- Yes: Can use Node.js APIs\n- No: Cannot use React hooks\n- No: Cannot use React DOM APIs\n- No: Cannot use browser APIs\n\n```tsx\n// Bad: This won't work - no React DOM in route handlers\nimport { renderToString } from 'react-dom/server'\n\nexport async function GET() {\n  const html = renderToString(<Component />)  // Error!\n  return new Response(html)\n}\n```\n\n## Dynamic Route Handlers\n\n```tsx\n// app/api/users/[id]/route.ts\nexport async function GET(\n  request: Request,\n  { params }: { params: Promise<{ id: string }> }\n) {\n  const { id } = await params\n  const user = await getUser(id)\n\n  if (!user) {\n    return Response.json({ error: 'Not found' }, { status: 404 })\n  }\n\n  return Response.json(user)\n}\n```\n\n## Request Helpers\n\n```tsx\nexport async function GET(request: Request) {\n  // URL and search params\n  const { searchParams } = new URL(request.url)\n  const query = searchParams.get('q')\n\n  // Headers\n  const authHeader = request.headers.get('authorization')\n\n  // Cookies (Next.js helper)\n  const cookieStore = await cookies()\n  const token = cookieStore.get('token')\n\n  return Response.json({ query, token })\n}\n```\n\n## Response Helpers\n\n```tsx\n// JSON response\nreturn Response.json({ data })\n\n// With status\nreturn Response.json({ error: 'Not found' }, { status: 404 })\n\n// With headers\nreturn Response.json(data, {\n  headers: {\n    'Cache-Control': 'max-age=3600',\n  },\n})\n\n// Redirect\nreturn Response.redirect(new URL('/login', request.url))\n\n// Stream\nreturn new Response(stream, {\n  headers: { 'Content-Type': 'text/event-stream' },\n})\n```\n\n## When to Use Route Handlers vs Server Actions\n\n| Use Case | Route Handlers | Server Actions |\n|----------|----------------|----------------|\n| Form submissions | No | Yes |\n| Data mutations from UI | No | Yes |\n| Third-party webhooks | Yes | No |\n| External API consumption | Yes | No |\n| Public REST API | Yes | No |\n| File uploads | Both work | Both work |\n\n**Prefer Server Actions** for mutations triggered from your UI.\n**Use Route Handlers** for external integrations and public APIs.\n","isInternal":false,"tokens":820,"sizeBytes":3244},{"name":"rsc-boundaries.md","path":".agents/skills/next-best-practices/rsc-boundaries.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/rsc-boundaries.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# RSC Boundaries\n\nDetect and prevent invalid patterns when crossing Server/Client component boundaries.\n\n## Detection Rules\n\n### 1. Async Client Components Are Invalid\n\nClient components **cannot** be async functions. Only Server Components can be async.\n\n**Detect:** File has `'use client'` AND component is `async function` or returns `Promise`\n\n```tsx\n// Bad: async client component\n'use client'\nexport default async function UserProfile() {\n  const user = await getUser() // Cannot await in client component\n  return <div>{user.name}</div>\n}\n\n// Good: Remove async, fetch data in parent server component\n// page.tsx (server component - no 'use client')\nexport default async function Page() {\n  const user = await getUser()\n  return <UserProfile user={user} />\n}\n\n// UserProfile.tsx (client component)\n'use client'\nexport function UserProfile({ user }: { user: User }) {\n  return <div>{user.name}</div>\n}\n```\n\n```tsx\n// Bad: async arrow function client component\n'use client'\nconst Dashboard = async () => {\n  const data = await fetchDashboard()\n  return <div>{data}</div>\n}\n\n// Good: Fetch in server component, pass data down\n```\n\n### 2. Non-Serializable Props to Client Components\n\nProps passed from Server → Client must be JSON-serializable.\n\n**Detect:** Server component passes these to a client component:\n- Functions (except Server Actions with `'use server'`)\n- `Date` objects\n- `Map`, `Set`, `WeakMap`, `WeakSet`\n- Class instances\n- `Symbol` (unless globally registered)\n- Circular references\n\n```tsx\n// Bad: Function prop\n// page.tsx (server)\nexport default function Page() {\n  const handleClick = () => console.log('clicked')\n  return <ClientButton onClick={handleClick} />\n}\n\n// Good: Define function inside client component\n// ClientButton.tsx\n'use client'\nexport function ClientButton() {\n  const handleClick = () => console.log('clicked')\n  return <button onClick={handleClick}>Click</button>\n}\n```\n\n```tsx\n// Bad: Date object (silently becomes string, then crashes)\n// page.tsx (server)\nexport default async function Page() {\n  const post = await getPost()\n  return <PostCard createdAt={post.createdAt} /> // Date object\n}\n\n// PostCard.tsx (client) - will crash on .getFullYear()\n'use client'\nexport function PostCard({ createdAt }: { createdAt: Date }) {\n  return <span>{createdAt.getFullYear()}</span> // Runtime error!\n}\n\n// Good: Serialize to string on server\n// page.tsx (server)\nexport default async function Page() {\n  const post = await getPost()\n  return <PostCard createdAt={post.createdAt.toISOString()} />\n}\n\n// PostCard.tsx (client)\n'use client'\nexport function PostCard({ createdAt }: { createdAt: string }) {\n  const date = new Date(createdAt)\n  return <span>{date.getFullYear()}</span>\n}\n```\n\n```tsx\n// Bad: Class instance\nconst user = new UserModel(data)\n<ClientProfile user={user} /> // Methods will be stripped\n\n// Good: Pass plain object\nconst user = await getUser()\n<ClientProfile user={{ id: user.id, name: user.name }} />\n```\n\n```tsx\n// Bad: Map/Set\n<ClientComponent items={new Map([['a', 1]])} />\n\n// Good: Convert to array/object\n<ClientComponent items={Object.fromEntries(map)} />\n<ClientComponent items={Array.from(set)} />\n```\n\n### 3. Server Actions Are the Exception\n\nFunctions marked with `'use server'` CAN be passed to client components.\n\n```tsx\n// Valid: Server Action can be passed\n// actions.ts\n'use server'\nexport async function submitForm(formData: FormData) {\n  // server-side logic\n}\n\n// page.tsx (server)\nimport { submitForm } from './actions'\nexport default function Page() {\n  return <ClientForm onSubmit={submitForm} /> // OK!\n}\n\n// ClientForm.tsx (client)\n'use client'\nexport function ClientForm({ onSubmit }: { onSubmit: (data: FormData) => Promise<void> }) {\n  return <form action={onSubmit}>...</form>\n}\n```\n\n## Quick Reference\n\n| Pattern | Valid? | Fix |\n|---------|--------|-----|\n| `'use client'` + `async function` | No | Fetch in server parent, pass data |\n| Pass `() => {}` to client | No | Define in client or use server action |\n| Pass `new Date()` to client | No | Use `.toISOString()` |\n| Pass `new Map()` to client | No | Convert to object/array |\n| Pass class instance to client | No | Pass plain object |\n| Pass server action to client | Yes | - |\n| Pass `string/number/boolean` | Yes | - |\n| Pass plain object/array | Yes | - |\n","isInternal":false,"tokens":1029,"sizeBytes":4309},{"name":"runtime-selection.md","path":".agents/skills/next-best-practices/runtime-selection.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/runtime-selection.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Runtime Selection\n\n## Use Node.js Runtime by Default\n\nUse the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.\n\n```tsx\n// Good: Default - no runtime config needed (uses Node.js)\nexport default function Page() { ... }\n\n// Caution: Only if already used in project or specifically required\nexport const runtime = 'edge'\n```\n\n## When to Use Each\n\n### Node.js Runtime (Default)\n\n- Full Node.js API support\n- File system access (`fs`)\n- Full `crypto` support\n- Database connections\n- Most npm packages work\n\n### Edge Runtime\n\n- Only for specific edge-location latency requirements\n- Limited API (no `fs`, limited `crypto`)\n- Smaller cold start\n- Geographic distribution needs\n\n## Detection\n\n**Before adding `runtime = 'edge'`**, check:\n1. Does the project already use Edge runtime?\n2. Is there a specific latency requirement?\n3. Are all dependencies Edge-compatible?\n\nIf unsure, use Node.js runtime.\n","isInternal":false,"tokens":220,"sizeBytes":985},{"name":"scripts.md","path":".agents/skills/next-best-practices/scripts.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/scripts.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Scripts\n\nLoading third-party scripts in Next.js.\n\n## Use next/script\n\nAlways use `next/script` instead of native `<script>` tags for better performance.\n\n```tsx\n// Bad: Native script tag\n<script src=\"https://example.com/script.js\"></script>\n\n// Good: Next.js Script component\nimport Script from 'next/script'\n\n<Script src=\"https://example.com/script.js\" />\n```\n\n## Inline Scripts Need ID\n\nInline scripts require an `id` attribute for Next.js to track them.\n\n```tsx\n// Bad: Missing id\n<Script dangerouslySetInnerHTML={{ __html: 'console.log(\"hi\")' }} />\n\n// Good: Has id\n<Script id=\"my-script\" dangerouslySetInnerHTML={{ __html: 'console.log(\"hi\")' }} />\n\n// Good: Inline with id\n<Script id=\"show-banner\">\n  {`document.getElementById('banner').classList.remove('hidden')`}\n</Script>\n```\n\n## Don't Put Script in Head\n\n`next/script` should not be placed inside `next/head`. It handles its own positioning.\n\n```tsx\n// Bad: Script inside Head\nimport Head from 'next/head'\nimport Script from 'next/script'\n\n<Head>\n  <Script src=\"/analytics.js\" />\n</Head>\n\n// Good: Script outside Head\n<Head>\n  <title>Page</title>\n</Head>\n<Script src=\"/analytics.js\" />\n```\n\n## Loading Strategies\n\n```tsx\n// afterInteractive (default) - Load after page is interactive\n<Script src=\"/analytics.js\" strategy=\"afterInteractive\" />\n\n// lazyOnload - Load during idle time\n<Script src=\"/widget.js\" strategy=\"lazyOnload\" />\n\n// beforeInteractive - Load before page is interactive (use sparingly)\n// Only works in app/layout.tsx or pages/_document.js\n<Script src=\"/critical.js\" strategy=\"beforeInteractive\" />\n\n// worker - Load in web worker (experimental)\n<Script src=\"/heavy.js\" strategy=\"worker\" />\n```\n\n## Google Analytics\n\nUse `@next/third-parties` instead of inline GA scripts.\n\n```tsx\n// Bad: Inline GA script\n<Script src=\"https://www.googletagmanager.com/gtag/js?id=G-XXXXX\" />\n<Script id=\"ga-init\">\n  {`window.dataLayer = window.dataLayer || [];\n    function gtag(){dataLayer.push(arguments);}\n    gtag('js', new Date());\n    gtag('config', 'G-XXXXX');`}\n</Script>\n\n// Good: Next.js component\nimport { GoogleAnalytics } from '@next/third-parties/google'\n\nexport default function Layout({ children }) {\n  return (\n    <html>\n      <body>{children}</body>\n      <GoogleAnalytics gaId=\"G-XXXXX\" />\n    </html>\n  )\n}\n```\n\n## Google Tag Manager\n\n```tsx\nimport { GoogleTagManager } from '@next/third-parties/google'\n\nexport default function Layout({ children }) {\n  return (\n    <html>\n      <GoogleTagManager gtmId=\"GTM-XXXXX\" />\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n## Other Third-Party Scripts\n\n```tsx\n// YouTube embed\nimport { YouTubeEmbed } from '@next/third-parties/google'\n\n<YouTubeEmbed videoid=\"dQw4w9WgXcQ\" />\n\n// Google Maps\nimport { GoogleMapsEmbed } from '@next/third-parties/google'\n\n<GoogleMapsEmbed\n  apiKey=\"YOUR_API_KEY\"\n  mode=\"place\"\n  q=\"Brooklyn+Bridge,New+York,NY\"\n/>\n```\n\n## Quick Reference\n\n| Pattern | Issue | Fix |\n|---------|-------|-----|\n| `<script src=\"...\">` | No optimization | Use `next/script` |\n| `<Script>` without id | Can't track inline scripts | Add `id` attribute |\n| `<Script>` inside `<Head>` | Wrong placement | Move outside Head |\n| Inline GA/GTM scripts | No optimization | Use `@next/third-parties` |\n| `strategy=\"beforeInteractive\"` outside layout | Won't work | Only use in root layout |\n","isInternal":false,"tokens":867,"sizeBytes":3326},{"name":"self-hosting.md","path":".agents/skills/next-best-practices/self-hosting.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/self-hosting.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Self-Hosting Next.js\n\nDeploy Next.js outside of Vercel with confidence.\n\n## Quick Start: Standalone Output\n\nFor Docker or any containerized deployment, use standalone output:\n\n```js\n// next.config.js\nmodule.exports = {\n  output: 'standalone',\n};\n```\n\nThis creates a minimal `standalone` folder with only production dependencies:\n\n```\n.next/\n├── standalone/\n│   ├── server.js          # Entry point\n│   ├── node_modules/      # Only production deps\n│   └── .next/             # Build output\n└── static/                # Must be copied separately\n```\n\n## Docker Deployment\n\n### Dockerfile\n\n```dockerfile\nFROM node:20-alpine AS base\n\n# Install dependencies\nFROM base AS deps\nWORKDIR /app\nCOPY package.json package-lock.json* ./\nRUN npm ci\n\n# Build\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\nRUN npm run build\n\n# Production\nFROM base AS runner\nWORKDIR /app\n\nENV NODE_ENV=production\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\n# Copy standalone output\nCOPY --from=builder /app/.next/standalone ./\nCOPY --from=builder /app/.next/static ./.next/static\nCOPY --from=builder /app/public ./public\n\nUSER nextjs\n\nEXPOSE 3000\nENV PORT=3000\nENV HOSTNAME=\"0.0.0.0\"\n\nCMD [\"node\", \"server.js\"]\n```\n\n### Docker Compose\n\n```yaml\nversion: '3.8'\n\nservices:\n  web:\n    build: .\n    ports:\n      - \"3000:3000\"\n    environment:\n      - NODE_ENV=production\n    restart: unless-stopped\n    healthcheck:\n      test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:3000/api/health\"]\n      interval: 30s\n      timeout: 10s\n      retries: 3\n```\n\n## PM2 Deployment\n\nFor traditional server deployments:\n\n```js\n// ecosystem.config.js\nmodule.exports = {\n  apps: [{\n    name: 'nextjs',\n    script: '.next/standalone/server.js',\n    instances: 'max',\n    exec_mode: 'cluster',\n    env: {\n      NODE_ENV: 'production',\n      PORT: 3000,\n    },\n  }],\n};\n```\n\n```bash\nnpm run build\npm2 start ecosystem.config.js\n```\n\n## ISR and Cache Handlers\n\n### The Problem\n\nISR (Incremental Static Regeneration) uses filesystem caching by default. This **breaks with multiple instances**:\n\n- Instance A regenerates page → saves to its local disk\n- Instance B serves stale page → doesn't see Instance A's cache\n- Load balancer sends users to random instances → inconsistent content\n\n### Solution: Custom Cache Handler\n\nNext.js 14+ supports custom cache handlers for shared storage:\n\n```js\n// next.config.js\nmodule.exports = {\n  cacheHandler: require.resolve('./cache-handler.js'),\n  cacheMaxMemorySize: 0, // Disable in-memory cache\n};\n```\n\n#### Redis Cache Handler Example\n\n```js\n// cache-handler.js\nconst Redis = require('ioredis');\n\nconst redis = new Redis(process.env.REDIS_URL);\nconst CACHE_PREFIX = 'nextjs:';\n\nmodule.exports = class CacheHandler {\n  constructor(options) {\n    this.options = options;\n  }\n\n  async get(key) {\n    const data = await redis.get(CACHE_PREFIX + key);\n    if (!data) return null;\n\n    const parsed = JSON.parse(data);\n    return {\n      value: parsed.value,\n      lastModified: parsed.lastModified,\n    };\n  }\n\n  async set(key, data, ctx) {\n    const cacheData = {\n      value: data,\n      lastModified: Date.now(),\n    };\n\n    // Set TTL based on revalidate option\n    if (ctx?.revalidate) {\n      await redis.setex(\n        CACHE_PREFIX + key,\n        ctx.revalidate,\n        JSON.stringify(cacheData)\n      );\n    } else {\n      await redis.set(CACHE_PREFIX + key, JSON.stringify(cacheData));\n    }\n  }\n\n  async revalidateTag(tags) {\n    // Implement tag-based invalidation\n    // This requires tracking which keys have which tags\n  }\n};\n```\n\n#### S3 Cache Handler Example\n\n```js\n// cache-handler.js\nconst { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3');\n\nconst s3 = new S3Client({ region: process.env.AWS_REGION });\nconst BUCKET = process.env.CACHE_BUCKET;\n\nmodule.exports = class CacheHandler {\n  async get(key) {\n    try {\n      const response = await s3.send(new GetObjectCommand({\n        Bucket: BUCKET,\n        Key: `cache/${key}`,\n      }));\n      const body = await response.Body.transformToString();\n      return JSON.parse(body);\n    } catch (err) {\n      if (err.name === 'NoSuchKey') return null;\n      throw err;\n    }\n  }\n\n  async set(key, data, ctx) {\n    await s3.send(new PutObjectCommand({\n      Bucket: BUCKET,\n      Key: `cache/${key}`,\n      Body: JSON.stringify({\n        value: data,\n        lastModified: Date.now(),\n      }),\n      ContentType: 'application/json',\n    }));\n  }\n};\n```\n\n## What Works vs What Needs Setup\n\n| Feature | Single Instance | Multi-Instance | Notes |\n|---------|----------------|----------------|-------|\n| SSR | Yes | Yes | No special setup |\n| SSG | Yes | Yes | Built at deploy time |\n| ISR | Yes | Needs cache handler | Filesystem cache breaks |\n| Image Optimization | Yes | Yes | CPU-intensive, consider CDN |\n| Middleware | Yes | Yes | Runs on Node.js |\n| Edge Runtime | Limited | Limited | Some features Node-only |\n| `revalidatePath/Tag` | Yes | Needs cache handler | Must share cache |\n| `next/font` | Yes | Yes | Fonts bundled at build |\n| Draft Mode | Yes | Yes | Cookie-based |\n\n## Image Optimization\n\nNext.js Image Optimization works out of the box but is CPU-intensive.\n\n### Option 1: Built-in (Simple)\n\nWorks automatically, but consider:\n- Set `deviceSizes` and `imageSizes` in config to limit variants\n- Use `minimumCacheTTL` to reduce regeneration\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    minimumCacheTTL: 60 * 60 * 24, // 24 hours\n    deviceSizes: [640, 750, 1080, 1920], // Limit sizes\n  },\n};\n```\n\n### Option 2: External Loader (Recommended for Scale)\n\nOffload to Cloudinary, Imgix, or similar:\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    loader: 'custom',\n    loaderFile: './lib/image-loader.js',\n  },\n};\n```\n\n```js\n// lib/image-loader.js\nexport default function cloudinaryLoader({ src, width, quality }) {\n  const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`];\n  return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}${src}`;\n}\n```\n\n## Environment Variables\n\n### Build-time vs Runtime\n\n```js\n// Available at build time only (baked into bundle)\nNEXT_PUBLIC_API_URL=https://api.example.com\n\n// Available at runtime (server-side only)\nDATABASE_URL=postgresql://...\nAPI_SECRET=...\n```\n\n### Runtime Configuration\n\nFor truly dynamic config, don't use `NEXT_PUBLIC_*`. Instead:\n\n```tsx\n// app/api/config/route.ts\nexport async function GET() {\n  return Response.json({\n    apiUrl: process.env.API_URL,\n    features: process.env.FEATURES?.split(','),\n  });\n}\n```\n\n## OpenNext: Serverless Without Vercel\n\n[OpenNext](https://open-next.js.org/) adapts Next.js for AWS Lambda, Cloudflare Workers, etc.\n\n```bash\nnpx create-sst@latest\n# or\nnpx @opennextjs/aws build\n```\n\nSupports:\n- AWS Lambda + CloudFront\n- Cloudflare Workers\n- Netlify Functions\n- Deno Deploy\n\n## Health Check Endpoint\n\nAlways include a health check for load balancers:\n\n```tsx\n// app/api/health/route.ts\nexport async function GET() {\n  try {\n    // Optional: check database connection\n    // await db.$queryRaw`SELECT 1`;\n\n    return Response.json({ status: 'healthy' }, { status: 200 });\n  } catch (error) {\n    return Response.json({ status: 'unhealthy' }, { status: 503 });\n  }\n}\n```\n\n## Pre-Deployment Checklist\n\n1. **Build locally first**: `npm run build` - catch errors before deploy\n2. **Test standalone output**: `node .next/standalone/server.js`\n3. **Set `output: 'standalone'`** for Docker\n4. **Configure cache handler** for multi-instance ISR\n5. **Set `HOSTNAME=\"0.0.0.0\"`** for containers\n6. **Copy `public/` and `.next/static/`** - not included in standalone\n7. **Add health check endpoint**\n8. **Test ISR revalidation** after deployment\n9. **Monitor memory usage** - Node.js defaults may need tuning\n\n## Testing Cache Handler\n\n**Critical**: Test your cache handler on every Next.js upgrade:\n\n```bash\n# Start multiple instances\nPORT=3001 node .next/standalone/server.js &\nPORT=3002 node .next/standalone/server.js &\n\n# Trigger ISR revalidation\ncurl http://localhost:3001/api/revalidate?path=/posts\n\n# Verify both instances see the update\ncurl http://localhost:3001/posts\ncurl http://localhost:3002/posts\n# Should return identical content\n```\n","isInternal":false,"tokens":2130,"sizeBytes":8334},{"name":"SKILL.md","path":".agents/skills/next-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/SKILL.md","title":"next-best-practices","category":"anthropic-skill","format":"markdown","content":"---\nname: next-best-practices\ndescription: Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling\nuser-invocable: false\n---\n\n# Next.js Best Practices\n\nApply these rules when writing or reviewing Next.js code.\n\n## File Conventions\n\nSee [file-conventions.md](./file-conventions.md) for:\n- Project structure and special files\n- Route segments (dynamic, catch-all, groups)\n- Parallel and intercepting routes\n- Middleware rename in v16 (middleware → proxy)\n\n## RSC Boundaries\n\nDetect invalid React Server Component patterns.\n\nSee [rsc-boundaries.md](./rsc-boundaries.md) for:\n- Async client component detection (invalid)\n- Non-serializable props detection\n- Server Action exceptions\n\n## Async Patterns\n\nNext.js 15+ async API changes.\n\nSee [async-patterns.md](./async-patterns.md) for:\n- Async `params` and `searchParams`\n- Async `cookies()` and `headers()`\n- Migration codemod\n\n## Runtime Selection\n\nSee [runtime-selection.md](./runtime-selection.md) for:\n- Default to Node.js runtime\n- When Edge runtime is appropriate\n\n## Directives\n\nSee [directives.md](./directives.md) for:\n- `'use client'`, `'use server'` (React)\n- `'use cache'` (Next.js)\n\n## Functions\n\nSee [functions.md](./functions.md) for:\n- Navigation hooks: `useRouter`, `usePathname`, `useSearchParams`, `useParams`\n- Server functions: `cookies`, `headers`, `draftMode`, `after`\n- Generate functions: `generateStaticParams`, `generateMetadata`\n\n## Error Handling\n\nSee [error-handling.md](./error-handling.md) for:\n- `error.tsx`, `global-error.tsx`, `not-found.tsx`\n- `redirect`, `permanentRedirect`, `notFound`\n- `forbidden`, `unauthorized` (auth errors)\n- `unstable_rethrow` for catch blocks\n\n## Data Patterns\n\nSee [data-patterns.md](./data-patterns.md) for:\n- Server Components vs Server Actions vs Route Handlers\n- Avoiding data waterfalls (`Promise.all`, Suspense, preload)\n- Client component data fetching\n\n## Route Handlers\n\nSee [route-handlers.md](./route-handlers.md) for:\n- `route.ts` basics\n- GET handler conflicts with `page.tsx`\n- Environment behavior (no React DOM)\n- When to use vs Server Actions\n\n## Metadata & OG Images\n\nSee [metadata.md](./metadata.md) for:\n- Static and dynamic metadata\n- `generateMetadata` function\n- OG image generation with `next/og`\n- File-based metadata conventions\n\n## Image Optimization\n\nSee [image.md](./image.md) for:\n- Always use `next/image` over `<img>`\n- Remote images configuration\n- Responsive `sizes` attribute\n- Blur placeholders\n- Priority loading for LCP\n\n## Font Optimization\n\nSee [font.md](./font.md) for:\n- `next/font` setup\n- Google Fonts, local fonts\n- Tailwind CSS integration\n- Preloading subsets\n\n## Bundling\n\nSee [bundling.md](./bundling.md) for:\n- Server-incompatible packages\n- CSS imports (not link tags)\n- Polyfills (already included)\n- ESM/CommonJS issues\n- Bundle analysis\n\n## Scripts\n\nSee [scripts.md](./scripts.md) for:\n- `next/script` vs native script tags\n- Inline scripts need `id`\n- Loading strategies\n- Google Analytics with `@next/third-parties`\n\n## Hydration Errors\n\nSee [hydration-error.md](./hydration-error.md) for:\n- Common causes (browser APIs, dates, invalid HTML)\n- Debugging with error overlay\n- Fixes for each cause\n\n## Suspense Boundaries\n\nSee [suspense-boundaries.md](./suspense-boundaries.md) for:\n- CSR bailout with `useSearchParams` and `usePathname`\n- Which hooks require Suspense boundaries\n\n## Parallel & Intercepting Routes\n\nSee [parallel-routes.md](./parallel-routes.md) for:\n- Modal patterns with `@slot` and `(.)` interceptors\n- `default.tsx` for fallbacks\n- Closing modals correctly with `router.back()`\n\n## Self-Hosting\n\nSee [self-hosting.md](./self-hosting.md) for:\n- `output: 'standalone'` for Docker\n- Cache handlers for multi-instance ISR\n- What works vs needs extra setup\n\n## Debug Tricks\n\nSee [debug-tricks.md](./debug-tricks.md) for:\n- MCP endpoint for AI-assisted debugging\n- Rebuild specific routes with `--debug-build-paths`\n\n","frontmatter":{"name":"next-best-practices","description":"Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling","user-invocable":false},"isInternal":false,"tokens":1011,"sizeBytes":4004},{"name":"suspense-boundaries.md","path":".agents/skills/next-best-practices/suspense-boundaries.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-best-practices/suspense-boundaries.md","title":"Next-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# Suspense Boundaries\n\nClient hooks that cause CSR bailout without Suspense boundaries.\n\n## useSearchParams\n\nAlways requires Suspense boundary in static routes. Without it, the entire page becomes client-side rendered.\n\n```tsx\n// Bad: Entire page becomes CSR\n'use client'\n\nimport { useSearchParams } from 'next/navigation'\n\nexport default function SearchBar() {\n  const searchParams = useSearchParams()\n  return <div>Query: {searchParams.get('q')}</div>\n}\n```\n\n```tsx\n// Good: Wrap in Suspense\nimport { Suspense } from 'react'\nimport SearchBar from './search-bar'\n\nexport default function Page() {\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <SearchBar />\n    </Suspense>\n  )\n}\n```\n\n## usePathname\n\nRequires Suspense boundary when route has dynamic parameters.\n\n```tsx\n// In dynamic route [slug]\n// Bad: No Suspense\n'use client'\nimport { usePathname } from 'next/navigation'\n\nexport function Breadcrumb() {\n  const pathname = usePathname()\n  return <nav>{pathname}</nav>\n}\n```\n\n```tsx\n// Good: Wrap in Suspense\n<Suspense fallback={<BreadcrumbSkeleton />}>\n  <Breadcrumb />\n</Suspense>\n```\n\nIf you use `generateStaticParams`, Suspense is optional.\n\n## Quick Reference\n\n| Hook | Suspense Required |\n|------|-------------------|\n| `useSearchParams()` | Yes |\n| `usePathname()` | Yes (dynamic routes) |\n| `useParams()` | No |\n| `useRouter()` | No |\n","isInternal":false,"tokens":332,"sizeBytes":1366},{"name":"SKILL.md","path":".agents/skills/next-cache-components/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-cache-components/SKILL.md","title":"next-cache-components","category":"anthropic-skill","format":"markdown","content":"---\nname: next-cache-components\ndescription: Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag\n---\n\n# Cache Components (Next.js 16+)\n\nCache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route.\n\n## Enable Cache Components\n\n```ts\n// next.config.ts\nimport type { NextConfig } from 'next'\n\nconst nextConfig: NextConfig = {\n  cacheComponents: true,\n}\n\nexport default nextConfig\n```\n\nThis replaces the old `experimental.ppr` flag.\n\n---\n\n## Three Content Types\n\nWith Cache Components enabled, content falls into three categories:\n\n### 1. Static (Auto-Prerendered)\n\nSynchronous code, imports, pure computations - prerendered at build time:\n\n```tsx\nexport default function Page() {\n  return (\n    <header>\n      <h1>Our Blog</h1>  {/* Static - instant */}\n      <nav>...</nav>\n    </header>\n  )\n}\n```\n\n### 2. Cached (`use cache`)\n\nAsync data that doesn't need fresh fetches every request:\n\n```tsx\nasync function BlogPosts() {\n  'use cache'\n  cacheLife('hours')\n\n  const posts = await db.posts.findMany()\n  return <PostList posts={posts} />\n}\n```\n\n### 3. Dynamic (Suspense)\n\nRuntime data that must be fresh - wrap in Suspense:\n\n```tsx\nimport { Suspense } from 'react'\n\nexport default function Page() {\n  return (\n    <>\n      <BlogPosts />  {/* Cached */}\n\n      <Suspense fallback={<p>Loading...</p>}>\n        <UserPreferences />  {/* Dynamic - streams in */}\n      </Suspense>\n    </>\n  )\n}\n\nasync function UserPreferences() {\n  const theme = (await cookies()).get('theme')?.value\n  return <p>Theme: {theme}</p>\n}\n```\n\n---\n\n## `use cache` Directive\n\n### File Level\n\n```tsx\n'use cache'\n\nexport default async function Page() {\n  // Entire page is cached\n  const data = await fetchData()\n  return <div>{data}</div>\n}\n```\n\n### Component Level\n\n```tsx\nexport async function CachedComponent() {\n  'use cache'\n  const data = await fetchData()\n  return <div>{data}</div>\n}\n```\n\n### Function Level\n\n```tsx\nexport async function getData() {\n  'use cache'\n  return db.query('SELECT * FROM posts')\n}\n```\n\n---\n\n## Cache Profiles\n\n### Built-in Profiles\n\n```tsx\n'use cache'                    // Default: 5m stale, 15m revalidate\n```\n\n```tsx\n'use cache: remote'           // Platform-provided cache (Redis, KV)\n```\n\n```tsx\n'use cache: private'          // For compliance, allows runtime APIs\n```\n\n### `cacheLife()` - Custom Lifetime\n\n```tsx\nimport { cacheLife } from 'next/cache'\n\nasync function getData() {\n  'use cache'\n  cacheLife('hours')  // Built-in profile\n  return fetch('/api/data')\n}\n```\n\nBuilt-in profiles: `'default'`, `'minutes'`, `'hours'`, `'days'`, `'weeks'`, `'max'`\n\n### Inline Configuration\n\n```tsx\nasync function getData() {\n  'use cache'\n  cacheLife({\n    stale: 3600,      // 1 hour - serve stale while revalidating\n    revalidate: 7200, // 2 hours - background revalidation interval\n    expire: 86400,    // 1 day - hard expiration\n  })\n  return fetch('/api/data')\n}\n```\n\n---\n\n## Cache Invalidation\n\n### `cacheTag()` - Tag Cached Content\n\n```tsx\nimport { cacheTag } from 'next/cache'\n\nasync function getProducts() {\n  'use cache'\n  cacheTag('products')\n  return db.products.findMany()\n}\n\nasync function getProduct(id: string) {\n  'use cache'\n  cacheTag('products', `product-${id}`)\n  return db.products.findUnique({ where: { id } })\n}\n```\n\n### `updateTag()` - Immediate Invalidation\n\nUse when you need the cache refreshed within the same request:\n\n```tsx\n'use server'\n\nimport { updateTag } from 'next/cache'\n\nexport async function updateProduct(id: string, data: FormData) {\n  await db.products.update({ where: { id }, data })\n  updateTag(`product-${id}`)  // Immediate - same request sees fresh data\n}\n```\n\n### `revalidateTag()` - Background Revalidation\n\nUse for stale-while-revalidate behavior:\n\n```tsx\n'use server'\n\nimport { revalidateTag } from 'next/cache'\n\nexport async function createPost(data: FormData) {\n  await db.posts.create({ data })\n  revalidateTag('posts')  // Background - next request sees fresh data\n}\n```\n\n---\n\n## Runtime Data Constraint\n\n**Cannot** access `cookies()`, `headers()`, or `searchParams` inside `use cache`.\n\n### Solution: Pass as Arguments\n\n```tsx\n// Wrong - runtime API inside use cache\nasync function CachedProfile() {\n  'use cache'\n  const session = (await cookies()).get('session')?.value  // Error!\n  return <div>{session}</div>\n}\n\n// Correct - extract outside, pass as argument\nasync function ProfilePage() {\n  const session = (await cookies()).get('session')?.value\n  return <CachedProfile sessionId={session} />\n}\n\nasync function CachedProfile({ sessionId }: { sessionId: string }) {\n  'use cache'\n  // sessionId becomes part of cache key automatically\n  const data = await fetchUserData(sessionId)\n  return <div>{data.name}</div>\n}\n```\n\n### Exception: `use cache: private`\n\nFor compliance requirements when you can't refactor:\n\n```tsx\nasync function getData() {\n  'use cache: private'\n  const session = (await cookies()).get('session')?.value  // Allowed\n  return fetchData(session)\n}\n```\n\n---\n\n## Cache Key Generation\n\nCache keys are automatic based on:\n- **Build ID** - invalidates all caches on deploy\n- **Function ID** - hash of function location\n- **Serializable arguments** - props become part of key\n- **Closure variables** - outer scope values included\n\n```tsx\nasync function Component({ userId }: { userId: string }) {\n  const getData = async (filter: string) => {\n    'use cache'\n    // Cache key = userId (closure) + filter (argument)\n    return fetch(`/api/users/${userId}?filter=${filter}`)\n  }\n  return getData('active')\n}\n```\n\n---\n\n## Complete Example\n\n```tsx\nimport { Suspense } from 'react'\nimport { cookies } from 'next/headers'\nimport { cacheLife, cacheTag } from 'next/cache'\n\nexport default function DashboardPage() {\n  return (\n    <>\n      {/* Static shell - instant from CDN */}\n      <header><h1>Dashboard</h1></header>\n      <nav>...</nav>\n\n      {/* Cached - fast, revalidates hourly */}\n      <Stats />\n\n      {/* Dynamic - streams in with fresh data */}\n      <Suspense fallback={<NotificationsSkeleton />}>\n        <Notifications />\n      </Suspense>\n    </>\n  )\n}\n\nasync function Stats() {\n  'use cache'\n  cacheLife('hours')\n  cacheTag('dashboard-stats')\n\n  const stats = await db.stats.aggregate()\n  return <StatsDisplay stats={stats} />\n}\n\nasync function Notifications() {\n  const userId = (await cookies()).get('userId')?.value\n  const notifications = await db.notifications.findMany({\n    where: { userId, read: false }\n  })\n  return <NotificationList items={notifications} />\n}\n```\n\n---\n\n## Migration from Previous Versions\n\n| Old Config | Replacement |\n|-----------|-------------|\n| `experimental.ppr` | `cacheComponents: true` |\n| `dynamic = 'force-dynamic'` | Remove (default behavior) |\n| `dynamic = 'force-static'` | `'use cache'` + `cacheLife('max')` |\n| `revalidate = N` | `cacheLife({ revalidate: N })` |\n| `unstable_cache()` | `'use cache'` directive |\n\n### Migrating `unstable_cache` to `use cache`\n\n`unstable_cache` has been replaced by the `use cache` directive in Next.js 16. When `cacheComponents` is enabled, convert `unstable_cache` calls to `use cache` functions:\n\n**Before (`unstable_cache`):**\n\n```tsx\nimport { unstable_cache } from 'next/cache'\n\nconst getCachedUser = unstable_cache(\n  async (id) => getUser(id),\n  ['my-app-user'],\n  {\n    tags: ['users'],\n    revalidate: 60,\n  }\n)\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const user = await getCachedUser(id)\n  return <div>{user.name}</div>\n}\n```\n\n**After (`use cache`):**\n\n```tsx\nimport { cacheLife, cacheTag } from 'next/cache'\n\nasync function getCachedUser(id: string) {\n  'use cache'\n  cacheTag('users')\n  cacheLife({ revalidate: 60 })\n  return getUser(id)\n}\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const user = await getCachedUser(id)\n  return <div>{user.name}</div>\n}\n```\n\nKey differences:\n- **No manual cache keys** - `use cache` generates keys automatically from function arguments and closures. The `keyParts` array from `unstable_cache` is no longer needed.\n- **Tags** - Replace `options.tags` with `cacheTag()` calls inside the function.\n- **Revalidation** - Replace `options.revalidate` with `cacheLife({ revalidate: N })` or a built-in profile like `cacheLife('minutes')`.\n- **Dynamic data** - `unstable_cache` did not support `cookies()` or `headers()` inside the callback. The same restriction applies to `use cache`, but you can use `'use cache: private'` if needed.\n\n---\n\n## Limitations\n\n- **Edge runtime not supported** - requires Node.js\n- **Static export not supported** - needs server\n- **Non-deterministic values** (`Math.random()`, `Date.now()`) execute once at build time inside `use cache`\n\nFor request-time randomness outside cache:\n\n```tsx\nimport { connection } from 'next/server'\n\nasync function DynamicContent() {\n  await connection()  // Defer to request time\n  const id = crypto.randomUUID()  // Different per request\n  return <div>{id}</div>\n}\n```\n\nSources:\n- [Cache Components Guide](https://nextjs.org/docs/app/getting-started/cache-components)\n- [use cache Directive](https://nextjs.org/docs/app/api-reference/directives/use-cache)\n- [unstable_cache (legacy)](https://nextjs.org/docs/app/api-reference/functions/unstable_cache)\n","frontmatter":{"name":"next-cache-components","description":"Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag"},"isInternal":false,"tokens":2342,"sizeBytes":9362},{"name":"SKILL.md","path":".agents/skills/next-upgrade/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/next-upgrade/SKILL.md","title":"next-upgrade","category":"anthropic-skill","format":"markdown","content":"---\nname: next-upgrade\ndescription: Upgrade Next.js to the latest version following official migration guides and codemods\nargument-hint: \"[target-version]\"\n---\n\n# Upgrade Next.js\n\nUpgrade the current project to the latest Next.js version following official migration guides.\n\n## Instructions\n\n1. **Detect current version**: Read `package.json` to identify the current Next.js version and related dependencies (React, React DOM, etc.)\n\n2. **Fetch the latest upgrade guide**: Use WebFetch to get the official upgrade documentation:\n   - Codemods: https://nextjs.org/docs/app/guides/upgrading/codemods\n   - Version-specific guides (adjust version as needed):\n     - https://nextjs.org/docs/app/guides/upgrading/version-16 \n     - https://nextjs.org/docs/app/guides/upgrading/version-15\n     - https://nextjs.org/docs/app/guides/upgrading/version-14\n\n3. **Determine upgrade path**: Based on current version, identify which migration steps apply. For major version jumps, upgrade incrementally (e.g., 13 → 14 → 15).\n\n4. **Run codemods first**: Next.js provides codemods to automate breaking changes:\n   ```bash\n   npx @next/codemod@latest <transform> <path>\n   ```\n   Common transforms:\n   - `next-async-request-api` - Updates async Request APIs (v15)\n   - `next-request-geo-ip` - Migrates geo/ip properties (v15)\n   - `next-dynamic-access-named-export` - Transforms dynamic imports (v15)\n\n5. **Update dependencies**: Upgrade Next.js and peer dependencies together:\n   ```bash\n   npm install next@latest react@latest react-dom@latest\n   ```\n\n6. **Review breaking changes**: Check the upgrade guide for manual changes needed:\n   - API changes (e.g., async params in v15)\n   - Configuration changes in `next.config.js`\n   - Deprecated features being removed\n\n7. **Update TypeScript types** (if applicable):\n   ```bash\n   npm install @types/react@latest @types/react-dom@latest\n   ```\n\n8. **Test the upgrade**:\n   - Run `npm run build` to check for build errors\n   - Run `npm run dev` and test key functionality\n","frontmatter":{"name":"next-upgrade","description":"Upgrade Next.js to the latest version following official migration guides and codemods","argument-hint":"[target-version]"},"isInternal":false,"tokens":487,"sizeBytes":2009},{"name":"advanced-patterns.md","path":".agents/skills/nodejs-backend-patterns/references/advanced-patterns.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/nodejs-backend-patterns/references/advanced-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Node.js Advanced Patterns\n\nAdvanced patterns for dependency injection, database integration, authentication, caching, and API response formatting.\n\n## Dependency Injection\n\n### DI Container\n\n```typescript\n// di-container.ts\nimport { Pool } from \"pg\";\nimport { UserRepository } from \"./repositories/user.repository\";\nimport { UserService } from \"./services/user.service\";\nimport { UserController } from \"./controllers/user.controller\";\nimport { AuthService } from \"./services/auth.service\";\n\nclass Container {\n  private instances = new Map<string, any>();\n\n  register<T>(key: string, factory: () => T): void {\n    this.instances.set(key, factory);\n  }\n\n  resolve<T>(key: string): T {\n    const factory = this.instances.get(key);\n    if (!factory) {\n      throw new Error(`No factory registered for ${key}`);\n    }\n    return factory();\n  }\n\n  singleton<T>(key: string, factory: () => T): void {\n    let instance: T;\n    this.instances.set(key, () => {\n      if (!instance) {\n        instance = factory();\n      }\n      return instance;\n    });\n  }\n}\n\nexport const container = new Container();\n\n// Register dependencies\ncontainer.singleton(\n  \"db\",\n  () =>\n    new Pool({\n      host: process.env.DB_HOST,\n      port: parseInt(process.env.DB_PORT || \"5432\"),\n      database: process.env.DB_NAME,\n      user: process.env.DB_USER,\n      password: process.env.DB_PASSWORD,\n      max: 20,\n      idleTimeoutMillis: 30000,\n      connectionTimeoutMillis: 2000,\n    }),\n);\n\ncontainer.singleton(\n  \"userRepository\",\n  () => new UserRepository(container.resolve(\"db\")),\n);\n\ncontainer.singleton(\n  \"userService\",\n  () => new UserService(container.resolve(\"userRepository\")),\n);\n\ncontainer.register(\n  \"userController\",\n  () => new UserController(container.resolve(\"userService\")),\n);\n\ncontainer.singleton(\n  \"authService\",\n  () => new AuthService(container.resolve(\"userRepository\")),\n);\n```\n\n## Database Patterns\n\n### PostgreSQL with Connection Pool\n\n```typescript\n// config/database.ts\nimport { Pool, PoolConfig } from \"pg\";\n\nconst poolConfig: PoolConfig = {\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT || \"5432\"),\n  database: process.env.DB_NAME,\n  user: process.env.DB_USER,\n  password: process.env.DB_PASSWORD,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n};\n\nexport const pool = new Pool(poolConfig);\n\n// Test connection\npool.on(\"connect\", () => {\n  console.log(\"Database connected\");\n});\n\npool.on(\"error\", (err) => {\n  console.error(\"Unexpected database error\", err);\n  process.exit(-1);\n});\n\n// Graceful shutdown\nexport const closeDatabase = async () => {\n  await pool.end();\n  console.log(\"Database connection closed\");\n};\n```\n\n### MongoDB with Mongoose\n\n```typescript\n// config/mongoose.ts\nimport mongoose from \"mongoose\";\n\nconst connectDB = async () => {\n  try {\n    await mongoose.connect(process.env.MONGODB_URI!, {\n      maxPoolSize: 10,\n      serverSelectionTimeoutMS: 5000,\n      socketTimeoutMS: 45000,\n    });\n\n    console.log(\"MongoDB connected\");\n  } catch (error) {\n    console.error(\"MongoDB connection error:\", error);\n    process.exit(1);\n  }\n};\n\nmongoose.connection.on(\"disconnected\", () => {\n  console.log(\"MongoDB disconnected\");\n});\n\nmongoose.connection.on(\"error\", (err) => {\n  console.error(\"MongoDB error:\", err);\n});\n\nexport { connectDB };\n\n// Model example\nimport { Schema, model, Document } from \"mongoose\";\n\ninterface IUser extends Document {\n  name: string;\n  email: string;\n  password: string;\n  createdAt: Date;\n  updatedAt: Date;\n}\n\nconst userSchema = new Schema<IUser>(\n  {\n    name: { type: String, required: true },\n    email: { type: String, required: true, unique: true },\n    password: { type: String, required: true },\n  },\n  {\n    timestamps: true,\n  },\n);\n\n// Indexes\nuserSchema.index({ email: 1 });\n\nexport const User = model<IUser>(\"User\", userSchema);\n```\n\n### Transaction Pattern\n\n```typescript\n// services/order.service.ts\nimport { Pool } from \"pg\";\n\nexport class OrderService {\n  constructor(private db: Pool) {}\n\n  async createOrder(userId: string, items: any[]) {\n    const client = await this.db.connect();\n\n    try {\n      await client.query(\"BEGIN\");\n\n      // Create order\n      const orderResult = await client.query(\n        \"INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id\",\n        [userId, calculateTotal(items)],\n      );\n      const orderId = orderResult.rows[0].id;\n\n      // Create order items\n      for (const item of items) {\n        await client.query(\n          \"INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)\",\n          [orderId, item.productId, item.quantity, item.price],\n        );\n\n        // Update inventory\n        await client.query(\n          \"UPDATE products SET stock = stock - $1 WHERE id = $2\",\n          [item.quantity, item.productId],\n        );\n      }\n\n      await client.query(\"COMMIT\");\n      return orderId;\n    } catch (error) {\n      await client.query(\"ROLLBACK\");\n      throw error;\n    } finally {\n      client.release();\n    }\n  }\n}\n```\n\n## Authentication & Authorization\n\n### JWT Authentication\n\n```typescript\n// services/auth.service.ts\nimport jwt from \"jsonwebtoken\";\nimport bcrypt from \"bcrypt\";\nimport { UserRepository } from \"../repositories/user.repository\";\nimport { UnauthorizedError } from \"../utils/errors\";\n\nexport class AuthService {\n  constructor(private userRepository: UserRepository) {}\n\n  async login(email: string, password: string) {\n    const user = await this.userRepository.findByEmail(email);\n\n    if (!user) {\n      throw new UnauthorizedError(\"Invalid credentials\");\n    }\n\n    const isValid = await bcrypt.compare(password, user.password);\n\n    if (!isValid) {\n      throw new UnauthorizedError(\"Invalid credentials\");\n    }\n\n    const token = this.generateToken({\n      userId: user.id,\n      email: user.email,\n    });\n\n    const refreshToken = this.generateRefreshToken({\n      userId: user.id,\n    });\n\n    return {\n      token,\n      refreshToken,\n      user: {\n        id: user.id,\n        name: user.name,\n        email: user.email,\n      },\n    };\n  }\n\n  async refreshToken(refreshToken: string) {\n    try {\n      const payload = jwt.verify(\n        refreshToken,\n        process.env.REFRESH_TOKEN_SECRET!,\n      ) as { userId: string };\n\n      const user = await this.userRepository.findById(payload.userId);\n\n      if (!user) {\n        throw new UnauthorizedError(\"User not found\");\n      }\n\n      const token = this.generateToken({\n        userId: user.id,\n        email: user.email,\n      });\n\n      return { token };\n    } catch (error) {\n      throw new UnauthorizedError(\"Invalid refresh token\");\n    }\n  }\n\n  private generateToken(payload: any): string {\n    return jwt.sign(payload, process.env.JWT_SECRET!, {\n      expiresIn: \"15m\",\n    });\n  }\n\n  private generateRefreshToken(payload: any): string {\n    return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {\n      expiresIn: \"7d\",\n    });\n  }\n}\n```\n\n## Caching Strategies\n\n```typescript\n// utils/cache.ts\nimport Redis from \"ioredis\";\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || \"6379\"),\n  retryStrategy: (times) => {\n    const delay = Math.min(times * 50, 2000);\n    return delay;\n  },\n});\n\nexport class CacheService {\n  async get<T>(key: string): Promise<T | null> {\n    const data = await redis.get(key);\n    return data ? JSON.parse(data) : null;\n  }\n\n  async set(key: string, value: any, ttl?: number): Promise<void> {\n    const serialized = JSON.stringify(value);\n    if (ttl) {\n      await redis.setex(key, ttl, serialized);\n    } else {\n      await redis.set(key, serialized);\n    }\n  }\n\n  async delete(key: string): Promise<void> {\n    await redis.del(key);\n  }\n\n  async invalidatePattern(pattern: string): Promise<void> {\n    const keys = await redis.keys(pattern);\n    if (keys.length > 0) {\n      await redis.del(...keys);\n    }\n  }\n}\n\n// Cache decorator\nexport function Cacheable(ttl: number = 300) {\n  return function (\n    target: any,\n    propertyKey: string,\n    descriptor: PropertyDescriptor,\n  ) {\n    const originalMethod = descriptor.value;\n\n    descriptor.value = async function (...args: any[]) {\n      const cache = new CacheService();\n      const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;\n\n      const cached = await cache.get(cacheKey);\n      if (cached) {\n        return cached;\n      }\n\n      const result = await originalMethod.apply(this, args);\n      await cache.set(cacheKey, result, ttl);\n\n      return result;\n    };\n\n    return descriptor;\n  };\n}\n```\n\n## API Response Format\n\n```typescript\n// utils/response.ts\nimport { Response } from \"express\";\n\nexport class ApiResponse {\n  static success<T>(\n    res: Response,\n    data: T,\n    message?: string,\n    statusCode = 200,\n  ) {\n    return res.status(statusCode).json({\n      status: \"success\",\n      message,\n      data,\n    });\n  }\n\n  static error(res: Response, message: string, statusCode = 500, errors?: any) {\n    return res.status(statusCode).json({\n      status: \"error\",\n      message,\n      ...(errors && { errors }),\n    });\n  }\n\n  static paginated<T>(\n    res: Response,\n    data: T[],\n    page: number,\n    limit: number,\n    total: number,\n  ) {\n    return res.json({\n      status: \"success\",\n      data,\n      pagination: {\n        page,\n        limit,\n        total,\n        pages: Math.ceil(total / limit),\n      },\n    });\n  }\n}\n```\n","isInternal":false,"tokens":2151,"sizeBytes":9405},{"name":"SKILL.md","path":".agents/skills/nodejs-backend-patterns/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/nodejs-backend-patterns/SKILL.md","title":"nodejs-backend-patterns","category":"anthropic-skill","format":"markdown","content":"---\nname: nodejs-backend-patterns\ndescription: Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.\n---\n\n# Node.js Backend Patterns\n\nComprehensive guidance for building scalable, maintainable, and production-ready Node.js backend applications with modern frameworks, architectural patterns, and best practices.\n\n## When to Use This Skill\n\n- Building REST APIs or GraphQL servers\n- Creating microservices with Node.js\n- Implementing authentication and authorization\n- Designing scalable backend architectures\n- Setting up middleware and error handling\n- Integrating databases (SQL and NoSQL)\n- Building real-time applications with WebSockets\n- Implementing background job processing\n\n## Core Frameworks\n\n### Express.js - Minimalist Framework\n\n**Basic Setup:**\n\n```typescript\nimport express, { Request, Response, NextFunction } from \"express\";\nimport helmet from \"helmet\";\nimport cors from \"cors\";\nimport compression from \"compression\";\n\nconst app = express();\n\n// Security middleware\napp.use(helmet());\napp.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(\",\") }));\napp.use(compression());\n\n// Body parsing\napp.use(express.json({ limit: \"10mb\" }));\napp.use(express.urlencoded({ extended: true, limit: \"10mb\" }));\n\n// Request logging\napp.use((req: Request, res: Response, next: NextFunction) => {\n  console.log(`${req.method} ${req.path}`);\n  next();\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => {\n  console.log(`Server running on port ${PORT}`);\n});\n```\n\n### Fastify - High Performance Framework\n\n**Basic Setup:**\n\n```typescript\nimport Fastify from \"fastify\";\nimport helmet from \"@fastify/helmet\";\nimport cors from \"@fastify/cors\";\nimport compress from \"@fastify/compress\";\n\nconst fastify = Fastify({\n  logger: {\n    level: process.env.LOG_LEVEL || \"info\",\n    transport: {\n      target: \"pino-pretty\",\n      options: { colorize: true },\n    },\n  },\n});\n\n// Plugins\nawait fastify.register(helmet);\nawait fastify.register(cors, { origin: true });\nawait fastify.register(compress);\n\n// Type-safe routes with schema validation\nfastify.post<{\n  Body: { name: string; email: string };\n  Reply: { id: string; name: string };\n}>(\n  \"/users\",\n  {\n    schema: {\n      body: {\n        type: \"object\",\n        required: [\"name\", \"email\"],\n        properties: {\n          name: { type: \"string\", minLength: 1 },\n          email: { type: \"string\", format: \"email\" },\n        },\n      },\n    },\n  },\n  async (request, reply) => {\n    const { name, email } = request.body;\n    return { id: \"123\", name };\n  },\n);\n\nawait fastify.listen({ port: 3000, host: \"0.0.0.0\" });\n```\n\n## Architectural Patterns\n\n### Pattern 1: Layered Architecture\n\n**Structure:**\n\n```\nsrc/\n├── controllers/     # Handle HTTP requests/responses\n├── services/        # Business logic\n├── repositories/    # Data access layer\n├── models/          # Data models\n├── middleware/      # Express/Fastify middleware\n├── routes/          # Route definitions\n├── utils/           # Helper functions\n├── config/          # Configuration\n└── types/           # TypeScript types\n```\n\n**Controller Layer:**\n\n```typescript\n// controllers/user.controller.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { UserService } from \"../services/user.service\";\nimport { CreateUserDTO, UpdateUserDTO } from \"../types/user.types\";\n\nexport class UserController {\n  constructor(private userService: UserService) {}\n\n  async createUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const userData: CreateUserDTO = req.body;\n      const user = await this.userService.createUser(userData);\n      res.status(201).json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async getUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const user = await this.userService.getUserById(id);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async updateUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const updates: UpdateUserDTO = req.body;\n      const user = await this.userService.updateUser(id, updates);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async deleteUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      await this.userService.deleteUser(id);\n      res.status(204).send();\n    } catch (error) {\n      next(error);\n    }\n  }\n}\n```\n\n**Service Layer:**\n\n```typescript\n// services/user.service.ts\nimport { UserRepository } from \"../repositories/user.repository\";\nimport { CreateUserDTO, UpdateUserDTO, User } from \"../types/user.types\";\nimport { NotFoundError, ValidationError } from \"../utils/errors\";\nimport bcrypt from \"bcrypt\";\n\nexport class UserService {\n  constructor(private userRepository: UserRepository) {}\n\n  async createUser(userData: CreateUserDTO): Promise<User> {\n    // Validation\n    const existingUser = await this.userRepository.findByEmail(userData.email);\n    if (existingUser) {\n      throw new ValidationError(\"Email already exists\");\n    }\n\n    // Hash password\n    const hashedPassword = await bcrypt.hash(userData.password, 10);\n\n    // Create user\n    const user = await this.userRepository.create({\n      ...userData,\n      password: hashedPassword,\n    });\n\n    // Remove password from response\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async getUserById(id: string): Promise<User> {\n    const user = await this.userRepository.findById(id);\n    if (!user) {\n      throw new NotFoundError(\"User not found\");\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async updateUser(id: string, updates: UpdateUserDTO): Promise<User> {\n    const user = await this.userRepository.update(id, updates);\n    if (!user) {\n      throw new NotFoundError(\"User not found\");\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async deleteUser(id: string): Promise<void> {\n    const deleted = await this.userRepository.delete(id);\n    if (!deleted) {\n      throw new NotFoundError(\"User not found\");\n    }\n  }\n}\n```\n\n**Repository Layer:**\n\n```typescript\n// repositories/user.repository.ts\nimport { Pool } from \"pg\";\nimport { CreateUserDTO, UpdateUserDTO, UserEntity } from \"../types/user.types\";\n\nexport class UserRepository {\n  constructor(private db: Pool) {}\n\n  async create(\n    userData: CreateUserDTO & { password: string },\n  ): Promise<UserEntity> {\n    const query = `\n      INSERT INTO users (name, email, password)\n      VALUES ($1, $2, $3)\n      RETURNING id, name, email, password, created_at, updated_at\n    `;\n    const { rows } = await this.db.query(query, [\n      userData.name,\n      userData.email,\n      userData.password,\n    ]);\n    return rows[0];\n  }\n\n  async findById(id: string): Promise<UserEntity | null> {\n    const query = \"SELECT * FROM users WHERE id = $1\";\n    const { rows } = await this.db.query(query, [id]);\n    return rows[0] || null;\n  }\n\n  async findByEmail(email: string): Promise<UserEntity | null> {\n    const query = \"SELECT * FROM users WHERE email = $1\";\n    const { rows } = await this.db.query(query, [email]);\n    return rows[0] || null;\n  }\n\n  async update(id: string, updates: UpdateUserDTO): Promise<UserEntity | null> {\n    const fields = Object.keys(updates);\n    const values = Object.values(updates);\n\n    const setClause = fields\n      .map((field, idx) => `${field} = $${idx + 2}`)\n      .join(\", \");\n\n    const query = `\n      UPDATE users\n      SET ${setClause}, updated_at = CURRENT_TIMESTAMP\n      WHERE id = $1\n      RETURNING *\n    `;\n\n    const { rows } = await this.db.query(query, [id, ...values]);\n    return rows[0] || null;\n  }\n\n  async delete(id: string): Promise<boolean> {\n    const query = \"DELETE FROM users WHERE id = $1\";\n    const { rowCount } = await this.db.query(query, [id]);\n    return rowCount > 0;\n  }\n}\n```\n\n### Pattern 2: Dependency Injection\n\nUse a DI container to wire up repositories, services, and controllers. For a full container implementation, see [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Middleware Patterns\n\n### Authentication Middleware\n\n```typescript\n// middleware/auth.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport jwt from \"jsonwebtoken\";\nimport { UnauthorizedError } from \"../utils/errors\";\n\ninterface JWTPayload {\n  userId: string;\n  email: string;\n}\n\ndeclare global {\n  namespace Express {\n    interface Request {\n      user?: JWTPayload;\n    }\n  }\n}\n\nexport const authenticate = async (\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  try {\n    const token = req.headers.authorization?.replace(\"Bearer \", \"\");\n\n    if (!token) {\n      throw new UnauthorizedError(\"No token provided\");\n    }\n\n    const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;\n\n    req.user = payload;\n    next();\n  } catch (error) {\n    next(new UnauthorizedError(\"Invalid token\"));\n  }\n};\n\nexport const authorize = (...roles: string[]) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) {\n      return next(new UnauthorizedError(\"Not authenticated\"));\n    }\n\n    // Check if user has required role\n    const hasRole = roles.some((role) => req.user?.roles?.includes(role));\n\n    if (!hasRole) {\n      return next(new UnauthorizedError(\"Insufficient permissions\"));\n    }\n\n    next();\n  };\n};\n```\n\n### Validation Middleware\n\n```typescript\n// middleware/validation.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { AnyZodObject, ZodError } from \"zod\";\nimport { ValidationError } from \"../utils/errors\";\n\nexport const validate = (schema: AnyZodObject) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    try {\n      await schema.parseAsync({\n        body: req.body,\n        query: req.query,\n        params: req.params,\n      });\n      next();\n    } catch (error) {\n      if (error instanceof ZodError) {\n        const errors = error.errors.map((err) => ({\n          field: err.path.join(\".\"),\n          message: err.message,\n        }));\n        next(new ValidationError(\"Validation failed\", errors));\n      } else {\n        next(error);\n      }\n    }\n  };\n};\n\n// Usage with Zod\nimport { z } from \"zod\";\n\nconst createUserSchema = z.object({\n  body: z.object({\n    name: z.string().min(1),\n    email: z.string().email(),\n    password: z.string().min(8),\n  }),\n});\n\nrouter.post(\"/users\", validate(createUserSchema), userController.createUser);\n```\n\n### Rate Limiting Middleware\n\n```typescript\n// middleware/rate-limit.middleware.ts\nimport rateLimit from \"express-rate-limit\";\nimport RedisStore from \"rate-limit-redis\";\nimport Redis from \"ioredis\";\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || \"6379\"),\n});\n\nexport const apiLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: \"rl:\",\n  }),\n  windowMs: 15 * 60 * 1000, // 15 minutes\n  max: 100, // Limit each IP to 100 requests per windowMs\n  message: \"Too many requests from this IP, please try again later\",\n  standardHeaders: true,\n  legacyHeaders: false,\n});\n\nexport const authLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: \"rl:auth:\",\n  }),\n  windowMs: 15 * 60 * 1000,\n  max: 5, // Stricter limit for auth endpoints\n  skipSuccessfulRequests: true,\n});\n```\n\n### Request Logging Middleware\n\n```typescript\n// middleware/logger.middleware.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport pino from \"pino\";\n\nconst logger = pino({\n  level: process.env.LOG_LEVEL || \"info\",\n  transport: {\n    target: \"pino-pretty\",\n    options: { colorize: true },\n  },\n});\n\nexport const requestLogger = (\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  const start = Date.now();\n\n  // Log response when finished\n  res.on(\"finish\", () => {\n    const duration = Date.now() - start;\n    logger.info({\n      method: req.method,\n      url: req.url,\n      status: res.statusCode,\n      duration: `${duration}ms`,\n      userAgent: req.headers[\"user-agent\"],\n      ip: req.ip,\n    });\n  });\n\n  next();\n};\n\nexport { logger };\n```\n\n## Error Handling\n\n### Custom Error Classes\n\n```typescript\n// utils/errors.ts\nexport class AppError extends Error {\n  constructor(\n    public message: string,\n    public statusCode: number = 500,\n    public isOperational: boolean = true,\n  ) {\n    super(message);\n    Object.setPrototypeOf(this, AppError.prototype);\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(\n    message: string,\n    public errors?: any[],\n  ) {\n    super(message, 400);\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(message: string = \"Resource not found\") {\n    super(message, 404);\n  }\n}\n\nexport class UnauthorizedError extends AppError {\n  constructor(message: string = \"Unauthorized\") {\n    super(message, 401);\n  }\n}\n\nexport class ForbiddenError extends AppError {\n  constructor(message: string = \"Forbidden\") {\n    super(message, 403);\n  }\n}\n\nexport class ConflictError extends AppError {\n  constructor(message: string) {\n    super(message, 409);\n  }\n}\n```\n\n### Global Error Handler\n\n```typescript\n// middleware/error-handler.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport { AppError } from \"../utils/errors\";\nimport { logger } from \"./logger.middleware\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) => {\n  if (err instanceof AppError) {\n    return res.status(err.statusCode).json({\n      status: \"error\",\n      message: err.message,\n      ...(err instanceof ValidationError && { errors: err.errors }),\n    });\n  }\n\n  // Log unexpected errors\n  logger.error({\n    error: err.message,\n    stack: err.stack,\n    url: req.url,\n    method: req.method,\n  });\n\n  // Don't leak error details in production\n  const message =\n    process.env.NODE_ENV === \"production\"\n      ? \"Internal server error\"\n      : err.message;\n\n  res.status(500).json({\n    status: \"error\",\n    message,\n  });\n};\n\n// Async error wrapper\nexport const asyncHandler = (\n  fn: (req: Request, res: Response, next: NextFunction) => Promise<any>,\n) => {\n  return (req: Request, res: Response, next: NextFunction) => {\n    Promise.resolve(fn(req, res, next)).catch(next);\n  };\n};\n```\n\n## Database Patterns\n\nNode.js supports both SQL and NoSQL databases. Use connection pooling for all production databases.\n\nKey patterns covered in [references/advanced-patterns.md](references/advanced-patterns.md):\n- **PostgreSQL with connection pool** — `pg` Pool configuration and graceful shutdown\n- **MongoDB with Mongoose** — connection management and schema definition\n- **Transaction pattern** — `BEGIN`/`COMMIT`/`ROLLBACK` with `pg` client\n\n## Authentication & Authorization\n\nJWT-based auth with access tokens (short-lived, 15m) and refresh tokens (7d). Full `AuthService` implementation with `bcrypt` password comparison in [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Caching Strategies\n\nRedis-backed `CacheService` with get/set/delete/invalidatePattern, plus a `@Cacheable` decorator for method-level caching. See [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## API Response Format\n\nStandardized `ApiResponse` helper with `success`, `error`, and `paginated` static methods. See [references/advanced-patterns.md](references/advanced-patterns.md).\n\n## Best Practices\n\n1. **Use TypeScript**: Type safety prevents runtime errors\n2. **Implement proper error handling**: Use custom error classes\n3. **Validate input**: Use libraries like Zod or Joi\n4. **Use environment variables**: Never hardcode secrets\n5. **Implement logging**: Use structured logging (Pino, Winston)\n6. **Add rate limiting**: Prevent abuse\n7. **Use HTTPS**: Always in production\n8. **Implement CORS properly**: Don't use `*` in production\n9. **Use dependency injection**: Easier testing and maintenance\n10. **Write tests**: Unit, integration, and E2E tests\n11. **Handle graceful shutdown**: Clean up resources\n12. **Use connection pooling**: For databases\n13. **Implement health checks**: For monitoring\n14. **Use compression**: Reduce response size\n15. **Monitor performance**: Use APM tools\n\n## Testing Patterns\n\nSee `javascript-testing-patterns` skill for comprehensive testing guidance.\n","frontmatter":{"name":"nodejs-backend-patterns","description":"Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures."},"isInternal":false,"tokens":3915,"sizeBytes":16802},{"name":"SKILL.md","path":".agents/skills/nodejs-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/nodejs-best-practices/SKILL.md","title":"nodejs-best-practices","category":"anthropic-skill","format":"markdown","content":"---\nname: nodejs-best-practices\ndescription: \"Node.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.\"\nrisk: unknown\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# Node.js Best Practices\n\n> Principles and decision-making for Node.js development in 2025.\n> **Learn to THINK, not memorize code patterns.**\n\n## When to Use\nUse this skill when making Node.js architecture decisions, choosing frameworks, designing async patterns, or applying security and deployment best practices.\n\n---\n\n## ⚠️ How to Use This Skill\n\nThis skill teaches **decision-making principles**, not fixed code to copy.\n\n- ASK user for preferences when unclear\n- Choose framework/pattern based on CONTEXT\n- Don't default to same solution every time\n\n---\n\n## 1. Framework Selection (2025)\n\n### Decision Tree\n\n```\nWhat are you building?\n│\n├── Edge/Serverless (Cloudflare, Vercel)\n│   └── Hono (zero-dependency, ultra-fast cold starts)\n│\n├── High Performance API\n│   └── Fastify (2-3x faster than Express)\n│\n├── Enterprise/Team familiarity\n│   └── NestJS (structured, DI, decorators)\n│\n├── Legacy/Stable/Maximum ecosystem\n│   └── Express (mature, most middleware)\n│\n└── Full-stack with frontend\n    └── Next.js API Routes or tRPC\n```\n\n### Comparison Principles\n\n| Factor | Hono | Fastify | Express |\n|--------|------|---------|---------|\n| **Best for** | Edge, serverless | Performance | Legacy, learning |\n| **Cold start** | Fastest | Fast | Moderate |\n| **Ecosystem** | Growing | Good | Largest |\n| **TypeScript** | Native | Excellent | Good |\n| **Learning curve** | Low | Medium | Low |\n\n### Selection Questions to Ask:\n1. What's the deployment target?\n2. Is cold start time critical?\n3. Does team have existing experience?\n4. Is there legacy code to maintain?\n\n---\n\n## 2. Runtime Considerations (2025)\n\n### Native TypeScript\n\n```\nNode.js 22+: --experimental-strip-types\n├── Run .ts files directly\n├── No build step needed for simple projects\n└── Consider for: scripts, simple APIs\n```\n\n### Module System Decision\n\n```\nESM (import/export)\n├── Modern standard\n├── Better tree-shaking\n├── Async module loading\n└── Use for: new projects\n\nCommonJS (require)\n├── Legacy compatibility\n├── More npm packages support\n└── Use for: existing codebases, some edge cases\n```\n\n### Runtime Selection\n\n| Runtime | Best For |\n|---------|----------|\n| **Node.js** | General purpose, largest ecosystem |\n| **Bun** | Performance, built-in bundler |\n| **Deno** | Security-first, built-in TypeScript |\n\n---\n\n## 3. Architecture Principles\n\n### Layered Structure Concept\n\n```\nRequest Flow:\n│\n├── Controller/Route Layer\n│   ├── Handles HTTP specifics\n│   ├── Input validation at boundary\n│   └── Calls service layer\n│\n├── Service Layer\n│   ├── Business logic\n│   ├── Framework-agnostic\n│   └── Calls repository layer\n│\n└── Repository Layer\n    ├── Data access only\n    ├── Database queries\n    └── ORM interactions\n```\n\n### Why This Matters:\n- **Testability**: Mock layers independently\n- **Flexibility**: Swap database without touching business logic\n- **Clarity**: Each layer has single responsibility\n\n### When to Simplify:\n- Small scripts → Single file OK\n- Prototypes → Less structure acceptable\n- Always ask: \"Will this grow?\"\n\n---\n\n## 4. Error Handling Principles\n\n### Centralized Error Handling\n\n```\nPattern:\n├── Create custom error classes\n├── Throw from any layer\n├── Catch at top level (middleware)\n└── Format consistent response\n```\n\n### Error Response Philosophy\n\n```\nClient gets:\n├── Appropriate HTTP status\n├── Error code for programmatic handling\n├── User-friendly message\n└── NO internal details (security!)\n\nLogs get:\n├── Full stack trace\n├── Request context\n├── User ID (if applicable)\n└── Timestamp\n```\n\n### Status Code Selection\n\n| Situation | Status | When |\n|-----------|--------|------|\n| Bad input | 400 | Client sent invalid data |\n| No auth | 401 | Missing or invalid credentials |\n| No permission | 403 | Valid auth, but not allowed |\n| Not found | 404 | Resource doesn't exist |\n| Conflict | 409 | Duplicate or state conflict |\n| Validation | 422 | Schema valid but business rules fail |\n| Server error | 500 | Our fault, log everything |\n\n---\n\n## 5. Async Patterns Principles\n\n### When to Use Each\n\n| Pattern | Use When |\n|---------|----------|\n| `async/await` | Sequential async operations |\n| `Promise.all` | Parallel independent operations |\n| `Promise.allSettled` | Parallel where some can fail |\n| `Promise.race` | Timeout or first response wins |\n\n### Event Loop Awareness\n\n```\nI/O-bound (async helps):\n├── Database queries\n├── HTTP requests\n├── File system\n└── Network operations\n\nCPU-bound (async doesn't help):\n├── Crypto operations\n├── Image processing\n├── Complex calculations\n└── → Use worker threads or offload\n```\n\n### Avoiding Event Loop Blocking\n\n- Never use sync methods in production (fs.readFileSync, etc.)\n- Offload CPU-intensive work\n- Use streaming for large data\n\n---\n\n## 6. Validation Principles\n\n### Validate at Boundaries\n\n```\nWhere to validate:\n├── API entry point (request body/params)\n├── Before database operations\n├── External data (API responses, file uploads)\n└── Environment variables (startup)\n```\n\n### Validation Library Selection\n\n| Library | Best For |\n|---------|----------|\n| **Zod** | TypeScript first, inference |\n| **Valibot** | Smaller bundle (tree-shakeable) |\n| **ArkType** | Performance critical |\n| **Yup** | Existing React Form usage |\n\n### Validation Philosophy\n\n- Fail fast: Validate early\n- Be specific: Clear error messages\n- Don't trust: Even \"internal\" data\n\n---\n\n## 7. Security Principles\n\n### Security Checklist (Not Code)\n\n- [ ] **Input validation**: All inputs validated\n- [ ] **Parameterized queries**: No string concatenation for SQL\n- [ ] **Password hashing**: bcrypt or argon2\n- [ ] **JWT verification**: Always verify signature and expiry\n- [ ] **Rate limiting**: Protect from abuse\n- [ ] **Security headers**: Helmet.js or equivalent\n- [ ] **HTTPS**: Everywhere in production\n- [ ] **CORS**: Properly configured\n- [ ] **Secrets**: Environment variables only\n- [ ] **Dependencies**: Regularly audited\n\n### Security Mindset\n\n```\nTrust nothing:\n├── Query params → validate\n├── Request body → validate\n├── Headers → verify\n├── Cookies → validate\n├── File uploads → scan\n└── External APIs → validate response\n```\n\n---\n\n## 8. Testing Principles\n\n### Test Strategy Selection\n\n| Type | Purpose | Tools |\n|------|---------|-------|\n| **Unit** | Business logic | node:test, Vitest |\n| **Integration** | API endpoints | Supertest |\n| **E2E** | Full flows | Playwright |\n\n### What to Test (Priorities)\n\n1. **Critical paths**: Auth, payments, core business\n2. **Edge cases**: Empty inputs, boundaries\n3. **Error handling**: What happens when things fail?\n4. **Not worth testing**: Framework code, trivial getters\n\n### Built-in Test Runner (Node.js 22+)\n\n```\nnode --test src/**/*.test.ts\n├── No external dependency\n├── Good coverage reporting\n└── Watch mode available\n```\n\n---\n\n## 10. Anti-Patterns to Avoid\n\n### ❌ DON'T:\n- Use Express for new edge projects (use Hono)\n- Use sync methods in production code\n- Put business logic in controllers\n- Skip input validation\n- Hardcode secrets\n- Trust external data without validation\n- Block event loop with CPU work\n\n### ✅ DO:\n- Choose framework based on context\n- Ask user for preferences when unclear\n- Use layered architecture for growing projects\n- Validate all inputs\n- Use environment variables for secrets\n- Profile before optimizing\n\n---\n\n## 11. Decision Checklist\n\nBefore implementing:\n\n- [ ] **Asked user about stack preference?**\n- [ ] **Chosen framework for THIS context?** (not just default)\n- [ ] **Considered deployment target?**\n- [ ] **Planned error handling strategy?**\n- [ ] **Identified validation points?**\n- [ ] **Considered security requirements?**\n\n---\n\n> **Remember**: Node.js best practices are about decision-making, not memorizing patterns. Every project deserves fresh consideration based on its requirements.\n","frontmatter":{"name":"nodejs-best-practices","description":"Node.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.","risk":"unknown","source":"community","date_added":"2026-02-27"},"isInternal":false,"tokens":1989,"sizeBytes":8454},{"name":"SKILL.md","path":".agents/skills/performance/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/performance/SKILL.md","title":"performance","category":"anthropic-skill","format":"markdown","content":"---\nname: performance\ndescription: Optimize web performance for faster loading and better user experience. Use when asked to \"speed up my site\", \"optimize performance\", \"reduce load time\", \"fix slow loading\", \"improve page speed\", or \"performance audit\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Performance optimization\n\nDeep performance optimization based on Lighthouse performance audits. Focuses on loading speed, runtime efficiency, and resource optimization.\n\n## How it works\n\n1. Identify performance bottlenecks in code and assets\n2. Prioritize by impact on Core Web Vitals\n3. Provide specific optimizations with code examples\n4. Measure improvement with before/after metrics\n\n## Performance budget\n\n| Resource | Budget | Rationale |\n|----------|--------|-----------|\n| Total page weight | < 1.5 MB | 3G loads in ~4s |\n| JavaScript (compressed) | < 300 KB | Parsing + execution time |\n| CSS (compressed) | < 100 KB | Render blocking |\n| Images (above-fold) | < 500 KB | LCP impact |\n| Fonts | < 100 KB | FOIT/FOUT prevention |\n| Third-party | < 200 KB | Uncontrolled latency |\n\n## Critical rendering path\n\n### Server response\n* **TTFB < 800ms.** Time to First Byte should be fast. Use CDN, caching, and efficient backends.\n* **Enable compression.** Gzip or Brotli for text assets. Brotli preferred (15-20% smaller).\n* **HTTP/2 or HTTP/3.** Multiplexing reduces connection overhead.\n* **Edge caching.** Cache HTML at CDN edge when possible.\n\n### Resource loading\n\n**Preconnect to required origins:**\n```html\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://cdn.example.com\" crossorigin>\n```\n\n**Preload critical resources:**\n```html\n<!-- LCP image -->\n<link rel=\"preload\" href=\"/hero.webp\" as=\"image\" fetchpriority=\"high\">\n\n<!-- Critical font -->\n<link rel=\"preload\" href=\"/font.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n```\n\n**Defer non-critical CSS:**\n```html\n<!-- Critical CSS inlined -->\n<style>/* Above-fold styles */</style>\n\n<!-- Non-critical CSS -->\n<link rel=\"preload\" href=\"/styles.css\" as=\"style\" onload=\"this.onload=null;this.rel='stylesheet'\">\n<noscript><link rel=\"stylesheet\" href=\"/styles.css\"></noscript>\n```\n\n### JavaScript optimization\n\n**Defer non-essential scripts:**\n```html\n<!-- Parser-blocking (avoid) -->\n<script src=\"/critical.js\"></script>\n\n<!-- Deferred (preferred) -->\n<script defer src=\"/app.js\"></script>\n\n<!-- Async (for independent scripts) -->\n<script async src=\"/analytics.js\"></script>\n\n<!-- Module (deferred by default) -->\n<script type=\"module\" src=\"/app.mjs\"></script>\n```\n\n**Code splitting patterns:**\n```javascript\n// Route-based splitting\nconst Dashboard = lazy(() => import('./Dashboard'));\n\n// Component-based splitting\nconst HeavyChart = lazy(() => import('./HeavyChart'));\n\n// Feature-based splitting\nif (user.isPremium) {\n  const PremiumFeatures = await import('./PremiumFeatures');\n}\n```\n\n**Tree shaking best practices:**\n```javascript\n// ❌ Imports entire library\nimport _ from 'lodash';\n_.debounce(fn, 300);\n\n// ✅ Imports only what's needed\nimport debounce from 'lodash/debounce';\ndebounce(fn, 300);\n```\n\n## Image optimization\n\n### Format selection\n| Format | Use case | Browser support |\n|--------|----------|-----------------|\n| AVIF | Photos, best compression | 92%+ |\n| WebP | Photos, good fallback | 97%+ |\n| PNG | Graphics with transparency | Universal |\n| SVG | Icons, logos, illustrations | Universal |\n\n### Responsive images\n```html\n<picture>\n  <!-- AVIF for modern browsers -->\n  <source \n    type=\"image/avif\"\n    srcset=\"hero-400.avif 400w,\n            hero-800.avif 800w,\n            hero-1200.avif 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\">\n  \n  <!-- WebP fallback -->\n  <source \n    type=\"image/webp\"\n    srcset=\"hero-400.webp 400w,\n            hero-800.webp 800w,\n            hero-1200.webp 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\">\n  \n  <!-- JPEG fallback -->\n  <img \n    src=\"hero-800.jpg\"\n    srcset=\"hero-400.jpg 400w,\n            hero-800.jpg 800w,\n            hero-1200.jpg 1200w\"\n    sizes=\"(max-width: 600px) 100vw, 50vw\"\n    width=\"1200\" \n    height=\"600\"\n    alt=\"Hero image\"\n    loading=\"lazy\"\n    decoding=\"async\">\n</picture>\n```\n\n### LCP image priority\n```html\n<!-- Above-fold LCP image: eager loading, high priority -->\n<img \n  src=\"hero.webp\" \n  fetchpriority=\"high\"\n  loading=\"eager\"\n  decoding=\"sync\"\n  alt=\"Hero\">\n\n<!-- Below-fold images: lazy loading -->\n<img \n  src=\"product.webp\" \n  loading=\"lazy\"\n  decoding=\"async\"\n  alt=\"Product\">\n```\n\n## Font optimization\n\n### Loading strategy\n```css\n/* System font stack as fallback */\nbody {\n  font-family: 'Custom Font', -apple-system, BlinkMacSystemFont, \n               'Segoe UI', Roboto, sans-serif;\n}\n\n/* Prevent invisible text */\n@font-face {\n  font-family: 'Custom Font';\n  src: url('/fonts/custom.woff2') format('woff2');\n  font-display: swap; /* or optional for non-critical */\n  font-weight: 400;\n  font-style: normal;\n  unicode-range: U+0000-00FF; /* Subset to Latin */\n}\n```\n\n### Preloading critical fonts\n```html\n<link rel=\"preload\" href=\"/fonts/heading.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n```\n\n### Variable fonts\n```css\n/* One file instead of multiple weights */\n@font-face {\n  font-family: 'Inter';\n  src: url('/fonts/Inter-Variable.woff2') format('woff2-variations');\n  font-weight: 100 900;\n  font-display: swap;\n}\n```\n\n## Caching strategy\n\n### Cache-Control headers\n```\n# HTML (short or no cache)\nCache-Control: no-cache, must-revalidate\n\n# Static assets with hash (immutable)\nCache-Control: public, max-age=31536000, immutable\n\n# Static assets without hash\nCache-Control: public, max-age=86400, stale-while-revalidate=604800\n\n# API responses\nCache-Control: private, max-age=0, must-revalidate\n```\n\n### Service worker caching\n```javascript\n// Cache-first for static assets\nself.addEventListener('fetch', (event) => {\n  if (event.request.destination === 'image' ||\n      event.request.destination === 'style' ||\n      event.request.destination === 'script') {\n    event.respondWith(\n      caches.match(event.request).then((cached) => {\n        return cached || fetch(event.request).then((response) => {\n          const clone = response.clone();\n          caches.open('static-v1').then((cache) => cache.put(event.request, clone));\n          return response;\n        });\n      })\n    );\n  }\n});\n```\n\n## Runtime performance\n\n### Avoid layout thrashing\n```javascript\n// ❌ Forces multiple reflows\nelements.forEach(el => {\n  const height = el.offsetHeight; // Read\n  el.style.height = height + 10 + 'px'; // Write\n});\n\n// ✅ Batch reads, then batch writes\nconst heights = elements.map(el => el.offsetHeight); // All reads\nelements.forEach((el, i) => {\n  el.style.height = heights[i] + 10 + 'px'; // All writes\n});\n```\n\n### Debounce expensive operations\n```javascript\nfunction debounce(fn, delay) {\n  let timeout;\n  return (...args) => {\n    clearTimeout(timeout);\n    timeout = setTimeout(() => fn(...args), delay);\n  };\n}\n\n// Debounce scroll/resize handlers\nwindow.addEventListener('scroll', debounce(handleScroll, 100));\n```\n\n### Use requestAnimationFrame\n```javascript\n// ❌ May cause jank\nsetInterval(animate, 16);\n\n// ✅ Synced with display refresh\nfunction animate() {\n  // Animation logic\n  requestAnimationFrame(animate);\n}\nrequestAnimationFrame(animate);\n```\n\n### Virtualize long lists\n```javascript\n// For lists > 100 items, render only visible items\n// Use libraries like react-window, vue-virtual-scroller, or native CSS:\n.virtual-list {\n  content-visibility: auto;\n  contain-intrinsic-size: 0 50px; /* Estimated item height */\n}\n```\n\n## Third-party scripts\n\n### Load strategies\n```javascript\n// ❌ Blocks main thread\n<script src=\"https://analytics.example.com/script.js\"></script>\n\n// ✅ Async loading\n<script async src=\"https://analytics.example.com/script.js\"></script>\n\n// ✅ Delay until interaction\n<script>\ndocument.addEventListener('DOMContentLoaded', () => {\n  const observer = new IntersectionObserver((entries) => {\n    if (entries[0].isIntersecting) {\n      const script = document.createElement('script');\n      script.src = 'https://widget.example.com/embed.js';\n      document.body.appendChild(script);\n      observer.disconnect();\n    }\n  });\n  observer.observe(document.querySelector('#widget-container'));\n});\n</script>\n```\n\n### Facade pattern\n```html\n<!-- Show static placeholder until interaction -->\n<div class=\"youtube-facade\" \n     data-video-id=\"abc123\" \n     onclick=\"loadYouTube(this)\">\n  <img src=\"/thumbnails/abc123.jpg\" alt=\"Video title\">\n  <button aria-label=\"Play video\">▶</button>\n</div>\n```\n\n## Measurement\n\n### Key metrics\n| Metric | Target | Tool |\n|--------|--------|------|\n| LCP | < 2.5s | Lighthouse, CrUX |\n| FCP | < 1.8s | Lighthouse |\n| Speed Index | < 3.4s | Lighthouse |\n| TBT | < 200ms | Lighthouse |\n| TTI | < 3.8s | Lighthouse |\n\n### Testing commands\n```bash\n# Lighthouse CLI\nnpx lighthouse https://example.com --output html --output-path report.html\n\n# Web Vitals library\nimport {onLCP, onINP, onCLS} from 'web-vitals';\nonLCP(console.log);\nonINP(console.log);\nonCLS(console.log);\n```\n\n## References\n\nFor Core Web Vitals specific optimizations, see [Core Web Vitals](../core-web-vitals/SKILL.md).\n","frontmatter":{"name":"performance","description":"Optimize web performance for faster loading and better user experience. Use when asked to \"speed up my site\", \"optimize performance\", \"reduce load time\", \"fix slow loading\", \"improve page speed\", or \"performance audit\".","license":"MIT","metadata":{"author":"web-quality-skills","version":"1.0"}},"isInternal":false,"tokens":2354,"sizeBytes":9229},{"name":"SKILL.md","path":".agents/skills/seo/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/seo/SKILL.md","title":"seo","category":"anthropic-skill","format":"markdown","content":"---\nname: seo\ndescription: Optimize for search engine visibility and ranking. Use when asked to \"improve SEO\", \"optimize for search\", \"fix meta tags\", \"add structured data\", \"sitemap optimization\", or \"search engine optimization\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# SEO optimization\n\nSearch engine optimization based on Lighthouse SEO audits and Google Search guidelines. Focus on technical SEO, on-page optimization, and structured data.\n\n## SEO fundamentals\n\nSearch ranking factors (approximate influence):\n\n| Factor | Influence | This Skill |\n|--------|-----------|------------|\n| Content quality & relevance | ~40% | Partial (structure) |\n| Backlinks & authority | ~25% | ✗ |\n| Technical SEO | ~15% | ✓ |\n| Page experience (Core Web Vitals) | ~10% | See [Core Web Vitals](../core-web-vitals/SKILL.md) |\n| On-page SEO | ~10% | ✓ |\n\n---\n\n## Technical SEO\n\n### Crawlability\n\n**robots.txt:**\n```text\n# /robots.txt\nUser-agent: *\nAllow: /\n\n# Block admin/private areas\nDisallow: /admin/\nDisallow: /api/\nDisallow: /private/\n\n# Don't block resources needed for rendering\n# ❌ Disallow: /static/\n\nSitemap: https://example.com/sitemap.xml\n```\n\n**Meta robots:**\n```html\n<!-- Default: indexable, followable -->\n<meta name=\"robots\" content=\"index, follow\">\n\n<!-- Noindex specific pages -->\n<meta name=\"robots\" content=\"noindex, nofollow\">\n\n<!-- Indexable but don't follow links -->\n<meta name=\"robots\" content=\"index, nofollow\">\n\n<!-- Control snippets -->\n<meta name=\"robots\" content=\"max-snippet:150, max-image-preview:large\">\n```\n\n**Canonical URLs:**\n```html\n<!-- Prevent duplicate content issues -->\n<link rel=\"canonical\" href=\"https://example.com/page\">\n\n<!-- Self-referencing canonical (recommended) -->\n<link rel=\"canonical\" href=\"https://example.com/current-page\">\n\n<!-- For paginated content -->\n<link rel=\"canonical\" href=\"https://example.com/products\">\n<!-- Or use rel=\"prev\" / rel=\"next\" for explicit pagination -->\n```\n\n### XML sitemap\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n  <url>\n    <loc>https://example.com/</loc>\n    <lastmod>2024-01-15</lastmod>\n    <changefreq>daily</changefreq>\n    <priority>1.0</priority>\n  </url>\n  <url>\n    <loc>https://example.com/products</loc>\n    <lastmod>2024-01-14</lastmod>\n    <changefreq>weekly</changefreq>\n    <priority>0.8</priority>\n  </url>\n</urlset>\n```\n\n**Sitemap best practices:**\n- Maximum 50,000 URLs or 50MB per sitemap\n- Use sitemap index for larger sites\n- Include only canonical, indexable URLs\n- Update `lastmod` when content changes\n- Submit to Google Search Console\n\n### URL structure\n\n```\n✅ Good URLs:\nhttps://example.com/products/blue-widget\nhttps://example.com/blog/how-to-use-widgets\n\n❌ Poor URLs:\nhttps://example.com/p?id=12345\nhttps://example.com/products/item/category/subcategory/blue-widget-2024-sale-discount\n```\n\n**URL guidelines:**\n- Use hyphens, not underscores\n- Lowercase only\n- Keep short (< 75 characters)\n- Include target keywords naturally\n- Avoid parameters when possible\n- Use HTTPS always\n\n### HTTPS & security\n\n```html\n<!-- Ensure all resources use HTTPS -->\n<img src=\"https://example.com/image.jpg\">\n\n<!-- Not: -->\n<img src=\"http://example.com/image.jpg\">\n```\n\n**Security headers for SEO trust signals:**\n```\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff\nX-Frame-Options: DENY\n```\n\n---\n\n## On-page SEO\n\n### Title tags\n\n```html\n<!-- ❌ Missing or generic -->\n<title>Page</title>\n<title>Home</title>\n\n<!-- ✅ Descriptive with primary keyword -->\n<title>Blue Widgets for Sale | Premium Quality | Example Store</title>\n```\n\n**Title tag guidelines:**\n- 50-60 characters (Google truncates ~60)\n- Primary keyword near the beginning\n- Unique for every page\n- Brand name at end (unless homepage)\n- Action-oriented when appropriate\n\n### Meta descriptions\n\n```html\n<!-- ❌ Missing or duplicate -->\n<meta name=\"description\" content=\"\">\n\n<!-- ✅ Compelling and unique -->\n<meta name=\"description\" content=\"Shop premium blue widgets with free shipping. 30-day returns. Rated 4.9/5 by 10,000+ customers. Order today and save 20%.\">\n```\n\n**Meta description guidelines:**\n- 150-160 characters\n- Include primary keyword naturally\n- Compelling call-to-action\n- Unique for every page\n- Matches page content\n\n### Heading structure\n\n```html\n<!-- ❌ Poor structure -->\n<h2>Welcome to Our Store</h2>\n<h4>Products</h4>\n<h1>Contact Us</h1>\n\n<!-- ✅ Proper hierarchy -->\n<h1>Blue Widgets - Premium Quality</h1>\n  <h2>Product Features</h2>\n    <h3>Durability</h3>\n    <h3>Design</h3>\n  <h2>Customer Reviews</h2>\n  <h2>Pricing</h2>\n```\n\n**Heading guidelines:**\n- Single `<h1>` per page (the main topic)\n- Logical hierarchy (don't skip levels)\n- Include keywords naturally\n- Descriptive, not generic\n\n### Image SEO\n\n```html\n<!-- ❌ Poor image SEO -->\n<img src=\"IMG_12345.jpg\">\n\n<!-- ✅ Optimized image -->\n<img src=\"blue-widget-product-photo.webp\"\n     alt=\"Blue widget with chrome finish, side view showing control panel\"\n     width=\"800\"\n     height=\"600\"\n     loading=\"lazy\">\n```\n\n**Image guidelines:**\n- Descriptive filenames with keywords\n- Alt text describes the image content\n- Compressed and properly sized\n- WebP/AVIF with fallbacks\n- Lazy load below-fold images\n\n### Internal linking\n\n```html\n<!-- ❌ Non-descriptive -->\n<a href=\"/products\">Click here</a>\n<a href=\"/widgets\">Read more</a>\n\n<!-- ✅ Descriptive anchor text -->\n<a href=\"/products/blue-widgets\">Browse our blue widget collection</a>\n<a href=\"/guides/widget-maintenance\">Learn how to maintain your widgets</a>\n```\n\n**Linking guidelines:**\n- Descriptive anchor text with keywords\n- Link to relevant internal pages\n- Reasonable number of links per page\n- Fix broken links promptly\n- Use breadcrumbs for hierarchy\n\n---\n\n## Structured data (JSON-LD)\n\n### Organization\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Organization\",\n  \"name\": \"Example Company\",\n  \"url\": \"https://example.com\",\n  \"logo\": \"https://example.com/logo.png\",\n  \"sameAs\": [\n    \"https://twitter.com/example\",\n    \"https://linkedin.com/company/example\"\n  ],\n  \"contactPoint\": {\n    \"@type\": \"ContactPoint\",\n    \"telephone\": \"+1-555-123-4567\",\n    \"contactType\": \"customer service\"\n  }\n}\n</script>\n```\n\n### Article\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Article\",\n  \"headline\": \"How to Choose the Right Widget\",\n  \"description\": \"Complete guide to selecting widgets for your needs.\",\n  \"image\": \"https://example.com/article-image.jpg\",\n  \"author\": {\n    \"@type\": \"Person\",\n    \"name\": \"Jane Smith\",\n    \"url\": \"https://example.com/authors/jane-smith\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"Example Blog\",\n    \"logo\": {\n      \"@type\": \"ImageObject\",\n      \"url\": \"https://example.com/logo.png\"\n    }\n  },\n  \"datePublished\": \"2024-01-15\",\n  \"dateModified\": \"2024-01-20\"\n}\n</script>\n```\n\n### Product\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Product\",\n  \"name\": \"Blue Widget Pro\",\n  \"image\": \"https://example.com/blue-widget.jpg\",\n  \"description\": \"Premium blue widget with advanced features.\",\n  \"brand\": {\n    \"@type\": \"Brand\",\n    \"name\": \"WidgetCo\"\n  },\n  \"offers\": {\n    \"@type\": \"Offer\",\n    \"price\": \"49.99\",\n    \"priceCurrency\": \"USD\",\n    \"availability\": \"https://schema.org/InStock\",\n    \"url\": \"https://example.com/products/blue-widget\"\n  },\n  \"aggregateRating\": {\n    \"@type\": \"AggregateRating\",\n    \"ratingValue\": \"4.8\",\n    \"reviewCount\": \"1250\"\n  }\n}\n</script>\n```\n\n### FAQ\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What colors are available?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Our widgets come in blue, red, and green.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What is the warranty?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"All widgets include a 2-year warranty.\"\n      }\n    }\n  ]\n}\n</script>\n```\n\n### Breadcrumbs\n\n```html\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https://example.com\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Products\",\n      \"item\": \"https://example.com/products\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Blue Widgets\",\n      \"item\": \"https://example.com/products/blue-widgets\"\n    }\n  ]\n}\n</script>\n```\n\n### Validation\n\nTest structured data at:\n- [Google Rich Results Test](https://search.google.com/test/rich-results)\n- [Schema.org Validator](https://validator.schema.org/)\n\n---\n\n## Mobile SEO\n\n### Responsive design\n\n```html\n<!-- ❌ Not mobile-friendly -->\n<meta name=\"viewport\" content=\"width=1024\">\n\n<!-- ✅ Responsive viewport -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n```\n\n### Tap targets\n\n```css\n/* ❌ Too small for mobile */\n.small-link {\n  padding: 4px;\n  font-size: 12px;\n}\n\n/* ✅ Adequate tap target */\n.mobile-friendly-link {\n  padding: 12px;\n  font-size: 16px;\n  min-height: 48px;\n  min-width: 48px;\n}\n```\n\n### Font sizes\n\n```css\n/* ❌ Too small on mobile */\nbody {\n  font-size: 10px;\n}\n\n/* ✅ Readable without zooming */\nbody {\n  font-size: 16px;\n  line-height: 1.5;\n}\n```\n\n---\n\n## International SEO\n\n### Hreflang tags\n\n```html\n<!-- For multi-language sites -->\n<link rel=\"alternate\" hreflang=\"en\" href=\"https://example.com/page\">\n<link rel=\"alternate\" hreflang=\"es\" href=\"https://example.com/es/page\">\n<link rel=\"alternate\" hreflang=\"fr\" href=\"https://example.com/fr/page\">\n<link rel=\"alternate\" hreflang=\"x-default\" href=\"https://example.com/page\">\n```\n\n### Language declaration\n\n```html\n<html lang=\"en\">\n<!-- or -->\n<html lang=\"es-MX\">\n```\n\n---\n\n## SEO audit checklist\n\n### Critical\n- [ ] HTTPS enabled\n- [ ] robots.txt allows crawling\n- [ ] No `noindex` on important pages\n- [ ] Title tags present and unique\n- [ ] Single `<h1>` per page\n\n### High priority\n- [ ] Meta descriptions present\n- [ ] Sitemap submitted\n- [ ] Canonical URLs set\n- [ ] Mobile-responsive\n- [ ] Core Web Vitals passing\n\n### Medium priority\n- [ ] Structured data implemented\n- [ ] Internal linking strategy\n- [ ] Image alt text\n- [ ] Descriptive URLs\n- [ ] Breadcrumb navigation\n\n### Ongoing\n- [ ] Fix crawl errors in Search Console\n- [ ] Update sitemap when content changes\n- [ ] Monitor ranking changes\n- [ ] Check for broken links\n- [ ] Review Search Console insights\n\n---\n\n## Tools\n\n| Tool | Use |\n|------|-----|\n| Google Search Console | Monitor indexing, fix issues |\n| Google PageSpeed Insights | Performance + Core Web Vitals |\n| Rich Results Test | Validate structured data |\n| Lighthouse | Full SEO audit |\n| Screaming Frog | Crawl analysis |\n\n## References\n\n- [Google Search Central](https://developers.google.com/search)\n- [Schema.org](https://schema.org/)\n- [Core Web Vitals](../core-web-vitals/SKILL.md)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n","frontmatter":{"name":"seo","description":"Optimize for search engine visibility and ranking. Use when asked to \"improve SEO\", \"optimize for search\", \"fix meta tags\", \"add structured data\", \"sitemap optimization\", or \"search engine optimization\".","license":"MIT","metadata":{"author":"web-quality-skills","version":"1.0"}},"isInternal":false,"tokens":2984,"sizeBytes":11251},{"name":"accessibility.md","path":".agents/skills/tailwind-css-patterns/references/accessibility.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/references/accessibility.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Tailwind CSS Accessibility Guidelines\n\n## Focus Management\n\n```html\n<!-- Custom focus styles that meet WCAG AA -->\n<button class=\"focus:outline-none focus:ring-4 focus:ring-blue-500 focus:ring-offset-2\">\n  Accessible Button\n</button>\n\n<!-- Skip links for keyboard navigation -->\n<a href=\"#main-content\" class=\"sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4\">\n  Skip to main content\n</a>\n```\n\n### Focus Visible vs Focus\n\n```html\n<!-- Only show focus ring on keyboard navigation -->\n<button class=\"focus-visible:ring-2 focus-visible:ring-blue-500\">\n  Keyboard-only focus indicator\n</button>\n```\n\n---\n\n## Screen Reader Support\n\n```html\n<!-- Semantic buttons with ARIA labels -->\n<button aria-label=\"Close dialog\" class=\"p-2\">\n  <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\">\n    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n  </svg>\n</button>\n\n<!-- Descriptive links -->\n<a href=\"/docs\" aria-describedby=\"docs-description\">\n  Documentation\n</a>\n<p id=\"docs-description\" class=\"sr-only\">\n  Learn how to use our API and integration guides\n</p>\n\n<!-- Live regions for dynamic content -->\n<div aria-live=\"polite\" class=\"sr-only\">\n  <p>3 new notifications</p>\n</div>\n```\n\n### Screen Reader Only Content\n\n```html\n<span class=\"sr-only\">Opens in new window</span>\n```\n\n---\n\n## Color Contrast\n\n```html\n<!-- Ensure sufficient contrast ratios -->\n<div class=\"bg-gray-900 text-white\">\n  High contrast text (WCAG AAA)\n</div>\n\n<div class=\"bg-blue-500 text-blue-100\">\n  Good contrast on colored backgrounds\n</div>\n\n<!-- Use contrast utilities for testing -->\n<div class=\"bg-red-500 text-white contrast-more:bg-red-600 contrast-more:text-red-100\">\n  Adjusts for high contrast mode\n</div>\n```\n\n### Contrast Guidelines\n\n- **Normal text**: Minimum 4.5:1 contrast ratio\n- **Large text**: Minimum 3:1 contrast ratio\n- **UI components**: Minimum 3:1 contrast ratio\n\n---\n\n## Motion Preferences\n\n```html\n<!-- Respect prefers-reduced-motion -->\n<div class=\"transform transition-transform motion-reduce:transition-none\">\n  Doesn't animate when user prefers reduced motion\n</div>\n\n<!-- Conditional animations -->\n<div class=\"animate-pulse motion-safe:hover:animate-spin\">\n  Only animates when motion is preferred\n</div>\n```\n\n### Reduced Motion Media Query\n\n```css\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n  }\n}\n```\n\n---\n\n## ARIA Patterns with Tailwind\n\n### Toggle Button\n\n```html\n<button\n  type=\"button\"\n  role=\"switch\"\n  aria-checked=\"false\"\n  class=\"relative inline-flex h-6 w-11 items-center rounded-full bg-gray-200 aria-checked:bg-blue-600\"\n>\n  <span class=\"sr-only\">Enable notifications</span>\n  <span class=\"inline-block h-4 w-4 transform rounded-full bg-white transition translate-x-1 aria-checked:translate-x-6\"></span>\n</button>\n```\n\n### Alert Dialog\n\n```html\n<div\n  role=\"alertdialog\"\n  aria-labelledby=\"alert-title\"\n  aria-describedby=\"alert-description\"\n  class=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50\"\n>\n  <div class=\"max-w-md w-full bg-white rounded-lg shadow-xl p-6\">\n    <h2 id=\"alert-title\" class=\"text-lg font-bold mb-2\">\n      Are you sure?\n    </h2>\n    <p id=\"alert-description\" class=\"text-gray-600 mb-4\">\n      This action cannot be undone.\n    </p>\n    <div class=\"flex justify-end gap-2\">\n      <button class=\"px-4 py-2 text-gray-600\">Cancel</button>\n      <button class=\"px-4 py-2 bg-red-500 text-white rounded\">Delete</button>\n    </div>\n  </div>\n</div>\n```\n\n---\n\n## Accessibility Checklist\n\n- [ ] All interactive elements have visible focus indicators\n- [ ] Color contrast meets WCAG standards\n- [ ] Images have alt text\n- [ ] Form inputs have associated labels\n- [ ] ARIA labels used for icon-only buttons\n- [ ] Animations respect reduced motion preference\n- [ ] Skip links provided for keyboard navigation\n- [ ] Landmarks (header, main, nav, footer) used appropriately\n","isInternal":false,"tokens":1043,"sizeBytes":4046},{"name":"animations.md","path":".agents/skills/tailwind-css-patterns/references/animations.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/references/animations.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Tailwind CSS Animations & Transitions\n\n## Basic Transitions\n\n```html\n<button class=\"bg-blue-500 hover:bg-blue-700 transition duration-300\">\n  Smooth transition\n</button>\n```\n\n### Transition Properties\n\n| Class | Description |\n|-------|-------------|\n| `transition` | All properties |\n| `transition-colors` | Color properties only |\n| `transition-opacity` | Opacity only |\n| `transition-transform` | Transform only |\n| `duration-150` | 150ms duration |\n| `duration-300` | 300ms duration |\n| `ease-in` | Ease in timing |\n| `ease-out` | Ease out timing |\n| `ease-in-out` | Ease in-out timing |\n\n---\n\n## Transform Effects\n\n```html\n<!-- Scale on hover -->\n<div class=\"transform hover:scale-110 transition duration-300\">\n  Scale on hover\n</div>\n\n<!-- Rotate on hover -->\n<img class=\"transform hover:rotate-6 transition duration-300\" />\n\n<!-- Multiple transforms -->\n<div class=\"transform hover:scale-105 hover:-translate-y-1 transition\">\n  Scale up and move up\n</div>\n```\n\n---\n\n## Built-in Animations\n\n```html\n<div class=\"animate-spin\">Spinning</div>\n<div class=\"animate-pulse\">Pulsing</div>\n<div class=\"animate-bounce\">Bouncing</div>\n<div class=\"animate-ping\">Pinging (radar effect)</div>\n```\n\n### Common Use Cases\n\n```html\n<!-- Loading spinner -->\n<svg class=\"animate-spin h-5 w-5 text-blue-500\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n  <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n  <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n</svg>\n\n<!-- Skeleton loading -->\n<div class=\"animate-pulse space-y-4\">\n  <div class=\"h-4 bg-gray-200 rounded w-3/4\"></div>\n  <div class=\"h-4 bg-gray-200 rounded\"></div>\n  <div class=\"h-4 bg-gray-200 rounded w-5/6\"></div>\n</div>\n\n<!-- Notification badge -->\n<span class=\"absolute -top-1 -right-1 h-3 w-3 animate-ping rounded-full bg-red-400 opacity-75\"></span>\n<span class=\"absolute -top-1 -right-1 h-3 w-3 rounded-full bg-red-500\"></span>\n```\n\n---\n\n## Custom Animations (v4.1+)\n\n```css\n/* In your CSS with @theme */\n@theme {\n  --animate-fade-in: fadeIn 0.5s ease-in-out;\n  --animate-slide-up: slideUp 0.3s ease-out;\n}\n\n@keyframes fadeIn {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n@keyframes slideUp {\n  from {\n    opacity: 0;\n    transform: translateY(20px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n```\n\nUsage:\n\n```html\n<div class=\"animate-fade-in\">Fades in on load</div>\n<div class=\"animate-slide-up\">Slides up on load</div>\n```\n\n---\n\n## Motion Preferences\n\nRespect user's motion preferences:\n\n```html\n<!-- Disable animations for users who prefer reduced motion -->\n<div class=\"transform transition-transform motion-reduce:transition-none\">\n  Doesn't animate when user prefers reduced motion\n</div>\n\n<!-- Only animate when motion is preferred -->\n<div class=\"animate-pulse motion-safe:hover:animate-spin\">\n  Only animates when motion is preferred\n</div>\n```\n\n### Global Reduced Motion Support\n\n```css\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n  }\n}\n```\n","isInternal":false,"tokens":963,"sizeBytes":3281},{"name":"component-patterns.md","path":".agents/skills/tailwind-css-patterns/references/component-patterns.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/references/component-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Tailwind CSS Component Patterns\n\n## Card Component\n\n```html\n<div class=\"bg-white rounded-lg shadow-lg overflow-hidden\">\n  <img class=\"w-full h-48 object-cover\" src=\"image.jpg\" alt=\"Card image\" />\n  <div class=\"p-6\">\n    <h3 class=\"text-xl font-bold mb-2\">Card Title</h3>\n    <p class=\"text-gray-700 mb-4\">Card description text goes here.</p>\n    <button class=\"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded\">\n      Action\n    </button>\n  </div>\n</div>\n```\n\n## Responsive User Card\n\n```html\n<div class=\"max-w-sm mx-auto bg-white rounded-xl shadow-lg overflow-hidden sm:flex sm:max-w-2xl\">\n  <img class=\"h-48 w-full object-cover sm:h-auto sm:w-48\"\n       src=\"profile.jpg\"\n       alt=\"Profile\" />\n  <div class=\"p-8\">\n    <div class=\"uppercase tracking-wide text-sm text-indigo-500 font-semibold\">\n      Product Engineer\n    </div>\n    <h2 class=\"mt-1 text-xl font-semibold text-gray-900\">\n      John Doe\n    </h2>\n    <p class=\"mt-2 text-gray-500\">\n      Building amazing products with modern technology.\n    </p>\n    <button class=\"mt-4 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition\">\n      Contact\n    </button>\n  </div>\n</div>\n```\n\n## Navigation Bar\n\n```html\n<nav class=\"bg-white shadow-lg\">\n  <div class=\"container mx-auto px-4\">\n    <div class=\"flex justify-between items-center h-16\">\n      <div class=\"flex items-center\">\n        <a href=\"#\" class=\"text-xl font-bold text-gray-800\">Logo</a>\n      </div>\n      <div class=\"hidden md:flex space-x-8\">\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Home</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">About</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Services</a>\n        <a href=\"#\" class=\"text-gray-700 hover:text-blue-600 transition\">Contact</a>\n      </div>\n      <button class=\"md:hidden\">\n        <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n          <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path>\n        </svg>\n      </button>\n    </div>\n  </div>\n</nav>\n```\n\n## Form Elements\n\n```html\n<form class=\"space-y-6 max-w-md mx-auto\">\n  <div>\n    <label class=\"block text-sm font-medium text-gray-700 mb-2\">\n      Email\n    </label>\n    <input\n      type=\"email\"\n      class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n      placeholder=\"you@example.com\"\n    />\n  </div>\n\n  <div>\n    <label class=\"block text-sm font-medium text-gray-700 mb-2\">\n      Password\n    </label>\n    <input\n      type=\"password\"\n      class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n    />\n  </div>\n\n  <div class=\"flex items-center\">\n    <input type=\"checkbox\" class=\"mr-2\" />\n    <label class=\"text-sm text-gray-600\">Remember me</label>\n  </div>\n\n  <button\n    type=\"submit\"\n    class=\"w-full bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition\"\n  >\n    Sign In\n  </button>\n</form>\n```\n\n## Modal/Dialog\n\n```html\n<div class=\"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4\">\n  <div class=\"bg-white rounded-lg shadow-xl max-w-md w-full p-6\">\n    <div class=\"flex justify-between items-center mb-4\">\n      <h3 class=\"text-xl font-bold\">Modal Title</h3>\n      <button class=\"text-gray-500 hover:text-gray-700\">\n        <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n          <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n        </svg>\n      </button>\n    </div>\n    <p class=\"text-gray-700 mb-6\">\n      Modal content goes here.\n    </p>\n    <div class=\"flex justify-end space-x-4\">\n      <button class=\"px-4 py-2 text-gray-600 hover:text-gray-800\">\n        Cancel\n      </button>\n      <button class=\"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700\">\n        Confirm\n      </button>\n    </div>\n  </div>\n</div>\n```\n\n## React Button Component with Variants\n\n```tsx\nimport { useState } from 'react';\n\nfunction Button({\n  variant = 'primary',\n  size = 'md',\n  children\n}: {\n  variant?: 'primary' | 'secondary';\n  size?: 'sm' | 'md' | 'lg';\n  children: React.ReactNode;\n}) {\n  const baseClasses = 'font-semibold rounded transition';\n\n  const variantClasses = {\n    primary: 'bg-blue-600 text-white hover:bg-blue-700',\n    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',\n  };\n\n  const sizeClasses = {\n    sm: 'px-3 py-1 text-sm',\n    md: 'px-4 py-2 text-base',\n    lg: 'px-6 py-3 text-lg',\n  };\n\n  return (\n    <button\n      className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}\n    >\n      {children}\n    </button>\n  );\n}\n```\n","isInternal":false,"tokens":1397,"sizeBytes":4812},{"name":"configuration.md","path":".agents/skills/tailwind-css-patterns/references/configuration.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/references/configuration.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Tailwind CSS Configuration\n\n## CSS-First Configuration (v4.1+)\n\nUse the `@theme` directive for CSS-based configuration:\n\n```css\n/* src/styles.css */\n@import \"tailwindcss\";\n\n@theme {\n  /* Custom colors */\n  --color-brand-50: #f0f9ff;\n  --color-brand-500: #3b82f6;\n  --color-brand-900: #1e3a8a;\n\n  /* Custom fonts */\n  --font-display: \"Inter\", system-ui, sans-serif;\n  --font-mono: \"Fira Code\", monospace;\n\n  /* Custom spacing */\n  --spacing-128: 32rem;\n\n  /* Custom animations */\n  --animate-fade-in: fadeIn 0.5s ease-in-out;\n\n  /* Custom breakpoints */\n  --breakpoint-3xl: 1920px;\n}\n\n/* Define custom animations */\n@keyframes fadeIn {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n/* Custom utilities */\n@utility content-auto {\n  content-visibility: auto;\n}\n```\n\n---\n\n## JavaScript Configuration (Legacy)\n\n```javascript\n/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\n    \"./index.html\",\n    \"./src/**/*.{js,ts,jsx,tsx,vue,svelte}\",\n  ],\n  theme: {\n    extend: {\n      colors: {\n        primary: {\n          50: '#f0f9ff',\n          500: '#3b82f6',\n          900: '#1e3a8a',\n        },\n      },\n      fontFamily: {\n        sans: ['Inter', 'system-ui', 'sans-serif'],\n      },\n      spacing: {\n        '128': '32rem',\n      },\n    },\n  },\n  plugins: [],\n}\n```\n\n---\n\n## Vite Integration (v4.1+)\n\n```javascript\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n  plugins: [\n    tailwindcss(),\n  ],\n})\n```\n\n---\n\n## Advanced v4.1 Features\n\n### Native CSS Custom Properties\n\n```html\n<div class=\"bg-[var(--color-brand-500)] text-[var(--color-white)]\">\n  Using CSS custom properties directly\n</div>\n```\n\n### Enhanced Arbitrary Values\n\n```html\n<!-- Complex grid with custom tracks -->\n<div class=\"grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4\">\n  Responsive grid without custom CSS\n</div>\n\n<!-- Custom animation timing -->\n<div class=\"animate-bounce ease-[cubic-bezier(0.68,-0.55,0.265,1.55)]\">\n  Bounce with custom easing\n</div>\n```\n\n### Custom Utilities\n\n```css\n@utility content-auto {\n  content-visibility: auto;\n}\n\n@utility text-shadow {\n  text-shadow: 0 2px 4px rgba(0,0,0,0.1);\n}\n```\n\n---\n\n## Plugins\n\n### Custom Plugin Example\n\n```javascript\n// tailwind.config.js\nconst plugin = require('tailwindcss/plugin')\n\nmodule.exports = {\n  plugins: [\n    plugin(function({ addUtilities, addComponents, e, config }) {\n      // Add custom utilities\n      addUtilities({\n        '.content-auto': {\n          contentVisibility: 'auto',\n        },\n      })\n\n      // Add custom components\n      addComponents({\n        '.btn': {\n          padding: '.5rem 1rem',\n          borderRadius: '.25rem',\n          fontWeight: '600',\n        },\n      })\n    }),\n  ],\n}\n```\n\n---\n\n## Presets\n\n### Creating a Reusable Preset\n\n```javascript\n// tailwind-preset.js\nmodule.exports = {\n  theme: {\n    extend: {\n      colors: {\n        brand: {\n          DEFAULT: '#3b82f6',\n          light: '#60a5fa',\n          dark: '#1d4ed8',\n        },\n      },\n    },\n  },\n  plugins: [\n    require('@tailwindcss/forms'),\n    require('@tailwindcss/typography'),\n  ],\n}\n```\n\n### Using a Preset\n\n```javascript\n// tailwind.config.js\nmodule.exports = {\n  presets: [\n    require('./tailwind-preset.js'),\n  ],\n}\n```\n","isInternal":false,"tokens":916,"sizeBytes":3284},{"name":"layout-patterns.md","path":".agents/skills/tailwind-css-patterns/references/layout-patterns.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/references/layout-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Tailwind CSS Layout Patterns\n\n## Layout Utilities\n\n### Flexbox Layouts\n\nBasic flex container:\n\n```html\n<div class=\"flex items-center justify-between\">\n  <div>Left</div>\n  <div>Center</div>\n  <div>Right</div>\n</div>\n```\n\nResponsive flex direction:\n\n```html\n<div class=\"flex flex-col md:flex-row gap-4\">\n  <div class=\"flex-1\">Item 1</div>\n  <div class=\"flex-1\">Item 2</div>\n  <div class=\"flex-1\">Item 3</div>\n</div>\n```\n\nCommon flex patterns:\n\n```html\n<!-- Center content -->\n<div class=\"flex items-center justify-center min-h-screen\">\n  <div>Centered Content</div>\n</div>\n\n<!-- Space between items -->\n<div class=\"flex justify-between items-center\">\n  <span>Left</span>\n  <span>Right</span>\n</div>\n\n<!-- Vertical stack with gap -->\n<div class=\"flex flex-col gap-4\">\n  <div>Item 1</div>\n  <div>Item 2</div>\n</div>\n```\n\n### Grid Layouts\n\nBasic grid:\n\n```html\n<div class=\"grid grid-cols-3 gap-4\">\n  <div>Column 1</div>\n  <div>Column 2</div>\n  <div>Column 3</div>\n</div>\n```\n\nResponsive grid:\n\n```html\n<div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6\">\n  <!-- 1 column mobile, 2 tablet, 4 desktop -->\n  <div>Item 1</div>\n  <div>Item 2</div>\n  <div>Item 3</div>\n  <div>Item 4</div>\n</div>\n```\n\nAuto-fit columns:\n\n```html\n<div class=\"grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4\">\n  <!-- Automatically fit columns based on container width -->\n</div>\n```\n\n### Container & Max Width\n\nCentered container with max width:\n\n```html\n<div class=\"container mx-auto px-4 max-w-7xl\">\n  <!-- Centered content with padding -->\n</div>\n```\n\nResponsive max width:\n\n```html\n<div class=\"w-full max-w-md mx-auto\">\n  <!-- Max 448px width, centered -->\n</div>\n```\n\n---\n\n## Spacing\n\n### Padding & Margin\n\nUniform spacing:\n\n```html\n<div class=\"p-4\">Padding all sides</div>\n<div class=\"m-4\">Margin all sides</div>\n```\n\nIndividual sides:\n\n```html\n<div class=\"pt-4 pr-8 pb-4 pl-8\">\n  <!-- Top 1rem, Right 2rem, Bottom 1rem, Left 2rem -->\n</div>\n```\n\nAxis-based spacing:\n\n```html\n<div class=\"px-4 py-8\">\n  <!-- Horizontal padding 1rem, Vertical padding 2rem -->\n</div>\n```\n\nResponsive spacing:\n\n```html\n<div class=\"p-4 md:p-8 lg:p-12\">\n  <!-- Increases padding at larger breakpoints -->\n</div>\n```\n\nSpace between children:\n\n```html\n<div class=\"space-y-4\">\n  <div>Item 1</div>\n  <div>Item 2</div>\n  <div>Item 3</div>\n</div>\n```\n\n---\n\n## Typography\n\n### Font Size & Weight\n\n```html\n<h1 class=\"text-4xl font-bold\">Large Heading</h1>\n<h2 class=\"text-2xl font-semibold\">Subheading</h2>\n<p class=\"text-base font-normal\">Body text</p>\n<small class=\"text-sm text-gray-600\">Small text</small>\n```\n\nResponsive typography:\n\n```html\n<h1 class=\"text-2xl md:text-4xl lg:text-6xl font-bold\">\n  Responsive Heading\n</h1>\n```\n\n### Line Height & Letter Spacing\n\n```html\n<p class=\"leading-relaxed tracking-wide\">\n  Text with relaxed line height and wide letter spacing\n</p>\n```\n\n### Text Alignment\n\n```html\n<p class=\"text-left md:text-center\">\n  Left aligned on mobile, centered on tablet+\n</p>\n```\n\n---\n\n## Colors\n\n### Background Colors\n\n```html\n<div class=\"bg-blue-500\">Blue background</div>\n<div class=\"bg-gray-100\">Light gray background</div>\n<div class=\"bg-gradient-to-r from-blue-500 to-purple-600\">\n  Gradient background\n</div>\n```\n\n### Text Colors\n\n```html\n<p class=\"text-gray-900\">Dark text</p>\n<p class=\"text-blue-600\">Blue text</p>\n<p class=\"text-red-500\">Error text</p>\n```\n\n### Opacity\n\n```html\n<div class=\"bg-blue-500 bg-opacity-50\">\n  Semi-transparent blue\n</div>\n```\n\n---\n\n## Responsive Breakpoints Reference\n\n| Prefix | Min Width | CSS Equivalent |\n|--------|-----------|----------------|\n| `sm:` | 640px | `@media (min-width: 640px)` |\n| `md:` | 768px | `@media (min-width: 768px)` |\n| `lg:` | 1024px | `@media (min-width: 1024px)` |\n| `xl:` | 1280px | `@media (min-width: 1280px)` |\n| `2xl:` | 1536px | `@media (min-width: 1536px)` |\n","isInternal":false,"tokens":1181,"sizeBytes":3830},{"name":"performance.md","path":".agents/skills/tailwind-css-patterns/references/performance.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/references/performance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Tailwind CSS Performance Optimization\n\n## Bundle Size Optimization\n\nConfigure content sources for optimal purging:\n\n```javascript\n// tailwind.config.js\nexport default {\n  content: [\n    \"./index.html\",\n    \"./src/**/*.{js,ts,jsx,tsx,vue,svelte}\",\n    \"./node_modules/@mycompany/ui-lib/**/*.{js,ts,jsx,tsx}\",\n  ],\n  // Enable JIT for faster builds\n  jit: true,\n}\n```\n\n### Content Path Best Practices\n\n1. **Be specific**: Don't use `\"./**/*\"` - it scans too many files\n2. **Include UI libraries**: Add paths to component libraries\n3. **Exclude tests**: Don't include test files in content paths\n\n---\n\n## CSS Optimization Techniques\n\n```html\n<!-- Use content-visibility for offscreen content -->\n<div class=\"content-visibility-auto\">\n  <div>Heavy content that's initially offscreen</div>\n</div>\n\n<!-- Optimize images with aspect-ratio -->\n<img class=\"aspect-video w-full object-cover\" src=\"video.jpg\" alt=\"Video thumbnail\" />\n\n<!-- Use contain for paint optimization -->\n<div class=\"contain-layout\">\n  Complex layout that doesn't affect outside elements\n</div>\n```\n\n---\n\n## Development Performance (v4.1+)\n\n```css\n/* Enable CSS-first configuration in v4.1 */\n@import \"tailwindcss\";\n\n@theme {\n  /* Define once, use everywhere */\n  --color-brand: #3b82f6;\n  --font-mono: \"Fira Code\", monospace;\n}\n\n/* Critical CSS for above-the-fold content */\n@layer critical {\n  .hero-title {\n    @apply text-4xl md:text-6xl font-bold;\n  }\n}\n```\n\n---\n\n## Production Build Optimization\n\n### PurgeCSS Configuration\n\n```javascript\n// tailwind.config.js\nmodule.exports = {\n  purge: {\n    enabled: process.env.NODE_ENV === 'production',\n    content: [\n      './src/**/*.html',\n      './src/**/*.jsx',\n      './src/**/*.tsx',\n    ],\n    options: {\n      safelist: [\n        'bg-red-500',\n        'text-center',\n        // Classes that shouldn't be purged\n      ],\n    },\n  },\n}\n```\n\n### Minification\n\n```bash\n# Use cssnano for minification\nnpm install -D cssnano\n\n# In postcss.config.js\nmodule.exports = {\n  plugins: [\n    require('tailwindcss'),\n    require('autoprefixer'),\n    ...(process.env.NODE_ENV === 'production' ? [require('cssnano')] : []),\n  ],\n}\n```\n\n---\n\n## Best Practices for Performance\n\n1. **Use the JIT engine**: Faster builds, smaller output\n2. **Configure content correctly**: Only include files that use Tailwind\n3. **Minimize custom CSS**: Use Tailwind utilities over custom CSS\n4. **Enable purging in production**: Removes unused styles\n5. **Use `@layer` for custom styles**: Helps with organization and purging\n6. **Avoid `@apply` in components**: Prefer composing utilities in markup\n","isInternal":false,"tokens":641,"sizeBytes":2585},{"name":"reference.md","path":".agents/skills/tailwind-css-patterns/references/reference.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/references/reference.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Tailwind CSS Documentation\n\nTailwind CSS is a utility-first CSS framework that generates styles by scanning HTML, JavaScript, and template files for class names. It provides a comprehensive design system through CSS utility classes, enabling rapid UI development without writing custom CSS. The framework operates at build-time, analyzing source files and generating only the CSS classes actually used in the project, resulting in optimized production bundles with zero runtime overhead.\n\nThe framework includes an extensive default color palette (18 colors with 11 shades each), responsive breakpoint system, customizable design tokens via CSS custom properties, and support for dark mode, pseudo-classes, pseudo-elements, and media queries through variant prefixes. Tailwind CSS v4.1 introduces CSS-first configuration using the `@theme` directive, native support for custom utilities via `@utility`, seamless integration with modern build tools through Vite, PostCSS, and framework-specific plugins, and enhanced arbitrary value syntax for maximum flexibility.\n\n## Installation with Vite\n\nInstalling Tailwind CSS using the Vite plugin for modern JavaScript frameworks.\n\n```bash\n# Create a new Vite project\nnpm create vite@latest my-project\ncd my-project\n\n# Install Tailwind CSS and Vite plugin\nnpm install tailwindcss @tailwindcss/vite\n```\n\n```javascript\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n plugins: [\n tailwindcss(),\n ],\n})\n```\n\n```css\n/* src/style.css */\n@import \"tailwindcss\";\n```\n\n```html\n\n\n\n# Hello world!\n\n```\n\n## Utility Classes with Variants\n\nApplying conditional styles using variant prefixes for hover, focus, and responsive breakpoints.\n\n```html\n\n Save changes\n\nContent adapts to color scheme preference\n\n Submit\n\n```\n\n## Custom Theme Configuration\n\nDefining custom design tokens using the `@theme` directive in CSS.\n\n```css\n/* app.css */\n@import \"tailwindcss\";\n\n@theme {\n /* Custom fonts */\n --font-display: \"Satoshi\", \"sans-serif\";\n --font-body: \"Inter\", system-ui, sans-serif;\n\n /* Custom colors */\n --color-brand-50: oklch(0.98 0.02 264);\n --color-brand-100: oklch(0.95 0.05 264);\n --color-brand-500: oklch(0.55 0.22 264);\n --color-brand-900: oklch(0.25 0.12 264);\n\n /* Custom breakpoints */\n --breakpoint-3xl: 120rem;\n --breakpoint-4xl: 160rem;\n\n /* Custom spacing */\n --spacing-18: calc(var(--spacing) * 18);\n\n /* Custom animations */\n --ease-fluid: cubic-bezier(0.3, 0, 0, 1);\n --ease-snappy: cubic-bezier(0.2, 0, 0, 1);\n}\n```\n\n```html\n\nCustom design system\n\n```\n\n## Arbitrary Values\n\nUsing square bracket notation for one-off custom values without leaving HTML.\n\n```html\n\nPixel-perfect positioning\n\nCustom hex colors, font sizes, and content\n\nAny CSS property\n\nReference custom properties\n\nComplex grid layouts\n\nFont size from CSS variable\n\nColor from CSS variable\n\n```\n\n## Color System\n\nWorking with Tailwind's comprehensive color palette and opacity modifiers.\n\n```html\n\nColor utilities across all properties\n\nAlpha channel with percentage\n\nArbitrary opacity values\n\nOpacity from CSS variable\n\nAdapts to color scheme\n\n\n```\n\n## Dark Mode\n\nImplementing dark mode with CSS media queries or manual toggle.\n\n```html\n\nContent automatically adapts\n\n\n```\n\n```css\n/* Manual dark mode toggle with class selector */\n@import \"tailwindcss\";\n\n@custom-variant dark (&:where(.dark, .dark *));\n```\n\n```html\n\nControlled by .dark class\n\n\n\n\n```\n\n```javascript\n// Dark mode toggle logic\n// On page load or theme change\ndocument.documentElement.classList.toggle(\n \"dark\",\n localStorage.theme === \"dark\" ||\n (!(\"theme\" in localStorage) && window.matchMedia(\"(prefers-color-scheme: dark)\").matches)\n);\n\n// User chooses light mode\nlocalStorage.theme = \"light\";\n\n// User chooses dark mode\nlocalStorage.theme = \"dark\";\n\n// User chooses system preference\nlocalStorage.removeItem(\"theme\");\n```\n\n## State Variants\n\nStyling elements based on pseudo-classes and parent/sibling state.\n\n```html\n\n- Item content\n\n\n**Title**\nDescription\n\nPlease provide a valid email address.\n\n\n Option\n\n```\n\n## Responsive Design\n\nBuilding mobile-first responsive layouts with breakpoint variants.\n\n```html\n\n# Responsive heading\n\nText scales with viewport\n\n\nDesktop only\n\nMobile only\n\nCustom breakpoint\n\nBelow medium\n\n```\n\n## Custom Utilities\n\nCreating reusable custom utility classes with variant support.\n\n```css\n/* Simple custom utility */\n@utility content-auto {\n content-visibility: auto;\n}\n\n/* Complex utility with nesting */\n@utility scrollbar-hidden {\n &::-webkit-scrollbar {\n display: none;\n }\n}\n\n/* Functional utility with theme values */\n@theme {\n --tab-size-2: 2;\n --tab-size-4: 4;\n --tab-size-github: 8;\n}\n\n@utility tab-* {\n tab-size: --value(--tab-size-*);\n}\n\n/* Supporting arbitrary, bare, and theme values */\n@utility opacity-* {\n opacity: --value([percentage]);\n opacity: calc(--value(integer) * 1%);\n opacity: --value(--opacity-*);\n}\n\n/* Utility with modifiers */\n@utility text-* {\n font-size: --value(--text-*, [length]);\n line-height: --modifier(--leading-*, [length], [*]);\n}\n\n/* Negative value support */\n@utility inset-* {\n inset: --spacing(--value(integer));\n inset: --value([percentage], [length]);\n}\n\n@utility -inset-* {\n inset: --spacing(--value(integer) * -1);\n inset: calc(--value([percentage], [length]) * -1);\n}\n```\n\n```html\n\nCustom utilities work with variants\n\nVariants and arbitrary values supported\n\nUtility with modifier (font-size/line-height)\n\n```\n\n## Custom Variants\n\nRegistering custom conditional styles with the `@custom-variant` directive.\n\n```css\n/* Simple custom variant */\n@custom-variant theme-midnight (&:where([data-theme=\"midnight\"] *));\n\n/* Variant with media query */\n@custom-variant any-hover {\n @media (any-hover: hover) {\n &:hover {\n @slot;\n }\n }\n}\n\n/* ARIA state variant */\n@custom-variant aria-asc (&[aria-sort=\"ascending\"]);\n@custom-variant aria-desc (&[aria-sort=\"descending\"]);\n\n/* Data attribute variant */\n@custom-variant data-checked (&[data-ui~=\"checked\"]);\n```\n\n```html\n\n Midnight theme button\n\n\n Sortable column\n\nChecked state\n\nOne-off custom selectors\n\n```\n\n## Applying Variants in CSS\n\nUsing the `@variant` directive to apply variants within custom CSS.\n\n```css\n/* Single variant */\n.my-element {\n background: white;\n\n @variant dark {\n background: black;\n }\n}\n\n/* Nested variants */\n.my-button {\n background: white;\n\n @variant dark {\n background: gray;\n\n @variant hover {\n background: black;\n }\n }\n}\n\n/* Compiled output */\n.my-element {\n background: white;\n}\n\n@media (prefers-color-scheme: dark) {\n .my-element {\n background: black;\n }\n}\n```\n\n## Layer Organization\n\nOrganizing custom styles into Tailwind's cascade layers.\n\n```css\n@import \"tailwindcss\";\n\n/* Base styles for HTML elements */\n@layer base {\n h1 {\n font-size: var(--text-2xl);\n font-weight: bold;\n }\n\n h2 {\n font-size: var(--text-xl);\n font-weight: 600;\n }\n\n body {\n font-family: var(--font-body);\n }\n}\n\n/* Reusable component classes */\n@layer components {\n .btn {\n padding: --spacing(2) --spacing(4);\n border-radius: var(--radius);\n font-weight: 600;\n transition: all 150ms;\n }\n\n .btn-primary {\n background-color: var(--color-blue-500);\n color: white;\n }\n\n .card {\n background-color: var(--color-white);\n border-radius: var(--radius-lg);\n padding: --spacing(6);\n box-shadow: var(--shadow-xl);\n }\n\n /* Third-party component overrides */\n .select2-dropdown {\n border-radius: var(--radius);\n box-shadow: var(--shadow-lg);\n }\n}\n```\n\n```html\n\nSquare corners despite card class\n\n Component with utility overrides\n\n```\n\n## Functions and Directives\n\nUsing Tailwind's CSS functions for dynamic values and opacity adjustments.\n\n```css\n/* Alpha function for opacity */\n.my-element {\n color: --alpha(var(--color-lime-300) / 50%);\n background: --alpha(var(--color-blue-500) / 25%);\n}\n\n/* Spacing function */\n.my-element {\n margin: --spacing(4);\n padding: calc(--spacing(6) - 1px);\n}\n\n/* In arbitrary values */\n\n/* Source directive for additional content */\n@source \"../node_modules/@my-company/ui-lib\";\n\n/* Apply directive for inline utilities */\n.select2-dropdown {\n @apply rounded-b-lg shadow-md;\n}\n\n.select2-search {\n @apply rounded border border-gray-300;\n}\n\n.select2-results__group {\n @apply text-lg font-bold text-gray-900;\n}\n```\n\n## Pseudo-elements\n\nStyling ::before, ::after, ::placeholder, and other pseudo-elements.\n\n```html\n\n Email\n\n\n- First item\n- Second item\n\nSelect this text to see custom colors\n\nTypography with pseudo-elements\n\n```\n\n## Media Queries\n\nConditional styling based on user preferences and device capabilities.\n\n```html\n\n Respects user preference\n\n Only animates if motion allowed\n\n Adjusts for contrast needs\n\n\nHidden in portrait mode\n\nLayout adapts to orientation\n\n Not shown when printing\n\nOnly visible in print\n\nProgressive enhancement\n\n```\n\n## Summary\n\nTailwind CSS provides a complete utility-first design system that eliminates the need for writing custom CSS in most cases. The framework's primary use cases include rapid prototyping, building production applications with consistent design systems, creating responsive layouts, implementing dark mode, and maintaining design consistency across large teams. By using utility classes directly in markup, developers can iterate quickly, avoid naming conventions, and prevent CSS bloat since only used styles are generated.\n\nThe v4.1 release enhances the developer experience with CSS-first configuration, eliminating JavaScript configuration files for most projects. Integration patterns include using the Vite plugin for modern frameworks, PostCSS for custom build pipelines, the Tailwind CLI for simple projects, and CDN scripts for rapid prototyping. The framework excels at component-driven development when combined with React, Vue, Svelte, or other modern frameworks, where utility classes are co-located with component logic. Custom design systems can be fully defined in CSS using `@theme`, with project-specific utilities and variants extending the framework's capabilities without writing JavaScript plugins.","isInternal":false,"tokens":2245,"sizeBytes":9988},{"name":"responsive-design.md","path":".agents/skills/tailwind-css-patterns/references/responsive-design.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/references/responsive-design.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Tailwind CSS Responsive Design & Dark Mode\n\n## Responsive Design Patterns\n\n### Mobile-First Responsive Layout\n\n```html\n<div class=\"container mx-auto px-4\">\n  <!-- Hero Section -->\n  <div class=\"flex flex-col md:flex-row items-center gap-8 py-12\">\n    <div class=\"flex-1\">\n      <h1 class=\"text-3xl md:text-5xl font-bold mb-4\">\n        Welcome to Our Site\n      </h1>\n      <p class=\"text-lg text-gray-600 mb-6\">\n        Build amazing things with Tailwind CSS\n      </p>\n      <button class=\"bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700\">\n        Get Started\n      </button>\n    </div>\n    <div class=\"flex-1\">\n      <img src=\"hero.jpg\" class=\"w-full rounded-lg shadow-lg\" />\n    </div>\n  </div>\n</div>\n```\n\n### Responsive Grid Gallery\n\n```html\n<div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4\">\n  <div class=\"aspect-square bg-gray-200 rounded-lg overflow-hidden\">\n    <img src=\"image1.jpg\" class=\"w-full h-full object-cover hover:scale-105 transition\" />\n  </div>\n  <div class=\"aspect-square bg-gray-200 rounded-lg overflow-hidden\">\n    <img src=\"image2.jpg\" class=\"w-full h-full object-cover hover:scale-105 transition\" />\n  </div>\n  <!-- More items... -->\n</div>\n```\n\n### Responsive Card Component\n\n```tsx\nfunction ProductCard({ product }: { product: Product }) {\n  return (\n    <div className=\"bg-white rounded-lg shadow-lg overflow-hidden\n                    sm:flex sm:max-w-2xl\">\n      <img\n        className=\"h-48 w-full object-cover sm:h-auto sm:w-48\"\n        src={product.image}\n        alt={product.name}\n      />\n      <div className=\"p-6\">\n        <h3 className=\"text-lg font-semibold text-gray-900\">\n          {product.name}\n        </h3>\n        <p className=\"mt-2 text-gray-600\">\n          {product.description}\n        </p>\n        <button className=\"mt-4 px-4 py-2 bg-indigo-600 text-white\n                          rounded-lg hover:bg-indigo-700 transition\">\n          Add to Cart\n        </button>\n      </div>\n    </div>\n  );\n}\n```\n\n---\n\n## Dark Mode\n\n### Basic Dark Mode Support\n\n```html\n<div class=\"bg-white dark:bg-gray-900 text-gray-900 dark:text-white\">\n  <h1 class=\"text-gray-900 dark:text-white\">Title</h1>\n  <p class=\"text-gray-600 dark:text-gray-400\">Description</p>\n</div>\n```\n\nEnable dark mode in tailwind.config.js:\n\n```javascript\nmodule.exports = {\n  darkMode: 'class', // or 'media'\n  // ...\n}\n```\n\n### Dark Mode Toggle (React)\n\n```tsx\nfunction ThemeToggle() {\n  const [darkMode, setDarkMode] = useState(false);\n\n  useEffect(() => {\n    if (darkMode) {\n      document.documentElement.classList.add('dark');\n    } else {\n      document.documentElement.classList.remove('dark');\n    }\n  }, [darkMode]);\n\n  return (\n    <button\n      onClick={() => setDarkMode(!darkMode)}\n      className=\"p-2 rounded-lg bg-gray-200 dark:bg-gray-800\"\n    >\n      {darkMode ? '🌙' : '☀️'}\n    </button>\n  );\n}\n```\n\n### Dark Mode Best Practices\n\n1. **Use semantic color names**: Instead of `bg-white` use `bg-surface` custom color\n2. **Test with real content**: Some colors look good in light but not in dark\n3. **Respect system preference**: Use `darkMode: 'media'` for OS-level preference\n4. **Smooth transitions**: Add transition utilities for theme changes\n\n```css\n/* Global transition for theme changes */\n* {\n  @apply transition-colors duration-200;\n}\n```\n\n---\n\n## Container Queries (v4.1+)\n\nComponent that responds to its container size, not viewport:\n\n```html\n<div class=\"@container\">\n  <div class=\"@lg:text-xl @2xl:text-2xl\">\n    Text size based on container, not viewport\n  </div>\n</div>\n```\n\nUsage in a card component:\n\n```html\n<div class=\"@container w-full\">\n  <div class=\"flex flex-col @[400px]:flex-row gap-4\">\n    <img class=\"w-full @[400px]:w-32 h-32 object-cover\" src=\"image.jpg\" />\n    <div>\n      <h3 class=\"text-base @[400px]:text-lg font-bold\">Title</h3>\n      <p class=\"text-sm @[400px]:text-base\">Description</p>\n    </div>\n  </div>\n</div>\n```\n","isInternal":false,"tokens":1073,"sizeBytes":3940},{"name":"SKILL.md","path":".agents/skills/tailwind-css-patterns/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/tailwind-css-patterns/SKILL.md","title":"tailwind-css-patterns","category":"anthropic-skill","format":"markdown","content":"---\nname: tailwind-css-patterns\ndescription: Provides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or optimizing CSS workflow.\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash\n---\n\n# Tailwind CSS Development Patterns\n\nExpert guide for building modern, responsive user interfaces with Tailwind CSS utility-first framework. Covers v4.1+ features including CSS-first configuration, custom utilities, and enhanced developer experience.\n\n## Overview\n\nProvides actionable patterns for responsive, accessible UIs with Tailwind CSS v4.1+. Covers utility composition, dark mode, component patterns, and performance optimization.\n\n## When to Use\n\n- Styling React/Vue/Svelte components\n- Building responsive layouts and grids\n- Implementing design systems\n- Adding dark mode support\n- Optimizing CSS workflow\n\n## Quick Reference\n\n### Responsive Breakpoints\n\n| Prefix | Min Width | Description |\n|--------|-----------|-------------|\n| `sm:` | 640px | Small screens |\n| `md:` | 768px | Tablets |\n| `lg:` | 1024px | Desktops |\n| `xl:` | 1280px | Large screens |\n| `2xl:` | 1536px | Extra large |\n\n### Common Patterns\n\n```html\n<!-- Center content -->\n<div class=\"flex items-center justify-center min-h-screen\">\n  Content\n</div>\n\n<!-- Responsive grid -->\n<div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n  <!-- Items -->\n</div>\n\n<!-- Card component -->\n<div class=\"bg-white rounded-lg shadow-lg p-6\">\n  <h3 class=\"text-xl font-bold\">Title</h3>\n  <p class=\"text-gray-600\">Description</p>\n</div>\n```\n\n## Instructions\n\n1. **Start Mobile-First**: Write base styles for mobile, add responsive prefixes (`sm:`, `md:`, `lg:`) for larger screens\n2. **Use Design Tokens**: Leverage Tailwind's spacing, color, and typography scales\n3. **Compose Utilities**: Combine multiple utilities for complex styles\n4. **Extract Components**: Create reusable component classes for repeated patterns\n5. **Configure Theme**: Customize design tokens in `tailwind.config.js` or using `@theme`\n6. **Verify Changes**: Test at each breakpoint using DevTools responsive mode. Check for visual regressions and accessibility issues before committing.\n\n## Examples\n\n### Responsive Card Component\n\n```tsx\nfunction ProductCard({ product }: { product: Product }) {\n  return (\n    <div className=\"bg-white rounded-lg shadow-lg overflow-hidden sm:flex\">\n      <img className=\"h-48 w-full object-cover sm:h-auto sm:w-48\" src={product.image} />\n      <div className=\"p-6\">\n        <h3 className=\"text-lg font-semibold\">{product.name}</h3>\n        <button className=\"mt-4 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700\">\n          Add to Cart\n        </button>\n      </div>\n    </div>\n  );\n}\n```\n\n### Dark Mode Toggle\n\n```html\n<div class=\"bg-white dark:bg-gray-900 text-gray-900 dark:text-white\">\n  <h1 class=\"dark:text-white\">Title</h1>\n</div>\n```\n\n### Form Input\n\n```html\n<input\n  class=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n  placeholder=\"you@example.com\"\n/>\n```\n\n## Best Practices\n\n1. **Consistent Spacing**: Use Tailwind's spacing scale (4, 8, 12, 16, etc.)\n2. **Color Palette**: Stick to Tailwind's color system for consistency\n3. **Component Extraction**: Extract repeated patterns into reusable components\n4. **Utility Composition**: Prefer utility classes over `@apply` for maintainability\n5. **Semantic HTML**: Use proper HTML elements with Tailwind classes\n6. **Performance**: Ensure content paths include all template files for optimal purging\n7. **Accessibility**: Include focus styles, ARIA labels, and respect user preferences (reduced-motion)\n\n## Troubleshooting\n\n### Classes Not Applying\n- **Check content paths**: Ensure all template files are included in `content: []` in config\n- **Verify build**: Run `npm run build` to regenerate purged CSS\n- **Dev mode**: Use `npx tailwindcss -o` with `--watch` flag for live updates\n\n### Responsive Styles Not Working\n- **Order matters**: Responsive prefixes must come before non-responsive (e.g., `md:flex` not `flex md:flex`)\n- **Check breakpoint values**: Verify breakpoints match your design requirements\n- **DevTools**: Use browser DevTools responsive mode to test at each breakpoint\n\n### Dark Mode Issues\n- **Verify config**: Ensure `darkMode: 'class'` or `'media'` is set correctly\n- **Toggle implementation**: Use `document.documentElement.classList.toggle('dark')` for class strategy\n- **Initial flash**: Add `dark` class to `<html>` before body renders\n\n## Constraints and Warnings\n\n- **Class Proliferation**: Long class strings reduce readability; extract into components\n- **Content Paths**: Misconfigured paths cause classes to be purged in production\n- **Arbitrary Values**: Use sparingly; prefer design tokens for consistency\n- **Specificity Issues**: Avoid `@apply` with complex selectors\n- **Dark Mode**: Requires correct configuration (`class` or `media` strategy)\n- **Browser Support**: Check Tailwind docs for compatibility notes\n\n## References\n\n- **[references/layout-patterns.md](references/layout-patterns.md)** — Flexbox, grid, spacing, typography, colors\n- **[references/component-patterns.md](references/component-patterns.md)** — Cards, navigation, forms, modals, React patterns\n- **[references/responsive-design.md](references/responsive-design.md)** — Responsive patterns, dark mode, container queries\n- **[references/animations.md](references/animations.md)** — Transitions, transforms, built-in animations, motion preferences\n- **[references/performance.md](references/performance.md)** — Bundle optimization, CSS optimization, production builds\n- **[references/accessibility.md](references/accessibility.md)** — Focus management, screen readers, color contrast, ARIA\n- **[references/configuration.md](references/configuration.md)** — CSS-first config, JavaScript config, plugins, presets\n- **[references/reference.md](references/reference.md)** — Additional reference materials\n\n## External Resources\n\n- [Tailwind CSS Docs](https://tailwindcss.com/docs)\n- [Tailwind UI](https://tailwindui.com)\n- [Tailwind Play](https://play.tailwindcss.com)\n","frontmatter":{"name":"tailwind-css-patterns","description":"Provides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or optimizing CSS workflow.","allowed-tools":"Read, Write, Edit, Glob, Grep, Bash","allowedTools":["Read,","Write,","Edit,","Glob,","Grep,","Bash"]},"allowedTools":["Read,","Write,","Edit,","Glob,","Grep,","Bash"],"isInternal":false,"tokens":1458,"sizeBytes":6342},{"name":"SKILL.md","path":".agents/skills/typescript-advanced-types/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/typescript-advanced-types/SKILL.md","title":"typescript-advanced-types","category":"anthropic-skill","format":"markdown","content":"---\nname: typescript-advanced-types\ndescription: Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.\n---\n\n# TypeScript Advanced Types\n\nComprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.\n\n## When to Use This Skill\n\n- Building type-safe libraries or frameworks\n- Creating reusable generic components\n- Implementing complex type inference logic\n- Designing type-safe API clients\n- Building form validation systems\n- Creating strongly-typed configuration objects\n- Implementing type-safe state management\n- Migrating JavaScript codebases to TypeScript\n\n## Core Concepts\n\n### 1. Generics\n\n**Purpose:** Create reusable, type-flexible components while maintaining type safety.\n\n**Basic Generic Function:**\n\n```typescript\nfunction identity<T>(value: T): T {\n  return value;\n}\n\nconst num = identity<number>(42); // Type: number\nconst str = identity<string>(\"hello\"); // Type: string\nconst auto = identity(true); // Type inferred: boolean\n```\n\n**Generic Constraints:**\n\n```typescript\ninterface HasLength {\n  length: number;\n}\n\nfunction logLength<T extends HasLength>(item: T): T {\n  console.log(item.length);\n  return item;\n}\n\nlogLength(\"hello\"); // OK: string has length\nlogLength([1, 2, 3]); // OK: array has length\nlogLength({ length: 10 }); // OK: object has length\n// logLength(42);             // Error: number has no length\n```\n\n**Multiple Type Parameters:**\n\n```typescript\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n\nconst merged = merge({ name: \"John\" }, { age: 30 });\n// Type: { name: string } & { age: number }\n```\n\n### 2. Conditional Types\n\n**Purpose:** Create types that depend on conditions, enabling sophisticated type logic.\n\n**Basic Conditional Type:**\n\n```typescript\ntype IsString<T> = T extends string ? true : false;\n\ntype A = IsString<string>; // true\ntype B = IsString<number>; // false\n```\n\n**Extracting Return Types:**\n\n```typescript\ntype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;\n\nfunction getUser() {\n  return { id: 1, name: \"John\" };\n}\n\ntype User = ReturnType<typeof getUser>;\n// Type: { id: number; name: string; }\n```\n\n**Distributive Conditional Types:**\n\n```typescript\ntype ToArray<T> = T extends any ? T[] : never;\n\ntype StrOrNumArray = ToArray<string | number>;\n// Type: string[] | number[]\n```\n\n**Nested Conditions:**\n\n```typescript\ntype TypeName<T> = T extends string\n  ? \"string\"\n  : T extends number\n    ? \"number\"\n    : T extends boolean\n      ? \"boolean\"\n      : T extends undefined\n        ? \"undefined\"\n        : T extends Function\n          ? \"function\"\n          : \"object\";\n\ntype T1 = TypeName<string>; // \"string\"\ntype T2 = TypeName<() => void>; // \"function\"\n```\n\n### 3. Mapped Types\n\n**Purpose:** Transform existing types by iterating over their properties.\n\n**Basic Mapped Type:**\n\n```typescript\ntype Readonly<T> = {\n  readonly [P in keyof T]: T[P];\n};\n\ninterface User {\n  id: number;\n  name: string;\n}\n\ntype ReadonlyUser = Readonly<User>;\n// Type: { readonly id: number; readonly name: string; }\n```\n\n**Optional Properties:**\n\n```typescript\ntype Partial<T> = {\n  [P in keyof T]?: T[P];\n};\n\ntype PartialUser = Partial<User>;\n// Type: { id?: number; name?: string; }\n```\n\n**Key Remapping:**\n\n```typescript\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\ninterface Person {\n  name: string;\n  age: number;\n}\n\ntype PersonGetters = Getters<Person>;\n// Type: { getName: () => string; getAge: () => number; }\n```\n\n**Filtering Properties:**\n\n```typescript\ntype PickByType<T, U> = {\n  [K in keyof T as T[K] extends U ? K : never]: T[K];\n};\n\ninterface Mixed {\n  id: number;\n  name: string;\n  age: number;\n  active: boolean;\n}\n\ntype OnlyNumbers = PickByType<Mixed, number>;\n// Type: { id: number; age: number; }\n```\n\n### 4. Template Literal Types\n\n**Purpose:** Create string-based types with pattern matching and transformation.\n\n**Basic Template Literal:**\n\n```typescript\ntype EventName = \"click\" | \"focus\" | \"blur\";\ntype EventHandler = `on${Capitalize<EventName>}`;\n// Type: \"onClick\" | \"onFocus\" | \"onBlur\"\n```\n\n**String Manipulation:**\n\n```typescript\ntype UppercaseGreeting = Uppercase<\"hello\">; // \"HELLO\"\ntype LowercaseGreeting = Lowercase<\"HELLO\">; // \"hello\"\ntype CapitalizedName = Capitalize<\"john\">; // \"John\"\ntype UncapitalizedName = Uncapitalize<\"John\">; // \"john\"\n```\n\n**Path Building:**\n\n```typescript\ntype Path<T> = T extends object\n  ? {\n      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;\n    }[keyof T]\n  : never;\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n  };\n  database: {\n    url: string;\n  };\n}\n\ntype ConfigPath = Path<Config>;\n// Type: \"server\" | \"database\" | \"server.host\" | \"server.port\" | \"database.url\"\n```\n\n### 5. Utility Types\n\n**Built-in Utility Types:**\n\n```typescript\n// Partial<T> - Make all properties optional\ntype PartialUser = Partial<User>;\n\n// Required<T> - Make all properties required\ntype RequiredUser = Required<PartialUser>;\n\n// Readonly<T> - Make all properties readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick<T, K> - Select specific properties\ntype UserName = Pick<User, \"name\" | \"email\">;\n\n// Omit<T, K> - Remove specific properties\ntype UserWithoutPassword = Omit<User, \"password\">;\n\n// Exclude<T, U> - Exclude types from union\ntype T1 = Exclude<\"a\" | \"b\" | \"c\", \"a\">; // \"b\" | \"c\"\n\n// Extract<T, U> - Extract types from union\ntype T2 = Extract<\"a\" | \"b\" | \"c\", \"a\" | \"b\">; // \"a\" | \"b\"\n\n// NonNullable<T> - Exclude null and undefined\ntype T3 = NonNullable<string | null | undefined>; // string\n\n// Record<K, T> - Create object type with keys K and values T\ntype PageInfo = Record<\"home\" | \"about\", { title: string }>;\n```\n\n## Advanced Patterns\n\n### Pattern 1: Type-Safe Event Emitter\n\n```typescript\ntype EventMap = {\n  \"user:created\": { id: string; name: string };\n  \"user:updated\": { id: string };\n  \"user:deleted\": { id: string };\n};\n\nclass TypedEventEmitter<T extends Record<string, any>> {\n  private listeners: {\n    [K in keyof T]?: Array<(data: T[K]) => void>;\n  } = {};\n\n  on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {\n    if (!this.listeners[event]) {\n      this.listeners[event] = [];\n    }\n    this.listeners[event]!.push(callback);\n  }\n\n  emit<K extends keyof T>(event: K, data: T[K]): void {\n    const callbacks = this.listeners[event];\n    if (callbacks) {\n      callbacks.forEach((callback) => callback(data));\n    }\n  }\n}\n\nconst emitter = new TypedEventEmitter<EventMap>();\n\nemitter.on(\"user:created\", (data) => {\n  console.log(data.id, data.name); // Type-safe!\n});\n\nemitter.emit(\"user:created\", { id: \"1\", name: \"John\" });\n// emitter.emit(\"user:created\", { id: \"1\" });  // Error: missing 'name'\n```\n\n### Pattern 2: Type-Safe API Client\n\n```typescript\ntype HTTPMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\ntype EndpointConfig = {\n  \"/users\": {\n    GET: { response: User[] };\n    POST: { body: { name: string; email: string }; response: User };\n  };\n  \"/users/:id\": {\n    GET: { params: { id: string }; response: User };\n    PUT: { params: { id: string }; body: Partial<User>; response: User };\n    DELETE: { params: { id: string }; response: void };\n  };\n};\n\ntype ExtractParams<T> = T extends { params: infer P } ? P : never;\ntype ExtractBody<T> = T extends { body: infer B } ? B : never;\ntype ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\nclass APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {\n  async request<Path extends keyof Config, Method extends keyof Config[Path]>(\n    path: Path,\n    method: Method,\n    ...[options]: ExtractParams<Config[Path][Method]> extends never\n      ? ExtractBody<Config[Path][Method]> extends never\n        ? []\n        : [{ body: ExtractBody<Config[Path][Method]> }]\n      : [\n          {\n            params: ExtractParams<Config[Path][Method]>;\n            body?: ExtractBody<Config[Path][Method]>;\n          },\n        ]\n  ): Promise<ExtractResponse<Config[Path][Method]>> {\n    // Implementation here\n    return {} as any;\n  }\n}\n\nconst api = new APIClient<EndpointConfig>();\n\n// Type-safe API calls\nconst users = await api.request(\"/users\", \"GET\");\n// Type: User[]\n\nconst newUser = await api.request(\"/users\", \"POST\", {\n  body: { name: \"John\", email: \"john@example.com\" },\n});\n// Type: User\n\nconst user = await api.request(\"/users/:id\", \"GET\", {\n  params: { id: \"123\" },\n});\n// Type: User\n```\n\n### Pattern 3: Builder Pattern with Type Safety\n\n```typescript\ntype BuilderState<T> = {\n  [K in keyof T]: T[K] | undefined;\n};\n\ntype RequiredKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\ntype OptionalKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\n\ntype IsComplete<T, S> =\n  RequiredKeys<T> extends keyof S\n    ? S[RequiredKeys<T>] extends undefined\n      ? false\n      : true\n    : false;\n\nclass Builder<T, S extends BuilderState<T> = {}> {\n  private state: S = {} as S;\n\n  set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {\n    this.state[key] = value;\n    return this as any;\n  }\n\n  build(this: IsComplete<T, S> extends true ? this : never): T {\n    return this.state as T;\n  }\n}\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age?: number;\n}\n\nconst builder = new Builder<User>();\n\nconst user = builder\n  .set(\"id\", \"1\")\n  .set(\"name\", \"John\")\n  .set(\"email\", \"john@example.com\")\n  .build(); // OK: all required fields set\n\n// const incomplete = builder\n//   .set(\"id\", \"1\")\n//   .build();  // Error: missing required fields\n```\n\n### Pattern 4: Deep Readonly/Partial\n\n```typescript\ntype DeepReadonly<T> = {\n  readonly [P in keyof T]: T[P] extends object\n    ? T[P] extends Function\n      ? T[P]\n      : DeepReadonly<T[P]>\n    : T[P];\n};\n\ntype DeepPartial<T> = {\n  [P in keyof T]?: T[P] extends object\n    ? T[P] extends Array<infer U>\n      ? Array<DeepPartial<U>>\n      : DeepPartial<T[P]>\n    : T[P];\n};\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n    ssl: {\n      enabled: boolean;\n      cert: string;\n    };\n  };\n  database: {\n    url: string;\n    pool: {\n      min: number;\n      max: number;\n    };\n  };\n}\n\ntype ReadonlyConfig = DeepReadonly<Config>;\n// All nested properties are readonly\n\ntype PartialConfig = DeepPartial<Config>;\n// All nested properties are optional\n```\n\n### Pattern 5: Type-Safe Form Validation\n\n```typescript\ntype ValidationRule<T> = {\n  validate: (value: T) => boolean;\n  message: string;\n};\n\ntype FieldValidation<T> = {\n  [K in keyof T]?: ValidationRule<T[K]>[];\n};\n\ntype ValidationErrors<T> = {\n  [K in keyof T]?: string[];\n};\n\nclass FormValidator<T extends Record<string, any>> {\n  constructor(private rules: FieldValidation<T>) {}\n\n  validate(data: T): ValidationErrors<T> | null {\n    const errors: ValidationErrors<T> = {};\n    let hasErrors = false;\n\n    for (const key in this.rules) {\n      const fieldRules = this.rules[key];\n      const value = data[key];\n\n      if (fieldRules) {\n        const fieldErrors: string[] = [];\n\n        for (const rule of fieldRules) {\n          if (!rule.validate(value)) {\n            fieldErrors.push(rule.message);\n          }\n        }\n\n        if (fieldErrors.length > 0) {\n          errors[key] = fieldErrors;\n          hasErrors = true;\n        }\n      }\n    }\n\n    return hasErrors ? errors : null;\n  }\n}\n\ninterface LoginForm {\n  email: string;\n  password: string;\n}\n\nconst validator = new FormValidator<LoginForm>({\n  email: [\n    {\n      validate: (v) => v.includes(\"@\"),\n      message: \"Email must contain @\",\n    },\n    {\n      validate: (v) => v.length > 0,\n      message: \"Email is required\",\n    },\n  ],\n  password: [\n    {\n      validate: (v) => v.length >= 8,\n      message: \"Password must be at least 8 characters\",\n    },\n  ],\n});\n\nconst errors = validator.validate({\n  email: \"invalid\",\n  password: \"short\",\n});\n// Type: { email?: string[]; password?: string[]; } | null\n```\n\n### Pattern 6: Discriminated Unions\n\n```typescript\ntype Success<T> = {\n  status: \"success\";\n  data: T;\n};\n\ntype Error = {\n  status: \"error\";\n  error: string;\n};\n\ntype Loading = {\n  status: \"loading\";\n};\n\ntype AsyncState<T> = Success<T> | Error | Loading;\n\nfunction handleState<T>(state: AsyncState<T>): void {\n  switch (state.status) {\n    case \"success\":\n      console.log(state.data); // Type: T\n      break;\n    case \"error\":\n      console.log(state.error); // Type: string\n      break;\n    case \"loading\":\n      console.log(\"Loading...\");\n      break;\n  }\n}\n\n// Type-safe state machine\ntype State =\n  | { type: \"idle\" }\n  | { type: \"fetching\"; requestId: string }\n  | { type: \"success\"; data: any }\n  | { type: \"error\"; error: Error };\n\ntype Event =\n  | { type: \"FETCH\"; requestId: string }\n  | { type: \"SUCCESS\"; data: any }\n  | { type: \"ERROR\"; error: Error }\n  | { type: \"RESET\" };\n\nfunction reducer(state: State, event: Event): State {\n  switch (state.type) {\n    case \"idle\":\n      return event.type === \"FETCH\"\n        ? { type: \"fetching\", requestId: event.requestId }\n        : state;\n    case \"fetching\":\n      if (event.type === \"SUCCESS\") {\n        return { type: \"success\", data: event.data };\n      }\n      if (event.type === \"ERROR\") {\n        return { type: \"error\", error: event.error };\n      }\n      return state;\n    case \"success\":\n    case \"error\":\n      return event.type === \"RESET\" ? { type: \"idle\" } : state;\n  }\n}\n```\n\n## Type Inference Techniques\n\n### 1. Infer Keyword\n\n```typescript\n// Extract array element type\ntype ElementType<T> = T extends (infer U)[] ? U : never;\n\ntype NumArray = number[];\ntype Num = ElementType<NumArray>; // number\n\n// Extract promise type\ntype PromiseType<T> = T extends Promise<infer U> ? U : never;\n\ntype AsyncNum = PromiseType<Promise<number>>; // number\n\n// Extract function parameters\ntype Parameters<T> = T extends (...args: infer P) => any ? P : never;\n\nfunction foo(a: string, b: number) {}\ntype FooParams = Parameters<typeof foo>; // [string, number]\n```\n\n### 2. Type Guards\n\n```typescript\nfunction isString(value: unknown): value is string {\n  return typeof value === \"string\";\n}\n\nfunction isArrayOf<T>(\n  value: unknown,\n  guard: (item: unknown) => item is T,\n): value is T[] {\n  return Array.isArray(value) && value.every(guard);\n}\n\nconst data: unknown = [\"a\", \"b\", \"c\"];\n\nif (isArrayOf(data, isString)) {\n  data.forEach((s) => s.toUpperCase()); // Type: string[]\n}\n```\n\n### 3. Assertion Functions\n\n```typescript\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== \"string\") {\n    throw new Error(\"Not a string\");\n  }\n}\n\nfunction processValue(value: unknown) {\n  assertIsString(value);\n  // value is now typed as string\n  console.log(value.toUpperCase());\n}\n```\n\n## Best Practices\n\n1. **Use `unknown` over `any`**: Enforce type checking\n2. **Prefer `interface` for object shapes**: Better error messages\n3. **Use `type` for unions and complex types**: More flexible\n4. **Leverage type inference**: Let TypeScript infer when possible\n5. **Create helper types**: Build reusable type utilities\n6. **Use const assertions**: Preserve literal types\n7. **Avoid type assertions**: Use type guards instead\n8. **Document complex types**: Add JSDoc comments\n9. **Use strict mode**: Enable all strict compiler options\n10. **Test your types**: Use type tests to verify type behavior\n\n## Type Testing\n\n```typescript\n// Type assertion tests\ntype AssertEqual<T, U> = [T] extends [U]\n  ? [U] extends [T]\n    ? true\n    : false\n  : false;\n\ntype Test1 = AssertEqual<string, string>; // true\ntype Test2 = AssertEqual<string, number>; // false\ntype Test3 = AssertEqual<string | number, string>; // false\n\n// Expect error helper\ntype ExpectError<T extends never> = T;\n\n// Example usage\ntype ShouldError = ExpectError<AssertEqual<string, number>>;\n```\n\n## Common Pitfalls\n\n1. **Over-using `any`**: Defeats the purpose of TypeScript\n2. **Ignoring strict null checks**: Can lead to runtime errors\n3. **Too complex types**: Can slow down compilation\n4. **Not using discriminated unions**: Misses type narrowing opportunities\n5. **Forgetting readonly modifiers**: Allows unintended mutations\n6. **Circular type references**: Can cause compiler errors\n7. **Not handling edge cases**: Like empty arrays or null values\n\n## Performance Considerations\n\n- Avoid deeply nested conditional types\n- Use simple types when possible\n- Cache complex type computations\n- Limit recursion depth in recursive types\n- Use build tools to skip type checking in production\n","frontmatter":{"name":"typescript-advanced-types","description":"Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects."},"isInternal":false,"tokens":4404,"sizeBytes":16806},{"name":"AGENTS.md","path":".agents/skills/vercel-composition-patterns/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/AGENTS.md","title":"Vercel-composition-patterns Skill","category":"anthropic-skill","format":"markdown","content":"# React Composition Patterns\n\n**Version 1.0.0**  \nEngineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React codebases using composition. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n---\n\n## Table of Contents\n\n1. [Component Architecture](#1-component-architecture) — **HIGH**\n   - 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation)\n   - 1.2 [Use Compound Components](#12-use-compound-components)\n2. [State Management](#2-state-management) — **MEDIUM**\n   - 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui)\n   - 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection)\n   - 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components)\n3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM**\n   - 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants)\n   - 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props)\n4. [React 19 APIs](#4-react-19-apis) — **MEDIUM**\n   - 4.1 [React 19 API Changes](#41-react-19-api-changes)\n\n---\n\n## 1. Component Architecture\n\n**Impact: HIGH**\n\nFundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n### 1.1 Avoid Boolean Prop Proliferation\n\n**Impact: CRITICAL (prevents unmaintainable component variants)**\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\n\ncomponent behavior. Each boolean doubles possible states and creates\n\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect: boolean props create exponential complexity**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct: composition eliminates conditionals**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\n\nsharing a single monolithic parent.\n\n### 1.2 Use Compound Components\n\n**Impact: HIGH (enables flexible composition without prop drilling)**\n\nStructure complex components as compound components with a shared context. Each\n\nsubcomponent accesses shared state via context, not props. Consumers compose the\n\npieces they need.\n\n**Incorrect: monolithic component with render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct: compound components with shared context**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n\n---\n\n## 2. State Management\n\n**Impact: MEDIUM**\n\nPatterns for lifting state and managing shared context across\ncomposed components.\n\n### 2.1 Decouple State Management from UI\n\n**Impact: MEDIUM (enables swapping state implementations without changing UI)**\n\nThe provider component should be the only place that knows how state is managed.\n\nUI components consume the context interface—they don't know if state comes from\n\nuseState, Zustand, or a server sync.\n\n**Incorrect: UI coupled to state implementation**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct: state management isolated in provider**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\n\ndepends on the context interface, not the implementation.\n\n### 2.2 Define Generic Context Interfaces for Dependency Injection\n\n**Impact: HIGH (enables dependency-injectable state across use-cases)**\n\nDefine a **generic interface** for your component context with three parts:\n\n`state`, `actions`, and `meta`. This interface is a contract that any provider\n\ncan implement—enabling the same UI components to work with completely different\n\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\n\ndependency-injectable.\n\n**Incorrect: UI coupled to specific state implementation**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct: generic interface enables dependency injection**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe provider boundary is what matters—not the visual nesting. Components that\n\nneed shared state don't have to be inside the `Composer.Frame`. They just need\n\nto be within the provider.\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\n\nbox, but they can still access its state and actions. This is the power of\n\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\n\nby the provider. Swap the provider, keep the UI.\n\n### 2.3 Lift State into Provider Components\n\n**Impact: HIGH (enables state sharing outside component boundaries)**\n\nMove state management into dedicated provider components. This allows sibling\n\ncomponents outside the main UI to access and modify state without prop drilling\n\nor awkward refs.\n\n**Incorrect: state trapped inside component**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect: useEffect to sync state up**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect: reading state from ref on submit**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct: state lifted to provider**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\n\nsubmit action because it's within the provider. Even though it's a one-off\n\ncomponent, it can still access the composer's state and actions from outside the\n\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\n\nnested inside each other—they just need to be within the same provider.\n\n---\n\n## 3. Implementation Patterns\n\n**Impact: MEDIUM**\n\nSpecific techniques for implementing compound components and\ncontext providers.\n\n### 3.1 Create Explicit Component Variants\n\n**Impact: MEDIUM (self-documenting code, no hidden conditionals)**\n\nInstead of one component with many boolean props, create explicit variant\n\ncomponents. Each variant composes the pieces it needs. The code documents\n\nitself.\n\n**Incorrect: one component, many modes**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct: explicit variants**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\n\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n\n- What UI elements it includes\n\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n\n### 3.2 Prefer Composing Children Over Render Props\n\n**Impact: MEDIUM (cleaner composition, better readability)**\n\nUse `children` for composition instead of `renderX` props. Children are more\n\nreadable, compose naturally, and don't require understanding callback\n\nsignatures.\n\n**Incorrect: render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct: compound components with children**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\n\nUse children when composing static structure.\n\n---\n\n## 4. React 19 APIs\n\n**Impact: MEDIUM**\n\nReact 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n\n### 4.1 React 19 API Changes\n\n**Impact: MEDIUM (cleaner component definitions and context usage)**\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect: forwardRef in React 19**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct: ref as a regular prop**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect: useContext in React 19**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct: use instead of useContext**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n\n---\n\n## References\n\n1. [https://react.dev](https://react.dev)\n2. [https://react.dev/learn/passing-data-deeply-with-context](https://react.dev/learn/passing-data-deeply-with-context)\n3. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)\n","isInternal":false,"tokens":5063,"sizeBytes":22627},{"name":"metadata.json","path":".agents/skills/vercel-composition-patterns/metadata.json","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/metadata.json","title":"Vercel-composition-patterns Skill","category":"anthropic-skill","format":"json","content":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://react.dev/learn/passing-data-deeply-with-context\",\n    \"https://react.dev/reference/react/use\"\n  ]\n}\n","frontmatter":{"version":"1.0.0","organization":"Engineering","date":"January 2026","abstract":"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.","references":["https://react.dev","https://react.dev/learn/passing-data-deeply-with-context","https://react.dev/reference/react/use"]},"isInternal":false,"tokens":122,"sizeBytes":530},{"name":"README.md","path":".agents/skills/vercel-composition-patterns/README.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/README.md","title":"Vercel-composition-patterns Skill","category":"anthropic-skill","format":"markdown","content":"# React Composition Patterns\n\nA structured repository for React composition patterns that scale. These\npatterns help avoid boolean prop proliferation by using compound components,\nlifting state, and composing internals.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Rules\n\n### Component Architecture (CRITICAL)\n\n- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize\n  behavior\n- `architecture-compound-components.md` - Structure as compound components with\n  shared context\n\n### State Management (HIGH)\n\n- `state-lift-state.md` - Lift state into provider components\n- `state-context-interface.md` - Define clear context interfaces\n  (state/actions/meta)\n- `state-decouple-implementation.md` - Decouple state management from UI\n\n### Implementation Patterns (MEDIUM)\n\n- `patterns-children-over-render-props.md` - Prefer children over renderX props\n- `patterns-explicit-variants.md` - Create explicit component variants\n\n## Core Principles\n\n1. **Composition over configuration** — Instead of adding props, let consumers\n   compose\n2. **Lift your state** — State in providers, not trapped in components\n3. **Compose your internals** — Subcomponents access context, not props\n4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer\n   with isThread\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `architecture-` for Component Architecture\n   - `state-` for State Management\n   - `patterns-` for Implementation Patterns\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n\n## Impact Levels\n\n- `CRITICAL` - Foundational patterns, prevents unmaintainable code\n- `HIGH` - Significant maintainability improvements\n- `MEDIUM` - Good practices for cleaner code\n","isInternal":false,"tokens":476,"sizeBytes":2140},{"name":"_sections.md","path":".agents/skills/vercel-composition-patterns/rules/_sections.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/_sections.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Component Architecture (architecture)\n\n**Impact:** HIGH  \n**Description:** Fundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n## 2. State Management (state)\n\n**Impact:** MEDIUM  \n**Description:** Patterns for lifting state and managing shared context across\ncomposed components.\n\n## 3. Implementation Patterns (patterns)\n\n**Impact:** MEDIUM  \n**Description:** Specific techniques for implementing compound components and\ncontext providers.\n\n## 4. React 19 APIs (react19)\n\n**Impact:** MEDIUM  \n**Description:** React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n","isInternal":false,"tokens":176,"sizeBytes":829},{"name":"_template.md","path":".agents/skills/vercel-composition-patterns/rules/_template.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/_template.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: brief description of impact\ntags: composition, components\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect:**\n\n```tsx\n// Bad code example\n```\n\n**Correct:**\n\n```tsx\n// Good code example\n```\n\nReference: [Link](https://example.com)\n","frontmatter":{"title":"Rule Title Here","impact":"MEDIUM","impactDescription":"brief description of impact","tags":"composition, components"},"isInternal":false,"tokens":80,"sizeBytes":329},{"name":"architecture-avoid-boolean-props.md","path":".agents/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Farchitecture-avoid-boolean-props.md -->","frontmatter":{"title":"Avoid Boolean Prop Proliferation","impact":"CRITICAL","impactDescription":"prevents unmaintainable component variants","tags":"composition, props, architecture"},"isInternal":false,"tokens":513,"sizeBytes":2267},{"name":"architecture-compound-components.md","path":".agents/skills/vercel-composition-patterns/rules/architecture-compound-components.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/architecture-compound-components.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Farchitecture-compound-components.md -->","frontmatter":{"title":"Use Compound Components","impact":"HIGH","impactDescription":"enables flexible composition without prop drilling","tags":"composition, compound-components, architecture"},"isInternal":false,"tokens":578,"sizeBytes":2600},{"name":"patterns-children-over-render-props.md","path":".agents/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fpatterns-children-over-render-props.md -->","frontmatter":{"title":"Prefer Composing Children Over Render Props","impact":"MEDIUM","impactDescription":"cleaner composition, better readability","tags":"composition, children, render-props"},"isInternal":false,"tokens":428,"sizeBytes":1886},{"name":"patterns-explicit-variants.md","path":".agents/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fpatterns-explicit-variants.md -->","frontmatter":{"title":"Create Explicit Component Variants","impact":"MEDIUM","impactDescription":"self-documenting code, no hidden conditionals","tags":"composition, variants, architecture"},"isInternal":false,"tokens":503,"sizeBytes":2395},{"name":"react19-no-forwardref.md","path":".agents/skills/vercel-composition-patterns/rules/react19-no-forwardref.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/react19-no-forwardref.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Freact19-no-forwardref.md -->","frontmatter":{"title":"React 19 API Changes","impact":"MEDIUM","impactDescription":"cleaner component definitions and context usage","tags":"react19, refs, context, hooks"},"isInternal":false,"tokens":255,"sizeBytes":953},{"name":"state-context-interface.md","path":".agents/skills/vercel-composition-patterns/rules/state-context-interface.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/state-context-interface.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-context-interface.md -->","frontmatter":{"title":"Define Generic Context Interfaces for Dependency Injection","impact":"HIGH","impactDescription":"enables dependency-injectable state across use-cases","tags":"composition, context, state, typescript, dependency-injection"},"isInternal":false,"tokens":1076,"sizeBytes":4974},{"name":"state-decouple-implementation.md","path":".agents/skills/vercel-composition-patterns/rules/state-decouple-implementation.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/state-decouple-implementation.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-decouple-implementation.md -->","frontmatter":{"title":"Decouple State Management from UI","impact":"MEDIUM","impactDescription":"enables swapping state implementations without changing UI","tags":"composition, state, architecture"},"isInternal":false,"tokens":579,"sizeBytes":2699},{"name":"state-lift-state.md","path":".agents/skills/vercel-composition-patterns/rules/state-lift-state.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/rules/state-lift-state.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2Frules%2Fstate-lift-state.md -->","frontmatter":{"title":"Lift State into Provider Components","impact":"HIGH","impactDescription":"enables state sharing outside component boundaries","tags":"composition, state, context, providers"},"isInternal":false,"tokens":686,"sizeBytes":3224},{"name":"SKILL.md","path":".agents/skills/vercel-composition-patterns/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-composition-patterns/SKILL.md","title":"vercel-composition-patterns","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-composition-patterns%2FSKILL.md -->","frontmatter":{"name":"vercel-composition-patterns","description":"React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.","license":"MIT","metadata":{"author":"vercel","version":"1.0.0"}},"isInternal":false,"tokens":630,"sizeBytes":2886},{"name":"AGENTS.md","path":".agents/skills/vercel-react-best-practices/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/AGENTS.md","title":"Vercel-react-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FAGENTS.md -->","isInternal":false,"tokens":12005,"sizeBytes":50062},{"name":"metadata.json","path":".agents/skills/vercel-react-best-practices/metadata.json","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/metadata.json","title":"Vercel-react-best-practices Skill","category":"anthropic-skill","format":"json","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Fmetadata.json -->","frontmatter":{"version":"1.0.0","organization":"Vercel Engineering","date":"January 2026","abstract":"Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.","references":["https://react.dev","https://nextjs.org","https://swr.vercel.app","https://github.com/shuding/better-all","https://github.com/isaacs/node-lru-cache","https://vercel.com/blog/how-we-optimized-package-imports-in-next-js","https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast"]},"isInternal":false,"tokens":217,"sizeBytes":921},{"name":"README.md","path":".agents/skills/vercel-react-best-practices/README.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/README.md","title":"Vercel-react-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FREADME.md -->","isInternal":false,"tokens":848,"sizeBytes":3360},{"name":"_sections.md","path":".agents/skills/vercel-react-best-practices/rules/_sections.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/_sections.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2F_sections.md -->","isInternal":false,"tokens":318,"sizeBytes":1554},{"name":"_template.md","path":".agents/skills/vercel-react-best-practices/rules/_template.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/_template.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2F_template.md -->","frontmatter":{"title":"Rule Title Here","impact":"MEDIUM","impactDescription":"Optional description of impact (e.g., \"20-50% improvement\")","tags":"tag1, tag2"},"isInternal":false,"tokens":143,"sizeBytes":631},{"name":"advanced-effect-event-deps.md","path":".agents/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-effect-event-deps.md -->","frontmatter":{"title":"Do Not Put Effect Events in Dependency Arrays","impact":"LOW","impactDescription":"avoids unnecessary effect re-runs and lint errors","tags":"advanced, hooks, useEffectEvent, dependencies, effects"},"isInternal":false,"tokens":375,"sizeBytes":1802},{"name":"advanced-event-handler-refs.md","path":".agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-event-handler-refs.md -->","frontmatter":{"title":"Store Event Handlers in Refs","impact":"LOW","impactDescription":"stable subscriptions","tags":"advanced, hooks, refs, event-handlers, optimization"},"isInternal":false,"tokens":337,"sizeBytes":1483},{"name":"advanced-init-once.md","path":".agents/skills/vercel-react-best-practices/rules/advanced-init-once.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-init-once.md -->","frontmatter":{"title":"Initialize App Once, Not Per Mount","impact":"LOW-MEDIUM","impactDescription":"avoids duplicate init in development","tags":"initialization, useEffect, app-startup, side-effects"},"isInternal":false,"tokens":230,"sizeBytes":958},{"name":"advanced-use-latest.md","path":".agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fadvanced-use-latest.md -->","frontmatter":{"title":"useEffectEvent for Stable Callback Refs","impact":"LOW","impactDescription":"prevents effect re-runs","tags":"advanced, hooks, useEffectEvent, refs, optimization"},"isInternal":false,"tokens":254,"sizeBytes":1072},{"name":"async-api-routes.md","path":".agents/skills/vercel-react-best-practices/rules/async-api-routes.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-api-routes.md -->","frontmatter":{"title":"Prevent Waterfall Chains in API Routes","impact":"CRITICAL","impactDescription":"2-10× improvement","tags":"api-routes, server-actions, waterfalls, parallelization"},"isInternal":false,"tokens":241,"sizeBytes":1125},{"name":"async-cheap-condition-before-await.md","path":".agents/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-cheap-condition-before-await.md -->","frontmatter":{"title":"Check Cheap Conditions Before Async Flags","impact":"HIGH","impactDescription":"avoids unnecessary async work when a synchronous guard already fails","tags":"async, await, feature-flags, short-circuit, conditional"},"isInternal":false,"tokens":277,"sizeBytes":1220},{"name":"async-defer-await.md","path":".agents/skills/vercel-react-best-practices/rules/async-defer-await.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-defer-await.md -->","frontmatter":{"title":"Defer Await Until Needed","impact":"HIGH","impactDescription":"avoids blocking unused code paths","tags":"async, await, conditional, optimization"},"isInternal":false,"tokens":474,"sizeBytes":2208},{"name":"async-dependencies.md","path":".agents/skills/vercel-react-best-practices/rules/async-dependencies.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-dependencies.md -->","frontmatter":{"title":"Dependency-Based Parallelization","impact":"CRITICAL","impactDescription":"2-10× improvement","tags":"async, parallelization, dependencies, better-all"},"isInternal":false,"tokens":291,"sizeBytes":1293},{"name":"async-parallel.md","path":".agents/skills/vercel-react-best-practices/rules/async-parallel.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/async-parallel.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-parallel.md -->","frontmatter":{"title":"Promise.all() for Independent Operations","impact":"CRITICAL","impactDescription":"2-10× improvement","tags":"async, parallelization, promises, waterfalls"},"isInternal":false,"tokens":144,"sizeBytes":654},{"name":"async-suspense-boundaries.md","path":".agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fasync-suspense-boundaries.md -->","frontmatter":{"title":"Strategic Suspense Boundaries","impact":"HIGH","impactDescription":"faster initial paint","tags":"async, suspense, streaming, layout-shift"},"isInternal":false,"tokens":591,"sizeBytes":2510},{"name":"bundle-barrel-imports.md","path":".agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-barrel-imports.md -->","frontmatter":{"title":"Avoid Barrel File Imports","impact":"CRITICAL","impactDescription":"200-800ms import cost, slow builds","tags":"bundle, imports, tree-shaking, barrel-files, performance"},"isInternal":false,"tokens":672,"sizeBytes":2795},{"name":"bundle-conditional.md","path":".agents/skills/vercel-react-best-practices/rules/bundle-conditional.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-conditional.md -->","frontmatter":{"title":"Conditional Module Loading","impact":"HIGH","impactDescription":"loads large data only when needed","tags":"bundle, conditional-loading, lazy-loading"},"isInternal":false,"tokens":213,"sizeBytes":949},{"name":"bundle-defer-third-party.md","path":".agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-defer-third-party.md -->","frontmatter":{"title":"Defer Non-Critical Third-Party Libraries","impact":"MEDIUM","impactDescription":"loads after hydration","tags":"bundle, third-party, analytics, defer"},"isInternal":false,"tokens":221,"sizeBytes":920},{"name":"bundle-dynamic-imports.md","path":".agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-dynamic-imports.md -->","frontmatter":{"title":"Dynamic Imports for Heavy Components","impact":"CRITICAL","impactDescription":"directly affects TTI and LCP","tags":"bundle, dynamic-import, code-splitting, next-dynamic"},"isInternal":false,"tokens":197,"sizeBytes":791},{"name":"bundle-preload.md","path":".agents/skills/vercel-react-best-practices/rules/bundle-preload.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fbundle-preload.md -->","frontmatter":{"title":"Preload Based on User Intent","impact":"MEDIUM","impactDescription":"reduces perceived latency","tags":"bundle, preload, user-intent, hover"},"isInternal":false,"tokens":267,"sizeBytes":1149},{"name":"client-event-listeners.md","path":".agents/skills/vercel-react-best-practices/rules/client-event-listeners.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-event-listeners.md -->","frontmatter":{"title":"Deduplicate Global Event Listeners","impact":"LOW","impactDescription":"single listener for N components","tags":"client, swr, event-listeners, subscription"},"isInternal":false,"tokens":466,"sizeBytes":1969},{"name":"client-localstorage-schema.md","path":".agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-localstorage-schema.md -->","frontmatter":{"title":"Version and Minimize localStorage Data","impact":"MEDIUM","impactDescription":"prevents schema conflicts, reduces storage size","tags":"client, localStorage, storage, versioning, data-minimization"},"isInternal":false,"tokens":437,"sizeBytes":1950},{"name":"client-passive-event-listeners.md","path":".agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-passive-event-listeners.md -->","frontmatter":{"title":"Use Passive Event Listeners for Scrolling Performance","impact":"MEDIUM","impactDescription":"eliminates scroll delay caused by event listeners","tags":"client, event-listeners, scrolling, performance, touch, wheel"},"isInternal":false,"tokens":361,"sizeBytes":1644},{"name":"client-swr-dedup.md","path":".agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fclient-swr-dedup.md -->","frontmatter":{"title":"Use SWR for Automatic Deduplication","impact":"MEDIUM-HIGH","impactDescription":"automatic deduplication","tags":"client, swr, deduplication, data-fetching"},"isInternal":false,"tokens":308,"sizeBytes":1159},{"name":"js-batch-dom-css.md","path":".agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-batch-dom-css.md -->","frontmatter":{"title":"Avoid Layout Thrashing","impact":"MEDIUM","impactDescription":"prevents forced synchronous layouts and reduces performance bottlenecks","tags":"javascript, dom, css, performance, reflow, layout-thrashing"},"isInternal":false,"tokens":757,"sizeBytes":3266},{"name":"js-cache-function-results.md","path":".agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-function-results.md -->","frontmatter":{"title":"Cache Repeated Function Calls","impact":"MEDIUM","impactDescription":"avoid redundant computation","tags":"javascript, cache, memoization, performance"},"isInternal":false,"tokens":448,"sizeBytes":1949},{"name":"js-cache-property-access.md","path":".agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-property-access.md -->","frontmatter":{"title":"Cache Property Access in Loops","impact":"LOW-MEDIUM","impactDescription":"reduces lookups","tags":"javascript, loops, optimization, caching"},"isInternal":false,"tokens":138,"sizeBytes":532},{"name":"js-cache-storage.md","path":".agents/skills/vercel-react-best-practices/rules/js-cache-storage.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-cache-storage.md -->","frontmatter":{"title":"Cache Storage API Calls","impact":"LOW-MEDIUM","impactDescription":"reduces expensive I/O","tags":"javascript, localStorage, storage, caching, performance"},"isInternal":false,"tokens":377,"sizeBytes":1651},{"name":"js-combine-iterations.md","path":".agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-combine-iterations.md -->","frontmatter":{"title":"Combine Multiple Array Iterations","impact":"LOW-MEDIUM","impactDescription":"reduces iterations","tags":"javascript, arrays, loops, performance"},"isInternal":false,"tokens":176,"sizeBytes":753},{"name":"js-early-exit.md","path":".agents/skills/vercel-react-best-practices/rules/js-early-exit.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-early-exit.md -->","frontmatter":{"title":"Early Return from Functions","impact":"LOW-MEDIUM","impactDescription":"avoids unnecessary computation","tags":"javascript, functions, optimization, early-return"},"isInternal":false,"tokens":266,"sizeBytes":1133},{"name":"js-flatmap-filter.md","path":".agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-flatmap-filter.md -->","frontmatter":{"title":"Use flatMap to Map and Filter in One Pass","impact":"LOW-MEDIUM","impactDescription":"eliminates intermediate array","tags":"javascript, arrays, flatMap, filter, performance"},"isInternal":false,"tokens":342,"sizeBytes":1411},{"name":"js-hoist-regexp.md","path":".agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-hoist-regexp.md -->","frontmatter":{"title":"Hoist RegExp Creation","impact":"LOW-MEDIUM","impactDescription":"avoids recreation","tags":"javascript, regexp, optimization, memoization"},"isInternal":false,"tokens":283,"sizeBytes":1028},{"name":"js-index-maps.md","path":".agents/skills/vercel-react-best-practices/rules/js-index-maps.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-index-maps.md -->","frontmatter":{"title":"Build Index Maps for Repeated Lookups","impact":"LOW-MEDIUM","impactDescription":"1M ops to 2K ops","tags":"javascript, map, indexing, optimization, performance"},"isInternal":false,"tokens":229,"sizeBytes":837},{"name":"js-length-check-first.md","path":".agents/skills/vercel-react-best-practices/rules/js-length-check-first.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-length-check-first.md -->","frontmatter":{"title":"Early Length Check for Array Comparisons","impact":"MEDIUM-HIGH","impactDescription":"avoids expensive operations when lengths differ","tags":"javascript, arrays, performance, optimization, comparison"},"isInternal":false,"tokens":373,"sizeBytes":1747},{"name":"js-min-max-loop.md","path":".agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-min-max-loop.md -->","frontmatter":{"title":"Use Loop for Min/Max Instead of Sort","impact":"LOW","impactDescription":"O(n) instead of O(n log n)","tags":"javascript, arrays, performance, sorting, algorithms"},"isInternal":false,"tokens":602,"sizeBytes":2290},{"name":"js-request-idle-callback.md","path":".agents/skills/vercel-react-best-practices/rules/js-request-idle-callback.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-request-idle-callback.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-request-idle-callback.md -->","frontmatter":{"title":"Defer Non-Critical Work with requestIdleCallback","impact":"MEDIUM","impactDescription":"keeps UI responsive during background tasks","tags":"javascript, performance, idle, scheduling, analytics"},"isInternal":false,"tokens":597,"sizeBytes":2647},{"name":"js-set-map-lookups.md","path":".agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-set-map-lookups.md -->","frontmatter":{"title":"Use Set/Map for O(1) Lookups","impact":"LOW-MEDIUM","impactDescription":"O(n) to O(1)","tags":"javascript, set, map, data-structures, performance"},"isInternal":false,"tokens":154,"sizeBytes":532},{"name":"js-tosorted-immutable.md","path":".agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fjs-tosorted-immutable.md -->","frontmatter":{"title":"Use toSorted() Instead of sort() for Immutability","impact":"MEDIUM-HIGH","impactDescription":"prevents mutation bugs in React state","tags":"javascript, arrays, immutability, react, state, mutation"},"isInternal":false,"tokens":436,"sizeBytes":1782},{"name":"rendering-activity.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-activity.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-activity.md -->","frontmatter":{"title":"Use Activity Component for Show/Hide","impact":"MEDIUM","impactDescription":"preserves state/DOM","tags":"rendering, activity, visibility, state-preservation"},"isInternal":false,"tokens":131,"sizeBytes":564},{"name":"rendering-animate-svg-wrapper.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-animate-svg-wrapper.md -->","frontmatter":{"title":"Animate SVG Wrapper Instead of SVG Element","impact":"LOW","impactDescription":"enables hardware acceleration","tags":"rendering, svg, css, animation, performance"},"isInternal":false,"tokens":291,"sizeBytes":1185},{"name":"rendering-conditional-render.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-conditional-render.md -->","frontmatter":{"title":"Use Explicit Conditional Rendering","impact":"LOW","impactDescription":"prevents rendering 0 or NaN","tags":"rendering, conditional, jsx, falsy-values"},"isInternal":false,"tokens":273,"sizeBytes":980},{"name":"rendering-content-visibility.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-content-visibility.md -->","frontmatter":{"title":"CSS content-visibility for Long Lists","impact":"HIGH","impactDescription":"faster initial render","tags":"rendering, css, content-visibility, long-lists"},"isInternal":false,"tokens":203,"sizeBytes":815},{"name":"rendering-hoist-jsx.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hoist-jsx.md -->","frontmatter":{"title":"Hoist Static JSX Elements","impact":"LOW","impactDescription":"avoids re-creation","tags":"rendering, jsx, static, optimization"},"isInternal":false,"tokens":244,"sizeBytes":1039},{"name":"rendering-hydration-no-flicker.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hydration-no-flicker.md -->","frontmatter":{"title":"Prevent Hydration Mismatch Without Flickering","impact":"MEDIUM","impactDescription":"avoids visual flicker and hydration errors","tags":"rendering, ssr, hydration, localStorage, flicker"},"isInternal":false,"tokens":490,"sizeBytes":2308},{"name":"rendering-hydration-suppress-warning.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-hydration-suppress-warning.md -->","frontmatter":{"title":"Suppress Expected Hydration Mismatches","impact":"LOW-MEDIUM","impactDescription":"avoids noisy hydration warnings for known differences","tags":"rendering, hydration, ssr, nextjs"},"isInternal":false,"tokens":197,"sizeBytes":872},{"name":"rendering-resource-hints.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-resource-hints.md -->","frontmatter":{"title":"Use React DOM Resource Hints","impact":"HIGH","impactDescription":"reduces load time for critical resources","tags":"rendering, preload, preconnect, prefetch, resource-hints"},"isInternal":false,"tokens":621,"sizeBytes":2549},{"name":"rendering-script-defer-async.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-script-defer-async.md -->","frontmatter":{"title":"Use defer or async on Script Tags","impact":"HIGH","impactDescription":"eliminates render-blocking","tags":"rendering, script, defer, async, performance"},"isInternal":false,"tokens":430,"sizeBytes":1868},{"name":"rendering-svg-precision.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-svg-precision.md -->","frontmatter":{"title":"Optimize SVG Precision","impact":"LOW","impactDescription":"reduces file size","tags":"rendering, svg, optimization, svgo"},"isInternal":false,"tokens":167,"sizeBytes":588},{"name":"rendering-usetransition-loading.md","path":".agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frendering-usetransition-loading.md -->","frontmatter":{"title":"Use useTransition Over Manual Loading States","impact":"LOW","impactDescription":"reduces re-renders and improves code clarity","tags":"rendering, transitions, useTransition, loading, state"},"isInternal":false,"tokens":457,"sizeBytes":2074},{"name":"rerender-defer-reads.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-defer-reads.md -->","frontmatter":{"title":"Defer State Reads to Usage Point","impact":"MEDIUM","impactDescription":"avoids unnecessary subscriptions","tags":"rerender, searchParams, localStorage, optimization"},"isInternal":false,"tokens":231,"sizeBytes":973},{"name":"rerender-dependencies.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-dependencies.md -->","frontmatter":{"title":"Narrow Effect Dependencies","impact":"LOW","impactDescription":"minimizes effect re-runs","tags":"rerender, useEffect, dependencies, optimization"},"isInternal":false,"tokens":214,"sizeBytes":824},{"name":"rerender-derived-state-no-effect.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-derived-state-no-effect.md -->","frontmatter":{"title":"Calculate Derived State During Rendering","impact":"MEDIUM","impactDescription":"avoids redundant renders and state drift","tags":"rerender, derived-state, useEffect, state"},"isInternal":false,"tokens":283,"sizeBytes":1201},{"name":"rerender-derived-state.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-derived-state.md -->","frontmatter":{"title":"Subscribe to Derived State","impact":"MEDIUM","impactDescription":"reduces re-render frequency","tags":"rerender, derived-state, media-query, optimization"},"isInternal":false,"tokens":175,"sizeBytes":728},{"name":"rerender-functional-setstate.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-functional-setstate.md -->","frontmatter":{"title":"Use Functional setState Updates","impact":"MEDIUM","impactDescription":"prevents stale closures and unnecessary callback recreations","tags":"react, hooks, useState, useCallback, callbacks, closures"},"isInternal":false,"tokens":654,"sizeBytes":2968},{"name":"rerender-lazy-state-init.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-lazy-state-init.md -->","frontmatter":{"title":"Use Lazy State Initialization","impact":"MEDIUM","impactDescription":"wasted computation on every render","tags":"react, hooks, useState, performance, initialization"},"isInternal":false,"tokens":438,"sizeBytes":2016},{"name":"rerender-memo-with-default-value.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-memo-with-default-value.md -->","frontmatter":{"title":"Extract Default Non-primitive Parameter Value from Memoized Component to Constant","impact":"MEDIUM","impactDescription":"restores memoization by using a constant for default value","tags":"rerender, memo, optimization"},"isInternal":false,"tokens":248,"sizeBytes":1173},{"name":"rerender-memo.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-memo.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-memo.md -->","frontmatter":{"title":"Extract to Memoized Components","impact":"MEDIUM","impactDescription":"enables early returns","tags":"rerender, memo, useMemo, optimization"},"isInternal":false,"tokens":278,"sizeBytes":1148},{"name":"rerender-move-effect-to-event.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-move-effect-to-event.md -->","frontmatter":{"title":"Put Interaction Logic in Event Handlers","impact":"MEDIUM","impactDescription":"avoids effect re-runs and duplicate side effects","tags":"rerender, useEffect, events, side-effects, dependencies"},"isInternal":false,"tokens":285,"sizeBytes":1268},{"name":"rerender-no-inline-components.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-no-inline-components.md -->","frontmatter":{"title":"Don't Define Components Inside Components","impact":"HIGH","impactDescription":"prevents remount on every render","tags":"rerender, components, remount, performance"},"isInternal":false,"tokens":523,"sizeBytes":2135},{"name":"rerender-simple-expression-in-memo.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-simple-expression-in-memo.md -->","frontmatter":{"title":"Do not wrap a simple expression with a primitive result type in useMemo","impact":"LOW-MEDIUM","impactDescription":"wasted computation on every render","tags":"rerender, useMemo, optimization"},"isInternal":false,"tokens":217,"sizeBytes":1018},{"name":"rerender-split-combined-hooks.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-split-combined-hooks.md -->","frontmatter":{"title":"Split Combined Hook Computations","impact":"MEDIUM","impactDescription":"avoids recomputing independent steps","tags":"rerender, useMemo, useEffect, dependencies, optimization"},"isInternal":false,"tokens":421,"sizeBytes":1844},{"name":"rerender-transitions.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-transitions.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-transitions.md -->","frontmatter":{"title":"Use Transitions for Non-Urgent Updates","impact":"MEDIUM","impactDescription":"maintains UI responsiveness","tags":"rerender, transitions, startTransition, performance"},"isInternal":false,"tokens":241,"sizeBytes":1055},{"name":"rerender-use-deferred-value.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-use-deferred-value.md -->","frontmatter":{"title":"Use useDeferredValue for Expensive Derived Renders","impact":"MEDIUM","impactDescription":"keeps input responsive during heavy computation","tags":"rerender, useDeferredValue, optimization, concurrent"},"isInternal":false,"tokens":421,"sizeBytes":1846},{"name":"rerender-use-ref-transient-values.md","path":".agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Frerender-use-ref-transient-values.md -->","frontmatter":{"title":"Use useRef for Transient Values","impact":"MEDIUM","impactDescription":"avoids unnecessary re-renders on frequent updates","tags":"rerender, useref, state, performance"},"isInternal":false,"tokens":436,"sizeBytes":1742},{"name":"server-after-nonblocking.md","path":".agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-after-nonblocking.md -->","frontmatter":{"title":"Use after() for Non-Blocking Operations","impact":"MEDIUM","impactDescription":"faster response times","tags":"server, async, logging, analytics, side-effects"},"isInternal":false,"tokens":458,"sizeBytes":2012},{"name":"server-auth-actions.md","path":".agents/skills/vercel-react-best-practices/rules/server-auth-actions.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-auth-actions.md -->","frontmatter":{"title":"Authenticate Server Actions Like API Routes","impact":"CRITICAL","impactDescription":"prevents unauthorized access to server mutations","tags":"server, server-actions, authentication, security, authorization"},"isInternal":false,"tokens":585,"sizeBytes":2649},{"name":"server-cache-lru.md","path":".agents/skills/vercel-react-best-practices/rules/server-cache-lru.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-cache-lru.md -->","frontmatter":{"title":"Cross-Request LRU Caching","impact":"HIGH","impactDescription":"caches across requests","tags":"server, cache, lru, cross-request"},"isInternal":false,"tokens":326,"sizeBytes":1353},{"name":"server-cache-react.md","path":".agents/skills/vercel-react-best-practices/rules/server-cache-react.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-cache-react.md -->","frontmatter":{"title":"Per-Request Deduplication with React.cache()","impact":"MEDIUM","impactDescription":"deduplicates within request","tags":"server, cache, react-cache, deduplication"},"isInternal":false,"tokens":513,"sizeBytes":2228},{"name":"server-dedup-props.md","path":".agents/skills/vercel-react-best-practices/rules/server-dedup-props.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-dedup-props.md -->","frontmatter":{"title":"Avoid Duplicate Serialization in RSC Props","impact":"LOW","impactDescription":"reduces network payload by avoiding duplicate serialization","tags":"server, rsc, serialization, props, client-components"},"isInternal":false,"tokens":499,"sizeBytes":2060},{"name":"server-hoist-static-io.md","path":".agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-hoist-static-io.md -->","frontmatter":{"title":"Hoist Static I/O to Module Level","impact":"HIGH","impactDescription":"avoids repeated file/network I/O per request","tags":"server, io, performance, next.js, route-handlers, og-image"},"isInternal":false,"tokens":1048,"sizeBytes":4423},{"name":"server-no-shared-module-state.md","path":".agents/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-no-shared-module-state.md -->","frontmatter":{"title":"Avoid Shared Module State for Request Data","impact":"HIGH","impactDescription":"prevents concurrency bugs and request data leaks","tags":"server, rsc, ssr, concurrency, security, state"},"isInternal":false,"tokens":368,"sizeBytes":1766},{"name":"server-parallel-fetching.md","path":".agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-parallel-fetching.md -->","frontmatter":{"title":"Parallel Data Fetching with Component Composition","impact":"CRITICAL","impactDescription":"eliminates server-side waterfalls","tags":"server, rsc, parallel-fetching, composition"},"isInternal":false,"tokens":365,"sizeBytes":1554},{"name":"server-parallel-nested-fetching.md","path":".agents/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-parallel-nested-fetching.md -->","frontmatter":{"title":"Parallel Nested Data Fetching","impact":"CRITICAL","impactDescription":"eliminates server-side waterfalls","tags":"server, rsc, parallel-fetching, promise-chaining"},"isInternal":false,"tokens":230,"sizeBytes":992},{"name":"server-serialization.md","path":".agents/skills/vercel-react-best-practices/rules/server-serialization.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/rules/server-serialization.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2Frules%2Fserver-serialization.md -->","frontmatter":{"title":"Minimize Serialization at RSC Boundaries","impact":"HIGH","impactDescription":"reduces data transfer size","tags":"server, rsc, serialization, props"},"isInternal":false,"tokens":242,"sizeBytes":996},{"name":"SKILL.md","path":".agents/skills/vercel-react-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/vercel-react-best-practices/SKILL.md","title":"vercel-react-best-practices","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fvercel-react-best-practices%2FSKILL.md -->","frontmatter":{"name":"vercel-react-best-practices","description":"React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.","license":"MIT","metadata":{"author":"vercel","version":"1.0.0"}},"isInternal":false,"tokens":1728,"sizeBytes":7129},{"name":"SKILL.md","path":".agents/skills/web-design-guidelines/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/web-design-guidelines/SKILL.md","title":"web-design-guidelines","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fweb-design-guidelines%2FSKILL.md -->","frontmatter":{"name":"web-design-guidelines","description":"Review UI code for Web Interface Guidelines compliance. Use when asked to \"review my UI\", \"check accessibility\", \"audit design\", \"review UX\", or \"check my site against best practices\".","metadata":{"author":"vercel","version":"1.0.0","argument-hint":"<file-or-pattern>"}},"isInternal":false,"tokens":268,"sizeBytes":1231},{"name":"SKILL.md","path":".agents/skills/web-quality-audit/SKILL.md","rawUrl":"https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/HEAD/.agents/skills/web-quality-audit/SKILL.md","title":"web-quality-audit","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/midudev/preguntas-entrevista-react?path=.agents%2Fskills%2Fweb-quality-audit%2FSKILL.md -->","frontmatter":{"name":"web-quality-audit","description":"Comprehensive web quality audit covering performance, accessibility, SEO, and best practices. Use when asked to \"audit my site\", \"review web quality\", \"run lighthouse audit\", \"check page quality\", or \"optimize my website\".","license":"MIT","metadata":{"author":"web-quality-skills","version":"1.0"}},"isInternal":false,"tokens":1578,"sizeBytes":6628}],"systemPromptSnippet":"<yakaai_skills repo=\"midudev/preguntas-entrevista-react\">\n<!-- File: CLAUDE.md (Tokens: ~6095 | Category: claude-rule) -->\n# CLAUDE.md\n\n<!-- autoskills:start -->\n\nSummary generated by `autoskills`. Check the full files inside `.claude/skills`.\n\n## Accessibility (a11y)\n\nAudit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".\n\n- `.claude/skills/accessibility/SKILL.md`\n- `.claude/skills/accessibility/references/WCAG.md`\n\n## Best practices\n\nApply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\n\n- `.claude/skills/best-practices/SKILL.md`\n\n## Core Web Vitals optimization\n\nOptimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\n\n- `.claude/skills/core-web-vitals/SKILL.md`\n- `.claude/skills/core-web-vitals/references/LCP.md`: Largest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:\n\n## Design Thinking\n\nCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beaut...\n\n- `.claude/skills/frontend-design/SKILL.md`\n\n## Next.js Best Practices\n\nNext.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling\n\n- `.claude/skills/next-best-practices/SKILL.md`\n- `.claude/skills/next-best-practices/async-patterns.md`: In Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.\n- `.claude/skills/next-best-practices/bundling.md`: Fix common bundling issues with third-party packages.\n- `.claude/skills/next-best-practices/data-patterns.md`: Choose the right data fetching pattern for each use case.\n- `.claude/skills/next-best-practices/debug-tricks.md`: Tricks to speed up debugging Next.js applications.\n- `.claude/skills/next-best-practices/directives.md`: These are React directives, not Next.js specific.\n- `.claude/skills/next-best-practices/error-handling.md`: Handle errors gracefully in Next.js applications.\n- `.claude/skills/next-best-practices/file-conventions.md`: Next.js App Router uses file-based routing with special file conventions.\n- `.claude/skills/next-best-practices/font.md`: Use `next/font` for automatic font optimization with zero layout shift.\n- `.claude/skills/next-best-practices/functions.md`: Next.js function APIs.\n- `.claude/skills/next-best-practices/hydration-error.md`: Diagnose and fix React hydration mismatch errors.\n- `.claude/skills/next-best-practices/image.md`: Use `next/image` for automatic image optimization.\n- `.claude/skills/next-best-practices/metadata.md`: Add SEO metadata to Next.js pages using the Metadata API.\n- `.claude/skills/next-best-practices/parallel-routes.md`: Parallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.\n- `.claude/skills/next-best-practices/route-handlers.md`: Create API endpoints with `route.ts` files.\n- `.claude/skills/next-best-practices/rsc-boundaries.md`: Detect and prevent invalid patterns when crossing Server/Client component boundaries.\n- `.claude/skills/next-best-practices/runtime-selection.md`: Use the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.\n- `.claude/skills/next-best-practices/scripts.md`: Loading third-party scripts in Next.js.\n- `.claude/skills/next-best-practices/self-hosting.md`: Deploy Next.js outside of Vercel with confidence.\n- `.claude/skills/next-best-practices/suspense-boundaries.md`: Client hooks that cause CSR bailout without Suspense boundaries.\n\n## Cache Components (Next.js 16+)\n\nNext.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag\n\n- `.claude/skills/next-cache-components/SKILL.md`\n\n## Upgrade Next.js\n\nUpgrade Next.js to the latest version following official migration guides and codemods\n\n- `.claude/skills/next-upgrade/SKILL.md`\n\n## Node.js Backend Patterns\n\nBuild production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.\n\n- `.claude/skills/nodejs-backend-patterns/SKILL.md`\n- `.claude/skills/nodejs-backend-patterns/references/advanced-patterns.md`: Advanced patterns for dependency injection, database integration, authentication, caching, and API response formatting.\n\n## Node.js Best Practices\n\nNode.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.\n\n- `.claude/skills/nodejs-best-practices/SKILL.md`\n\n## Performance optimization\n\nOptimize web performance for faster loading and better user experience. Use when asked to \"speed up my site\", \"optimize performance\", \"reduce load time\", \"fix slow loading\", \"improve page speed\", or \"performance audit\".\n\n- `.claude/skills/performance/SKILL.md`\n\n## SEO optimization\n\nOptimize for search engine visibility and ranking. Use when asked to \"improve SEO\", \"optimize for search\", \"fix meta tags\", \"add structured data\", \"sitemap optimization\", or \"search engine optimization\".\n\n- `.claude/skills/seo/SKILL.md`\n\n## Tailwind CSS Development Patterns\n\nProvides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or o...\n\n- `.claude/skills/tailwind-css-patterns/SKILL.md`\n- `.claude/skills/tailwind-css-patterns/references/accessibility.md`\n- `.claude/skills/tailwind-css-patterns/references/animations.md`: Usage:\n- `.claude/skills/tailwind-css-patterns/references/component-patterns.md`\n- `.claude/skills/tailwind-css-patterns/references/configuration.md`: Use the `@theme` directive for CSS-based configuration:\n- `.claude/skills/tailwind-css-patterns/references/layout-patterns.md`: Basic flex container:\n- `.claude/skills/tailwind-css-patterns/references/performance.md`: Configure content sources for optimal purging:\n- `.claude/skills/tailwind-css-patterns/references/reference.md`: Tailwind CSS is a utility-first CSS framework that generates styles by scanning HTML, JavaScript, and template files for class names. It provides a comprehensive design system through CSS utility classes, enabling rapid UI development without writing custom CSS. The framework operates at build-ti...\n- `.claude/skills/tailwind-css-patterns/references/responsive-design.md`: Enable dark mode in tailwind.config.js:\n\n## TypeScript Advanced Types\n\nMaster TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript pr...\n\n- `.claude/skills/typescript-advanced-types/SKILL.md`\n\n## React Composition Patterns\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n- `.claude/skills/vercel-composition-patterns/SKILL.md`\n- `.claude/skills/vercel-composition-patterns/AGENTS.md`: **Version 1.0.0** Engineering January 2026\n- `.claude/skills/vercel-composition-patterns/README.md`: A structured repository for React composition patterns that scale. These patterns help avoid boolean prop proliferation by using compound components, lifting state, and composing internals.\n- `.claude/skills/vercel-composition-patterns/rules/_sections.md`: This file defines all sections, their ordering, impact levels, and descriptions. The section ID (in parentheses) is the filename prefix used to group rules.\n- `.claude/skills/vercel-composition-patterns/rules/_template.md`: Brief explanation of the rule and why it matters.\n- `.claude/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md`: Don't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize component behavior. Each boolean doubles possible states and creates unmaintainable conditional logic. Use composition instead.\n- `.claude/skills/vercel-composition-patterns/rules/architecture-compound-components.md`: Structure complex components as compound components with a shared context. Each subcomponent accesses shared state via context, not props. Consumers compose the pieces they need.\n- `.claude/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md`: Use `children` for composition instead of `renderX` props. Children are more readable, compose naturally, and don't require understanding callback signatures.\n- `.claude/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md`: Instead of one component with many boolean props, create explicit variant components. Each variant composes the pieces it needs. The code documents itself.\n- `.claude/skills/vercel-composition-patterns/rules/react19-no-forwardref.md`: In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n- `.claude/skills/vercel-composition-patterns/rules/state-context-interface.md`: Define a **generic interface** for your component context with three parts: can implement—enabling the same UI components to work with completely different state implementations.\n- `.claude/skills/vercel-composition-patterns/rules/state-decouple-implementation.md`: The provider component should be the only place that knows how state is managed. UI components consume the context interface—they don't know if state comes from useState, Zustand, or a server sync.\n- `.claude/skills/vercel-composition-patterns/rules/state-lift-state.md`: Move state management into dedicated provider components. This allows sibling components outside the main UI to access and modify state without prop drilling or awkward refs.\n\n## Vercel React Best Practices\n\nReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimizati...\n\n- `.claude/skills/vercel-react-best-practices/SKILL.md`\n- `.claude/skills/vercel-react-best-practices/AGENTS.md`: **Version 1.0.0** Vercel Engineering January 2026\n- `.claude/skills/vercel-react-best-practices/README.md`: A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.\n- `.claude/skills/vercel-react-best-practices/rules/_sections.md`: This file defines all sections, their ordering, impact levels, and descriptions. The section ID (in parentheses) is the filename prefix used to group rules.\n- `.claude/skills/vercel-react-best-practices/rules/_template.md`: **Impact: MEDIUM (optional impact description)**\n- `.claude/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md`: Effect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect...\n- `.claude/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md`: Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.\n- `.claude/skills/vercel-react-best-practices/rules/advanced-init-once.md`: Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.\n- `.claude/skills/vercel-react-best-practices/rules/advanced-use-latest.md`: Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.\n- `.claude/skills/vercel-react-best-practices/rules/async-api-routes.md`: In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n- `.claude/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md`: When a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n- `.claude/skills/vercel-react-best-practices/rules/async-defer-await.md`: Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n- `.claude/skills/vercel-react-best-practices/rules/async-dependencies.md`: For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n- `.claude/skills/vercel-react-best-practices/rules/async-parallel.md`: When async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n- `.claude/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md`: Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md`: Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n- `.claude/skills/vercel-react-best-practices/rules/bundle-conditional.md`: Load large data or modules only when a feature is activated.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md`: Analytics, logging, and error tracking don't block user interaction. Load them after hydration.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md`: Use `next/dynamic` to lazy-load large components not needed on initial render.\n- `.claude/skills/vercel-react-best-practices/rules/bundle-preload.md`: Preload heavy bundles before they're needed to reduce perceived latency.\n- `.claude/skills/vercel-react-best-practices/rules/client-event-listeners.md`: Use `useSWRSubscription()` to share global event listeners across component instances.\n- `.claude/skills/vercel-react-best-practices/rules/client-localstorage-schema.md`: Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n- `.claude/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md`: Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n- `.claude/skills/vercel-react-best-practices/rules/client-swr-dedup.md`: SWR enables request deduplication, caching, and revalidation across component instances.\n- `.claude/skills/vercel-react-best-practices/rules/js-batch-dom-css.md`: Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-function-results.md`: Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-property-access.md`: Cache object property lookups in hot paths.\n- `.claude/skills/vercel-react-best-practices/rules/js-cache-storage.md`: **Incorrect (reads storage on every call):**\n- `.claude/skills/vercel-react-best-practices/rules/js-combine-iterations.md`: Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.\n- `.claude/skills/vercel-react-best-practices/rules/js-early-exit.md`: Return early when result is determined to skip unnecessary processing.\n- `.claude/skills/vercel-react-best-practices/rules/js-flatmap-filter.md`: **Impact: LOW-MEDIUM (eliminates intermediate array)**\n- `.claude/skills/vercel-react-best-practices/rules/js-hoist-regexp.md`: Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.\n- `.claude/skills/vercel-react-best-practices/rules/js-index-maps.md`: Multiple `.find()` calls by the same key should use a Map.\n- `.claude/skills/vercel-react-best-practices/rules/js-length-check-first.md`: When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.\n- `.claude/skills/vercel-react-best-practices/rules/js-min-max-loop.md`: Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.\n- `.claude/skills/vercel-react-best-practices/rules/js-request-idle-callback.md`: **Impact: MEDIUM (keeps UI responsive during background tasks)**\n- `.claude/skills/vercel-react-best-practices/rules/js-set-map-lookups.md`: Convert arrays to Set/Map for repeated membership checks.\n- `.claude/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md`: **Incorrect (mutates original array):**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-activity.md`: Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md`: Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-conditional-render.md`: Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-content-visibility.md`: Apply `content-visibility: auto` to defer off-screen rendering.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md`: Extract static JSX outside components to avoid re-creation.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md`: When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md`: In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide r...\n- `.claude/skills/vercel-react-best-practices/rules/rendering-resource-hints.md`: **Impact: HIGH (reduces load time for critical resources)**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md`: **Impact: HIGH (eliminates render-blocking)**\n- `.claude/skills/vercel-react-best-practices/rules/rendering-svg-precision.md`: Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.\n- `.claude/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md`: Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-defer-reads.md`: Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-dependencies.md`: Specify primitive dependencies instead of objects to minimize effect re-runs.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md`: If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-derived-state.md`: Subscribe to derived boolean state instead of continuous values to reduce re-render frequency.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md`: When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md`: Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md`: When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict...\n- `.claude/skills/vercel-react-best-practices/rules/rerender-memo.md`: Extract expensive work into memoized components to enable early returns before computation.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md`: If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md`: **Impact: HIGH (prevents remount on every render)**\n- `.claude/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md`: When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`. Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md`: When a hook contains multiple independent tasks with different dependencies, split them into separate hooks. A combined hook reruns all tasks when any dependency changes, even if some tasks don't use the changed value.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-transitions.md`: Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md`: When user input triggers expensive computations or renders, use `useDeferredValue` to keep the input responsive. The deferred value lags behind, allowing React to prioritize the input update and render the expensive result when idle.\n- `.claude/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md`: When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.\n- `.claude/skills/vercel-react-best-practices/rules/server-after-nonblocking.md`: Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.\n- `.claude/skills/vercel-react-best-practices/rules/server-auth-actions.md`: **Impact: CRITICAL (prevents unauthorized access to server mutations)**\n- `.claude/skills/vercel-react-best-practices/rules/server-cache-lru.md`: **Implementation:**\n- `.claude/skills/vercel-react-best-practices/rules/server-cache-react.md`: Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.\n- `.claude/skills/vercel-react-best-practices/rules/server-dedup-props.md`: **Impact: LOW (reduces network payload by avoiding duplicate serialization)**\n- `.claude/skills/vercel-react-best-practices/rules/server-hoist-static-io.md`: **Impact: HIGH (avoids repeated file/network I/O per request)**\n- `.claude/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md`: For React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race condit...\n- `.claude/skills/vercel-react-best-practices/rules/server-parallel-fetching.md`: React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.\n- `.claude/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md`: When fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.\n- `.claude/skills/vercel-react-best-practices/rules/server-serialization.md`: The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.\n\n## Web Interface Guidelines\n\nReview UI code for Web Interface Guidelines compliance. Use when asked to \"review my UI\", \"check accessibility\", \"audit design\", \"review UX\", or \"check my site against best practices\".\n\n- `.claude/skills/web-design-guidelines/SKILL.md`\n\n## Web quality audit\n\nComprehensive web quality audit covering performance, accessibility, SEO, and best practices. Use when asked to \"audit my site\", \"review web quality\", \"run lighthouse audit\", \"check page quality\", or \"optimize my website\".\n\n- `.claude/skills/web-quality-audit/SKILL.md`\n\n<!-- autoskills:end -->\n\n<!-- File: .agents/skills/accessibility/references/WCAG.md (Tokens: ~1980 | Category: anthropic-skill) -->\n# WCAG 2.1 Quick Reference\n\n## Success criteria by level\n\n### Level A (minimum)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.1.1** Non-text Content | All images, icons have text alternatives |\n| **1.2.1** Audio-only/Video-only | Provide transcript or audio description |\n| **1.2.2** Captions | Video with audio has captions |\n| **1.2.3** Audio Description | Video has audio description |\n| **1.3.1** Info and Relationships | Information conveyed through presentation is available programmatically |\n| **1.3.2** Meaningful Sequence | Reading order is logical |\n| **1.3.3** Sensory Characteristics | Instructions don't rely solely on shape, color, size, location, orientation, or sound |\n| **1.4.1** Use of Color | Color is not the only visual means of conveying information |\n| **1.4.2** Audio Control | Audio playing automatically can be paused/stopped |\n| **2.1.1** Keyboard | All functionality available via keyboard |\n| **2.1.2** No Keyboard Trap | Keyboard focus can be moved away from any component |\n| **2.1.4** Character Key Shortcuts | Single-key shortcuts can be turned off or remapped |\n| **2.2.1** Timing Adjustable | Time limits can be extended |\n| **2.2.2** Pause, Stop, Hide | Moving/blinking content can be paused |\n| **2.3.1** Three Flashes | Nothing flashes more than 3 times per second |\n| **2.4.1** Bypass Blocks | Skip link or landmark navigation available |\n| **2.4.2** Page Titled | Pages have descriptive titles |\n| **2.4.3** Focus Order | Focus order preserves meaning |\n| **2.4.4** Link Purpose | Link purpose clear from link text or context |\n| **2.5.1** Pointer Gestures | Multi-point gestures have single-pointer alternatives |\n| **2.5.2** Pointer Cancellation | Down-event doesn't trigger action (use up-event or click) |\n| **2.5.3** Label in Name | Accessible name contains visible label text |\n| **2.5.4** Motion Actuation | Motion-triggered functions have alternatives |\n| **3.1.1** Language of Page | Default language specified in HTML |\n| **3.2.1** On Focus | Focus doesn't trigger unexpected changes |\n| **3.2.2** On Input | Input doesn't trigger unexpected changes |\n| **3.3.1** Error Identification | Input errors clearly described |\n| **3.3.2** Labels or Instructions | Form inputs have labels or instructions |\n| **4.1.1** Parsing | HTML is well-formed (no duplicate IDs, proper nesting) |\n| **4.1.2** Name, Role, Value | UI components have accessible names and correct roles |\n\n### Level AA (standard)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.2.4** Captions (Live) | Live audio has captions |\n| **1.2.5** Audio Description | Pre-recorded video has audio description |\n| **1.3.4** Orientation | Content doesn't restrict orientation |\n| **1.3.5** Identify Input Purpose | Input purpose can be programmatically determined |\n| **1.4.3** Contrast (Minimum) | 4.5:1 for normal text, 3:1 for large text |\n| **1.4.4** Resize Text | Text can be resized to 200% without loss of functionality |\n| **1.4.5** Images of Text | Text used instead of images of text |\n| **1.4.10** Reflow | Content reflows at 320px width without horizontal scroll |\n| **1.4.11** Non-text Contrast | UI components have 3:1 contrast |\n| **1.4.12** Text Spacing | Content adapts to text spacing changes |\n| **1.4.13** Content on Hover/Focus | Additional content is dismissible, hoverable, persistent |\n| **2.4.5** Multiple Ways | Multiple ways to find pages |\n| **2.4.6** Headings and Labels | Headings and labels are descriptive |\n| **2.4.7** Focus Visible | Focus indicator is visible |\n| **3.1.2** Language of Parts | Language changes are marked |\n| **3.2.3** Consistent Navigation | Navigation is consistent across pages |\n| **3.2.4** Consistent Identification | Same functionality uses same labels |\n| **3.3.3** Error Suggestion | Error corrections suggested when known |\n| **3.3.4** Error Prevention (Legal) | Actions can be reversed or confirmed |\n| **4.1.3** Status Messages | Status messages announced to screen readers |\n\n### Level AAA (enhanced)\n\n| Criterion | Description |\n|-----------|-------------|\n| **1.4.6** Contrast (Enhanced) | 7:1 for normal text, 4.5:1 for large text |\n| **1.4.8** Visual Presentation | Foreground/background colors can be selected |\n| **1.4.9** Images of Text (No Exception) | No images of text |\n| **2.1.3** Keyboard (No Exception) | All functionality keyboard accessible |\n| **2.2.3** No Timing | No time limits |\n| **2.2.4** Interruptions | Interruptions can be postponed |\n| **2.2.5** Re-authenticating | Data preserved on re-authentication |\n| **2.2.6** Timeouts | Users warned about data loss from inactivity |\n| **2.3.2** Three Flashes | No content flashes more than 3 times |\n| **2.3.3** Animation from Interactions | Motion animation can be disabled |\n| **2.4.8** Location | User location within site is available |\n| **2.4.9** Link Purpose (Link Only) | Link purpose clear from link text alone |\n| **2.4.10** Section Headings | Sections have headings |\n| **3.1.3** Unusual Words | Definitions available for unusual words |\n| **3.1.4** Abbreviations | Abbreviations expanded |\n| **3.1.5** Reading Level | Alternative content for complex text |\n| **3.1.6** Pronunciation | Pronunciation available where needed |\n| **3.2.5** Change on Request | Changes initiated only by user |\n| **3.3.5** Help | Context-sensitive help available |\n| **3.3.6** Error Prevention (All) | All form submissions can be reviewed |\n\n## Common ARIA patterns\n\n### Buttons\n```html\n<button>Label</button>\n<!-- or -->\n<button aria-label=\"Close dialog\">×</button>\n```\n\n### Links\n```html\n<a href=\"/page\">Descriptive link text</a>\n<!-- External links -->\n<a href=\"https://external.com\" target=\"_blank\" rel=\"noopener\">\n  External site\n  <span class=\"visually-hidden\">(opens in new tab)</span>\n</a>\n```\n\n### Form fields\n```html\n<label for=\"email\">Email address</label>\n<input type=\"email\" id=\"email\" aria-describedby=\"email-hint\">\n<p id=\"email-hint\">We'll never share your email.</p>\n```\n\n### Error states\n```html\n<label for=\"email\">Email</label>\n<input type=\"email\" id=\"email\" aria-invalid=\"true\" aria-describedby=\"email-error\">\n<p id=\"email-error\" role=\"alert\">Please enter a valid email address.</p>\n```\n\n### Navigation\n```html\n<nav aria-label=\"Main\">\n  <ul>\n    <li><a href=\"/\" aria-current=\"page\">Home</a></li>\n    <li><a href=\"/about\">About</a></li>\n  </ul>\n</nav>\n```\n\n### Modals\n```html\n<div role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"dialog-title\">\n  <h2 id=\"dialog-title\">Confirm Action</h2>\n  <!-- content -->\n</div>\n```\n\n### Live regions\n```html\n<!-- Polite (waits for pause in speech) -->\n<div aria-live=\"polite\">Status update here</div>\n\n<!-- Assertive (interrupts immediately) -->\n<div aria-live=\"assertive\" role=\"alert\">Error message here</div>\n\n<!-- Status (polite, implicit) -->\n<div role=\"status\">Loading complete</div>\n```\n\n## Testing tools\n\n| Tool | Type | URL |\n|------|------|-----|\n| axe DevTools | Browser extension | [deque.com/axe](https://www.deque.com/axe/) |\n| WAVE | Browser extension | [wave.webaim.org](https://wave.webaim.org/) |\n| Lighthouse | Built into Chrome | DevTools → Lighthouse |\n| NVDA | Screen reader (Windows) | [nvaccess.org](https://www.nvaccess.org/) |\n| VoiceOver | Screen reader (Mac) | Built into macOS |\n| Colour Contrast Analyser | Desktop app | [tpgi.com](https://www.tpgi.com/color-contrast-checker/) |\n\n<!-- File: .agents/skills/accessibility/SKILL.md (Tokens: ~3275 | Category: anthropic-skill) -->\n---\nname: accessibility\ndescription: Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to \"improve accessibility\", \"a11y audit\", \"WCAG compliance\", \"screen reader support\", \"keyboard navigation\", or \"make accessible\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Accessibility (a11y)\n\nComprehensive accessibility guidelines based on WCAG 2.1 and Lighthouse accessibility audits. Goal: make content usable by everyone, including people with disabilities.\n\n## WCAG Principles: POUR\n\n| Principle | Description |\n|-----------|-------------|\n| **P**erceivable | Content can be perceived through different senses |\n| **O**perable | Interface can be operated by all users |\n| **U**nderstandable | Content and interface are understandable |\n| **R**obust | Content works with assistive technologies |\n\n## Conformance levels\n\n| Level | Requirement | Target |\n|-------|-------------|--------|\n| **A** | Minimum accessibility | Must pass |\n| **AA** | Standard compliance | Should pass (legal requirement in many jurisdictions) |\n| **AAA** | Enhanced accessibility | Nice to have |\n\n---\n\n## Perceivable\n\n### Text alternatives (1.1)\n\n**Images require alt text:**\n```html\n<!-- ❌ Missing alt -->\n<img src=\"chart.png\">\n\n<!-- ✅ Descriptive alt -->\n<img src=\"chart.png\" alt=\"Bar chart showing 40% increase in Q3 sales\">\n\n<!-- ✅ Decorative image (empty alt) -->\n<img src=\"decorative-border.png\" alt=\"\" role=\"presentation\">\n\n<!-- ✅ Complex image with longer description -->\n<figure>\n  <img src=\"infographic.png\" alt=\"2024 market trends infographic\" \n       aria-describedby=\"infographic-desc\">\n  <figcaption id=\"infographic-desc\">\n    <!-- Detailed description -->\n  </figcaption>\n</figure>\n```\n\n**Icon buttons need accessible names:**\n```html\n<!-- ❌ No accessible name -->\n<button><svg><!-- menu icon --></svg></button>\n\n<!-- ✅ Using aria-label -->\n<button aria-label=\"Open menu\">\n  <svg aria-hidden=\"true\"><!-- menu icon --></svg>\n</button>\n\n<!-- ✅ Using visually hidden text -->\n<button>\n  <svg aria-hidden=\"true\"><!-- menu icon --></svg>\n  <span class=\"visually-hidden\">Open menu</span>\n</button>\n```\n\n**Visually hidden class:**\n```css\n.visually-hidden {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n```\n\n### Color contrast (1.4.3, 1.4.6)\n\n| Text Size | AA minimum | AAA enhanced |\n|-----------|------------|--------------|\n| Normal text (< 18px / < 14px bold) | 4.5:1 | 7:1 |\n| Large text (≥ 18px / ≥ 14px bold) | 3:1 | 4.5:1 |\n| UI components & graphics | 3:1 | 3:1 |\n\n```css\n/* ❌ Low contrast (2.5:1) */\n.low-contrast {\n  color: #999;\n  background: #fff;\n}\n\n/* ✅ Sufficient contrast (7:1) */\n.high-contrast {\n  color: #333;\n  background: #fff;\n}\n\n/* ✅ Focus states need contrast too */\n:focus-visible {\n  outline: 2px solid #005fcc;\n  outline-offset: 2px;\n}\n```\n\n**Don't rely on color alone:**\n```html\n<!-- ❌ Only color indicates error -->\n<input class=\"error-border\">\n<style>.error-border { border-color: red; }</style>\n\n<!-- ✅ Color + icon + text -->\n<div class=\"field-error\">\n  <input aria-invalid=\"true\" aria-describedby=\"email-error\">\n  <span id=\"email-error\" class=\"error-message\">\n    <svg aria-hidden=\"true\"><!-- error icon --></svg>\n    Please enter a valid email address\n  </span>\n</div>\n```\n\n### Media alternatives (1.2)\n\n```html\n<!-- Video with captions -->\n<video controls>\n  <source src=\"video.mp4\" type=\"video/mp4\">\n  <track kind=\"captions\" src=\"captions.vtt\" srclang=\"en\" label=\"English\" default>\n  <track kind=\"descriptions\" src=\"descriptions.vtt\" srclang=\"en\" label=\"Descriptions\">\n</video>\n\n<!-- Audio with transcript -->\n<audio controls>\n  <source src=\"podcast.mp3\" type=\"audio/mp3\">\n</audio>\n<details>\n  <summary>Transcript</summary>\n  <p>Full transcript text...</p>\n</details>\n```\n\n---\n\n## Operable\n\n### Keyboard accessible (2.1)\n\n**All functionality must be keyboard accessible:**\n```javascript\n// ❌ Only handles click\nelement.addEventListener('click', handleAction);\n\n// ✅ Handles both click and keyboard\nelement.addEventListener('click', handleAction);\nelement.addEventListener('keydown', (e) => {\n  if (e.key === 'Enter' || e.key === ' ') {\n    e.preventDefault();\n    handleAction();\n  }\n});\n```\n\n**No keyboard traps:**\n```javascript\n// Modal focus management\nfunction openModal(modal) {\n  const focusableElements = modal.querySelectorAll(\n    'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n  );\n  const firstElement = focusableElements[0];\n  const lastElement = focusableElements[focusableElements.length - 1];\n  \n  // Trap focus within modal\n  modal.addEventListener('keydown', (e) => {\n    if (e.key === 'Tab') {\n      if (e.shiftKey && document.activeElement === firstElement) {\n        e.preventDefault();\n        lastElement.focus();\n      } else if (!e.shiftKey && document.activeElement === lastElement) {\n        e.preventDefault();\n        firstElement.focus();\n      }\n    }\n    if (e.key === 'Escape') {\n      closeModal();\n    }\n  });\n  \n  firstElement.focus();\n}\n```\n\n### Focus visible (2.4.7)\n\n```css\n/* ❌ Never remove focus outlines */\n*:focus { outline: none; }\n\n/* ✅ Use :focus-visible for keyboard-only focus */\n:focus {\n  outline: none;\n}\n\n:focus-visible {\n  outline: 2px solid #005fcc;\n  outline-offset: 2px;\n}\n\n/* ✅ Or custom focus styles */\nbutton:focus-visible {\n  box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);\n}\n```\n\n### Skip links (2.4.1)\n\n```html\n<body>\n  <a href=\"#main-content\" class=\"skip-link\">Skip to main content</a>\n  <header><!-- navigation --></header>\n  <main id=\"main-content\" tabindex=\"-1\">\n    <!-- main content -->\n  </main>\n</body>\n```\n\n```css\n.skip-link {\n  position: absolute;\n  top: -40px;\n  left: 0;\n  background: #000;\n  color: #fff;\n  padding: 8px 16px;\n  z-index: 100;\n}\n\n.skip-link:focus {\n  top: 0;\n}\n```\n\n### Timing (2.2)\n\n```javascript\n// Allow users to extend time limits\nfunction showSessionWarning() {\n  const modal = createModal({\n    title: 'Session Expiring',\n    content: 'Your session will expire in 2 minutes.',\n    actions: [\n      { label: 'Extend session', action: extendSession },\n      { label: 'Log out', action: logout }\n    ],\n    timeout: 120000 // 2 minutes to respond\n  });\n}\n```\n\n### Motion (2.3)\n\n```css\n/* Respect reduced motion preference */\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important;\n  }\n}\n```\n\n---\n\n## Understandable\n\n### Page language (3.1.1)\n\n```html\n<!-- ❌ No language specified -->\n<html>\n\n<!-- ✅ Language specified -->\n<html lang=\"en\">\n\n<!-- ✅ Language changes within page -->\n<p>The French word for hello is <span lang=\"fr\">bonjour</span>.</p>\n```\n\n### Consistent navigation (3.2.3)\n\n```html\n<!-- Navigation should be consistent across pages -->\n<nav aria-label=\"Main\">\n  <ul>\n    <li><a href=\"/\" aria-current=\"page\">Home</a></li>\n    <li><a href=\"/products\">Products</a></li>\n    <li><a href=\"/about\">About</a></li>\n  </ul>\n</nav>\n```\n\n### Form labels (3.3.2)\n\n```html\n<!-- ❌ No label association -->\n<input type=\"email\" placeholder=\"Email\">\n\n<!-- ✅ Explicit label -->\n<label for=\"email\">Email address</label>\n<input type=\"email\" id=\"email\" name=\"email\" \n       autocomplete=\"email\" required>\n\n<!-- ✅ Implicit label -->\n<label>\n  Email address\n  <input type=\"email\" name=\"email\" autocomplete=\"email\" required>\n</label>\n\n<!-- ✅ With instructions -->\n<label for=\"password\">Password</label>\n<input type=\"password\" id=\"password\" \n       aria-describedby=\"password-requirements\">\n<p id=\"password-requirements\">\n  Must be at least 8 characters with one number.\n</p>\n```\n\n### Error handling (3.3.1, 3.3.3)\n\n```html\n<!-- Announce errors to screen readers -->\n<form novalidate>\n  <div class=\"field\" aria-live=\"polite\">\n    <label for=\"email\">Email</label>\n    <input type=\"email\" id=\"email\" \n           aria-invalid=\"true\"\n           aria-describedby=\"email-error\">\n    <p id=\"email-error\" class=\"error\" role=\"alert\">\n      Please enter a valid email address (e.g., name@example.com)\n    </p>\n  </div>\n</form>\n```\n\n```javascript\n// Focus first error on submit\nform.addEventListener('submit', (e) => {\n  const firstError = form.querySelector('[aria-invalid=\"true\"]');\n  if (firstError) {\n    e.preventDefault();\n    firstError.focus();\n    \n    // Announce error summary\n    const errorSummary = document.getElementById('error-summary');\n    errorSummary.textContent = `${errors.length} errors found. Please fix them and try again.`;\n    errorSummary.focus();\n  }\n});\n```\n\n---\n\n## Robust\n\n### Valid HTML (4.1.1)\n\n```html\n<!-- ❌ Duplicate IDs -->\n<div id=\"content\">...</div>\n<div id=\"content\">...</div>\n\n<!-- ❌ Invalid nesting -->\n<a href=\"/\"><button>Click</button></a>\n\n<!-- ✅ Unique IDs -->\n<div id=\"main-content\">...</div>\n<div id=\"sidebar-content\">...</div>\n\n<!-- ✅ Proper nesting -->\n<a href=\"/\" class=\"button-link\">Click</a>\n```\n\n### ARIA usage (4.1.2)\n\n**Prefer native elements:**\n```html\n<!-- ❌ ARIA role on div -->\n<div role=\"button\" tabindex=\"0\">Click me</div>\n\n<!-- ✅ Native button -->\n<button>Click me</button>\n\n<!-- ❌ ARIA checkbox -->\n<div role=\"checkbox\" aria-checked=\"false\">Option</div>\n\n<!-- ✅ Native checkbox -->\n<label><input type=\"checkbox\"> Option</label>\n```\n\n**When ARIA is needed:**\n```html\n<!-- Custom tabs component -->\n<div role=\"tablist\" aria-label=\"Product information\">\n  <button role=\"tab\" id=\"tab-1\" aria-selected=\"true\" \n          aria-controls=\"panel-1\">Description</button>\n  <button role=\"tab\" id=\"tab-2\" aria-selected=\"false\" \n          aria-controls=\"panel-2\" tabindex=\"-1\">Reviews</button>\n</div>\n<div role=\"tabpanel\" id=\"panel-1\" aria-labelledby=\"tab-1\">\n  <!-- Panel content -->\n</div>\n<div role=\"tabpanel\" id=\"panel-2\" aria-labelledby=\"tab-2\" hidden>\n  <!-- Panel content -->\n</div>\n```\n\n### Live regions (4.1.3)\n\n```html\n<!-- Status updates -->\n<div aria-live=\"polite\" aria-atomic=\"true\" class=\"status\">\n  <!-- Content updates announced to screen readers -->\n</div>\n\n<!-- Urgent alerts -->\n<div role=\"alert\" aria-live=\"assertive\">\n  <!-- Interrupts current announcement -->\n</div>\n```\n\n```javascript\n// Announce dynamic content changes\nfunction showNotification(message, type = 'polite') {\n  const container = document.getElementById(`${type}-announcer`);\n  container.textContent = ''; // Clear first\n  requestAnimationFrame(() => {\n    container.textContent = message;\n  });\n}\n```\n\n---\n\n## Testing checklist\n\n### Automated testing\n```bash\n# Lighthouse accessibility audit\nnpx lighthouse https://example.com --only-categories=accessibility\n\n# axe-core\nnpm install @axe-core/cli -g\naxe https://example.com\n```\n\n### Manual testing\n\n- [ ] **Keyboard navigation:** Tab through entire page, use Enter/Space to activate\n- [ ] **Screen reader:** Test with VoiceOver (Mac), NVDA (Windows), or TalkBack (Android)\n- [ ] **Zoom:** Content usable at 200% zoom\n- [ ] **High contrast:** Test with Windows High Contrast Mode\n- [ ] **Reduced motion:** Test with `prefers-reduced-motion: reduce`\n- [ ] **Focus order:** Logical and follows visual order\n\n### Screen reader commands\n\n| Action | VoiceOver (Mac) | NVDA (Windows) |\n|--------|-----------------|----------------|\n| Start/Stop | ⌘ + F5 | Ctrl + Alt + N |\n| Next item | VO + → | ↓ |\n| Previous item | VO + ← | ↑ |\n| Activate | VO + Space | Enter |\n| Headings list | VO + U, then arrows | H / Shift + H |\n| Links list | VO + U | K / Shift + K |\n\n---\n\n## Common issues by impact\n\n### Critical (fix immediately)\n1. Missing form labels\n2. Missing image alt text\n3. Insufficient color contrast\n4. Keyboard traps\n5. No focus indicators\n\n### Serious (fix before launch)\n1. Missing page language\n2. Missing heading structure\n3. Non-descriptive link text\n4. Auto-playing media\n5. Missing skip links\n\n### Moderate (fix soon)\n1. Missing ARIA labels on icons\n2. Inconsistent navigation\n3. Missing error identification\n4. Timing without controls\n5. Missing landmark regions\n\n## References\n\n- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/)\n- [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)\n- [Deque axe Rules](https://dequeuniversity.com/rules/axe/)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n\n<!-- File: .agents/skills/best-practices/SKILL.md (Tokens: ~2955 | Category: anthropic-skill) -->\n---\nname: best-practices\ndescription: Apply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Best practices\n\nModern web development standards based on Lighthouse best practices audits. Covers security, browser compatibility, and code quality patterns.\n\n## Security\n\n### HTTPS everywhere\n\n**Enforce HTTPS:**\n```html\n<!-- ❌ Mixed content -->\n<img src=\"http://example.com/image.jpg\">\n<script src=\"http://cdn.example.com/script.js\"></script>\n\n<!-- ✅ HTTPS only -->\n<img src=\"https://example.com/image.jpg\">\n<script src=\"https://cdn.example.com/script.js\"></script>\n\n<!-- ✅ Protocol-relative (will use page's protocol) -->\n<img src=\"//example.com/image.jpg\">\n```\n\n**HSTS Header:**\n```\nStrict-Transport-Security: max-age=31536000; includeSubDomains; preload\n```\n\n### Content Security Policy (CSP)\n\n```html\n<!-- Basic CSP via meta tag -->\n<meta http-equiv=\"Content-Security-Policy\" \n      content=\"default-src 'self'; \n               script-src 'self' https://trusted-cdn.com; \n               style-src 'self' 'unsafe-inline';\n               img-src 'self' data: https:;\n               connect-src 'self' https://api.example.com;\">\n\n<!-- Better: HTTP header -->\n```\n\n**CSP Header (recommended):**\n```\nContent-Security-Policy: \n  default-src 'self';\n  script-src 'self' 'nonce-abc123' https://trusted.com;\n  style-src 'self' 'nonce-abc123';\n  img-src 'self' data: https:;\n  connect-src 'self' https://api.example.com;\n  frame-ancestors 'self';\n  base-uri 'self';\n  form-action 'self';\n```\n\n**Using nonces for inline scripts:**\n```html\n<script nonce=\"abc123\">\n  // This inline script is allowed\n</script>\n```\n\n### Security headers\n\n```\n# Prevent clickjacking\nX-Frame-Options: DENY\n\n# Prevent MIME type sniffing\nX-Content-Type-Options: nosniff\n\n# Enable XSS filter (legacy browsers)\nX-XSS-Protection: 1; mode=block\n\n# Control referrer information\nReferrer-Policy: strict-origin-when-cross-origin\n\n# Permissions policy (formerly Feature-Policy)\nPermissions-Policy: geolocation=(), microphone=(), camera=()\n```\n\n### No vulnerable libraries\n\n```bash\n# Check for vulnerabilities\nnpm audit\nyarn audit\n\n# Auto-fix when possible\nnpm audit fix\n\n# Check specific package\nnpm ls lodash\n```\n\n**Keep dependencies updated:**\n```json\n// package.json\n{\n  \"scripts\": {\n    \"audit\": \"npm audit --audit-level=moderate\",\n    \"update\": \"npm update && npm audit fix\"\n  }\n}\n```\n\n**Known vulnerable patterns to avoid:**\n```javascript\n// ❌ Prototype pollution vulnerable patterns\nObject.assign(target, userInput);\n_.merge(target, userInput);\n\n// ✅ Safer alternatives\nconst safeData = JSON.parse(JSON.stringify(userInput));\n```\n\n### Input sanitization\n\n```javascript\n// ❌ XSS vulnerable\nelement.innerHTML = userInput;\ndocument.write(userInput);\n\n// ✅ Safe text content\nelement.textContent = userInput;\n\n// ✅ If HTML needed, sanitize\nimport DOMPurify from 'dompurify';\nelement.innerHTML = DOMPurify.sanitize(userInput);\n```\n\n### Secure cookies\n\n```javascript\n// ❌ Insecure cookie\ndocument.cookie = \"session=abc123\";\n\n// ✅ Secure cookie (server-side)\nSet-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/\n```\n\n---\n\n## Browser compatibility\n\n### Doctype declaration\n\n```html\n<!-- ❌ Missing or invalid doctype -->\n<HTML>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n\n<!-- ✅ HTML5 doctype -->\n<!DOCTYPE html>\n<html lang=\"en\">\n```\n\n### Character encoding\n\n```html\n<!-- ❌ Missing or late charset -->\n<html>\n<head>\n  <title>Page</title>\n  <meta charset=\"UTF-8\">\n</head>\n\n<!-- ✅ Charset as first element in head -->\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Page</title>\n</head>\n```\n\n### Viewport meta tag\n\n```html\n<!-- ❌ Missing viewport -->\n<head>\n  <title>Page</title>\n</head>\n\n<!-- ✅ Responsive viewport -->\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>Page</title>\n</head>\n```\n\n### Feature detection\n\n```javascript\n// ❌ Browser detection (brittle)\nif (navigator.userAgent.includes('Chrome')) {\n  // Chrome-specific code\n}\n\n// ✅ Feature detection\nif ('IntersectionObserver' in window) {\n  // Use IntersectionObserver\n} else {\n  // Fallback\n}\n\n// ✅ Using @supports in CSS\n@supports (display: grid) {\n  .container {\n    display: grid;\n  }\n}\n\n@supports not (display: grid) {\n  .container {\n    display: flex;\n  }\n}\n```\n\n### Polyfills (when needed)\n\n```html\n<!-- Load polyfills conditionally -->\n<script>\n  if (!('fetch' in window)) {\n    document.write('<script src=\"/polyfills/fetch.js\"><\\/script>');\n  }\n</script>\n\n<!-- Or use polyfill.io -->\n<script src=\"https://polyfill.io/v3/polyfill.min.js?features=fetch,IntersectionObserver\"></script>\n```\n\n---\n\n## Deprecated APIs\n\n### Avoid these\n\n```javascript\n// ❌ document.write (blocks parsing)\ndocument.write('<script src=\"...\"></script>');\n\n// ✅ Dynamic script loading\nconst script = document.createElement('script');\nscript.src = '...';\ndocument.head.appendChild(script);\n\n// ❌ Synchronous XHR (blocks main thread)\nconst xhr = new XMLHttpRequest();\nxhr.open('GET', url, false); // false = synchronous\n\n// ✅ Async fetch\nconst response = await fetch(url);\n\n// ❌ Application Cache (deprecated)\n<html manifest=\"cache.manifest\">\n\n// ✅ Service Workers\nif ('serviceWorker' in navigator) {\n  navigator.serviceWorker.register('/sw.js');\n}\n```\n\n### Event listener passive\n\n```javascript\n// ❌ Non-passive touch/wheel (may block scrolling)\nelement.addEventListener('touchstart', handler);\nelement.addEventListener('wheel', handler);\n\n// ✅ Passive listeners (allows smooth scrolling)\nelement.addEventListener('touchstart', handler, { passive: true });\nelement.addEventListener('wheel', handler, { passive: true });\n\n// ✅ If you need preventDefault, be explicit\nelement.addEventListener('touchstart', handler, { passive: false });\n```\n\n---\n\n## Console & errors\n\n### No console errors\n\n```javascript\n// ❌ Errors in production\nconsole.log('Debug info'); // Remove in production\nthrow new Error('Unhandled'); // Catch all errors\n\n// ✅ Proper error handling\ntry {\n  riskyOperation();\n} catch (error) {\n  // Log to error tracking service\n  errorTracker.captureException(error);\n  // Show user-friendly message\n  showErrorMessage('Something went wrong. Please try again.');\n}\n```\n\n### Error boundaries (React)\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n  \n  static getDerivedStateFromError(error) {\n    return { hasError: true };\n  }\n  \n  componentDidCatch(error, info) {\n    errorTracker.captureException(error, { extra: info });\n  }\n  \n  render() {\n    if (this.state.hasError) {\n      return <FallbackUI />;\n    }\n    return this.props.children;\n  }\n}\n\n// Usage\n<ErrorBoundary>\n  <App />\n</ErrorBoundary>\n```\n\n### Global error handler\n\n```javascript\n// Catch unhandled errors\nwindow.addEventListener('error', (event) => {\n  errorTracker.captureException(event.error);\n});\n\n// Catch unhandled promise rejections\nwindow.addEventListener('unhandledrejection', (event) => {\n  errorTracker.captureException(event.reason);\n});\n```\n\n---\n\n## Source maps\n\n### Production configuration\n\n```javascript\n// ❌ Source maps exposed in production\n// webpack.config.js\nmodule.exports = {\n  devtool: 'source-map', // Exposes source code\n};\n\n// ✅ Hidden source maps (uploaded to error tracker)\nmodule.exports = {\n  devtool: 'hidden-source-map',\n};\n\n// ✅ Or no source maps in production\nmodule.exports = {\n  devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',\n};\n```\n\n---\n\n## Performance best practices\n\n### Avoid blocking patterns\n\n```javascript\n// ❌ Blocking script\n<script src=\"heavy-library.js\"></script>\n\n// ✅ Deferred script\n<script defer src=\"heavy-library.js\"></script>\n\n// ❌ Blocking CSS import\n@import url('other-styles.css');\n\n// ✅ Link tags (parallel loading)\n<link rel=\"stylesheet\" href=\"styles.css\">\n<link rel=\"stylesheet\" href=\"other-styles.css\">\n```\n\n### Efficient event handlers\n\n```javascript\n// ❌ Handler on every element\nitems.forEach(item => {\n  item.addEventListener('click', handleClick);\n});\n\n// ✅ Event delegation\ncontainer.addEventListener('click', (e) => {\n  if (e.target.matches('.item')) {\n    handleClick(e);\n  }\n});\n```\n\n### Memory management\n\n```javascript\n// ❌ Memory leak (never removed)\nconst handler = () => { /* ... */ };\nwindow.addEventListener('resize', handler);\n\n// ✅ Cleanup when done\nconst handler = () => { /* ... */ };\nwindow.addEventListener('resize', handler);\n\n// Later, when component unmounts:\nwindow.removeEventListener('resize', handler);\n\n// ✅ Using AbortController\nconst controller = new AbortController();\nwindow.addEventListener('resize', handler, { signal: controller.signal });\n\n// Cleanup:\ncontroller.abort();\n```\n\n---\n\n## Code quality\n\n### Valid HTML\n\n```html\n<!-- ❌ Invalid HTML -->\n<div id=\"header\">\n<div id=\"header\"> <!-- Duplicate ID -->\n\n<ul>\n  <div>Item</div> <!-- Invalid child -->\n</ul>\n\n<a href=\"/\"><button>Click</button></a> <!-- Invalid nesting -->\n\n<!-- ✅ Valid HTML -->\n<header id=\"site-header\">\n</header>\n\n<ul>\n  <li>Item</li>\n</ul>\n\n<a href=\"/\" class=\"button\">Click</a>\n```\n\n### Semantic HTML\n\n```html\n<!-- ❌ Non-semantic -->\n<div class=\"header\">\n  <div class=\"nav\">\n    <div class=\"nav-item\">Home</div>\n  </div>\n</div>\n<div class=\"main\">\n  <div class=\"article\">\n    <div class=\"title\">Headline</div>\n  </div>\n</div>\n\n<!-- ✅ Semantic HTML5 -->\n<header>\n  <nav>\n    <a href=\"/\">Home</a>\n  </nav>\n</header>\n<main>\n  <article>\n    <h1>Headline</h1>\n  </article>\n</main>\n```\n\n### Image aspect ratios\n\n```html\n<!-- ❌ Distorted images -->\n<img src=\"photo.jpg\" width=\"300\" height=\"100\">\n<!-- If actual ratio is 4:3, this squishes the image -->\n\n<!-- ✅ Preserve aspect ratio -->\n<img src=\"photo.jpg\" width=\"300\" height=\"225\">\n<!-- Actual 4:3 dimensions -->\n\n<!-- ✅ CSS object-fit for flexibility -->\n<img src=\"photo.jpg\" style=\"width: 300px; height: 200px; object-fit: cover;\">\n```\n\n---\n\n## Permissions & privacy\n\n### Request permissions properly\n\n```javascript\n// ❌ Request on page load (bad UX, often denied)\nnavigator.geolocation.getCurrentPosition(success, error);\n\n// ✅ Request in context, after user action\nfindNearbyButton.addEventListener('click', async () => {\n  // Explain why you need it\n  if (await showPermissionExplanation()) {\n    navigator.geolocation.getCurrentPosition(success, error);\n  }\n});\n```\n\n### Permissions policy\n\n```html\n<!-- Restrict powerful features -->\n<meta http-equiv=\"Permissions-Policy\" \n      content=\"geolocation=(), camera=(), microphone=()\">\n\n<!-- Or allow for specific origins -->\n<meta http-equiv=\"Permissions-Policy\" \n      content=\"geolocation=(self 'https://maps.example.com')\">\n```\n\n---\n\n## Audit checklist\n\n### Security (critical)\n- [ ] HTTPS enabled, no mixed content\n- [ ] No vulnerable dependencies (`npm audit`)\n- [ ] CSP headers configured\n- [ ] Security headers present\n- [ ] No exposed source maps\n\n### Compatibility\n- [ ] Valid HTML5 doctype\n- [ ] Charset declared first in head\n- [ ] Viewport meta tag present\n- [ ] No deprecated APIs used\n- [ ] Passive event listeners for scroll/touch\n\n### Code quality\n- [ ] No console errors\n- [ ] Valid HTML (no duplicate IDs)\n- [ ] Semantic HTML elements used\n- [ ] Proper error handling\n- [ ] Memory cleanup in components\n\n### UX\n- [ ] No intrusive interstitials\n- [ ] Permission requests in context\n- [ ] Clear error messages\n- [ ] Appropriate image aspect ratios\n\n## Tools\n\n| Tool | Purpose |\n|------|---------|\n| `npm audit` | Dependency vulnerabilities |\n| [SecurityHeaders.com](https://securityheaders.com) | Header analysis |\n| [W3C Validator](https://validator.w3.org) | HTML validation |\n| Lighthouse | Best practices audit |\n| [Observatory](https://observatory.mozilla.org) | Security scan |\n\n## References\n\n- [MDN Web Security](https://developer.mozilla.org/en-US/docs/Web/Security)\n- [OWASP Top 10](https://owasp.org/www-project-top-ten/)\n- [Web Quality Audit](../web-quality-audit/SKILL.md)\n\n<!-- File: .agents/skills/core-web-vitals/references/LCP.md (Tokens: ~1196 | Category: anthropic-skill) -->\n# LCP optimization reference\n\n## What is LCP?\n\nLargest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:\n\n- An `<img>` element\n- An `<image>` element inside `<svg>`\n- A `<video>` element with poster image\n- An element with a background image via `url()`\n- A block-level element containing text nodes\n\n## LCP timeline\n\n```\n[  Server Response  ][  Resource Load  ][  Render  ]\n       TTFB              Download         Paint\n       └─────────────────────────────────────┘\n                         LCP Time\n```\n\n## Detailed optimizations\n\n### 1. Server response time (TTFB)\n\nTarget: < 800ms\n\n**Causes:**\n- Slow server/database queries\n- No CDN/edge caching\n- Inefficient backend code\n- Cold starts (serverless)\n\n**Solutions:**\n```javascript\n// Use edge functions for dynamic content\n// Vercel example\nexport const config = { runtime: 'edge' };\n\n// Use stale-while-revalidate caching\n// Cache-Control header\nres.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate=300');\n```\n\n### 2. Resource load time\n\n**For images:**\n```html\n<!-- Preload LCP image -->\n<link rel=\"preload\" as=\"image\" href=\"/hero.webp\" \n      imagesrcset=\"/hero-400.webp 400w, /hero-800.webp 800w\"\n      imagesizes=\"100vw\"\n      fetchpriority=\"high\">\n\n<!-- Modern format with fallback -->\n<picture>\n  <source srcset=\"/hero.avif\" type=\"image/avif\">\n  <source srcset=\"/hero.webp\" type=\"image/webp\">\n  <img src=\"/hero.jpg\" width=\"1200\" height=\"600\" \n       fetchpriority=\"high\" alt=\"Hero\">\n</picture>\n```\n\n**For text (web fonts):**\n```css\n@font-face {\n  font-family: 'Heading';\n  src: url('/fonts/heading.woff2') format('woff2');\n  font-display: swap; /* Show fallback immediately */\n}\n```\n\n### 3. Render blocking resources\n\n**Critical CSS pattern:**\n```html\n<head>\n  <!-- Inline critical CSS -->\n  <style>\n    /* Only above-fold styles, < 14KB */\n    .hero { /* ... */ }\n    .nav { /* ... */ }\n  </style>\n  \n  <!-- Defer non-critical CSS -->\n  <link rel=\"preload\" href=\"/styles.css\" as=\"style\" \n        onload=\"this.onload=null;this.rel='stylesheet'\">\n</head>\n```\n\n**Defer JavaScript:**\n```html\n<!-- ❌ Blocks parsing -->\n<script src=\"/app.js\"></script>\n\n<!-- ✅ Deferred (runs after HTML parsed) -->\n<script defer src=\"/app.js\"></script>\n\n<!-- ✅ Module (deferred by default) -->\n<script type=\"module\" src=\"/app.mjs\"></script>\n```\n\n### 4. Client-side rendering\n\n**Problem:** Content not in initial HTML.\n\n**Solutions:**\n\n**Server-side rendering (SSR):**\n```javascript\n// Next.js\nexport async function getServerSideProps() {\n  const data = await fetchHeroContent();\n  return { props: { hero: data } };\n}\n```\n\n**Static site generation (SSG):**\n```javascript\n// Next.js\nexport async function getStaticProps() {\n  const data = await fetchHeroContent();\n  return { props: { hero: data }, revalidate: 3600 };\n}\n```\n\n**Streaming SSR:**\n```jsx\n// React 18+\nimport { Suspense } from 'react';\n\nfunction Page() {\n  return (\n    <Suspense fallback={<HeroSkeleton />}>\n      <Hero />\n    </Suspense>\n  );\n}\n```\n\n## Framework-specific tips\n\n### Next.js\n```jsx\nimport Image from 'next/image';\n\n// LCP image with priority\n<Image \n  src=\"/hero.jpg\"\n  priority\n  fill\n  sizes=\"100vw\"\n  alt=\"Hero\"\n/>\n```\n\n### Nuxt\n```vue\n<NuxtImg\n  src=\"/hero.jpg\"\n  preload\n  loading=\"eager\"\n  sizes=\"100vw\"\n/>\n```\n\n### Astro\n```astro\n---\nimport { Image } from 'astro:assets';\nimport hero from '../assets/hero.jpg';\n---\n<Image \n  src={hero} \n  loading=\"eager\" \n  decoding=\"sync\"\n  alt=\"Hero\" \n/>\n```\n\n## Debugging LCP\n\n```javascript\n// Identify LCP element\nnew PerformanceObserver((entryList) => {\n  const entries = entryList.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  \n  console.log('LCP:', {\n    element: lastEntry.element,\n    time: lastEntry.startTime,\n    size: lastEntry.size,\n    url: lastEntry.url,\n    renderTime: lastEntry.renderTime,\n    loadTime: lastEntry.loadTime\n  });\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n## Common issues\n\n| Issue | Impact | Fix |\n|-------|--------|-----|\n| No preload for LCP image | +500-1000ms | Add `<link rel=\"preload\">` |\n| Large unoptimized image | +300-800ms | Compress, use WebP/AVIF |\n| Render-blocking CSS | +200-500ms | Inline critical CSS |\n| Slow TTFB | +300-2000ms | CDN, edge caching |\n| Client-rendered content | +500-2000ms | SSR/SSG |\n\n<!-- File: .agents/skills/core-web-vitals/SKILL.md (Tokens: ~2973 | Category: anthropic-skill) -->\n---\nname: core-web-vitals\ndescription: Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Core Web Vitals optimization\n\nTargeted optimization for the three Core Web Vitals metrics that affect Google Search ranking and user experience.\n\n## The three metrics\n\n| Metric | Measures | Good | Needs work | Poor |\n|--------|----------|------|------------|------|\n| **LCP** | Loading | ≤ 2.5s | 2.5s – 4s | > 4s |\n| **INP** | Interactivity | ≤ 200ms | 200ms – 500ms | > 500ms |\n| **CLS** | Visual Stability | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |\n\nGoogle measures at the **75th percentile** — 75% of page visits must meet \"Good\" thresholds.\n\n---\n\n## LCP: Largest Contentful Paint\n\nLCP measures when the largest visible content element renders. Usually this is:\n- Hero image or video\n- Large text block\n- Background image\n- `<svg>` element\n\n### Common LCP issues\n\n**1. Slow server response (TTFB > 800ms)**\n```\nFix: CDN, caching, optimized backend, edge rendering\n```\n\n**2. Render-blocking resources**\n```html\n<!-- ❌ Blocks rendering -->\n<link rel=\"stylesheet\" href=\"/all-styles.css\">\n\n<!-- ✅ Critical CSS inlined, rest deferred -->\n<style>/* Critical above-fold CSS */</style>\n<link rel=\"preload\" href=\"/styles.css\" as=\"style\" \n      onload=\"this.onload=null;this.rel='stylesheet'\">\n```\n\n**3. Slow resource load times**\n```html\n<!-- ❌ No hints, discovered late -->\n<img src=\"/hero.jpg\" alt=\"Hero\">\n\n<!-- ✅ Preloaded with high priority -->\n<link rel=\"preload\" href=\"/hero.webp\" as=\"image\" fetchpriority=\"high\">\n<img src=\"/hero.webp\" alt=\"Hero\" fetchpriority=\"high\">\n```\n\n**4. Client-side rendering delays**\n```javascript\n// ❌ Content loads after JavaScript\nuseEffect(() => {\n  fetch('/api/hero-text').then(r => r.json()).then(setHeroText);\n}, []);\n\n// ✅ Server-side or static rendering\n// Use SSR, SSG, or streaming to send HTML with content\nexport async function getServerSideProps() {\n  const heroText = await fetchHeroText();\n  return { props: { heroText } };\n}\n```\n\n### LCP optimization checklist\n\n```markdown\n- [ ] TTFB < 800ms (use CDN, edge caching)\n- [ ] LCP image preloaded with fetchpriority=\"high\"\n- [ ] LCP image optimized (WebP/AVIF, correct size)\n- [ ] Critical CSS inlined (< 14KB)\n- [ ] No render-blocking JavaScript in <head>\n- [ ] Fonts don't block text rendering (font-display: swap)\n- [ ] LCP element in initial HTML (not JS-rendered)\n```\n\n### LCP element identification\n```javascript\n// Find your LCP element\nnew PerformanceObserver((list) => {\n  const entries = list.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  console.log('LCP element:', lastEntry.element);\n  console.log('LCP time:', lastEntry.startTime);\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n---\n\n## INP: Interaction to Next Paint\n\nINP measures responsiveness across ALL interactions (clicks, taps, key presses) during a page visit. It reports the worst interaction (at 98th percentile for high-traffic pages).\n\n### INP breakdown\n\nTotal INP = **Input Delay** + **Processing Time** + **Presentation Delay**\n\n| Phase | Target | Optimization |\n|-------|--------|--------------|\n| Input Delay | < 50ms | Reduce main thread blocking |\n| Processing | < 100ms | Optimize event handlers |\n| Presentation | < 50ms | Minimize rendering work |\n\n### Common INP issues\n\n**1. Long tasks blocking main thread**\n```javascript\n// ❌ Long synchronous task\nfunction processLargeArray(items) {\n  items.forEach(item => expensiveOperation(item));\n}\n\n// ✅ Break into chunks with yielding\nasync function processLargeArray(items) {\n  const CHUNK_SIZE = 100;\n  for (let i = 0; i < items.length; i += CHUNK_SIZE) {\n    const chunk = items.slice(i, i + CHUNK_SIZE);\n    chunk.forEach(item => expensiveOperation(item));\n    \n    // Yield to main thread\n    await new Promise(r => setTimeout(r, 0));\n    // Or use scheduler.yield() when available\n  }\n}\n```\n\n**2. Heavy event handlers**\n```javascript\n// ❌ All work in handler\nbutton.addEventListener('click', () => {\n  // Heavy computation\n  const result = calculateComplexThing();\n  // DOM updates\n  updateUI(result);\n  // Analytics\n  trackEvent('click');\n});\n\n// ✅ Prioritize visual feedback\nbutton.addEventListener('click', () => {\n  // Immediate visual feedback\n  button.classList.add('loading');\n  \n  // Defer non-critical work\n  requestAnimationFrame(() => {\n    const result = calculateComplexThing();\n    updateUI(result);\n  });\n  \n  // Use requestIdleCallback for analytics\n  requestIdleCallback(() => trackEvent('click'));\n});\n```\n\n**3. Third-party scripts**\n```javascript\n// ❌ Eagerly loaded, blocks interactions\n<script src=\"https://heavy-widget.com/widget.js\"></script>\n\n// ✅ Lazy loaded on interaction or visibility\nconst loadWidget = () => {\n  import('https://heavy-widget.com/widget.js')\n    .then(widget => widget.init());\n};\nbutton.addEventListener('click', loadWidget, { once: true });\n```\n\n**4. Excessive re-renders (React/Vue)**\n```javascript\n// ❌ Re-renders entire tree\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Counter count={count} />\n      <ExpensiveComponent /> {/* Re-renders on every count change */}\n    </div>\n  );\n}\n\n// ✅ Memoized expensive components\nconst MemoizedExpensive = React.memo(ExpensiveComponent);\n\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Counter count={count} />\n      <MemoizedExpensive />\n    </div>\n  );\n}\n```\n\n### INP optimization checklist\n\n```markdown\n- [ ] No tasks > 50ms on main thread\n- [ ] Event handlers complete quickly (< 100ms)\n- [ ] Visual feedback provided immediately\n- [ ] Heavy work deferred with requestIdleCallback\n- [ ] Third-party scripts don't block interactions\n- [ ] Debounced input handlers where appropriate\n- [ ] Web Workers for CPU-intensive operations\n```\n\n### INP debugging\n```javascript\n// Identify slow interactions\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (entry.duration > 200) {\n      console.warn('Slow interaction:', {\n        type: entry.name,\n        duration: entry.duration,\n        processingStart: entry.processingStart,\n        processingEnd: entry.processingEnd,\n        target: entry.target\n      });\n    }\n  }\n}).observe({ type: 'event', buffered: true, durationThreshold: 16 });\n```\n\n---\n\n## CLS: Cumulative Layout Shift\n\nCLS measures unexpected layout shifts. A shift occurs when a visible element changes position between frames without user interaction.\n\n**CLS Formula:** `impact fraction × distance fraction`\n\n### Common CLS causes\n\n**1. Images without dimensions**\n```html\n<!-- ❌ Causes layout shift when loaded -->\n<img src=\"photo.jpg\" alt=\"Photo\">\n\n<!-- ✅ Space reserved -->\n<img src=\"photo.jpg\" alt=\"Photo\" width=\"800\" height=\"600\">\n\n<!-- ✅ Or use aspect-ratio -->\n<img src=\"photo.jpg\" alt=\"Photo\" style=\"aspect-ratio: 4/3; width: 100%;\">\n```\n\n**2. Ads, embeds, and iframes**\n```html\n<!-- ❌ Unknown size until loaded -->\n<iframe src=\"https://ad-network.com/ad\"></iframe>\n\n<!-- ✅ Reserve space with min-height -->\n<div style=\"min-height: 250px;\">\n  <iframe src=\"https://ad-network.com/ad\" height=\"250\"></iframe>\n</div>\n\n<!-- ✅ Or use aspect-ratio container -->\n<div style=\"aspect-ratio: 16/9;\">\n  <iframe src=\"https://youtube.com/embed/...\" \n          style=\"width: 100%; height: 100%;\"></iframe>\n</div>\n```\n\n**3. Dynamically injected content**\n```javascript\n// ❌ Inserts content above viewport\nnotifications.prepend(newNotification);\n\n// ✅ Insert below viewport or use transform\nconst insertBelow = viewport.bottom < newNotification.top;\nif (insertBelow) {\n  notifications.prepend(newNotification);\n} else {\n  // Animate in without shifting\n  newNotification.style.transform = 'translateY(-100%)';\n  notifications.prepend(newNotification);\n  requestAnimationFrame(() => {\n    newNotification.style.transform = '';\n  });\n}\n```\n\n**4. Web fonts causing FOUT**\n```css\n/* ❌ Font swap shifts text */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n}\n\n/* ✅ Optional font (no shift if slow) */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: optional;\n}\n\n/* ✅ Or match fallback metrics */\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: swap;\n  size-adjust: 105%; /* Match fallback size */\n  ascent-override: 95%;\n  descent-override: 20%;\n}\n```\n\n**5. Animations triggering layout**\n```css\n/* ❌ Animates layout properties */\n.animate {\n  transition: height 0.3s, width 0.3s;\n}\n\n/* ✅ Use transform instead */\n.animate {\n  transition: transform 0.3s;\n}\n.animate.expanded {\n  transform: scale(1.2);\n}\n```\n\n### CLS optimization checklist\n\n```markdown\n- [ ] All images have width/height or aspect-ratio\n- [ ] All videos/embeds have reserved space\n- [ ] Ads have min-height containers\n- [ ] Fonts use font-display: optional or matched metrics\n- [ ] Dynamic content inserted below viewport\n- [ ] Animations use transform/opacity only\n- [ ] No content injected above existing content\n```\n\n### CLS debugging\n```javascript\n// Track layout shifts\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (!entry.hadRecentInput) {\n      console.log('Layout shift:', entry.value);\n      entry.sources?.forEach(source => {\n        console.log('  Shifted element:', source.node);\n        console.log('  Previous rect:', source.previousRect);\n        console.log('  Current rect:', source.currentRect);\n      });\n    }\n  }\n}).observe({ type: 'layout-shift', buffered: true });\n```\n\n---\n\n## Measurement tools\n\n### Lab testing\n- **Chrome DevTools** → Performance panel, Lighthouse\n- **WebPageTest** → Detailed waterfall, filmstrip\n- **Lighthouse CLI** → `npx lighthouse <url>`\n\n### Field data (real users)\n- **Chrome User Experience Report (CrUX)** → BigQuery or API\n- **Search Console** → Core Web Vitals report\n- **web-vitals library** → Send to your analytics\n\n```javascript\nimport {onLCP, onINP, onCLS} from 'web-vitals';\n\nfunction sendToAnalytics({name, value, rating}) {\n  gtag('event', name, {\n    event_category: 'Web Vitals',\n    value: Math.round(name === 'CLS' ? value * 1000 : value),\n    event_label: rating\n  });\n}\n\nonLCP(sendToAnalytics);\nonINP(sendToAnalytics);\nonCLS(sendToAnalytics);\n```\n\n---\n\n## Framework quick fixes\n\n### Next.js\n```jsx\n// LCP: Use next/image with priority\nimport Image from 'next/image';\n<Image src=\"/hero.jpg\" priority fill alt=\"Hero\" />\n\n// INP: Use dynamic imports\nconst HeavyComponent = dynamic(() => import('./Heavy'), { ssr: false });\n\n// CLS: Image component handles dimensions automatically\n```\n\n### React\n```jsx\n// LCP: Preload in head\n<link rel=\"preload\" href=\"/hero.jpg\" as=\"image\" fetchpriority=\"high\" />\n\n// INP: Memoize and useTransition\nconst [isPending, startTransition] = useTransition();\nstartTransition(() => setExpensiveState(newValue));\n\n// CLS: Always specify dimensions in img tags\n```\n\n### Vue/Nuxt\n```vue\n<!-- LCP: Use nuxt/image with preload -->\n<NuxtImg src=\"/hero.jpg\" preload loading=\"eager\" />\n\n<!-- INP: Use async components -->\n<component :is=\"() => import('./Heavy.vue')\" />\n\n<!-- CLS: Use aspect-ratio CSS -->\n<img :style=\"{ aspectRatio: '16/9' }\" />\n```\n\n## References\n\n- [web.dev LCP](https://web.dev/articles/lcp)\n- [web.dev INP](https://web.dev/articles/inp)\n- [web.dev CLS](https://web.dev/articles/cls)\n- [Performance skill](../performance/SKILL.md)\n\n<!-- File: .agents/skills/frontend-design/SKILL.md (Tokens: ~858 | Category: anthropic-skill) -->\n---\nname: frontend-design\ndescription: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.\nlicense: Complete terms in LICENSE.txt\n---\n\nThis skill guides creation of distinctive, production-grade frontend interfaces that avoid generic \"AI slop\" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.\n\nThe user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.\n\n## Design Thinking\n\nBefore coding, understand the context and commit to a BOLD aesthetic direction:\n- **Purpose**: What problem does this interface solve? Who uses it?\n- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.\n- **Constraints**: Technical requirements (framework, performance, accessibility).\n- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?\n\n**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.\n\nThen implement working code (HTML/CSS/JS, React, Vue, etc.) that is:\n- Production-grade and functional\n- Visually striking and memorable\n- Cohesive with a clear aesthetic point-of-view\n- Meticulously refined in every detail\n\n## Frontend Aesthetics Guidelines\n\nFocus on:\n- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.\n- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.\n- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.\n- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.\n- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.\n\nNEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.\n\nInterpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.\n\n**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.\n\nRemember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.\n\n<!-- File: .agents/skills/migrate-oxlint/SKILL.md (Tokens: ~2165 | Category: anthropic-skill) -->\n---\nname: migrate-oxlint\ndescription: Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint.\n---\n\nThis skill guides you through migrating a JavaScript/TypeScript project from ESLint to [Oxlint](https://oxc.rs/docs/guide/usage/linter/).\n\n## Overview\n\nOxlint is a high-performance linter that implements many popular ESLint rules natively in Rust. It can be used alongside ESLint or as a full replacement.\n\nAn official migration tool is available, and will be used by this skill: [`@oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate)\n\n## Step 1: Run Automated Migration\n\nRun the migration tool in the project root:\n\n```bash\nnpx @oxlint/migrate\n```\n\nThis reads your ESLint flat config (`eslint.config.js` for example) and generates a `.oxlintrc.json` file from it. It will find your ESLint config file automatically in most cases.\n\nSee options below for more info.\n\n### Key Options\n\n| Option                      | Description                                                                                                                     |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `--type-aware`              | Include type-aware rules from `@typescript-eslint` (will require the `oxlint-tsgolint` package to be installed after migrating) |\n| `--with-nursery`            | Include experimental rules still under development, may not be fully stable or consistent with ESLint equivalents               |\n| `--js-plugins [bool]`       | Enable/disable ESLint plugin migration via `jsPlugins` (default: enabled)                                                       |\n| `--details`                 | List rules that could not be migrated                                                                                           |\n| `--replace-eslint-comments` | Convert all `// eslint-disable` comments to `// oxlint-disable`                                                                 |\n| `--output-file <file>`      | Specify a different output path (default: `.oxlintrc.json`)                                                                     |\n\nIf your ESLint config is not at the default location, pass the path explicitly:\n\n```bash\nnpx @oxlint/migrate ./path/to/eslint.config.js\n```\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxlintrc.json`.\n\n### Plugin Mapping\n\nThe migration tool automatically maps ESLint plugins to oxlint's built-in equivalents. The following table is for reference when reviewing the generated config:\n\n| ESLint Plugin                                       | Oxlint Plugin Name |\n| --------------------------------------------------- | ------------------ |\n| `@typescript-eslint/eslint-plugin`                  | `typescript`       |\n| `eslint-plugin-react` / `eslint-plugin-react-hooks` | `react`            |\n| `eslint-plugin-import` / `eslint-plugin-import-x`   | `import`           |\n| `eslint-plugin-unicorn`                             | `unicorn`          |\n| `eslint-plugin-jsx-a11y`                            | `jsx-a11y`         |\n| `eslint-plugin-react-perf`                          | `react-perf`       |\n| `eslint-plugin-promise`                             | `promise`          |\n| `eslint-plugin-jest`                                | `jest`             |\n| `@vitest/eslint-plugin`                             | `vitest`           |\n| `eslint-plugin-jsdoc`                               | `jsdoc`            |\n| `eslint-plugin-next`                                | `nextjs`           |\n| `eslint-plugin-node`                                | `node`             |\n| `eslint-plugin-vue`                                 | `vue`              |\n\nDefault plugins (enabled when `plugins` field is omitted): `unicorn`, `typescript`, `oxc`.\nSetting the `plugins` array explicitly overrides these defaults.\n\nESLint core rules are usable in oxlint without needing to configure a plugin in the config file.\n\n### Rule Categories\n\nOxlint groups rules into categories for bulk configuration, though only `correctness` is enabled by default:\n\n```json\n{\n  \"categories\": {\n    \"correctness\": \"error\",\n    \"suspicious\": \"warn\"\n  }\n}\n```\n\nAvailable categories: `correctness` (default: enabled), `suspicious`, `pedantic`, `perf`, `style`, `restriction`, `nursery`.\n\nIndividual rule settings in `rules` override category settings.\n\n`@oxlint/migrate` will turn `correctness` off to avoid enabling additional rules that weren't enabled by your ESLint config. You can choose to enable additional categories after migration if desired.\n\n### Check Unmigrated Rules\n\nRun with `--details` to see which ESLint rules could not be migrated:\n\n```bash\nnpx @oxlint/migrate --details\n```\n\nReview the output and decide whether to keep ESLint for those rules or not. Some rules may be mentioned in the output from `--details` as having equivalents in oxlint that were not automatically mapped by the migration tool. In those cases, consider enabling the equivalent oxlint rule manually after migration.\n\n## Step 3: Install Oxlint\n\nInstall the core oxlint package (use `yarn install`, `pnpm install`, `vp install`, `bun install`, etc. depending on your package manager):\n\n```bash\nnpm install -D oxlint\n```\n\nIf you want to add the `oxlint-tsgolint` package, if you intend to use type-aware rules that require TypeScript type information:\n\n```bash\nnpm install -D oxlint-tsgolint\n```\n\nNo other packages besides the above are needed by default, though you will need to keep/install any additional ESLint plugins that were migrated into `jsPlugins`. Do not add `@oxlint/migrate` to the package.json, it is meant for one-off usage.\n\n## Step 4: Handle Unsupported Features\n\nSome features require manual attention:\n\n- Local plugins (relative path imports): Must be migrated manually to `jsPlugins`\n- `eslint-plugin-prettier`: Supported, but very slow. It is recommended to use [oxfmt](https://oxc.rs/docs/guide/usage/formatter) instead, or switch to `prettier --check` as a separate step alongside oxlint.\n- `settings` in override configs: Oxlint does not support `settings` inside `overrides` blocks.\n- ESLint v9+ plugins: Not all work with oxlint's JS Plugins API, but the majority will.\n\n### Local Plugins\n\nIf you have any custom ESLint rules in the project repo itself, you can migrate them manually after running the migration tool by adding them to the `jsPlugins` field in `.oxlintrc.json`:\n\n```json\n{\n  \"jsPlugins\": [\"./path/to/my-plugin.js\"],\n  \"rules\": {\n    \"local-plugin/rule-name\": \"error\"\n  }\n}\n```\n\n### External ESLint Plugins\n\nFor ESLint plugins without a built-in oxlint equivalent, use the `jsPlugins` field to load them:\n\n```json\n{\n  \"jsPlugins\": [\"eslint-plugin-custom\"],\n  \"rules\": {\n    \"custom/my-rule\": \"warn\"\n  }\n}\n```\n\n## Step 5: Update CI and Scripts\n\nReplace ESLint commands with oxlint. Path arguments are optional; oxlint defaults to the current working directory.\n\n```bash\n# Before\nnpx eslint src/\nnpx eslint --fix src/\n\n# After\nnpx oxlint src/\nnpx oxlint --fix src/\n```\n\n### Common CLI Options\n\n| ESLint                    | oxlint equivalent                              |\n| ------------------------- | ---------------------------------------------- |\n| `eslint .`                | `oxlint` (default: lints the cwd)              |\n| `eslint src/`             | `oxlint src/`                                  |\n| `eslint --fix`            | `oxlint --fix`                                 |\n| `eslint --max-warnings 0` | `oxlint --deny-warnings` or `--max-warnings 0` |\n| `eslint --format json`    | `oxlint --format json`                         |\n\nAdditional oxlint options:\n\n- `--tsconfig <path>`: Specify tsconfig.json path, likely unnecessary unless you have a non-standard name for `tsconfig.json`.\n\n## Tips\n\n- You can run alongside ESLint if necessary: Oxlint is designed to complement ESLint during migration, but with JS Plugins many projects can switch over fully without losing many rules.\n- Disable comments work: `// eslint-disable` and `// eslint-disable-next-line` comments are supported by oxlint. Use `--replace-eslint-comments` when running @oxlint/migrate to convert them to `// oxlint-disable` equivalents if desired.\n- List available rules: Run `npx oxlint --rules` to see all supported rules, or refer to the [rule documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).\n- Schema support: Add `\"$schema\": \"./node_modules/oxlint/configuration_schema.json\"` to `.oxlintrc.json` for editor autocompletion if the migration tool didn't do it automatically.\n- Output formats: `default`, `stylish`, `json`, `github`, `gitlab`, `junit`, `checkstyle`, `unix`\n- Ignore files: `.eslintignore` is supported by oxlint if you have it, but it's recommended to move any ignore patterns into the `ignorePatterns` field in `.oxlintrc.json` for consistency and simplicity. All files and paths ignored via a `.gitignore` file will be ignored by oxlint by default as well.\n- If you ran the migration tool multiple times, remove the `.oxlintrc.json.bak` backup file created by the migration tool once you've finished migrating.\n- If you are not using any JS Plugins and have replaced your ESLint configuration, you can remove all ESLint packages from your project dependencies.\n- Ensure your editor is configured to use oxlint instead of ESLint for linting and error reporting. You may want to install the Oxc extension for your preferred editor. See https://oxc.rs/docs/guide/usage/linter/editors.html for more details.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/linter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html)\n- [Complete Oxlint rule list and docs](https://oxc.rs/docs/guide/usage/linter/rules.html)\n\n<!-- File: .agents/skills/next-best-practices/async-patterns.md (Tokens: ~415 | Category: anthropic-skill) -->\n# Async Patterns\n\nIn Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.\n\n## Async Params and SearchParams\n\nAlways type them as `Promise<...>` and await them.\n\n### Pages and Layouts\n\n```tsx\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default async function Page({ params }: Props) {\n  const { slug } = await params\n}\n```\n\n### Route Handlers\n\n```tsx\nexport async function GET(\n  request: Request,\n  { params }: { params: Promise<{ id: string }> }\n) {\n  const { id } = await params\n}\n```\n\n### SearchParams\n\n```tsx\ntype Props = {\n  params: Promise<{ slug: string }>\n  searchParams: Promise<{ query?: string }>\n}\n\nexport default async function Page({ params, searchParams }: Props) {\n  const { slug } = await params\n  const { query } = await searchParams\n}\n```\n\n### Synchronous Components\n\nUse `React.use()` for non-async components:\n\n```tsx\nimport { use } from 'react'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default function Page({ params }: Props) {\n  const { slug } = use(params)\n}\n```\n\n### generateMetadata\n\n```tsx\ntype Props = { params: Promise<{ slug: string }> }\n\nexport async function generateMetadata({ params }: Props): Promise<Metadata> {\n  const { slug } = await params\n  return { title: slug }\n}\n```\n\n## Async Cookies and Headers\n\n```tsx\nimport { cookies, headers } from 'next/headers'\n\nexport default async function Page() {\n  const cookieStore = await cookies()\n  const headersList = await headers()\n\n  const theme = cookieStore.get('theme')\n  const userAgent = headersList.get('user-agent')\n}\n```\n\n## Migration Codemod\n\n```bash\nnpx @next/codemod@latest next-async-request-api .\n```\n\n<!-- File: .agents/skills/next-best-practices/bundling.md (Tokens: ~1105 | Category: anthropic-skill) -->\n# Bundling\n\nFix common bundling issues with third-party packages.\n\n## Server-Incompatible Packages\n\nSome packages use browser APIs (`window`, `document`, `localStorage`) and fail in Server Components.\n\n### Error Signs\n\n```\nReferenceError: window is not defined\nReferenceError: document is not defined\nReferenceError: localStorage is not defined\nModule not found: Can't resolve 'fs'\n```\n\n### Solution 1: Mark as Client-Only\n\nIf the package is only needed on client:\n\n```tsx\n// Bad: Fails - package uses window\nimport SomeChart from 'some-chart-library'\n\nexport default function Page() {\n  return <SomeChart />\n}\n\n// Good: Use dynamic import with ssr: false\nimport dynamic from 'next/dynamic'\n\nconst SomeChart = dynamic(() => import('some-chart-library'), {\n  ssr: false,\n})\n\nexport default function Page() {\n  return <SomeChart />\n}\n```\n\n### Solution 2: Externalize from Server Bundle\n\nFor packages that should run on server but have bundling issues:\n\n```js\n// next.config.js\nmodule.exports = {\n  serverExternalPackages: ['problematic-package'],\n}\n```\n\nUse this for:\n- Packages with native bindings (sharp, bcrypt)\n- Packages that don't bundle well (some ORMs)\n- Packages with circular dependencies\n\n### Solution 3: Client Component Wrapper\n\nWrap the entire usage in a client component:\n\n```tsx\n// components/ChartWrapper.tsx\n'use client'\n\nimport { Chart } from 'chart-library'\n\nexport function ChartWrapper(props) {\n  return <Chart {...props} />\n}\n\n// app/page.tsx (server component)\nimport { ChartWrapper } from '@/components/ChartWrapper'\n\nexport default function Page() {\n  return <ChartWrapper data={data} />\n}\n```\n\n## CSS Imports\n\nImport CSS files instead of using `<link>` tags. Next.js handles bundling and optimization.\n\n```tsx\n// Bad: Manual link tag\n<link rel=\"stylesheet\" href=\"/styles.css\" />\n\n// Good: Import CSS\nimport './styles.css'\n\n// Good: CSS Modules\nimport styles from './Button.module.css'\n```\n\n## Polyfills\n\nNext.js includes common polyfills automatically. Don't load redundant ones from polyfill.io or similar CDNs.\n\nAlready included: `Array.from`, `Object.assign`, `Promise`, `fetch`, `Map`, `Set`, `Symbol`, `URLSearchParams`, and 50+ others.\n\n```tsx\n// Bad: Redundant polyfills\n<script src=\"https://polyfill.io/v3/polyfill.min.js?features=fetch,Promise,Array.from\" />\n\n// Good: Next.js includes these automatically\n```\n\n## ESM/CommonJS Issues\n\n### Error Signs\n\n```\nSyntaxError: Cannot use import statement outside a module\nError: require() of ES Module\nModule not found: ESM packages need to be imported\n```\n\n### Solution: Transpile Package\n\n```js\n// next.config.js\nmodule.exports = {\n  transpilePackages: ['some-esm-package', 'another-package'],\n}\n```\n\n## Common Problematic Packages\n\n| Package | Issue | Solution |\n|---------|-------|----------|\n| `sharp` | Native bindings | `serverExternalPackages: ['sharp']` |\n| `bcrypt` | Native bindings | `serverExternalPackages: ['bcrypt']` or use `bcryptjs` |\n| `canvas` | Native bindings | `serverExternalPackages: ['canvas']` |\n| `recharts` | Uses window | `dynamic(() => import('recharts'), { ssr: false })` |\n| `react-quill` | Uses document | `dynamic(() => import('react-quill'), { ssr: false })` |\n| `mapbox-gl` | Uses window | `dynamic(() => import('mapbox-gl'), { ssr: false })` |\n| `monaco-editor` | Uses window | `dynamic(() => import('@monaco-editor/react'), { ssr: false })` |\n| `lottie-web` | Uses document | `dynamic(() => import('lottie-react'), { ssr: false })` |\n\n## Bundle Analysis\n\nAnalyze bundle size with the built-in analyzer (Next.js 16.1+):\n\n```bash\nnext experimental-analyze\n```\n\nThis opens an interactive UI to:\n- Filter by route, environment (client/server), and type\n- Inspect module sizes and import chains\n- View treemap visualization\n\nSave output for comparison:\n\n```bash\nnext experimental-analyze --output\n# Output saved to .next/diagnostics/analyze\n```\n\nReference: https://nextjs.org/docs/app/guides/package-bundling\n\n## Migrating from Webpack to Turbopack\n\nTurbopack is the default bundler in Next.js 15+. If you have custom webpack config, migrate to Turbopack-compatible alternatives:\n\n```js\n// next.config.js\nmodule.exports = {\n  // Good: Works with Turbopack\n  serverExternalPackages: ['package'],\n  transpilePackages: ['package'],\n\n  // Bad: Webpack-only - migrate away from this\n  webpack: (config) => {\n    // custom webpack config\n  },\n}\n```\n\nReference: https://nextjs.org/docs/app/building-your-application/upgrading/from-webpack-to-turbopack\n\n<!-- File: .agents/skills/next-best-practices/data-patterns.md (Tokens: ~1614 | Category: anthropic-skill) -->\n# Data Patterns\n\nChoose the right data fetching pattern for each use case.\n\n## Decision Tree\n\n```\nNeed to fetch data?\n├── From a Server Component?\n│   └── Use: Fetch directly (no API needed)\n│\n├── From a Client Component?\n│   ├── Is it a mutation (POST/PUT/DELETE)?\n│   │   └── Use: Server Action\n│   └── Is it a read (GET)?\n│       └── Use: Route Handler OR pass from Server Component\n│\n├── Need external API access (webhooks, third parties)?\n│   └── Use: Route Handler\n│\n└── Need REST API for mobile app / external clients?\n    └── Use: Route Handler\n```\n\n## Pattern 1: Server Components (Preferred for Reads)\n\nFetch data directly in Server Components - no API layer needed.\n\n```tsx\n// app/users/page.tsx\nasync function UsersPage() {\n  // Direct database access - no API round-trip\n  const users = await db.user.findMany();\n\n  // Or fetch from external API\n  const posts = await fetch('https://api.example.com/posts').then(r => r.json());\n\n  return (\n    <ul>\n      {users.map(user => <li key={user.id}>{user.name}</li>)}\n    </ul>\n  );\n}\n```\n\n**Benefits**:\n- No API to maintain\n- No client-server waterfall\n- Secrets stay on server\n- Direct database access\n\n## Pattern 2: Server Actions (Preferred for Mutations)\n\nServer Actions are the recommended way to handle mutations.\n\n```tsx\n// app/actions.ts\n'use server';\n\nimport { revalidatePath } from 'next/cache';\n\nexport async function createPost(formData: FormData) {\n  const title = formData.get('title') as string;\n\n  await db.post.create({ data: { title } });\n\n  revalidatePath('/posts');\n}\n\nexport async function deletePost(id: string) {\n  await db.post.delete({ where: { id } });\n\n  revalidateTag('posts');\n}\n```\n\n```tsx\n// app/posts/new/page.tsx\nimport { createPost } from '@/app/actions';\n\nexport default function NewPost() {\n  return (\n    <form action={createPost}>\n      <input name=\"title\" required />\n      <button type=\"submit\">Create</button>\n    </form>\n  );\n}\n```\n\n**Benefits**:\n- End-to-end type safety\n- Progressive enhancement (works without JS)\n- Automatic request handling\n- Integrated with React transitions\n\n**Constraints**:\n- POST only (no GET caching semantics)\n- Internal use only (no external access)\n- Cannot return non-serializable data\n\n## Pattern 3: Route Handlers (APIs)\n\nUse Route Handlers when you need a REST API.\n\n```tsx\n// app/api/posts/route.ts\nimport { NextRequest, NextResponse } from 'next/server';\n\n// GET is cacheable\nexport async function GET(request: NextRequest) {\n  const posts = await db.post.findMany();\n  return NextResponse.json(posts);\n}\n\n// POST for mutations\nexport async function POST(request: NextRequest) {\n  const body = await request.json();\n  const post = await db.post.create({ data: body });\n  return NextResponse.json(post, { status: 201 });\n}\n```\n\n**When to use**:\n- External API access (mobile apps, third parties)\n- Webhooks from external services\n- GET endpoints that need HTTP caching\n- OpenAPI/Swagger documentation needed\n\n**When NOT to use**:\n- Internal data fetching (use Server Components)\n- Mutations from your UI (use Server Actions)\n\n## Avoiding Data Waterfalls\n\n### Problem: Sequential Fetches\n\n```tsx\n// Bad: Sequential waterfalls\nasync function Dashboard() {\n  const user = await getUser();        // Wait...\n  const posts = await getPosts();      // Then wait...\n  const comments = await getComments(); // Then wait...\n\n  return <div>...</div>;\n}\n```\n\n### Solution 1: Parallel Fetching with Promise.all\n\n```tsx\n// Good: Parallel fetching\nasync function Dashboard() {\n  const [user, posts, comments] = await Promise.all([\n    getUser(),\n    getPosts(),\n    getComments(),\n  ]);\n\n  return <div>...</div>;\n}\n```\n\n### Solution 2: Streaming with Suspense\n\n```tsx\n// Good: Show content progressively\nimport { Suspense } from 'react';\n\nasync function Dashboard() {\n  return (\n    <div>\n      <Suspense fallback={<UserSkeleton />}>\n        <UserSection />\n      </Suspense>\n      <Suspense fallback={<PostsSkeleton />}>\n        <PostsSection />\n      </Suspense>\n    </div>\n  );\n}\n\nasync function UserSection() {\n  const user = await getUser(); // Fetches independently\n  return <div>{user.name}</div>;\n}\n\nasync function PostsSection() {\n  const posts = await getPosts(); // Fetches independently\n  return <PostList posts={posts} />;\n}\n```\n\n### Solution 3: Preload Pattern\n\n```tsx\n// lib/data.ts\nimport { cache } from 'react';\n\nexport const getUser = cache(async (id: string) => {\n  return db.user.findUnique({ where: { id } });\n});\n\nexport const preloadUser = (id: string) => {\n  void getUser(id); // Fire and forget\n};\n```\n\n```tsx\n// app/user/[id]/page.tsx\nimport { getUser, preloadUser } from '@/lib/data';\n\nexport default async function UserPage({ params }) {\n  const { id } = await params;\n\n  // Start fetching early\n  preloadUser(id);\n\n  // Do other work...\n\n  // Data likely ready by now\n  const user = await getUser(id);\n  return <div>{user.name}</div>;\n}\n```\n\n## Client Component Data Fetching\n\nWhen Client Components need data:\n\n### Option 1: Pass from Server Component (Preferred)\n\n```tsx\n// Server Component\nasync function Page() {\n  const data = await fetchData();\n  return <ClientComponent initialData={data} />;\n}\n\n// Client Component\n'use client';\nfunction ClientComponent({ initialData }) {\n  const [data, setData] = useState(initialData);\n  // ...\n}\n```\n\n### Option 2: Fetch on Mount (When Necessary)\n\n```tsx\n'use client';\nimport { useEffect, useState } from 'react';\n\nfunction ClientComponent() {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    fetch('/api/data')\n      .then(r => r.json())\n      .then(setData);\n  }, []);\n\n  if (!data) return <Loading />;\n  return <div>{data.value}</div>;\n}\n```\n\n### Option 3: Server Action for Reads (Works But Not Ideal)\n\nServer Actions can be called from Client Components for reads, but this is not their intended purpose:\n\n```tsx\n'use client';\nimport { getData } from './actions';\nimport { useEffect, useState } from 'react';\n\nfunction ClientComponent() {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    getData().then(setData);\n  }, []);\n\n  return <div>{data?.value}</div>;\n}\n```\n\n**Note**: Server Actions always use POST, so no HTTP caching. Prefer Route Handlers for cacheable reads.\n\n## Quick Reference\n\n| Pattern | Use Case | HTTP Method | Caching |\n|---------|----------|-------------|---------|\n| Server Component fetch | Internal reads | Any | Full Next.js caching |\n| Server Action | Mutations, form submissions | POST only | No |\n| Route Handler | External APIs, webhooks | Any | GET can be cached |\n| Client fetch to API | Client-side reads | Any | HTTP cache headers |\n\n<!-- File: .agents/skills/next-best-practices/debug-tricks.md (Tokens: ~781 | Category: anthropic-skill) -->\n# Debug Tricks\n\nTricks to speed up debugging Next.js applications.\n\n## MCP Endpoint (Dev Server)\n\nNext.js exposes a `/_next/mcp` endpoint in development for AI-assisted debugging via MCP (Model Context Protocol).\n\n- **Next.js 16+**: Enabled by default, use `next-devtools-mcp`\n- **Next.js < 16**: Requires `experimental.mcpServer: true` in next.config.js\n\nReference: https://nextjs.org/docs/app/guides/mcp\n\n**Important**: Find the actual port of the running Next.js dev server (check terminal output or `package.json` scripts). Don't assume port 3000.\n\n### Request Format\n\nThe endpoint uses JSON-RPC 2.0 over HTTP POST:\n\n```bash\ncurl -X POST http://localhost:<port>/_next/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": \"1\",\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"<tool-name>\",\n      \"arguments\": {}\n    }\n  }'\n```\n\n### Available Tools\n\n#### `get_errors`\nGet current errors from dev server (build errors, runtime errors with source-mapped stacks):\n```json\n{ \"name\": \"get_errors\", \"arguments\": {} }\n```\n\n#### `get_routes`\nDiscover all routes by scanning filesystem:\n```json\n{ \"name\": \"get_routes\", \"arguments\": {} }\n// Optional: { \"name\": \"get_routes\", \"arguments\": { \"routerType\": \"app\" } }\n```\nReturns: `{ \"appRouter\": [\"/\", \"/api/users/[id]\", ...], \"pagesRouter\": [...] }`\n\n#### `get_project_metadata`\nGet project path and dev server URL:\n```json\n{ \"name\": \"get_project_metadata\", \"arguments\": {} }\n```\nReturns: `{ \"projectPath\": \"/path/to/project\", \"devServerUrl\": \"http://localhost:3000\" }`\n\n#### `get_page_metadata`\nGet runtime metadata about current page render (requires active browser session):\n```json\n{ \"name\": \"get_page_metadata\", \"arguments\": {} }\n```\nReturns segment trie data showing layouts, boundaries, and page components.\n\n#### `get_logs`\nGet path to Next.js development log file:\n```json\n{ \"name\": \"get_logs\", \"arguments\": {} }\n```\nReturns path to `<distDir>/logs/next-development.log`\n\n#### `get_server_action_by_id`\nLocate a Server Action by ID:\n```json\n{ \"name\": \"get_server_action_by_id\", \"arguments\": { \"actionId\": \"<action-id>\" } }\n```\n\n### Example: Get Errors\n\n```bash\ncurl -X POST http://localhost:<port>/_next/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"method\":\"tools/call\",\"params\":{\"name\":\"get_errors\",\"arguments\":{}}}'\n```\n\n## Rebuild Specific Routes (Next.js 16+)\n\nUse `--debug-build-paths` to rebuild only specific routes instead of the entire app:\n\n```bash\n# Rebuild a specific route\nnext build --debug-build-paths \"/dashboard\"\n\n# Rebuild routes matching a glob\nnext build --debug-build-paths \"/api/*\"\n\n# Dynamic routes\nnext build --debug-build-paths \"/blog/[slug]\"\n```\n\nUse this to:\n- Quickly verify a build fix without full rebuild\n- Debug static generation issues for specific pages\n- Iterate faster on build errors\n\n<!-- File: .agents/skills/next-best-practices/directives.md (Tokens: ~351 | Category: anthropic-skill) -->\n# Directives\n\n## React Directives\n\nThese are React directives, not Next.js specific.\n\n### `'use client'`\n\nMarks a component as a Client Component. Required for:\n- React hooks (`useState`, `useEffect`, etc.)\n- Event handlers (`onClick`, `onChange`)\n- Browser APIs (`window`, `localStorage`)\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\n\nexport function Counter() {\n  const [count, setCount] = useState(0)\n  return <button onClick={() => setCount(count + 1)}>{count}</button>\n}\n```\n\nReference: https://react.dev/reference/rsc/use-client\n\n### `'use server'`\n\nMarks a function as a Server Action. Can be passed to Client Components.\n\n```tsx\n'use server'\n\nexport async function submitForm(formData: FormData) {\n  // Runs on server\n}\n```\n\nOr inline within a Server Component:\n\n```tsx\nexport default function Page() {\n  async function submit() {\n    'use server'\n    // Runs on server\n  }\n  return <form action={submit}>...</form>\n}\n```\n\nReference: https://react.dev/reference/rsc/use-server\n\n---\n\n## Next.js Directive\n\n### `'use cache'`\n\nMarks a function or component for caching. Part of Next.js Cache Components.\n\n```tsx\n'use cache'\n\nexport async function getCachedData() {\n  return await fetchData()\n}\n```\n\nRequires `cacheComponents: true` in `next.config.ts`.\n\nFor detailed usage including cache profiles, `cacheLife()`, `cacheTag()`, and `updateTag()`, see the `next-cache-components` skill.\n\nReference: https://nextjs.org/docs/app/api-reference/directives/use-cache\n\n<!-- File: .agents/skills/next-best-practices/error-handling.md (Tokens: ~1198 | Category: anthropic-skill) -->\n# Error Handling\n\nHandle errors gracefully in Next.js applications.\n\nReference: https://nextjs.org/docs/app/getting-started/error-handling\n\n## Error Boundaries\n\n### `error.tsx`\n\nCatches errors in a route segment and its children:\n\n```tsx\n'use client'\n\nexport default function Error({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  return (\n    <div>\n      <h2>Something went wrong!</h2>\n      <button onClick={() => reset()}>Try again</button>\n    </div>\n  )\n}\n```\n\n**Important:** `error.tsx` must be a Client Component.\n\n### `global-error.tsx`\n\nCatches errors in root layout:\n\n```tsx\n'use client'\n\nexport default function GlobalError({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  return (\n    <html>\n      <body>\n        <h2>Something went wrong!</h2>\n        <button onClick={() => reset()}>Try again</button>\n      </body>\n    </html>\n  )\n}\n```\n\n**Important:** Must include `<html>` and `<body>` tags.\n\n## Server Actions: Navigation API Gotcha\n\n**Do NOT wrap navigation APIs in try-catch.** They throw special errors that Next.js handles internally.\n\nReference: https://nextjs.org/docs/app/api-reference/functions/redirect#behavior\n\n```tsx\n'use server'\n\nimport { redirect } from 'next/navigation'\nimport { notFound } from 'next/navigation'\n\n// Bad: try-catch catches the navigation \"error\"\nasync function createPost(formData: FormData) {\n  try {\n    const post = await db.post.create({ ... })\n    redirect(`/posts/${post.id}`)  // This throws!\n  } catch (error) {\n    // redirect() throw is caught here - navigation fails!\n    return { error: 'Failed to create post' }\n  }\n}\n\n// Good: Call navigation APIs outside try-catch\nasync function createPost(formData: FormData) {\n  let post\n  try {\n    post = await db.post.create({ ... })\n  } catch (error) {\n    return { error: 'Failed to create post' }\n  }\n  redirect(`/posts/${post.id}`)  // Outside try-catch\n}\n\n// Good: Re-throw navigation errors\nasync function createPost(formData: FormData) {\n  try {\n    const post = await db.post.create({ ... })\n    redirect(`/posts/${post.id}`)\n  } catch (error) {\n    if (error instanceof Error && error.message === 'NEXT_REDIRECT') {\n      throw error  // Re-throw navigation errors\n    }\n    return { error: 'Failed to create post' }\n  }\n}\n```\n\nSame applies to:\n- `redirect()` - 307 temporary redirect\n- `permanentRedirect()` - 308 permanent redirect\n- `notFound()` - 404 not found\n- `forbidden()` - 403 forbidden\n- `unauthorized()` - 401 unauthorized\n\nUse `unstable_rethrow()` to re-throw these errors in catch blocks:\n\n```tsx\nimport { unstable_rethrow } from 'next/navigation'\n\nasync function action() {\n  try {\n    // ...\n    redirect('/success')\n  } catch (error) {\n    unstable_rethrow(error) // Re-throws Next.js internal errors\n    return { error: 'Something went wrong' }\n  }\n}\n```\n\n## Redirects\n\n```tsx\nimport { redirect, permanentRedirect } from 'next/navigation'\n\n// 307 Temporary - use for most cases\nredirect('/new-path')\n\n// 308 Permanent - use for URL migrations (cached by browsers)\npermanentRedirect('/new-url')\n```\n\n## Auth Errors\n\nTrigger auth-related error pages:\n\n```tsx\nimport { forbidden, unauthorized } from 'next/navigation'\n\nasync function Page() {\n  const session = await getSession()\n\n  if (!session) {\n    unauthorized() // Renders unauthorized.tsx (401)\n  }\n\n  if (!session.hasAccess) {\n    forbidden() // Renders forbidden.tsx (403)\n  }\n\n  return <Dashboard />\n}\n```\n\nCreate corresponding error pages:\n\n```tsx\n// app/forbidden.tsx\nexport default function Forbidden() {\n  return <div>You don't have access to this resource</div>\n}\n\n// app/unauthorized.tsx\nexport default function Unauthorized() {\n  return <div>Please log in to continue</div>\n}\n```\n\n## Not Found\n\n### `not-found.tsx`\n\nCustom 404 page for a route segment:\n\n```tsx\nexport default function NotFound() {\n  return (\n    <div>\n      <h2>Not Found</h2>\n      <p>Could not find the requested resource</p>\n    </div>\n  )\n}\n```\n\n### Triggering Not Found\n\n```tsx\nimport { notFound } from 'next/navigation'\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const post = await getPost(id)\n\n  if (!post) {\n    notFound()  // Renders closest not-found.tsx\n  }\n\n  return <div>{post.title}</div>\n}\n```\n\n## Error Hierarchy\n\nErrors bubble up to the nearest error boundary:\n\n```\napp/\n├── error.tsx           # Catches errors from all children\n├── blog/\n│   ├── error.tsx       # Catches errors in /blog/*\n│   └── [slug]/\n│       ├── error.tsx   # Catches errors in /blog/[slug]\n│       └── page.tsx\n└── layout.tsx          # Errors here go to global-error.tsx\n```\n\n<!-- File: .agents/skills/next-best-practices/file-conventions.md (Tokens: ~951 | Category: anthropic-skill) -->\n# File Conventions\n\nNext.js App Router uses file-based routing with special file conventions.\n\n## Project Structure\n\nReference: https://nextjs.org/docs/app/getting-started/project-structure\n\n```\napp/\n├── layout.tsx          # Root layout (required)\n├── page.tsx            # Home page (/)\n├── loading.tsx         # Loading UI\n├── error.tsx           # Error UI\n├── not-found.tsx       # 404 UI\n├── global-error.tsx    # Global error UI\n├── route.ts            # API endpoint\n├── template.tsx        # Re-rendered layout\n├── default.tsx         # Parallel route fallback\n├── blog/\n│   ├── page.tsx        # /blog\n│   └── [slug]/\n│       └── page.tsx    # /blog/:slug\n└── (group)/            # Route group (no URL impact)\n    └── page.tsx\n```\n\n## Special Files\n\n| File | Purpose |\n|------|---------|\n| `page.tsx` | UI for a route segment |\n| `layout.tsx` | Shared UI for segment and children |\n| `loading.tsx` | Loading UI (Suspense boundary) |\n| `error.tsx` | Error UI (Error boundary) |\n| `not-found.tsx` | 404 UI |\n| `route.ts` | API endpoint |\n| `template.tsx` | Like layout but re-renders on navigation |\n| `default.tsx` | Fallback for parallel routes |\n\n## Route Segments\n\n```\napp/\n├── blog/               # Static segment: /blog\n├── [slug]/             # Dynamic segment: /:slug\n├── [...slug]/          # Catch-all: /a/b/c\n├── [[...slug]]/        # Optional catch-all: / or /a/b/c\n└── (marketing)/        # Route group (ignored in URL)\n```\n\n## Parallel Routes\n\n```\napp/\n├── @analytics/\n│   └── page.tsx\n├── @sidebar/\n│   └── page.tsx\n└── layout.tsx          # Receives { analytics, sidebar } as props\n```\n\n## Intercepting Routes\n\n```\napp/\n├── feed/\n│   └── page.tsx\n├── @modal/\n│   └── (.)photo/[id]/  # Intercepts /photo/[id] from /feed\n│       └── page.tsx\n└── photo/[id]/\n    └── page.tsx\n```\n\nConventions:\n- `(.)` - same level\n- `(..)` - one level up\n- `(..)(..)` - two levels up\n- `(...)` - from root\n\n## Private Folders\n\n```\napp/\n├── _components/        # Private folder (not a route)\n│   └── Button.tsx\n└── page.tsx\n```\n\nPrefix with `_` to exclude from routing.\n\n## Middleware / Proxy\n\n### Next.js 14-15: `middleware.ts`\n\n```ts\n// middleware.ts (root of project)\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\n\nexport function middleware(request: NextRequest) {\n  // Auth, redirects, rewrites, etc.\n  return NextResponse.next();\n}\n\nexport const config = {\n  matcher: ['/dashboard/:path*', '/api/:path*'],\n};\n```\n\n### Next.js 16+: `proxy.ts`\n\nRenamed for clarity - same capabilities, different names:\n\n```ts\n// proxy.ts (root of project)\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\n\nexport function proxy(request: NextRequest) {\n  // Same logic as middleware\n  return NextResponse.next();\n}\n\nexport const proxyConfig = {\n  matcher: ['/dashboard/:path*', '/api/:path*'],\n};\n```\n\n| Version | File | Export | Config |\n|---------|------|--------|--------|\n| v14-15 | `middleware.ts` | `middleware()` | `config` |\n| v16+ | `proxy.ts` | `proxy()` | `proxyConfig` |\n\n**Migration**: Run `npx @next/codemod@latest upgrade` to auto-rename.\n\n## File Conventions Reference\n\nReference: https://nextjs.org/docs/app/api-reference/file-conventions\n\n<!-- File: .agents/skills/next-best-practices/font.md (Tokens: ~1316 | Category: anthropic-skill) -->\n# Font Optimization\n\nUse `next/font` for automatic font optimization with zero layout shift.\n\n## Google Fonts\n\n```tsx\n// app/layout.tsx\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({ subsets: ['latin'] })\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\" className={inter.className}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n## Multiple Fonts\n\n```tsx\nimport { Inter, Roboto_Mono } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  variable: '--font-inter',\n})\n\nconst robotoMono = Roboto_Mono({\n  subsets: ['latin'],\n  variable: '--font-roboto-mono',\n})\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\" className={`${inter.variable} ${robotoMono.variable}`}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\nUse in CSS:\n```css\nbody {\n  font-family: var(--font-inter);\n}\n\ncode {\n  font-family: var(--font-roboto-mono);\n}\n```\n\n## Font Weights and Styles\n\n```tsx\n// Single weight\nconst inter = Inter({\n  subsets: ['latin'],\n  weight: '400',\n})\n\n// Multiple weights\nconst inter = Inter({\n  subsets: ['latin'],\n  weight: ['400', '500', '700'],\n})\n\n// Variable font (recommended) - includes all weights\nconst inter = Inter({\n  subsets: ['latin'],\n  // No weight needed - variable fonts support all weights\n})\n\n// With italic\nconst inter = Inter({\n  subsets: ['latin'],\n  style: ['normal', 'italic'],\n})\n```\n\n## Local Fonts\n\n```tsx\nimport localFont from 'next/font/local'\n\nconst myFont = localFont({\n  src: './fonts/MyFont.woff2',\n})\n\n// Multiple files for different weights\nconst myFont = localFont({\n  src: [\n    {\n      path: './fonts/MyFont-Regular.woff2',\n      weight: '400',\n      style: 'normal',\n    },\n    {\n      path: './fonts/MyFont-Bold.woff2',\n      weight: '700',\n      style: 'normal',\n    },\n  ],\n})\n\n// Variable font\nconst myFont = localFont({\n  src: './fonts/MyFont-Variable.woff2',\n  variable: '--font-my-font',\n})\n```\n\n## Tailwind CSS Integration\n\n```tsx\n// app/layout.tsx\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  variable: '--font-inter',\n})\n\nexport default function RootLayout({ children }) {\n  return (\n    <html lang=\"en\" className={inter.variable}>\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    extend: {\n      fontFamily: {\n        sans: ['var(--font-inter)'],\n      },\n    },\n  },\n}\n```\n\n## Preloading Subsets\n\nOnly load needed character subsets:\n\n```tsx\n// Latin only (most common)\nconst inter = Inter({ subsets: ['latin'] })\n\n// Multiple subsets\nconst inter = Inter({ subsets: ['latin', 'latin-ext', 'cyrillic'] })\n```\n\n## Display Strategy\n\nControl font loading behavior:\n\n```tsx\nconst inter = Inter({\n  subsets: ['latin'],\n  display: 'swap', // Default - shows fallback, swaps when loaded\n})\n\n// Options:\n// 'auto' - browser decides\n// 'block' - short block period, then swap\n// 'swap' - immediate fallback, swap when ready (recommended)\n// 'fallback' - short block, short swap, then fallback\n// 'optional' - short block, no swap (use if font is optional)\n```\n\n## Don't Use Manual Font Links\n\nAlways use `next/font` instead of `<link>` tags for Google Fonts.\n\n```tsx\n// Bad: Manual link tag (blocks rendering, no optimization)\n<link href=\"https://fonts.googleapis.com/css2?family=Inter\" rel=\"stylesheet\" />\n\n// Bad: Missing display and preconnect\n<link href=\"https://fonts.googleapis.com/css2?family=Inter\" rel=\"stylesheet\" />\n\n// Good: Use next/font (self-hosted, zero layout shift)\nimport { Inter } from 'next/font/google'\n\nconst inter = Inter({ subsets: ['latin'] })\n```\n\n## Common Mistakes\n\n```tsx\n// Bad: Importing font in every component\n// components/Button.tsx\nimport { Inter } from 'next/font/google'\nconst inter = Inter({ subsets: ['latin'] }) // Creates new instance each time!\n\n// Good: Import once in layout, use CSS variable\n// app/layout.tsx\nconst inter = Inter({ subsets: ['latin'], variable: '--font-inter' })\n\n// Bad: Using @import in CSS (blocks rendering)\n/* globals.css */\n@import url('https://fonts.googleapis.com/css2?family=Inter');\n\n// Good: Use next/font (self-hosted, no network request)\nimport { Inter } from 'next/font/google'\n\n// Bad: Loading all weights when only using a few\nconst inter = Inter({ subsets: ['latin'] }) // Loads all weights\n\n// Good: Specify only needed weights (for non-variable fonts)\nconst inter = Inter({ subsets: ['latin'], weight: ['400', '700'] })\n\n// Bad: Missing subset - loads all characters\nconst inter = Inter({})\n\n// Good: Always specify subset\nconst inter = Inter({ subsets: ['latin'] })\n```\n\n## Font in Specific Components\n\n```tsx\n// For component-specific fonts, export from a shared file\n// lib/fonts.ts\nimport { Inter, Playfair_Display } from 'next/font/google'\n\nexport const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })\nexport const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-playfair' })\n\n// components/Heading.tsx\nimport { playfair } from '@/lib/fonts'\n\nexport function Heading({ children }) {\n  return <h1 className={playfair.className}>{children}</h1>\n}\n```\n\n<!-- File: .agents/skills/next-best-practices/functions.md (Tokens: ~1027 | Category: anthropic-skill) -->\n# Functions\n\nNext.js function APIs.\n\nReference: https://nextjs.org/docs/app/api-reference/functions\n\n## Navigation Hooks (Client)\n\n| Hook | Purpose | Reference |\n|------|---------|-----------|\n| `useRouter` | Programmatic navigation (`push`, `replace`, `back`, `refresh`) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-router) |\n| `usePathname` | Get current pathname | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-pathname) |\n| `useSearchParams` | Read URL search parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-search-params) |\n| `useParams` | Access dynamic route parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-params) |\n| `useSelectedLayoutSegment` | Active child segment (one level) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment) |\n| `useSelectedLayoutSegments` | All active segments below layout | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segments) |\n| `useLinkStatus` | Check link prefetch status | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-link-status) |\n| `useReportWebVitals` | Report Core Web Vitals metrics | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals) |\n\n## Server Functions\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `cookies` | Read/write cookies | [Docs](https://nextjs.org/docs/app/api-reference/functions/cookies) |\n| `headers` | Read request headers | [Docs](https://nextjs.org/docs/app/api-reference/functions/headers) |\n| `draftMode` | Enable preview of unpublished CMS content | [Docs](https://nextjs.org/docs/app/api-reference/functions/draft-mode) |\n| `after` | Run code after response finishes streaming | [Docs](https://nextjs.org/docs/app/api-reference/functions/after) |\n| `connection` | Wait for connection before dynamic rendering | [Docs](https://nextjs.org/docs/app/api-reference/functions/connection) |\n| `userAgent` | Parse User-Agent header | [Docs](https://nextjs.org/docs/app/api-reference/functions/userAgent) |\n\n## Generate Functions\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `generateStaticParams` | Pre-render dynamic routes at build time | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) |\n| `generateMetadata` | Dynamic metadata | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) |\n| `generateViewport` | Dynamic viewport config | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-viewport) |\n| `generateSitemaps` | Multiple sitemaps for large sites | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-sitemaps) |\n| `generateImageMetadata` | Multiple OG images per route | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-image-metadata) |\n\n## Request/Response\n\n| Function | Purpose | Reference |\n|----------|---------|-----------|\n| `NextRequest` | Extended Request with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-request) |\n| `NextResponse` | Extended Response with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-response) |\n| `ImageResponse` | Generate OG images | [Docs](https://nextjs.org/docs/app/api-reference/functions/image-response) |\n\n## Common Examples\n\n### Navigation\n\nUse `next/link` for internal navigation instead of `<a>` tags.\n\n```tsx\n// Bad: Plain anchor tag\n<a href=\"/about\">About</a>\n\n// Good: Next.js Link\nimport Link from 'next/link'\n\n<Link href=\"/about\">About</Link>\n```\n\nActive link styling:\n\n```tsx\n'use client'\n\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\n\nexport function NavLink({ href, children }) {\n  const pathname = usePathname()\n\n  return (\n    <Link href={href} className={pathname === href ? 'active' : ''}>\n      {children}\n    </Link>\n  )\n}\n```\n\n### Static Generation\n\n```tsx\n// app/blog/[slug]/page.tsx\nexport async function generateStaticParams() {\n  const posts = await getPosts()\n  return posts.map((post) => ({ slug: post.slug }))\n}\n```\n\n### After Response\n\n```tsx\nimport { after } from 'next/server'\n\nexport async function POST(request: Request) {\n  const data = await processRequest(request)\n\n  after(async () => {\n    await logAnalytics(data)\n  })\n\n  return Response.json({ success: true })\n}\n```\n\n<!-- File: .agents/skills/next-best-practices/hydration-error.md (Tokens: ~437 | Category: anthropic-skill) -->\n# Hydration Errors\n\nDiagnose and fix React hydration mismatch errors.\n\n## Error Signs\n\n- \"Hydration failed because the initial UI does not match\"\n- \"Text content does not match server-rendered HTML\"\n\n## Debugging\n\nIn development, click the hydration error to see the server/client diff.\n\n## Common Causes and Fixes\n\n### Browser-only APIs\n\n```tsx\n// Bad: Causes mismatch - window doesn't exist on server\n<div>{window.innerWidth}</div>\n\n// Good: Use client component with mounted check\n'use client'\nimport { useState, useEffect } from 'react'\n\nexport function ClientOnly({ children }: { children: React.ReactNode }) {\n  const [mounted, setMounted] = useState(false)\n  useEffect(() => setMounted(true), [])\n  return mounted ? children : null\n}\n```\n\n### Date/Time Rendering\n\nServer and client may be in different timezones:\n\n```tsx\n// Bad: Causes mismatch\n<span>{new Date().toLocaleString()}</span>\n\n// Good: Render on client only\n'use client'\nconst [time, setTime] = useState<string>()\nuseEffect(() => setTime(new Date().toLocaleString()), [])\n```\n\n### Random Values or IDs\n\n```tsx\n// Bad: Random values differ between server and client\n<div id={Math.random().toString()}>\n\n// Good: Use useId hook\nimport { useId } from 'react'\n\nfunction Input() {\n  const id = useId()\n  return <input id={id} />\n}\n```\n\n### Invalid HTML Nesting\n\n```tsx\n// Bad: Invalid - div inside p\n<p><div>Content</div></p>\n\n// Bad: Invalid - p inside p\n<p><p>Nested</p></p>\n\n// Good: Valid nesting\n<div><p>Content</p></div>\n```\n\n### Third-party Scripts\n\nScripts that modify DOM during hydration.\n\n```tsx\n// Good: Use next/script with afterInteractive\nimport Script from 'next/script'\n\nexport default function Page() {\n  return (\n    <Script\n      src=\"https://example.com/script.js\"\n      strategy=\"afterInteractive\"\n    />\n  )\n}\n```\n\n<!-- File: .agents/skills/next-best-practices/image.md (Tokens: ~1025 | Category: anthropic-skill) -->\n# Image Optimization\n\nUse `next/image` for automatic image optimization.\n\n## Always Use next/image\n\n```tsx\n// Bad: Avoid native img\n<img src=\"/hero.png\" alt=\"Hero\" />\n\n// Good: Use next/image\nimport Image from 'next/image'\n<Image src=\"/hero.png\" alt=\"Hero\" width={800} height={400} />\n```\n\n## Required Props\n\nImages need explicit dimensions to prevent layout shift:\n\n```tsx\n// Local images - dimensions inferred automatically\nimport heroImage from './hero.png'\n<Image src={heroImage} alt=\"Hero\" />\n\n// Remote images - must specify width/height\n<Image src=\"https://example.com/image.jpg\" alt=\"Hero\" width={800} height={400} />\n\n// Or use fill for parent-relative sizing\n<div style={{ position: 'relative', width: '100%', height: 400 }}>\n  <Image src=\"/hero.png\" alt=\"Hero\" fill style={{ objectFit: 'cover' }} />\n</div>\n```\n\n## Remote Images Configuration\n\nRemote domains must be configured in `next.config.js`:\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: 'https',\n        hostname: 'example.com',\n        pathname: '/images/**',\n      },\n      {\n        protocol: 'https',\n        hostname: '*.cdn.com', // Wildcard subdomain\n      },\n    ],\n  },\n}\n```\n\n## Responsive Images\n\nUse `sizes` to tell the browser which size to download:\n\n```tsx\n// Full-width hero\n<Image\n  src=\"/hero.png\"\n  alt=\"Hero\"\n  fill\n  sizes=\"100vw\"\n/>\n\n// Responsive grid (3 columns on desktop, 1 on mobile)\n<Image\n  src=\"/card.png\"\n  alt=\"Card\"\n  fill\n  sizes=\"(max-width: 768px) 100vw, 33vw\"\n/>\n\n// Fixed sidebar image\n<Image\n  src=\"/avatar.png\"\n  alt=\"Avatar\"\n  width={200}\n  height={200}\n  sizes=\"200px\"\n/>\n```\n\n## Blur Placeholder\n\nPrevent layout shift with placeholders:\n\n```tsx\n// Local images - automatic blur hash\nimport heroImage from './hero.png'\n<Image src={heroImage} alt=\"Hero\" placeholder=\"blur\" />\n\n// Remote images - provide blurDataURL\n<Image\n  src=\"https://example.com/image.jpg\"\n  alt=\"Hero\"\n  width={800}\n  height={400}\n  placeholder=\"blur\"\n  blurDataURL=\"data:image/jpeg;base64,/9j/4AAQSkZJRg...\"\n/>\n\n// Or use color placeholder\n<Image\n  src=\"https://example.com/image.jpg\"\n  alt=\"Hero\"\n  width={800}\n  height={400}\n  placeholder=\"empty\"\n  style={{ backgroundColor: '#e0e0e0' }}\n/>\n```\n\n## Priority Loading\n\nUse `priority` for above-the-fold images (LCP):\n\n```tsx\n// Hero image - loads immediately\n<Image src=\"/hero.png\" alt=\"Hero\" fill priority />\n\n// Below-fold images - lazy loaded by default (no priority needed)\n<Image src=\"/card.png\" alt=\"Card\" width={400} height={300} />\n```\n\n## Common Mistakes\n\n```tsx\n// Bad: Missing sizes with fill - downloads largest image\n<Image src=\"/hero.png\" alt=\"Hero\" fill />\n\n// Good: Add sizes for proper responsive behavior\n<Image src=\"/hero.png\" alt=\"Hero\" fill sizes=\"100vw\" />\n\n// Bad: Using width/height for aspect ratio only\n<Image src=\"/hero.png\" alt=\"Hero\" width={16} height={9} />\n\n// Good: Use actual display dimensions or fill with sizes\n<Image src=\"/hero.png\" alt=\"Hero\" fill sizes=\"100vw\" style={{ objectFit: 'cover' }} />\n\n// Bad: Remote image without config\n<Image src=\"https://untrusted.com/image.jpg\" alt=\"Image\" width={400} height={300} />\n// Error: Invalid src prop, hostname not configured\n\n// Good: Add hostname to next.config.js remotePatterns\n```\n\n## Static Export\n\nWhen using `output: 'export'`, use `unoptimized` or custom loader:\n\n```tsx\n// Option 1: Disable optimization\n<Image src=\"/hero.png\" alt=\"Hero\" width={800} height={400} unoptimized />\n\n// Option 2: Global config\n// next.config.js\nmodule.exports = {\n  output: 'export',\n  images: { unoptimized: true },\n}\n\n// Option 3: Custom loader (Cloudinary, Imgix, etc.)\nconst cloudinaryLoader = ({ src, width, quality }) => {\n  return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`\n}\n\n<Image loader={cloudinaryLoader} src=\"sample.jpg\" alt=\"Sample\" width={800} height={400} />\n```\n\n<!-- File: .agents/skills/next-best-practices/metadata.md (Tokens: ~1810 | Category: anthropic-skill) -->\n# Metadata\n\nAdd SEO metadata to Next.js pages using the Metadata API.\n\n## Important: Server Components Only\n\nThe `metadata` object and `generateMetadata` function are **only supported in Server Components**. They cannot be used in Client Components.\n\nIf the target page has `'use client'`:\n1. Remove `'use client'` if possible, move client logic to child components\n2. Or extract metadata to a parent Server Component layout\n3. Or split the file: Server Component with metadata imports Client Components\n\n## Static Metadata\n\n```tsx\nimport type { Metadata } from 'next'\n\nexport const metadata: Metadata = {\n  title: 'Page Title',\n  description: 'Page description for search engines',\n}\n```\n\n## Dynamic Metadata\n\n```tsx\nimport type { Metadata } from 'next'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport async function generateMetadata({ params }: Props): Promise<Metadata> {\n  const { slug } = await params\n  const post = await getPost(slug)\n  return { title: post.title, description: post.description }\n}\n```\n\n## Avoid Duplicate Fetches\n\nUse React `cache()` when the same data is needed for both metadata and page:\n\n```tsx\nimport { cache } from 'react'\n\nexport const getPost = cache(async (slug: string) => {\n  return await db.posts.findFirst({ where: { slug } })\n})\n```\n\n## Viewport\n\nSeparate from metadata for streaming support:\n\n```tsx\nimport type { Viewport } from 'next'\n\nexport const viewport: Viewport = {\n  width: 'device-width',\n  initialScale: 1,\n  themeColor: '#000000',\n}\n\n// Or dynamic\nexport function generateViewport({ params }): Viewport {\n  return { themeColor: getThemeColor(params) }\n}\n```\n\n## Title Templates\n\nIn root layout for consistent naming:\n\n```tsx\nexport const metadata: Metadata = {\n  title: { default: 'Site Name', template: '%s | Site Name' },\n}\n```\n\n## Metadata File Conventions\n\nReference: https://nextjs.org/docs/app/getting-started/project-structure#metadata-file-conventions\n\nPlace these files in `app/` directory (or route segments):\n\n| File | Purpose |\n|------|---------|\n| `favicon.ico` | Favicon |\n| `icon.png` / `icon.svg` | App icon |\n| `apple-icon.png` | Apple app icon |\n| `opengraph-image.png` | OG image |\n| `twitter-image.png` | Twitter card image |\n| `sitemap.ts` / `sitemap.xml` | Sitemap (use `generateSitemaps` for multiple) |\n| `robots.ts` / `robots.txt` | Robots directives |\n| `manifest.ts` / `manifest.json` | Web app manifest |\n\n## SEO Best Practice: Static Files Are Often Enough\n\nFor most sites, **static metadata files provide excellent SEO coverage**:\n\n```\napp/\n├── favicon.ico\n├── opengraph-image.png     # Works for both OG and Twitter\n├── sitemap.ts\n├── robots.ts\n└── layout.tsx              # With title/description metadata\n```\n\n**Tips:**\n- A single `opengraph-image.png` covers both Open Graph and Twitter (Twitter falls back to OG)\n- Static `title` and `description` in layout metadata is sufficient for most pages\n- Only use dynamic `generateMetadata` when content varies per page\n\n---\n\n# OG Image Generation\n\nGenerate dynamic Open Graph images using `next/og`.\n\n## Important Rules\n\n1. **Use `next/og`** - not `@vercel/og` (it's built into Next.js)\n2. **No searchParams** - OG images can't access search params, use route params instead\n3. **Avoid Edge runtime** - Use default Node.js runtime\n\n```tsx\n// Good\nimport { ImageResponse } from 'next/og'\n\n// Bad\n// import { ImageResponse } from '@vercel/og'\n// export const runtime = 'edge'\n```\n\n## Basic OG Image\n\n```tsx\n// app/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport const alt = 'Site Name'\nexport const size = { width: 1200, height: 630 }\nexport const contentType = 'image/png'\n\nexport default function Image() {\n  return new ImageResponse(\n    (\n      <div\n        style={{\n          fontSize: 128,\n          background: 'white',\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n        }}\n      >\n        Hello World\n      </div>\n    ),\n    { ...size }\n  )\n}\n```\n\n## Dynamic OG Image\n\n```tsx\n// app/blog/[slug]/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport const alt = 'Blog Post'\nexport const size = { width: 1200, height: 630 }\nexport const contentType = 'image/png'\n\ntype Props = { params: Promise<{ slug: string }> }\n\nexport default async function Image({ params }: Props) {\n  const { slug } = await params\n  const post = await getPost(slug)\n\n  return new ImageResponse(\n    (\n      <div\n        style={{\n          fontSize: 48,\n          background: 'linear-gradient(to bottom, #1a1a1a, #333)',\n          color: 'white',\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          flexDirection: 'column',\n          alignItems: 'center',\n          justifyContent: 'center',\n          padding: 48,\n        }}\n      >\n        <div style={{ fontSize: 64, fontWeight: 'bold' }}>{post.title}</div>\n        <div style={{ marginTop: 24, opacity: 0.8 }}>{post.description}</div>\n      </div>\n    ),\n    { ...size }\n  )\n}\n```\n\n## Custom Fonts\n\n```tsx\nimport { ImageResponse } from 'next/og'\nimport { join } from 'path'\nimport { readFile } from 'fs/promises'\n\nexport default async function Image() {\n  const fontPath = join(process.cwd(), 'assets/fonts/Inter-Bold.ttf')\n  const fontData = await readFile(fontPath)\n\n  return new ImageResponse(\n    (\n      <div style={{ fontFamily: 'Inter', fontSize: 64 }}>\n        Custom Font Text\n      </div>\n    ),\n    {\n      width: 1200,\n      height: 630,\n      fonts: [{ name: 'Inter', data: fontData, style: 'normal' }],\n    }\n  )\n}\n```\n\n## File Naming\n\n- `opengraph-image.tsx` - Open Graph (Facebook, LinkedIn)\n- `twitter-image.tsx` - Twitter/X cards (optional, falls back to OG)\n\n## Styling Notes\n\nImageResponse uses Flexbox layout:\n- Use `display: 'flex'`\n- No CSS Grid support\n- Styles must be inline objects\n\n## Multiple OG Images\n\nUse `generateImageMetadata` for multiple images per route:\n\n```tsx\n// app/blog/[slug]/opengraph-image.tsx\nimport { ImageResponse } from 'next/og'\n\nexport async function generateImageMetadata({ params }) {\n  const images = await getPostImages(params.slug)\n  return images.map((img, idx) => ({\n    id: idx,\n    alt: img.alt,\n    size: { width: 1200, height: 630 },\n    contentType: 'image/png',\n  }))\n}\n\nexport default async function Image({ params, id }) {\n  const images = await getPostImages(params.slug)\n  const image = images[id]\n  return new ImageResponse(/* ... */)\n}\n```\n\n## Multiple Sitemaps\n\nUse `generateSitemaps` for large sites:\n\n```tsx\n// app/sitemap.ts\nimport type { MetadataRoute } from 'next'\n\nexport async function generateSitemaps() {\n  // Return array of sitemap IDs\n  return [{ id: 0 }, { id: 1 }, { id: 2 }]\n}\n\nexport default async function sitemap({\n  id,\n}: {\n  id: number\n}): Promise<MetadataRoute.Sitemap> {\n  const start = id * 50000\n  const end = start + 50000\n  const products = await getProducts(start, end)\n\n  return products.map((product) => ({\n    url: `https://example.com/product/${product.id}`,\n    lastModified: product.updatedAt,\n  }))\n}\n```\n\nGenerates `/sitemap/0.xml`, `/sitemap/1.xml`, etc.\n\n<!-- File: .agents/skills/next-best-practices/parallel-routes.md (Tokens: ~1826 | Category: anthropic-skill) -->\n# Parallel & Intercepting Routes\n\nParallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.\n\n## File Structure\n\n```\napp/\n├── @modal/                    # Parallel route slot\n│   ├── default.tsx            # Required! Returns null\n│   ├── (.)photos/             # Intercepts /photos/*\n│   │   └── [id]/\n│   │       └── page.tsx       # Modal content\n│   └── [...]catchall/         # Optional: catch unmatched\n│       └── page.tsx\n├── photos/\n│   └── [id]/\n│       └── page.tsx           # Full page (direct access)\n├── layout.tsx                 # Renders both children and @modal\n└── page.tsx\n```\n\n## Step 1: Root Layout with Slot\n\n```tsx\n// app/layout.tsx\nexport default function RootLayout({\n  children,\n  modal,\n}: {\n  children: React.ReactNode;\n  modal: React.ReactNode;\n}) {\n  return (\n    <html>\n      <body>\n        {children}\n        {modal}\n      </body>\n    </html>\n  );\n}\n```\n\n## Step 2: Default File (Critical!)\n\n**Every parallel route slot MUST have a `default.tsx`** to prevent 404s on hard navigation.\n\n```tsx\n// app/@modal/default.tsx\nexport default function Default() {\n  return null;\n}\n```\n\nWithout this file, refreshing any page will 404 because Next.js can't determine what to render in the `@modal` slot.\n\n## Step 3: Intercepting Route (Modal)\n\nThe `(.)` prefix intercepts routes at the same level.\n\n```tsx\n// app/@modal/(.)photos/[id]/page.tsx\nimport { Modal } from '@/components/modal';\n\nexport default async function PhotoModal({\n  params\n}: {\n  params: Promise<{ id: string }>\n}) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  return (\n    <Modal>\n      <img src={photo.url} alt={photo.title} />\n    </Modal>\n  );\n}\n```\n\n## Step 4: Full Page (Direct Access)\n\n```tsx\n// app/photos/[id]/page.tsx\nexport default async function PhotoPage({\n  params\n}: {\n  params: Promise<{ id: string }>\n}) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  return (\n    <div className=\"full-page\">\n      <img src={photo.url} alt={photo.title} />\n      <h1>{photo.title}</h1>\n    </div>\n  );\n}\n```\n\n## Step 5: Modal Component with Correct Closing\n\n**Critical: Use `router.back()` to close modals, NOT `router.push()` or `<Link>`.**\n\n```tsx\n// components/modal.tsx\n'use client';\n\nimport { useRouter } from 'next/navigation';\nimport { useCallback, useEffect, useRef } from 'react';\n\nexport function Modal({ children }: { children: React.ReactNode }) {\n  const router = useRouter();\n  const overlayRef = useRef<HTMLDivElement>(null);\n\n  // Close on escape key\n  useEffect(() => {\n    function onKeyDown(e: KeyboardEvent) {\n      if (e.key === 'Escape') {\n        router.back(); // Correct\n      }\n    }\n    document.addEventListener('keydown', onKeyDown);\n    return () => document.removeEventListener('keydown', onKeyDown);\n  }, [router]);\n\n  // Close on overlay click\n  const handleOverlayClick = useCallback((e: React.MouseEvent) => {\n    if (e.target === overlayRef.current) {\n      router.back(); // Correct\n    }\n  }, [router]);\n\n  return (\n    <div\n      ref={overlayRef}\n      onClick={handleOverlayClick}\n      className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\"\n    >\n      <div className=\"bg-white rounded-lg p-6 max-w-2xl w-full mx-4\">\n        <button\n          onClick={() => router.back()} // Correct!\n          className=\"absolute top-4 right-4\"\n        >\n          Close\n        </button>\n        {children}\n      </div>\n    </div>\n  );\n}\n```\n\n### Why NOT `router.push('/')` or `<Link href=\"/\">`?\n\nUsing `push` or `Link` to \"close\" a modal:\n1. Adds a new history entry (back button shows modal again)\n2. Doesn't properly clear the intercepted route\n3. Can cause the modal to flash or persist unexpectedly\n\n`router.back()` correctly:\n1. Removes the intercepted route from history\n2. Returns to the previous page\n3. Properly unmounts the modal\n\n## Route Matcher Reference\n\nMatchers match **route segments**, not filesystem paths:\n\n| Matcher | Matches | Example |\n|---------|---------|---------|\n| `(.)` | Same level | `@modal/(.)photos` intercepts `/photos` |\n| `(..)` | One level up | `@modal/(..)settings` from `/dashboard/@modal` intercepts `/settings` |\n| `(..)(..)` | Two levels up | Rarely used |\n| `(...)` | From root | `@modal/(...)photos` intercepts `/photos` from anywhere |\n\n**Common mistake**: Thinking `(..)` means \"parent folder\" - it means \"parent route segment\".\n\n## Handling Hard Navigation\n\nWhen users directly visit `/photos/123` (bookmark, refresh, shared link):\n- The intercepting route is bypassed\n- The full `photos/[id]/page.tsx` renders\n- Modal doesn't appear (expected behavior)\n\nIf you want the modal to appear on direct access too, you need additional logic:\n\n```tsx\n// app/photos/[id]/page.tsx\nimport { Modal } from '@/components/modal';\n\nexport default async function PhotoPage({ params }) {\n  const { id } = await params;\n  const photo = await getPhoto(id);\n\n  // Option: Render as modal on direct access too\n  return (\n    <Modal>\n      <img src={photo.url} alt={photo.title} />\n    </Modal>\n  );\n}\n```\n\n## Common Gotchas\n\n### 1. Missing `default.tsx` → 404 on Refresh\n\nEvery `@slot` folder needs a `default.tsx` that returns `null` (or appropriate content).\n\n### 2. Modal Persists After Navigation\n\nYou're using `router.push()` instead of `router.back()`.\n\n### 3. Nested Parallel Routes Need Defaults Too\n\nIf you have `@modal` inside a route group, each level needs its own `default.tsx`:\n\n```\napp/\n├── (marketing)/\n│   ├── @modal/\n│   │   └── default.tsx     # Needed!\n│   └── layout.tsx\n└── layout.tsx\n```\n\n### 4. Intercepted Route Shows Wrong Content\n\nCheck your matcher:\n- `(.)photos` intercepts `/photos` from the same route level\n- If your `@modal` is in `app/dashboard/@modal`, use `(.)photos` to intercept `/dashboard/photos`, not `/photos`\n\n### 5. TypeScript Errors with `params`\n\nIn Next.js 15+, `params` is a Promise:\n\n```tsx\n// Correct\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params;\n}\n```\n\n## Complete Example: Photo Gallery Modal\n\n```\napp/\n├── @modal/\n│   ├── default.tsx\n│   └── (.)photos/\n│       └── [id]/\n│           └── page.tsx\n├── photos/\n│   ├── page.tsx           # Gallery grid\n│   └── [id]/\n│       └── page.tsx       # Full photo page\n├── layout.tsx\n└── page.tsx\n```\n\nLinks in the gallery:\n\n```tsx\n// app/photos/page.tsx\nimport Link from 'next/link';\n\nexport default async function Gallery() {\n  const photos = await getPhotos();\n\n  return (\n    <div className=\"grid grid-cols-3 gap-4\">\n      {photos.map(photo => (\n        <Link key={photo.id} href={`/photos/${photo.id}`}>\n          <img src={photo.thumbnail} alt={photo.title} />\n        </Link>\n      ))}\n    </div>\n  );\n}\n```\n\nClicking a photo → Modal opens (intercepted)\nDirect URL → Full page renders\nRefresh while modal open → Full page renders\n\n<!-- File: .agents/skills/next-best-practices/route-handlers.md (Tokens: ~820 | Category: anthropic-skill) -->\n# Route Handlers\n\nCreate API endpoints with `route.ts` files.\n\n## Basic Usage\n\n```tsx\n// app/api/users/route.ts\nexport async function GET() {\n  const users = await getUsers()\n  return Response.json(users)\n}\n\nexport async function POST(request: Request) {\n  const body = await request.json()\n  const user = await createUser(body)\n  return Response.json(user, { status: 201 })\n}\n```\n\n## Supported Methods\n\n`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`\n\n## GET Handler Conflicts with page.tsx\n\n**A `route.ts` and `page.tsx` cannot coexist in the same folder.**\n\n```\napp/\n├── api/\n│   └── users/\n│       └── route.ts    # /api/users\n└── users/\n    ├── page.tsx        # /users (page)\n    └── route.ts        # Warning: Conflicts with page.tsx!\n```\n\nIf you need both a page and an API at the same path, use different paths:\n\n```\napp/\n├── users/\n│   └── page.tsx        # /users (page)\n└── api/\n    └── users/\n        └── route.ts    # /api/users (API)\n```\n\n## Environment Behavior\n\nRoute handlers run in a **Server Component-like environment**:\n\n- Yes: Can use `async/await`\n- Yes: Can access `cookies()`, `headers()`\n- Yes: Can use Node.js APIs\n- No: Cannot use React hooks\n- No: Cannot use React DOM APIs\n- No: Cannot use browser APIs\n\n```tsx\n// Bad: This won't work - no React DOM in route handlers\nimport { renderToString } from 'react-dom/server'\n\nexport async function GET() {\n  const html = renderToString(<Component />)  // Error!\n  return new Response(html)\n}\n```\n\n## Dynamic Route Handlers\n\n```tsx\n// app/api/users/[id]/route.ts\nexport async function GET(\n  request: Request,\n  { params }: { params: Promise<{ id: string }> }\n) {\n  const { id } = await params\n  const user = await getUser(id)\n\n  if (!user) {\n    return Response.json({ error: 'Not found' }, { status: 404 })\n  }\n\n  return Response.json(user)\n}\n```\n\n## Request Helpers\n\n```tsx\nexport async function GET(request: Request) {\n  // URL and search params\n  const { searchParams } = new URL(request.url)\n  const query = searchParams.get('q')\n\n  // Headers\n  const authHeader = request.headers.get('authorization')\n\n  // Cookies (Next.js helper)\n  const cookieStore = await cookies()\n  const token = cookieStore.get('token')\n\n  return Response.json({ query, token })\n}\n```\n\n## Response Helpers\n\n```tsx\n// JSON response\nreturn Response.json({ data })\n\n// With status\nreturn Response.json({ error: 'Not found' }, { status: 404 })\n\n// With headers\nreturn Response.json(data, {\n  headers: {\n    'Cache-Control': 'max-age=3600',\n  },\n})\n\n// Redirect\nreturn Response.redirect(new URL('/login', request.url))\n\n// Stream\nreturn new Response(stream, {\n  headers: { 'Content-Type': 'text/event-stream' },\n})\n```\n\n## When to Use Route Handlers vs Server Actions\n\n| Use Case | Route Handlers | Server Actions |\n|----------|----------------|----------------|\n| Form submissions | No | Yes |\n| Data mutations from UI | No | Yes |\n| Third-party webhooks | Yes | No |\n| External API consumption | Yes | No |\n| Public REST API | Yes | No |\n| File uploads | Both work | Both work |\n\n**Prefer Server Actions** for mutations triggered from your UI.\n**Use Route Handlers** for external integrations and public APIs.\n\n<!-- File: .agents/skills/next-best-practices/rsc-boundaries.md (Tokens: ~1029 | Category: anthropic-skill) -->\n# RSC Boundaries\n\nDetect and prevent invalid patterns when crossing Server/Client component boundaries.\n\n## Detection Rules\n\n### 1. Async Client Components Are Invalid\n\nClient components **cannot** be async functions. Only Server Components can be async.\n\n**Detect:** File has `'use client'` AND component is `async function` or returns `Promise`\n\n```tsx\n// Bad: async client component\n'use client'\nexport default async function UserProfile() {\n  const user = await getUser() // Cannot await in client component\n  return <div>{user.name}</div>\n}\n\n// Good: Remove async, fetch data in parent server component\n// page.tsx (server component - no 'use client')\nexport default async function Page() {\n  const user = await getUser()\n  return <UserProfile user={user} />\n}\n\n// UserProfile.tsx (client component)\n'use client'\nexport function UserProfile({ user }: { user: User }) {\n  return <div>{user.name}</div>\n}\n```\n\n```tsx\n// Bad: async arrow function client component\n'use client'\nconst Dashboard = async () => {\n  const data = await fetchDashboard()\n  return <div>{data}</div>\n}\n\n// Good: Fetch in server component, pass data down\n```\n\n### 2. Non-Serializable Props to Client Components\n\nProps passed from Server → Client must be JSON-serializable.\n\n**Detect:** Server component passes these to a client component:\n- Functions (except Server Actions with `'use server'`)\n- `Date` objects\n- `Map`, `Set`, `WeakMap`, `WeakSet`\n- Class instances\n- `Symbol` (unless globally registered)\n- Circular references\n\n```tsx\n// Bad: Function prop\n// page.tsx (server)\nexport default function Page() {\n  const handleClick = () => console.log('clicked')\n  return <ClientButton onClick={handleClick} />\n}\n\n// Good: Define function inside client component\n// ClientButton.tsx\n'use client'\nexport function ClientButton() {\n  const handleClick = () => console.log('clicked')\n  return <button onClick={handleClick}>Click</button>\n}\n```\n\n```tsx\n// Bad: Date object (silently becomes string, then crashes)\n// page.tsx (server)\nexport default async function Page() {\n  const post = await getPost()\n  return <PostCard createdAt={post.createdAt} /> // Date object\n}\n\n// PostCard.tsx (client) - will crash on .getFullYear()\n'use client'\nexport function PostCard({ createdAt }: { createdAt: Date }) {\n  return <span>{createdAt.getFullYear()}</span> // Runtime error!\n}\n\n// Good: Serialize to string on server\n// page.tsx (server)\nexport default async function Page() {\n  const post = await getPost()\n  return <PostCard createdAt={post.createdAt.toISOString()} />\n}\n\n// PostCard.tsx (client)\n'use client'\nexport function PostCard({ createdAt }: { createdAt: string }) {\n  const date = new Date(createdAt)\n  return <span>{date.getFullYear()}</span>\n}\n```\n\n```tsx\n// Bad: Class instance\nconst user = new UserModel(data)\n<ClientProfile user={user} /> // Methods will be stripped\n\n// Good: Pass plain object\nconst user = await getUser()\n<ClientProfile user={{ id: user.id, name: user.name }} />\n```\n\n```tsx\n// Bad: Map/Set\n<ClientComponent items={new Map([['a', 1]])} />\n\n// Good: Convert to array/object\n<ClientComponent items={Object.fromEntries(map)} />\n<ClientComponent items={Array.from(set)} />\n```\n\n### 3. Server Actions Are the Exception\n\nFunctions marked with `'use server'` CAN be passed to client components.\n\n```tsx\n// Valid: Server Action can be passed\n// actions.ts\n'use server'\nexport async function submitForm(formData: FormData) {\n  // server-side logic\n}\n\n// page.tsx (server)\nimport { submitForm } from './actions'\nexport default function Page() {\n  return <ClientForm onSubmit={submitForm} /> // OK!\n}\n\n// ClientForm.tsx (client)\n'use client'\nexport function ClientForm({ onSubmit }: { onSubmit: (data: FormData) => Promise<void> }) {\n  return <form action={onSubmit}>...</form>\n}\n```\n\n## Quick Reference\n\n| Pattern | Valid? | Fix |\n|---------|--------|-----|\n| `'use client'` + `async function` | No | Fetch in server parent, pass data |\n| Pass `() => {}` to client | No | Define in client or use server action |\n| Pass `new Date()` to client | No | Use `.toISOString()` |\n| Pass `new Map()` to client | No | Convert to object/array |\n| Pass class instance to client | No | Pass plain object |\n| Pass server action to client | Yes | - |\n| Pass `string/number/boolean` | Yes | - |\n| Pass plain object/array | Yes | - |\n\n<!-- File: .agents/skills/next-best-practices/runtime-selection.md (Tokens: ~220 | Category: anthropic-skill) -->\n# Runtime Selection\n\n## Use Node.js Runtime by Default\n\nUse the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.\n\n```tsx\n// Good: Default - no runtime config needed (uses Node.js)\nexport default function Page() { ... }\n\n// Caution: Only if already used in project or specifically required\nexport const runtime = 'edge'\n```\n\n## When to Use Each\n\n### Node.js Runtime (Default)\n\n- Full Node.js API support\n- File system access (`fs`)\n- Full `crypto` support\n- Database connections\n- Most npm packages work\n\n### Edge Runtime\n\n- Only for specific edge-location latency requirements\n- Limited API (no `fs`, limited `crypto`)\n- Smaller cold start\n- Geographic distribution needs\n\n## Detection\n\n**Before adding `runtime = 'edge'`**, check:\n1. Does the project already use Edge runtime?\n2. Is there a specific latency requirement?\n3. Are all dependencies Edge-compatible?\n\nIf unsure, use Node.js runtime.\n\n<!-- File: .agents/skills/next-best-practices/scripts.md (Tokens: ~867 | Category: anthropic-skill) -->\n# Scripts\n\nLoading third-party scripts in Next.js.\n\n## Use next/script\n\nAlways use `next/script` instead of native `<script>` tags for better performance.\n\n```tsx\n// Bad: Native script tag\n<script src=\"https://example.com/script.js\"></script>\n\n// Good: Next.js Script component\nimport Script from 'next/script'\n\n<Script src=\"https://example.com/script.js\" />\n```\n\n## Inline Scripts Need ID\n\nInline scripts require an `id` attribute for Next.js to track them.\n\n```tsx\n// Bad: Missing id\n<Script dangerouslySetInnerHTML={{ __html: 'console.log(\"hi\")' }} />\n\n// Good: Has id\n<Script id=\"my-script\" dangerouslySetInnerHTML={{ __html: 'console.log(\"hi\")' }} />\n\n// Good: Inline with id\n<Script id=\"show-banner\">\n  {`document.getElementById('banner').classList.remove('hidden')`}\n</Script>\n```\n\n## Don't Put Script in Head\n\n`next/script` should not be placed inside `next/head`. It handles its own positioning.\n\n```tsx\n// Bad: Script inside Head\nimport Head from 'next/head'\nimport Script from 'next/script'\n\n<Head>\n  <Script src=\"/analytics.js\" />\n</Head>\n\n// Good: Script outside Head\n<Head>\n  <title>Page</title>\n</Head>\n<Script src=\"/analytics.js\" />\n```\n\n## Loading Strategies\n\n```tsx\n// afterInteractive (default) - Load after page is interactive\n<Script src=\"/analytics.js\" strategy=\"afterInteractive\" />\n\n// lazyOnload - Load during idle time\n<Script src=\"/widget.js\" strategy=\"lazyOnload\" />\n\n// beforeInteractive - Load before page is interactive (use sparingly)\n// Only works in app/layout.tsx or pages/_document.js\n<Script src=\"/critical.js\" strategy=\"beforeInteractive\" />\n\n// worker - Load in web worker (experimental)\n<Script src=\"/heavy.js\" strategy=\"worker\" />\n```\n\n## Google Analytics\n\nUse `@next/third-parties` instead of inline GA scripts.\n\n```tsx\n// Bad: Inline GA script\n<Script src=\"https://www.googletagmanager.com/gtag/js?id=G-XXXXX\" />\n<Script id=\"ga-init\">\n  {`window.dataLayer = window.dataLayer || [];\n    function gtag(){dataLayer.push(arguments);}\n    gtag('js', new Date());\n    gtag('config', 'G-XXXXX');`}\n</Script>\n\n// Good: Next.js component\nimport { GoogleAnalytics } from '@next/third-parties/google'\n\nexport default function Layout({ children }) {\n  return (\n    <html>\n      <body>{children}</body>\n      <GoogleAnalytics gaId=\"G-XXXXX\" />\n    </html>\n  )\n}\n```\n\n## Google Tag Manager\n\n```tsx\nimport { GoogleTagManager } from '@next/third-parties/google'\n\nexport default function Layout({ children }) {\n  return (\n    <html>\n      <GoogleTagManager gtmId=\"GTM-XXXXX\" />\n      <body>{children}</body>\n    </html>\n  )\n}\n```\n\n## Other Third-Party Scripts\n\n```tsx\n// YouTube embed\nimport { YouTubeEmbed } from '@next/third-parties/google'\n\n<YouTubeEmbed videoid=\"dQw4w9WgXcQ\" />\n\n// Google Maps\nimport { GoogleMapsEmbed } from '@next/third-parties/google'\n\n<GoogleMapsEmbed\n  apiKey=\"YOUR_API_KEY\"\n  mode=\"place\"\n  q=\"Brooklyn+Bridge,New+York,NY\"\n/>\n```\n\n## Quick Reference\n\n| Pattern | Issue | Fix |\n|---------|-------|-----|\n| `<script src=\"...\">` | No optimization | Use `next/script` |\n| `<Script>` without id | Can't track inline scripts | Add `id` attribute |\n| `<Script>` inside `<Head>` | Wrong placement | Move outside Head |\n| Inline GA/GTM scripts | No optimization | Use `@next/third-parties` |\n| `strategy=\"beforeInteractive\"` outside layout | Won't work | Only use in root layout |\n\n<!-- File: .agents/skills/next-best-practices/self-hosting.md (Tokens: ~2130 | Category: anthropic-skill) -->\n# Self-Hosting Next.js\n\nDeploy Next.js outside of Vercel with confidence.\n\n## Quick Start: Standalone Output\n\nFor Docker or any containerized deployment, use standalone output:\n\n```js\n// next.config.js\nmodule.exports = {\n  output: 'standalone',\n};\n```\n\nThis creates a minimal `standalone` folder with only production dependencies:\n\n```\n.next/\n├── standalone/\n│   ├── server.js          # Entry point\n│   ├── node_modules/      # Only production deps\n│   └── .next/             # Build output\n└── static/                # Must be copied separately\n```\n\n## Docker Deployment\n\n### Dockerfile\n\n```dockerfile\nFROM node:20-alpine AS base\n\n# Install dependencies\nFROM base AS deps\nWORKDIR /app\nCOPY package.json package-lock.json* ./\nRUN npm ci\n\n# Build\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\nRUN npm run build\n\n# Production\nFROM base AS runner\nWORKDIR /app\n\nENV NODE_ENV=production\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\n# Copy standalone output\nCOPY --from=builder /app/.next/standalone ./\nCOPY --from=builder /app/.next/static ./.next/static\nCOPY --from=builder /app/public ./public\n\nUSER nextjs\n\nEXPOSE 3000\nENV PORT=3000\nENV HOSTNAME=\"0.0.0.0\"\n\nCMD [\"node\", \"server.js\"]\n```\n\n### Docker Compose\n\n```yaml\nversion: '3.8'\n\nservices:\n  web:\n    build: .\n    ports:\n      - \"3000:3000\"\n    environment:\n      - NODE_ENV=production\n    restart: unless-stopped\n    healthcheck:\n      test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:3000/api/health\"]\n      interval: 30s\n      timeout: 10s\n      retries: 3\n```\n\n## PM2 Deployment\n\nFor traditional server deployments:\n\n```js\n// ecosystem.config.js\nmodule.exports = {\n  apps: [{\n    name: 'nextjs',\n    script: '.next/standalone/server.js',\n    instances: 'max',\n    exec_mode: 'cluster',\n    env: {\n      NODE_ENV: 'production',\n      PORT: 3000,\n    },\n  }],\n};\n```\n\n```bash\nnpm run build\npm2 start ecosystem.config.js\n```\n\n## ISR and Cache Handlers\n\n### The Problem\n\nISR (Incremental Static Regeneration) uses filesystem caching by default. This **breaks with multiple instances**:\n\n- Instance A regenerates page → saves to its local disk\n- Instance B serves stale page → doesn't see Instance A's cache\n- Load balancer sends users to random instances → inconsistent content\n\n### Solution: Custom Cache Handler\n\nNext.js 14+ supports custom cache handlers for shared storage:\n\n```js\n// next.config.js\nmodule.exports = {\n  cacheHandler: require.resolve('./cache-handler.js'),\n  cacheMaxMemorySize: 0, // Disable in-memory cache\n};\n```\n\n#### Redis Cache Handler Example\n\n```js\n// cache-handler.js\nconst Redis = require('ioredis');\n\nconst redis = new Redis(process.env.REDIS_URL);\nconst CACHE_PREFIX = 'nextjs:';\n\nmodule.exports = class CacheHandler {\n  constructor(options) {\n    this.options = options;\n  }\n\n  async get(key) {\n    const data = await redis.get(CACHE_PREFIX + key);\n    if (!data) return null;\n\n    const parsed = JSON.parse(data);\n    return {\n      value: parsed.value,\n      lastModified: parsed.lastModified,\n    };\n  }\n\n  async set(key, data, ctx) {\n    const cacheData = {\n      value: data,\n      lastModified: Date.now(),\n    };\n\n    // Set TTL based on revalidate option\n    if (ctx?.revalidate) {\n      await redis.setex(\n        CACHE_PREFIX + key,\n        ctx.revalidate,\n        JSON.stringify(cacheData)\n      );\n    } else {\n      await redis.set(CACHE_PREFIX + key, JSON.stringify(cacheData));\n    }\n  }\n\n  async revalidateTag(tags) {\n    // Implement tag-based invalidation\n    // This requires tracking which keys have which tags\n  }\n};\n```\n\n#### S3 Cache Handler Example\n\n```js\n// cache-handler.js\nconst { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3');\n\nconst s3 = new S3Client({ region: process.env.AWS_REGION });\nconst BUCKET = process.env.CACHE_BUCKET;\n\nmodule.exports = class CacheHandler {\n  async get(key) {\n    try {\n      const response = await s3.send(new GetObjectCommand({\n        Bucket: BUCKET,\n        Key: `cache/${key}`,\n      }));\n      const body = await response.Body.transformToString();\n      return JSON.parse(body);\n    } catch (err) {\n      if (err.name === 'NoSuchKey') return null;\n      throw err;\n    }\n  }\n\n  async set(key, data, ctx) {\n    await s3.send(new PutObjectCommand({\n      Bucket: BUCKET,\n      Key: `cache/${key}`,\n      Body: JSON.stringify({\n        value: data,\n        lastModified: Date.now(),\n      }),\n      ContentType: 'application/json',\n    }));\n  }\n};\n```\n\n## What Works vs What Needs Setup\n\n| Feature | Single Instance | Multi-Instance | Notes |\n|---------|----------------|----------------|-------|\n| SSR | Yes | Yes | No special setup |\n| SSG | Yes | Yes | Built at deploy time |\n| ISR | Yes | Needs cache handler | Filesystem cache breaks |\n| Image Optimization | Yes | Yes | CPU-intensive, consider CDN |\n| Middleware | Yes | Yes | Runs on Node.js |\n| Edge Runtime | Limited | Limited | Some features Node-only |\n| `revalidatePath/Tag` | Yes | Needs cache handler | Must share cache |\n| `next/font` | Yes | Yes | Fonts bundled at build |\n| Draft Mode | Yes | Yes | Cookie-based |\n\n## Image Optimization\n\nNext.js Image Optimization works out of the box but is CPU-intensive.\n\n### Option 1: Built-in (Simple)\n\nWorks automatically, but consider:\n- Set `deviceSizes` and `imageSizes` in config to limit variants\n- Use `minimumCacheTTL` to reduce regeneration\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    minimumCacheTTL: 60 * 60 * 24, // 24 hours\n    deviceSizes: [640, 750, 1080, 1920], // Limit sizes\n  },\n};\n```\n\n### Option 2: External Loader (Recommended for Scale)\n\nOffload to Cloudinary, Imgix, or similar:\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    loader: 'custom',\n    loaderFile: './lib/image-loader.js',\n  },\n};\n```\n\n```js\n// lib/image-loader.js\nexport default function cloudinaryLoader({ src, width, quality }) {\n  const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`];\n  return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}${src}`;\n}\n```\n\n## Environment Variables\n\n### Build-time vs Runtime\n\n```js\n// Available at build time only (baked into bundle)\nNEXT_PUBLIC_API_URL=https://api.example.com\n\n// Available at runtime (server-side only)\nDATABASE_URL=postgresql://...\nAPI_SECRET=...\n```\n\n### Runtime Configuration\n\nFor truly dynamic config, don't use `NEXT_PUBLIC_*`. Instead:\n\n```tsx\n// app/api/config/route.ts\nexport async function GET() {\n  return Response.json({\n    apiUrl: process.env.API_URL,\n    features: process.env.FEATURES?.split(','),\n  });\n}\n```\n\n## OpenNext: Serverless Without Vercel\n\n[OpenNext](https://open-next.js.org/) adapts Next.js for AWS Lambda, Cloudflare Workers, etc.\n\n```bash\nnpx create-sst@latest\n# or\nnpx @opennextjs/aws build\n```\n\nSupports:\n- AWS Lambda + CloudFront\n- Cloudflare Workers\n- Netlify Functions\n- Deno Deploy\n\n## Health Check Endpoint\n\nAlways include a health check for load balancers:\n\n```tsx\n// app/api/health/route.ts\nexport async function GET() {\n  try {\n    // Optional: check database connection\n    // await db.$queryRaw`SELECT 1`;\n\n    return Response.json({ status: 'healthy' }, { status: 200 });\n  } catch (error) {\n    return Response.json({ status: 'unhealthy' }, { status: 503 });\n  }\n}\n```\n\n## Pre-Deployment Checklist\n\n1. **Build locally first**: `npm run build` - catch errors before deploy\n2. **Test standalone output**: `node .next/standalone/server.js`\n3. **Set `output: 'standalone'`** for Docker\n4. **Configure cache handler** for multi-instance ISR\n5. **Set `HOSTNAME=\"0.0.0.0\"`** for containers\n6. **Copy `public/` and `.next/static/`** - not included in standalone\n7. **Add health check endpoint**\n8. **Test ISR revalidation** after deployment\n9. **Monitor memory usage** - Node.js defaults may need tuning\n\n## Testing Cache Handler\n\n**Critical**: Test your cache handler on every Next.js upgrade:\n\n```bash\n# Start multiple instances\nPORT=3001 node .next/standalone/server.js &\nPORT=3002 node .next/standalone/server.js &\n\n# Trigger ISR revalidation\ncurl http://localhost:3001/api/revalidate?path=/posts\n\n# Verify both instances see the update\ncurl http://localhost:3001/posts\ncurl http://localhost:3002/posts\n# Should return identical content\n```\n\n<!-- File: .agents/skills/next-best-practices/SKILL.md (Tokens: ~1011 | Category: anthropic-skill) -->\n---\nname: next-best-practices\ndescription: Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling\nuser-invocable: false\n---\n\n# Next.js Best Practices\n\nApply these rules when writing or reviewing Next.js code.\n\n## File Conventions\n\nSee [file-conventions.md](./file-conventions.md) for:\n- Project structure and special files\n- Route segments (dynamic, catch-all, groups)\n- Parallel and intercepting routes\n- Middleware rename in v16 (middleware → proxy)\n\n## RSC Boundaries\n\nDetect invalid React Server Component patterns.\n\nSee [rsc-boundaries.md](./rsc-boundaries.md) for:\n- Async client component detection (invalid)\n- Non-serializable props detection\n- Server Action exceptions\n\n## Async Patterns\n\nNext.js 15+ async API changes.\n\nSee [async-patterns.md](./async-patterns.md) for:\n- Async `params` and `searchParams`\n- Async `cookies()` and `headers()`\n- Migration codemod\n\n## Runtime Selection\n\nSee [runtime-selection.md](./runtime-selection.md) for:\n- Default to Node.js runtime\n- When Edge runtime is appropriate\n\n## Directives\n\nSee [directives.md](./directives.md) for:\n- `'use client'`, `'use server'` (React)\n- `'use cache'` (Next.js)\n\n## Functions\n\nSee [functions.md](./functions.md) for:\n- Navigation hooks: `useRouter`, `usePathname`, `useSearchParams`, `useParams`\n- Server functions: `cookies`, `headers`, `draftMode`, `after`\n- Generate functions: `generateStaticParams`, `generateMetadata`\n\n## Error Handling\n\nSee [error-handling.md](./error-handling.md) for:\n- `error.tsx`, `global-error.tsx`, `not-found.tsx`\n- `redirect`, `permanentRedirect`, `notFound`\n- `forbidden`, `unauthorized` (auth errors)\n- `unstable_rethrow` for catch blocks\n\n## Data Patterns\n\nSee [data-patterns.md](./data-patterns.md) for:\n- Server Components vs Server Actions vs Route Handlers\n- Avoiding data waterfalls (`Promise.all`, Suspense, preload)\n- Client component data fetching\n\n## Route Handlers\n\nSee [route-handlers.md](./route-handlers.md) for:\n- `route.ts` basics\n- GET handler conflicts with `page.tsx`\n- Environment behavior (no React DOM)\n- When to use vs Server Actions\n\n## Metadata & OG Images\n\nSee [metadata.md](./metadata.md) for:\n- Static and dynamic metadata\n- `generateMetadata` function\n- OG image generation with `next/og`\n- File-based metadata conventions\n\n## Image Optimization\n\nSee [image.md](./image.md) for:\n- Always use `next/image` over `<img>`\n- Remote images configuration\n- Responsive `sizes` attribute\n- Blur placeholders\n- Priority loading for LCP\n\n## Font Optimization\n\nSee [font.md](./font.md) for:\n- `next/font` setup\n- Google Fonts, local fonts\n- Tailwind CSS integration\n- Preloading subsets\n\n## Bundling\n\nSee [bundling.md](./bundling.md) for:\n- Server-incompatible packages\n- CSS imports (not link tags)\n- Polyfills (already included)\n- ESM/CommonJS issues\n- Bundle analysis\n\n## Scripts\n\nSee [scripts.md](./scripts.md) for:\n- `next/script` vs native script tags\n- Inline scripts need `id`\n- Loading strategies\n- Google Analytics with `@next/third-parties`\n\n## Hydration Errors\n\nSee [hydration-error.md](./hydration-error.md) for:\n- Common causes (browser APIs, dates, invalid HTML)\n- Debugging with error overlay\n- Fixes for each cause\n\n## Suspense Boundaries\n\nSee [suspense-boundaries.md](./suspense-boundaries.md) for:\n- CSR bailout with `useSearchParams` and `usePathname`\n- Which hooks require Suspense boundaries\n\n## Parallel & Intercepting Routes\n\nSee [parallel-routes.md](./parallel-routes.md) for:\n- Modal patterns with `@slot` and `(.)` interceptors\n- `default.tsx` for fallbacks\n- Closing modals correctly with `router.back()`\n\n## Self-Hosting\n\nSee [self-hosting.md](./self-hosting.md) for:\n- `output: 'standalone'` for Docker\n- Cache handlers for multi-instance ISR\n- What works vs needs extra setup\n\n## Debug Tricks\n\nSee [debug-tricks.md](./debug-tricks.md) for:\n- MCP endpoint for AI-assisted debugging\n- Rebuild specific routes with `--debug-build-paths`\n\n<!-- File: .agents/skills/next-best-practices/suspense-boundaries.md (Tokens: ~332 | Category: anthropic-skill) -->\n# Suspense Boundaries\n\nClient hooks that cause CSR bailout without Suspense boundaries.\n\n## useSearchParams\n\nAlways requires Suspense boundary in static routes. Without it, the entire page becomes client-side rendered.\n\n```tsx\n// Bad: Entire page becomes CSR\n'use client'\n\nimport { useSearchParams } from 'next/navigation'\n\nexport default function SearchBar() {\n  const searchParams = useSearchParams()\n  return <div>Query: {searchParams.get('q')}</div>\n}\n```\n\n```tsx\n// Good: Wrap in Suspense\nimport { Suspense } from 'react'\nimport SearchBar from './search-bar'\n\nexport default function Page() {\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <SearchBar />\n    </Suspense>\n  )\n}\n```\n\n## usePathname\n\nRequires Suspense boundary when route has dynamic parameters.\n\n```tsx\n// In dynamic route [slug]\n// Bad: No Suspense\n'use client'\nimport { usePathname } from 'next/navigation'\n\nexport function Breadcrumb() {\n  const pathname = usePathname()\n  return <nav>{pathname}</nav>\n}\n```\n\n```tsx\n// Good: Wrap in Suspense\n<Suspense fallback={<BreadcrumbSkeleton />}>\n  <Breadcrumb />\n</Suspense>\n```\n\nIf you use `generateStaticParams`, Suspense is optional.\n\n## Quick Reference\n\n| Hook | Suspense Required |\n|------|-------------------|\n| `useSearchParams()` | Yes |\n| `usePathname()` | Yes (dynamic routes) |\n| `useParams()` | No |\n| `useRouter()` | No |\n\n<!-- File: .agents/skills/next-cache-components/SKILL.md (Tokens: ~2342 | Category: anthropic-skill) -->\n---\nname: next-cache-components\ndescription: Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag\n---\n\n# Cache Components (Next.js 16+)\n\nCache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route.\n\n## Enable Cache Components\n\n```ts\n// next.config.ts\nimport type { NextConfig } from 'next'\n\nconst nextConfig: NextConfig = {\n  cacheComponents: true,\n}\n\nexport default nextConfig\n```\n\nThis replaces the old `experimental.ppr` flag.\n\n---\n\n## Three Content Types\n\nWith Cache Components enabled, content falls into three categories:\n\n### 1. Static (Auto-Prerendered)\n\nSynchronous code, imports, pure computations - prerendered at build time:\n\n```tsx\nexport default function Page() {\n  return (\n    <header>\n      <h1>Our Blog</h1>  {/* Static - instant */}\n      <nav>...</nav>\n    </header>\n  )\n}\n```\n\n### 2. Cached (`use cache`)\n\nAsync data that doesn't need fresh fetches every request:\n\n```tsx\nasync function BlogPosts() {\n  'use cache'\n  cacheLife('hours')\n\n  const posts = await db.posts.findMany()\n  return <PostList posts={posts} />\n}\n```\n\n### 3. Dynamic (Suspense)\n\nRuntime data that must be fresh - wrap in Suspense:\n\n```tsx\nimport { Suspense } from 'react'\n\nexport default function Page() {\n  return (\n    <>\n      <BlogPosts />  {/* Cached */}\n\n      <Suspense fallback={<p>Loading...</p>}>\n        <UserPreferences />  {/* Dynamic - streams in */}\n      </Suspense>\n    </>\n  )\n}\n\nasync function UserPreferences() {\n  const theme = (await cookies()).get('theme')?.value\n  return <p>Theme: {theme}</p>\n}\n```\n\n---\n\n## `use cache` Directive\n\n### File Level\n\n```tsx\n'use cache'\n\nexport default async function Page() {\n  // Entire page is cached\n  const data = await fetchData()\n  return <div>{data}</div>\n}\n```\n\n### Component Level\n\n```tsx\nexport async function CachedComponent() {\n  'use cache'\n  const data = await fetchData()\n  return <div>{data}</div>\n}\n```\n\n### Function Level\n\n```tsx\nexport async function getData() {\n  'use cache'\n  return db.query('SELECT * FROM posts')\n}\n```\n\n---\n\n## Cache Profiles\n\n### Built-in Profiles\n\n```tsx\n'use cache'                    // Default: 5m stale, 15m revalidate\n```\n\n```tsx\n'use cache: remote'           // Platform-provided cache (Redis, KV)\n```\n\n```tsx\n'use cache: private'          // For compliance, allows runtime APIs\n```\n\n### `cacheLife()` - Custom Lifetime\n\n```tsx\nimport { cacheLife } from 'next/cache'\n\nasync function getData() {\n  'use cache'\n  cacheLife('hours')  // Built-in profile\n  return fetch('/api/data')\n}\n```\n\nBuilt-in profiles: `'default'`, `'minutes'`, `'hours'`, `'days'`, `'weeks'`, `'max'`\n\n### Inline Configuration\n\n```tsx\nasync function getData() {\n  'use cache'\n  cacheLife({\n    stale: 3600,      // 1 hour - serve stale while revalidating\n    revalidate: 7200, // 2 hours - background revalidation interval\n    expire: 86400,    // 1 day - hard expiration\n  })\n  return fetch('/api/data')\n}\n```\n\n---\n\n## Cache Invalidation\n\n### `cacheTag()` - Tag Cached Content\n\n```tsx\nimport { cacheTag } from 'next/cache'\n\nasync function getProducts() {\n  'use cache'\n  cacheTag('products')\n  return db.products.findMany()\n}\n\nasync function getProduct(id: string) {\n  'use cache'\n  cacheTag('products', `product-${id}`)\n  return db.products.findUnique({ where: { id } })\n}\n```\n\n### `updateTag()` - Immediate Invalidation\n\nUse when you need the cache refreshed within the same request:\n\n```tsx\n'use server'\n\nimport { updateTag } from 'next/cache'\n\nexport async function updateProduct(id: string, data: FormData) {\n  await db.products.update({ where: { id }, data })\n  updateTag(`product-${id}`)  // Immediate - same request sees fresh data\n}\n```\n\n### `revalidateTag()` - Background Revalidation\n\nUse for stale-while-revalidate behavior:\n\n```tsx\n'use server'\n\nimport { revalidateTag } from 'next/cache'\n\nexport async function createPost(data: FormData) {\n  await db.posts.create({ data })\n  revalidateTag('posts')  // Background - next request sees fresh data\n}\n```\n\n---\n\n## Runtime Data Constraint\n\n**Cannot** access `cookies()`, `headers()`, or `searchParams` inside `use cache`.\n\n### Solution: Pass as Arguments\n\n```tsx\n// Wrong - runtime API inside use cache\nasync function CachedProfile() {\n  'use cache'\n  const session = (await cookies()).get('session')?.value  // Error!\n  return <div>{session}</div>\n}\n\n// Correct - extract outside, pass as argument\nasync function ProfilePage() {\n  const session = (await cookies()).get('session')?.value\n  return <CachedProfile sessionId={session} />\n}\n\nasync function CachedProfile({ sessionId }: { sessionId: string }) {\n  'use cache'\n  // sessionId becomes part of cache key automatically\n  const data = await fetchUserData(sessionId)\n  return <div>{data.name}</div>\n}\n```\n\n### Exception: `use cache: private`\n\nFor compliance requirements when you can't refactor:\n\n```tsx\nasync function getData() {\n  'use cache: private'\n  const session = (await cookies()).get('session')?.value  // Allowed\n  return fetchData(session)\n}\n```\n\n---\n\n## Cache Key Generation\n\nCache keys are automatic based on:\n- **Build ID** - invalidates all caches on deploy\n- **Function ID** - hash of function location\n- **Serializable arguments** - props become part of key\n- **Closure variables** - outer scope values included\n\n```tsx\nasync function Component({ userId }: { userId: string }) {\n  const getData = async (filter: string) => {\n    'use cache'\n    // Cache key = userId (closure) + filter (argument)\n    return fetch(`/api/users/${userId}?filter=${filter}`)\n  }\n  return getData('active')\n}\n```\n\n---\n\n## Complete Example\n\n```tsx\nimport { Suspense } from 'react'\nimport { cookies } from 'next/headers'\nimport { cacheLife, cacheTag } from 'next/cache'\n\nexport default function DashboardPage() {\n  return (\n    <>\n      {/* Static shell - instant from CDN */}\n      <header><h1>Dashboard</h1></header>\n      <nav>...</nav>\n\n      {/* Cached - fast, revalidates hourly */}\n      <Stats />\n\n      {/* Dynamic - streams in with fresh data */}\n      <Suspense fallback={<NotificationsSkeleton />}>\n        <Notifications />\n      </Suspense>\n    </>\n  )\n}\n\nasync function Stats() {\n  'use cache'\n  cacheLife('hours')\n  cacheTag('dashboard-stats')\n\n  const stats = await db.stats.aggregate()\n  return <StatsDisplay stats={stats} />\n}\n\nasync function Notifications() {\n  const userId = (await cookies()).get('userId')?.value\n  const notifications = await db.notifications.findMany({\n    where: { userId, read: false }\n  })\n  return <NotificationList items={notifications} />\n}\n```\n\n---\n\n## Migration from Previous Versions\n\n| Old Config | Replacement |\n|-----------|-------------|\n| `experimental.ppr` | `cacheComponents: true` |\n| `dynamic = 'force-dynamic'` | Remove (default behavior) |\n| `dynamic = 'force-static'` | `'use cache'` + `cacheLife('max')` |\n| `revalidate = N` | `cacheLife({ revalidate: N })` |\n| `unstable_cache()` | `'use cache'` directive |\n\n### Migrating `unstable_cache` to `use cache`\n\n`unstable_cache` has been replaced by the `use cache` directive in Next.js 16. When `cacheComponents` is enabled, convert `unstable_cache` calls to `use cache` functions:\n\n**Before (`unstable_cache`):**\n\n```tsx\nimport { unstable_cache } from 'next/cache'\n\nconst getCachedUser = unstable_cache(\n  async (id) => getUser(id),\n  ['my-app-user'],\n  {\n    tags: ['users'],\n    revalidate: 60,\n  }\n)\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const user = await getCachedUser(id)\n  return <div>{user.name}</div>\n}\n```\n\n**After (`use cache`):**\n\n```tsx\nimport { cacheLife, cacheTag } from 'next/cache'\n\nasync function getCachedUser(id: string) {\n  'use cache'\n  cacheTag('users')\n  cacheLife({ revalidate: 60 })\n  return getUser(id)\n}\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n  const { id } = await params\n  const user = await getCachedUser(id)\n  return <div>{user.name}</div>\n}\n```\n\nKey differences:\n- **No manual cache keys** - `use cache` generates keys automatically from function arguments and closures. The `keyParts` array from `unstable_cache` is no longer needed.\n- **Tags** - Replace `options.tags` with `cacheTag()` calls inside the function.\n- **Revalidation** - Replace `options.revalidate` with `cacheLife({ revalidate: N })` or a built-in profile like `cacheLife('minutes')`.\n- **Dynamic data** - `unstable_cache` did not support `cookies()` or `headers()` inside the callback. The same restriction applies to `use cache`, but you can use `'use cache: private'` if needed.\n\n---\n\n## Limitations\n\n- **Edge runtime not supported** - requires Node.js\n- **Static export not supported** - needs server\n- **Non-deterministic values** (`Math.random()`, `Date.now()`) execute once at build time inside `use cache`\n\nFor request-time randomness outside cache:\n\n```tsx\nimport { connection } from 'next/server'\n\nasync function DynamicContent() {\n  await connection()  // Defer to request time\n  const id = crypto.randomUUID()  // Different per request\n  return <div>{id}</div>\n}\n```\n\nSources:\n- [Cache Components Guide](https://nextjs.org/docs/app/getting-started/cache-components)\n- [use cache Directive](https://nextjs.org/docs/app/api-reference/directives/use-cache)\n- [unstable_cache (legacy)](https://nextjs.org/docs/app/api-reference/functions/unstable_cache)\n\n<!-- File: .agents/skills/next-upgrade/SKILL.md (Tokens: ~487 | Category: anthropic-skill) -->\n---\nname: next-upgrade\ndescription: Upgrade Next.js to the latest version following official migration guides and codemods\nargument-hint: \"[target-version]\"\n---\n\n# Upgrade Next.js\n\nUpgrade the current project to the latest Next.js version following official migration guides.\n\n## Instructions\n\n1. **Detect current version**: Read `package.json` to identify the current Next.js version and related dependencies (React, React DOM, etc.)\n\n2. **Fetch the latest upgrade guide**: Use WebFetch to get the official upgrade documentation:\n   - Codemods: https://nextjs.org/docs/app/guides/upgrading/codemods\n   - Version-specific guides (adjust version as needed):\n     - https://nextjs.org/docs/app/guides/upgrading/version-16 \n     - https://nextjs.org/docs/app/guides/upgrading/version-15\n     - https://nextjs.org/docs/app/guides/upgrading/version-14\n\n3. **Determine upgrade path**: Based on current version, identify which migration steps apply. For major version jumps, upgrade incrementally (e.g., 13 → 14 → 15).\n\n4. **Run codemods first**: Next.js provides codemods to automate breaking changes:\n   ```bash\n   npx @next/codemod@latest <transform> <path>\n   ```\n   Common transforms:\n   - `next-async-request-api` - Updates async Request APIs (v15)\n   - `next-request-geo-ip` - Migrates geo/ip properties (v15)\n   - `next-dynamic-access-named-export` - Transforms dynamic imports (v15)\n\n5. **Update dependencies**: Upgrade Next.js and peer dependencies together:\n   ```bash\n   npm install next@latest react@latest react-dom@latest\n   ```\n\n6. **Review breaking changes**: Check the upgrade guide for manual changes needed:\n   - API changes (e.g., async params in v15)\n   - Configuration changes in `next.config.js`\n   - Deprecated features being removed\n\n7. **Update TypeScript types** (if applicable):\n   ```bash\n   npm install @types/react@latest @types/react-dom@latest\n   ```\n\n8. **Test the upgrade**:\n   - Run `npm run build` to check for build errors\n   - Run `npm run dev` and test key functionality\n\n<!-- ... +106 more modular skills available via /api/skills/midudev/preguntas-entrevista-react?path={path} -->\n</yakaai_skills>"}