Back to skills
SKILL.md
Frontend UI Generation Skill
ASecurityDefine the frontend skill that wraps react_advanced_skill and frontend-design skill for creating production-grade React components with TypeScript and Tailwind CSS.
- 3 stars
- 0 votes
- 0 copies
- 2 views
- Added September 5, 2026
Works with
Security analysis
100/100Pro scans all 21 files and shows the line behind each finding
npx -y skills add sharmapuneet1510/awesome-prompts --skill skills --agent claude-codeAre you the author of Frontend UI Generation Skill?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/sharmapuneet1510-frontend-ui-generation-skill-awesome-prompts)---
name: Frontend UI Generation Skill
version: 1.1
description: Define the frontend skill that wraps react_advanced_skill and frontend-design skill for creating production-grade React components with TypeScript and Tailwind CSS.
applies_to: [react, typescript, tailwindcss, frontend]
---
# Frontend UI Generation Skill — v1.1
## Quick Card
> Read this card first. Load a section below only when the task needs it.
| | |
|---|---|
| **Use when** | Generating React + TypeScript components and pages — `implementer:build`, `architect:frontend` |
| **Skip when** | React idioms alone — `react_advanced_skill` |
| **Inputs** | Component spec, design system, API contract, accessibility level |
| **Produces** | Components, pages, hooks, Tailwind styles, tests |
| **Steps** | 1. Analyse requirements → 2. Design prop interfaces → 3. Build presentational then container components → 4. Wire data via TanStack Query → 5. Accessibility pass → 6. Tests |
| **Done when** | §Validation Checklist passes; loading, error, and empty states rendered and tested |
| **Senior defaults** | Server state in TanStack Query, never copied into local state · every async view renders loading / error / empty · forms: react-hook-form + Zod schema shared with the API types · semantic HTML first, ARIA only to fill gaps · no `any`; props typed from the API contract |
| **Load on demand** | §Input · §Output · §Process · §Code Example · §Validation Checklist |
| **Run report** | `html_report_skill` — adds: Component tree · Accessibility checks |
| **Pairs with** | `react_advanced_skill`, `test_skill` §4 |
---
## Purpose
This skill provides a comprehensive guide for generating production-grade frontend components using React 18+, TypeScript, and Tailwind CSS. It wraps and integrates `react_advanced_skill` and `frontend-design` skill to ensure components are not only functionally correct but also visually distinctive and accessible.
## Input
### Task Specification
- Component requirements (name, props, functionality)
- UI/UX requirements (layout, design patterns, accessibility)
- Integration points (APIs, state management, routing)
- Form handling requirements (validation, submission, error display)
### Context
- Existing component library patterns
- Design system and Tailwind CSS configuration
- Application's TypeScript strict mode requirements
- Accessibility audit results and WCAG 2.1 compliance level
### Tech Stack
- React 18+
- TypeScript 5.x (strict mode enabled)
- Tailwind CSS 3.x+
- React Router v6+ (for routing)
- TanStack Query (for data fetching)
- Zustand (for state management)
- Vitest + React Testing Library (for testing)
## Output
### Components
- Functional React components with `React.FC<Props>` TypeScript interface
- Reusable prop interfaces for all components
- Custom hooks for cross-cutting concerns (useAuth, useForm, useApi)
- Proper separation of presentational and container components
### Pages
- Full-page layouts with header, sidebar, and content sections
- Responsive design that works on mobile, tablet, and desktop
- Proper route integration using React Router
### Hooks
- Custom hooks with TypeScript typing for all parameters and return types
- Proper cleanup and dependency arrays in useEffect
- Testing utilities for hook testing
### Styles
- Tailwind CSS classNames for all styling
- CSS modules for component-scoped styles where needed
- Responsive design using Tailwind breakpoints (sm, md, lg, xl, 2xl)
- Dark mode support using Tailwind's dark mode utilities
### Tests
- Unit tests for components using Vitest + React Testing Library
- Integration tests for multi-component workflows
- Accessibility tests using @testing-library/jest-dom and jest-axe
- Minimum 85% code coverage
## Process
### 1. Analyze Requirements
- Read the component specification and design requirements
- Identify all props and their TypeScript types
- Map out user interactions and form validation rules
- Document accessibility requirements and WCAG level
### 2. Design TypeScript Interfaces
- Create prop interfaces for all components
- Use strict TypeScript mode (no implicit any)
- Leverage union types and discriminated unions for complex prop combinations
- Document all props with JSDoc comments
### 3. Build Component Structure
- Create the functional component with React.FC<Props> type
- Implement all hooks (useState, useEffect, useContext, useCallback)
- Add error boundaries where appropriate
- Implement proper loading and error states
### 4. Add Form Handling
- Validate input using controlled components
- Implement try/catch blocks for async operations
- Display user-friendly error messages
- Show loading states during form submission
- Use useCallback to memoize form handlers
### 5. Style with Tailwind CSS
- Use semantic HTML elements (button, input, form, label, etc.)
- Apply Tailwind classes for layout, spacing, colors, and typography
- Ensure responsive design with mobile-first approach
- Support dark mode using dark: prefix
### 6. Implement Accessibility
- Add htmlFor attributes on labels and id on inputs
- Use semantic HTML for better screen reader support
- Add ARIA labels where semantic HTML is insufficient
- Test with accessibility audit tools (axe, Lighthouse)
### 7. Write Comprehensive Tests
- Create unit tests for component rendering and interactions
- Test all form submission scenarios (success, error, validation)
- Test accessibility with jest-axe
- Achieve minimum 85% code coverage
## Code Example
### LoginForm.tsx Component
```typescript
import React, { useState, useCallback } from 'react';
import { useAuth } from '../hooks/useAuth';
/**
* LoginFormProps - Props interface for LoginForm component
*/
interface LoginFormProps {
onSuccess?: () => void;
redirectTo?: string;
}
/**
* LoginForm - User login form component with email and password fields
*
* Features:
* - Email and password input validation
* - Loading state during submission
* - Error message display
* - Accessible form with proper labels and ARIA attributes
* - Tailwind CSS responsive styling
*
* @component
* @example
* <LoginForm onSuccess={() => navigate('/dashboard')} />
*/
const LoginForm: React.FC<LoginFormProps> = ({ onSuccess, redirectTo = '/' }) => {
const [email, setEmail] = useState<string>('');
const [password, setPassword] = useState<string>('');
const [error, setError] = useState<string>('');
const [loading, setLoading] = useState<boolean>(false);
const { login } = useAuth();
/**
* Validate email format using regex
*/
const validateEmail = useCallback((email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}, []);
/**
* Validate password minimum length
*/
const validatePassword = useCallback((password: string): boolean => {
return password.length >= 8;
}, []);
/**
* Handle form submission with error handling
*/
const handleSubmit = useCallback(
async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setError('');
// Validate inputs
if (!email || !password) {
setError('Email and password are required');
return;
}
if (!validateEmail(email)) {
setError('Please enter a valid email address');
return;
}
if (!validatePassword(password)) {
setError('Password must be at least 8 characters');
return;
}
try {
setLoading(true);
// Call authentication hook
await login({
email: email.toLowerCase(),
password,
});
// Clear form on success
setEmail('');
setPassword('');
// Call success callback if provided
if (onSuccess) {
onSuccess();
}
} catch (err) {
// Handle authentication errors
const errorMessage = err instanceof Error ? err.message : 'Login failed. Please try again.';
setError(errorMessage);
} finally {
setLoading(false);
}
},
[email, password, validateEmail, validatePassword, login, onSuccess],
);
/**
* Handle email input change
*/
const handleEmailChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setEmail(e.target.value);
setError(''); // Clear error on input change
}, []);
/**
* Handle password input change
*/
const handlePasswordChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setPassword(e.target.value);
setError(''); // Clear error on input change
}, []);
return (
<div className="flex items-center justify-center min-h-screen bg-gray-50 dark:bg-gray-900 py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-md space-y-8">
{/* Header */}
<div className="text-center">
<h1 className="text-3xl font-extrabold text-gray-900 dark:text-white">
Sign in
</h1>
<p className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Access your account to continue
</p>
</div>
{/* Form */}
<form
className="mt-8 space-y-6"
onSubmit={handleSubmit}
noValidate
aria-label="Login form"
>
{/* Error Message */}
{error && (
<div
className="rounded-md bg-red-50 dark:bg-red-900/20 p-4 border border-red-200 dark:border-red-800"
role="alert"
aria-live="polite"
>
<p className="text-sm font-medium text-red-800 dark:text-red-200">
{error}
</p>
</div>
)}
{/* Email Field */}
<div>
<label
htmlFor="email"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
>
Email address
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
value={email}
onChange={handleEmailChange}
disabled={loading}
aria-label="Email address"
aria-describedby={error ? 'error-message' : undefined}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:text-white disabled:opacity-50 disabled:cursor-not-allowed transition"
placeholder="you@example.com"
/>
</div>
{/* Password Field */}
<div>
<label
htmlFor="password"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
>
Password
</label>
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
value={password}
onChange={handlePasswordChange}
disabled={loading}
aria-label="Password"
aria-describedby={error ? 'error-message' : undefined}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:text-white disabled:opacity-50 disabled:cursor-not-allowed transition"
placeholder="••••••••"
/>
</div>
{/* Submit Button */}
<button
type="submit"
disabled={loading}
aria-busy={loading}
className="w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white font-medium rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed transition"
>
{loading ? (
<span className="flex items-center justify-center">
<svg
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
aria-hidden="true"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
Signing in...
</span>
) : (
'Sign in'
)}
</button>
{/* Footer */}
<p className="text-center text-sm text-gray-600 dark:text-gray-400">
Don't have an account?{' '}
<a
href="/signup"
className="font-medium text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300"
>
Sign up
</a>
</p>
</form>
</div>
</div>
);
};
export default LoginForm;
```
### useAuth Hook
```typescript
import { useCallback } from 'react';
import { useAuthStore } from '../store/authStore';
import { apiClient } from '../services/apiClient';
interface LoginPayload {
email: string;
password: string;
}
interface AuthResponse {
token: string;
user: {
id: string;
email: string;
name: string;
};
}
/**
* useAuth - Custom hook for authentication operations
*/
export const useAuth = () => {
const { setUser, setToken, clearAuth } = useAuthStore();
const login = useCallback(async (payload: LoginPayload): Promise<void> => {
const response = await apiClient.post<AuthResponse>('/auth/login', payload);
if (!response.token) {
throw new Error('Invalid authentication response');
}
setToken(response.token);
setUser(response.user);
}, [setUser, setToken]);
const logout = useCallback(() => {
clearAuth();
}, [clearAuth]);
return { login, logout };
};
```
### LoginForm.test.tsx
```typescript
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { axe, toHaveNoViolations } from 'jest-axe';
import LoginForm from './LoginForm';
import * as authHooks from '../hooks/useAuth';
expect.extend(toHaveNoViolations);
describe('LoginForm', () => {
const mockLogin = vi.fn();
const mockOnSuccess = vi.fn();
beforeEach(() => {
mockLogin.mockClear();
mockOnSuccess.mockClear();
vi.spyOn(authHooks, 'useAuth').mockReturnValue({
login: mockLogin,
logout: vi.fn(),
} as any);
});
describe('Rendering', () => {
it('should render login form with email and password fields', () => {
render(<LoginForm />);
expect(screen.getByLabelText(/email address/i)).toBeInTheDocument();
expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /sign in/i })).toBeInTheDocument();
});
it('should render sign up link', () => {
render(<LoginForm />);
const signUpLink = screen.getByRole('link', { name: /sign up/i });
expect(signUpLink).toBeInTheDocument();
expect(signUpLink).toHaveAttribute('href', '/signup');
});
});
describe('Validation', () => {
it('should show error for empty email and password', async () => {
const user = userEvent.setup();
render(<LoginForm />);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.click(submitButton);
await waitFor(() => {
expect(
screen.getByText(/email and password are required/i),
).toBeInTheDocument();
});
});
it('should show error for invalid email format', async () => {
const user = userEvent.setup();
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email address/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.type(emailInput, 'invalid-email');
await user.type(passwordInput, 'password123');
await user.click(submitButton);
await waitFor(() => {
expect(
screen.getByText(/please enter a valid email address/i),
).toBeInTheDocument();
});
});
it('should show error for password less than 8 characters', async () => {
const user = userEvent.setup();
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email address/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.type(emailInput, 'user@example.com');
await user.type(passwordInput, 'short');
await user.click(submitButton);
await waitFor(() => {
expect(
screen.getByText(/password must be at least 8 characters/i),
).toBeInTheDocument();
});
});
});
describe('Form Submission', () => {
it('should call login with email and password on successful validation', async () => {
const user = userEvent.setup();
mockLogin.mockResolvedValue(undefined);
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email address/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.type(emailInput, 'user@example.com');
await user.type(passwordInput, 'password123');
await user.click(submitButton);
await waitFor(() => {
expect(mockLogin).toHaveBeenCalledWith({
email: 'user@example.com',
password: 'password123',
});
});
});
it('should clear form after successful login', async () => {
const user = userEvent.setup();
mockLogin.mockResolvedValue(undefined);
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email address/i) as HTMLInputElement;
const passwordInput = screen.getByLabelText(/password/i) as HTMLInputElement;
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.type(emailInput, 'user@example.com');
await user.type(passwordInput, 'password123');
await user.click(submitButton);
await waitFor(() => {
expect(emailInput.value).toBe('');
expect(passwordInput.value).toBe('');
});
});
it('should call onSuccess callback after successful login', async () => {
const user = userEvent.setup();
mockLogin.mockResolvedValue(undefined);
render(<LoginForm onSuccess={mockOnSuccess} />);
const emailInput = screen.getByLabelText(/email address/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.type(emailInput, 'user@example.com');
await user.type(passwordInput, 'password123');
await user.click(submitButton);
await waitFor(() => {
expect(mockOnSuccess).toHaveBeenCalled();
});
});
it('should show error message on login failure', async () => {
const user = userEvent.setup();
mockLogin.mockRejectedValue(new Error('Invalid credentials'));
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email address/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.type(emailInput, 'user@example.com');
await user.type(passwordInput, 'password123');
await user.click(submitButton);
await waitFor(() => {
expect(screen.getByText(/invalid credentials/i)).toBeInTheDocument();
});
});
});
describe('Loading State', () => {
it('should show loading state during form submission', async () => {
const user = userEvent.setup();
mockLogin.mockImplementation(
() => new Promise((resolve) => setTimeout(resolve, 100)),
);
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email address/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.type(emailInput, 'user@example.com');
await user.type(passwordInput, 'password123');
await user.click(submitButton);
expect(screen.getByRole('button', { name: /signing in/i })).toBeInTheDocument();
});
it('should disable inputs during form submission', async () => {
const user = userEvent.setup();
mockLogin.mockImplementation(
() => new Promise((resolve) => setTimeout(resolve, 100)),
);
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email address/i) as HTMLInputElement;
const passwordInput = screen.getByLabelText(/password/i) as HTMLInputElement;
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.type(emailInput, 'user@example.com');
await user.type(passwordInput, 'password123');
await user.click(submitButton);
expect(emailInput.disabled).toBe(true);
expect(passwordInput.disabled).toBe(true);
expect(submitButton).toBeDisabled();
});
});
describe('Accessibility', () => {
it('should not have accessibility violations', async () => {
const { container } = render(<LoginForm />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('should have proper labels for form fields', () => {
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email address/i);
const passwordInput = screen.getByLabelText(/password/i);
expect(emailInput).toHaveAttribute('id', 'email');
expect(passwordInput).toHaveAttribute('id', 'password');
});
it('should display error message with proper ARIA attributes', async () => {
const user = userEvent.setup();
render(<LoginForm />);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.click(submitButton);
await waitFor(() => {
const errorAlert = screen.getByRole('alert');
expect(errorAlert).toBeInTheDocument();
});
});
it('should clear error on input change', async () => {
const user = userEvent.setup();
render(<LoginForm />);
const submitButton = screen.getByRole('button', { name: /sign in/i });
await user.click(submitButton);
await waitFor(() => {
expect(
screen.getByText(/email and password are required/i),
).toBeInTheDocument();
});
const emailInput = screen.getByLabelText(/email address/i);
await user.type(emailInput, 'user@example.com');
await waitFor(() => {
expect(
screen.queryByText(/email and password are required/i),
).not.toBeInTheDocument();
});
});
});
});
```
## Validation Checklist
- [ ] TypeScript strict mode enabled (`strict: true` in tsconfig.json)
- [ ] All component props have interfaces with JSDoc comments
- [ ] All hooks have proper TypeScript typing
- [ ] Responsive design implemented with Tailwind breakpoints
- [ ] Dark mode support using `dark:` prefix in Tailwind classes
- [ ] Accessibility: All form inputs have labels with matching `htmlFor` and `id` attributes
- [ ] Accessibility: Semantic HTML used (button, input, form, label, a)
- [ ] Accessibility: ARIA attributes added where semantic HTML is insufficient (aria-label, aria-describedby, role)
- [ ] Error handling with try/catch blocks for async operations
- [ ] Loading states for async operations
- [ ] No hardcoded strings - all user-facing text is in appropriate places
- [ ] All event handlers use useCallback to prevent unnecessary re-renders
- [ ] Component prop defaults are sensible and documented
- [ ] CSS classes follow Tailwind naming conventions
## Success Criteria
- [ ] Components render correctly in browser
- [ ] All form submissions work as expected
- [ ] Mobile responsive design works on all breakpoints
- [ ] Test coverage reaches minimum 85%
- [ ] Accessibility audit passes (axe or Lighthouse)
- [ ] All TypeScript types are correct (no implicit any)
- [ ] Error messages are user-friendly and helpful
- [ ] Loading states prevent multiple submissions
## Internal Calls
This skill internally calls and wraps:
- **frontend-design** - For high-quality UI/UX design patterns and visual polish
- **react_advanced_skill** - For React 18+ patterns, TypeScript typing, and component best practices
Files in this skill
- README.md
- adr_skill.md
- agent_skill_design_skill.md
- apache_camel_skill.md
- apache_pulsar_skill.md
- ba_create_skill.md
- backend_skill.md
- code_documentation_skill.md
- code_formatting_skill.md
- code_health_skill.md
- code_review_skill.md
- context_builder_skill.md
- current_tech_spec_skill.md
- database_skill.md
- debugging_skill.md
- error_handling_skill.md
- frontend_skill.md
- java_advanced_skill.md
- jira_html_report_skill.md
- jira_incremental_spec_generator_skill.md
Attribution
Comments
Loading comments…