Define the frontend skill that wraps react_advanced_skill and frontend-design skill for creating production-grade React components with TypeScript and Tailwind CSS.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add sharmapuneet1510/awesome-prompts --skill skills --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Frontend UI Generation Skill?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/sharmapuneet1510-frontend-ui-generation-skill-awesome-prompts)More formats (shields.io, HTML) on the badges page.
---
name: Frontend UI Generation Skill
version: 1.0
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.
---
# Frontend UI Generation Skill
## 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
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!