Install
$ agentstack add skill-peterhdd-agent-skills-engineering-frontend-developer ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Frontend Development Guide
Overview
This guide covers modern frontend development with React, Vue, Angular, and Svelte, including component architecture, performance optimization, accessibility, and testing. Use it when building web applications, component libraries, or optimizing frontend performance.
Framework and Layout Decision Rules
- When choosing a framework, match it to team expertise and project constraints; default to React for broad ecosystem needs, Vue for progressive enhancement into existing pages, Svelte for bundle-size-critical apps, and Angular when the project requires an opinionated full-framework with built-in DI and routing.
- When implementing a design, use CSS Grid for two-dimensional page layouts and Flexbox for one-dimensional component alignment; avoid absolute positioning for layout purposes because it breaks responsive reflow.
- When building a component library, expose each component as a named export with TypeScript props interface, a Storybook story, and a unit test -- components without all three are not merged.
- When integrating with backend APIs, centralize fetch logic in a typed API client layer (e.g., a single
api.tsmodule usingfetchoraxioswith interceptors) so auth headers, error transforms, and retries are handled in one place.
Performance Decision Rules
- When a page's Largest Contentful Paint exceeds 2.5 seconds in Lighthouse CI, treat it as a blocking bug -- profile with Chrome DevTools Performance tab and fix the largest bottleneck before merging.
- When adding animations, use CSS
transformandopacityproperties (compositor-only) rather thanwidth,height, ortop/leftto avoid triggering layout recalculations that cause jank. - When the app needs offline support, register a service worker with a cache-first strategy for static assets and a network-first strategy for API requests, falling back to cached responses when offline.
- When initial JS bundle exceeds the budget (e.g., 200 KB gzipped), add route-based code splitting with
React.lazy()or dynamicimport()and defer non-critical scripts below the fold. - When supporting older browsers, define a browserslist config and let the build tool (Vite, Webpack) auto-polyfill; do not manually add polyfills or feature checks unless browserslist coverage is insufficient.
- When adding images, use `
with WebP/AVIF sources and explicitwidth/heightattributes to prevent layout shift; for images below the fold, addloading="lazy"`. - When a route is not needed on initial page load, wrap it in
React.lazy()(or framework equivalent) with a `` fallback so the main bundle excludes that route's code. - When serving static assets, configure the CDN or server to set
Cache-Control: public, max-age=31536000, immutableon content-hashed filenames andno-cacheonindex.html.
Accessibility Decision Rules
- When building any interactive component, add ARIA attributes, keyboard handlers (
Enter,Space,Escapeas appropriate), and test with axe-core before marking the task complete. - When building forms, associate every `
with aviahtmlFor/id, provide visible error messages linked witharia-describedby`, and ensure the form is fully operable with keyboard-only navigation. - When using color to convey meaning (e.g., error states, status badges), always include a secondary indicator (icon, text, pattern) so color-blind users can distinguish states.
- When adding a modal or dropdown, trap focus inside the element while it is open and return focus to the trigger element on close; test by tabbing through the entire flow without a mouse.
- When a pull request adds a new interactive component, the PR must include an axe-core integration test that asserts zero WCAG 2.1 AA violations before it can be merged.
CSS Debugging Decision Rules
When a layout breaks, diagnose by symptom:
- Flex child overflows its container: Add
min-width: 0(row) ormin-height: 0(column) to the flex child. Flex items default tomin-width: auto, which prevents shrinking below content size. - Grid items ignore column width: Use
minmax(0, 1fr)instead of1fr. Plain1frmeansminmax(auto, 1fr), which lets content push the column wider than intended. position: stickydoes not stick: Check every ancestor foroverflow: hidden,overflow: auto, oroverflow: scroll. Any of these creates a new scrolling context that contains the sticky element. Also verify the element has atop/bottomvalue set.- Element centered with
margin: autodoes not center: Verify the element has an explicitwidth(block) or the parent hasdisplay: flex(flex child).margin: autodoes nothing on full-width block elements. z-indexdoes not work: The element needsposition: relative/absolute/fixed/stickyto create a stacking context. Also check if an ancestor creates a stacking context (viatransform, `opacity 20 packages or polyglot = Nx. Never add monorepo tooling to a single-package project.
SSR and Server Components Decision Rules
When to Use SSR vs CSR vs SSG
- SSG (Static Site Generation): Use for content that changes less than once per hour (marketing pages, docs, blog posts). Build at deploy time. Fastest possible TTFB.
- SSR (Server-Side Rendering): Use for personalized content (dashboards, user profiles), SEO-critical pages with dynamic data, or pages where stale data is unacceptable. Adds server latency to every request — cache aggressively with
Cache-Controlor CDN. - CSR (Client-Side Rendering): Use for authenticated-only pages (admin panels, internal tools) where SEO does not matter and interactivity is the priority. Simplest to build and deploy (static hosting).
- ISR (Incremental Static Regeneration): Use for pages that are mostly static but need periodic updates (product listings, pricing pages). Set
revalidateinterval based on how stale the data can be: 60s for prices, 3600s for blog posts.
React Server Components (RSC) Decision Rules
- Default to Server Components: Every component is a Server Component unless it needs interactivity. If a component has
onClick,useState,useEffect, or browser APIs — it must be a Client Component ('use client'). - Data fetching: Fetch data in Server Components with
async/awaitdirectly — nouseEffect, no TanStack Query, no loading states needed. The data is resolved before the HTML is sent. - Auth in Server Components: Read the session/cookie in a Server Component and pass the user object as a prop to Client Components. Never read cookies or sessions in Client Components — they execute on the client.
- When to use Server Actions vs API routes: Use Server Actions (
'use server') for form submissions and mutations that are called from the UI. Use API routes (/api/*) for webhooks, third-party integrations, or endpoints called by external services. - Sharing state between Server and Client Components: Pass serializable data as props from Server → Client. If you need client-side state derived from server data, initialize
useStatewith the server prop and manage updates on the client. - Large data sets: Stream with `
boundaries. Wrap the slow Server Component in}>`. The shell renders immediately; the slow component streams in when ready. Place Suspense boundaries at meaningful UI sections (sidebar, main content, comments) — not around every component.
Hydration Error Decision Tree
- Error: "Text content does not match": A value differs between server and client render. Common causes:
Date.now(),Math.random(), ornew Date().toLocaleString()in render → move touseEffect+useState.typeof window !== 'undefined'conditional rendering → useuseEffectto set ahasMountedflag.- Browser extensions injecting DOM nodes → not your bug, but test in incognito to confirm.
- Error: "Hydration failed because the server rendered HTML didn't match": A structural difference (different elements). Common causes:
- `
nested inside, orinside` → fix the nesting, HTML spec does not allow it. - A third-party component renders differently on server vs client → wrap in
dynamic(() => import(...), { ssr: false }). - Missing closing tags or self-closing tags where HTML requires explicit close.
Performance Debugging Workflow
When a page is slow, follow this sequence — do not skip steps:
Step 1: Identify the bottleneck type
- Run Lighthouse. Check which metric is failing:
- LCP >2.5s: The largest visible element loads too slowly. Go to Step 2.
- FID/INP >200ms: User interaction is blocked by JavaScript. Go to Step 3.
- CLS >0.1: Layout shifts after initial paint. Go to Step 4.
Step 2: Fix LCP
- Check what the LCP element is (Lighthouse shows it). Usually: hero image, heading text, or video.
- If image: Add
priority(Next.js) orfetchpriority="high". Add explicitwidth/height. Serve in WebP/AVIF. If served from a CDN, verify cache headers. - If text: Check for render-blocking fonts. Use
font-display: swaporfont-display: optional. Preload the critical font file with ``. - If blocked by JS: The bundle is too large. Check Step 3. The LCP element cannot render until the JS that creates it is loaded and executed.
- Check the server response time (TTFB). If TTFB >600ms, the bottleneck is backend or CDN — not frontend.
Step 3: Fix INP (Interaction to Next Paint)
- Open Chrome DevTools → Performance → record a click/type interaction.
- Find the Long Task (>50ms yellow bar). Click it to see the call stack.
- If the long task is your code: Break the work into smaller chunks with
requestIdleCallback,scheduler.yield()(Chrome 115+), orsetTimeout(fn, 0)to yield to the browser between frames. - If the long task is React re-rendering: Open React DevTools Profiler. Find the component that re-renders. Common fixes:
React.memo(expensive children),useMemo(derived values), or move state closer to where it is used. If >500 components re-render on a single state change, the state is too high in the tree. - If the long task is third-party script (analytics, ads): Defer with
asyncordeferattribute, or load afterrequestIdleCallback.
Step 4: Fix CLS
- Check which element shifts (Lighthouse shows it, or use DevTools → "Layout Shift Regions").
- Image/video without dimensions: Add
widthandheightattributes (oraspect-ratioCSS). The browser reserves space before the asset loads. - Font swap: The fallback font has different metrics than the web font. Use
size-adjustin@font-faceto match metrics, or usefont-display: optionalto eliminate the swap entirely. - Dynamic content injected above the fold: Ads, banners, cookie notices. Reserve space with
min-heighton the container. If the content height varies, usecontain: layoutto prevent shifts from propagating. - Late-loading CSS: If a stylesheet loads after first paint and changes visible layout, inline the critical CSS or preload the stylesheet.
Code Quality Decision Rules
- When writing tests, require every component to have at least one unit test covering its primary render path and one interaction test (click, keyboard) -- enforce via a CI coverage gate of 80% line coverage minimum.
- When starting a project, enable
strict: trueintsconfig.jsonon day one; retrofitting strict mode later is exponentially harder as the codebase grows. - When an API call or async operation fails, display a user-facing error message with a retry action -- never swallow errors silently or show raw exception text.
- When a component file exceeds 300 lines, split it into smaller sub-components with a shared barrel export; large files signal mixed responsibilities.
- When setting up CI, include lint (ESLint), type-check (
tsc --noEmit), test (Vitest/Jest), and bundle-size check as required gates -- all four must pass before merge.
Workflow
Step 1: Project Setup and Architecture
- Set up modern development environment with proper tooling.
- Configure build optimization and performance monitoring.
- Establish testing framework and CI/CD integration.
- Create component architecture and design system foundation.
Step 2: Component Development
- Create reusable component library with proper TypeScript types.
- Implement responsive design with mobile-first approach.
- Build accessibility into components from the start.
- Create comprehensive unit tests for all components.
Step 3: Performance Optimization
- Implement code splitting and lazy loading strategies.
- Optimize images and assets for web delivery.
- Monitor Core Web Vitals and optimize accordingly.
- Set up performance budgets and monitoring.
Step 4: Testing and Quality Assurance
- Write comprehensive unit and integration tests.
- Perform accessibility testing with real assistive technologies.
- Test cross-browser compatibility and responsive behavior.
- Implement end-to-end testing for critical user flows.
Reference
Lighthouse CI Targets
- Performance score: 90+
- All interactive elements keyboard-navigable with visible focus indicators
- axe-core: zero violations at WCAG 2.1 AA level
- Bundle size: 3 levels, add React Context for that specific slice.
- 5-15 components with shared state: Use React Context +
useReducerfor structured state, or Zustand for simpler API. If the state is server data, use TanStack Query instead — not Context. - >15 components or complex derived state: Zustand (simple, small bundle) or Jotai (atomic, bottom-up). Use Redux Toolkit only if the team already knows Redux. Never introduce Redux to a new project.
- Server state (API data, caching, sync): TanStack Query (React), SWR (simpler needs), or Apollo Client (GraphQL). Never store server data in a client state manager (Zustand, Redux) — it causes stale data and duplicate cache management.
- Form state: react-hook-form for complex forms (>5 fields, validation, dynamic fields). Native
useStatefor simple forms (=90, Accessibility >=90, Best Practices >=90. If any score drops below 90, fix before merge. - Run
npx axe-coreoraxe-playwrighton every new page/component. Zero WCAG 2.1 AA violations. - Test keyboard navigation: Tab through the entire page. Every interactive element must be reachable and operable. Focus indicators must be visible.
- Open the browser DevTools Performance tab, record a user interaction, and check for: layout thrashing (forced reflows), long tasks >50ms, excessive re-renders.
- Check bundle size: run
npx bundlesizeor check the build output. If initial JS exceeds 200KB gzipped, investigate what is in the bundle (usenpx source-map-explorer). - Test responsive layout at 320px, 768px, and 1280px. No horizontal scroll, no overlapping elements, no unreadable text.
- Test with browser devtools network throttling set to "Slow 3G." If the page is unusable, add loading states and optimize critical rendering path.
Failure Recovery
- Component re-renders excessively: Use React DevTools Profiler to identify the cause. Common fixes: memoize with
React.memo(for expensive children),useMemo/useCallback(for derived values/callbacks passed as props), or move state closer to where it is used. - Bundle size suddenly increased: Run
source-map-explorerto find the culprit. Common causes: importing an entire library (import _ from 'lodash'→import groupBy from 'lodash/groupBy'), accidentally bundling a dev dependency, or a heavy polyfill. - Hydration mismatch (SSR/SSG): The server-rendered HTML differs from client render. Common causes: using
Date.now()orMath.random()in render, accessingwindow/documentduring SSR, or conditional rendering based on client-only state. Fix: useuseEffectfor client-only values, orsuppressHydrationWarningas last resort. - **CSS layout breaks at specif
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: PeterHdd
- Source: PeterHdd/agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.