This document outlines the standards and best practices for frontend architecture across all Bayat projects. Following these guidelines ensures consistency, maintainability, and scalability of frontend applications.
- Core Principles
- Application Architecture
- State Management
- Component Design
- Code Organization
- Performance Optimization
- Testing Strategy
- Build and Bundling
- Styling Architecture
- Routing Standards
- API Integration
- Security Practices
- Accessibility Standards
- Internationalization
- Error Handling
- Monitoring and Analytics
- Documentation
All frontend architectures at Bayat should adhere to these core principles:
- Component-Based: Design systems as composable, reusable components
- Single Responsibility: Each component should do one thing well
- Separation of Concerns: Separate business logic, presentation, and state management
- Unidirectional Data Flow: Data flows in one direction for predictability
- Performance First: Optimize for fast initial load and interaction
- Accessibility by Default: Ensure applications are usable by everyone
- Responsive Design: Applications work across all device sizes
- Testable Code: Architecture supports comprehensive testing
- Maintainable: Code is organized for long-term maintenance
- Framework Agnostic Core: Core business logic should be framework-agnostic where possible
Choose the appropriate architecture pattern based on project requirements:
Pattern | Best For | Key Characteristics |
---|---|---|
Component-Based | Most applications | Composable, reusable components |
Atomic Design | Design system implementations | Hierarchy of components (atoms, molecules, organisms, etc.) |
Micro-Frontend | Large applications with multiple teams | Independent frontend applications composed together |
JAMstack | Content-heavy applications | JavaScript, APIs, and Markup architecture |
Server-Side Rendering | SEO-critical applications | Render initial HTML on the server |
Static Site Generation | Content sites with infrequent updates | Pre-render pages at build time |
Standard layered architecture for frontend applications:
├── UI Layer (Components)
│ ├── Pages/Screens
│ ├── Layouts
│ ├── UI Components
│ └── Design System
├── Application Layer
│ ├── State Management
│ ├── Routing
│ ├── Forms
│ └── Services
├── Domain Layer
│ ├── Business Logic
│ ├── Models/Entities
│ └── Validation
└── Infrastructure Layer
├── API Clients
├── Storage
├── Authentication
└── Analytics
Approved frontend technologies:
Category | Approved Technologies | Notes |
---|---|---|
Frameworks | React, Angular, Vue.js | React preferred for new projects |
State Management | Redux, MobX, Zustand, React Context | Choose based on complexity |
CSS Approaches | CSS Modules, Styled Components, Tailwind CSS | Tailwind preferred for new projects |
Build Tools | Webpack, Vite, Next.js | Vite preferred for new projects |
Testing | Jest, React Testing Library, Cypress | Combination recommended |
Organize application state into these categories:
- UI State: Temporary visual states (e.g., open/closed, loading, validation)
- Application State: Session-level state (e.g., current user, theme preferences)
- Domain State: Business data (e.g., products, orders, users)
- Server Cache: Local cache of server data (e.g., API responses)
Choose the appropriate state management pattern based on complexity:
State Type | Simple Applications | Medium Complexity | High Complexity |
---|---|---|---|
UI State | Component State | Context API | Redux Toolkit |
Application State | Context API | Context + Hooks | Redux Toolkit |
Domain State | Context API | React Query/SWR | Redux Toolkit + Middleware |
Server Cache | Fetch + useEffect | React Query/SWR | Redux Toolkit Query |
- Minimize State: Store only necessary data in state
- Single Source of Truth: Avoid duplicating state
- Immutability: Never directly mutate state
- Normalize Complex Data: Flatten nested data structures
- Colocation: Keep state close to where it's used
- Persistence: Use local storage for persistent state
- Hydration: Restore state on page reload when needed
Organize components in a clear hierarchy:
- Page Components: Represent entire pages/routes
- Container Components: Manage data and state
- Feature Components: Implement specific features
- UI Components: Reusable UI elements
- Design System Components: Primitive UI components
- Props Interface: Define clear prop interfaces for all components
- Prop Validation: Validate all component props
- Default Props: Provide sensible defaults
- Composition: Favor composition over inheritance
- Pure Components: Make components pure where possible
- Memoization: Use memoization for expensive calculations
- Error Boundaries: Implement error boundaries for fault isolation
// Button.tsx
import React, { memo } from 'react';
import classNames from 'classnames';
import './Button.css';
export type ButtonVariant = 'primary' | 'secondary' | 'tertiary';
export type ButtonSize = 'small' | 'medium' | 'large';
export interface ButtonProps {
/** Button label content */
children: React.ReactNode;
/** Visual variant of the button */
variant?: ButtonVariant;
/** Size of the button */
size?: ButtonSize;
/** Disabled state */
disabled?: boolean;
/** Click handler */
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
/** Additional class names */
className?: string;
/** Button type attribute */
type?: 'button' | 'submit' | 'reset';
}
/**
* Primary button component for user interaction
*/
const Button = ({
children,
variant = 'primary',
size = 'medium',
disabled = false,
onClick,
className,
type = 'button',
...rest
}: ButtonProps) => {
return (
<button
type={type}
className={classNames(
'btn',
`btn--${variant}`,
`btn--${size}`,
{ 'btn--disabled': disabled },
className
)}
disabled={disabled}
onClick={onClick}
{...rest}
>
{children}
</button>
);
};
// Use memo for performance optimization
export default memo(Button);
Follow this standard project structure:
src/
├── assets/ # Static assets
├── components/ # Shared components
│ ├── ui/ # UI components
│ └── features/ # Feature components
├── config/ # Application configuration
├── hooks/ # Custom hooks
├── layouts/ # Page layouts
├── pages/ # Page components
├── services/ # External service integrations
├── store/ # State management
├── styles/ # Global styles
├── types/ # TypeScript type definitions
├── utils/ # Utility functions
├── App.tsx # Application root
└── main.tsx # Entry point
For larger applications, consider a feature-based structure:
src/
├── common/ # Shared code
│ ├── components/ # Shared components
│ ├── hooks/ # Shared hooks
│ └── utils/ # Shared utilities
├── features/ # Feature modules
│ ├── auth/ # Authentication feature
│ │ ├── components/ # Feature-specific components
│ │ ├── hooks/ # Feature-specific hooks
│ │ ├── services/ # Feature-specific services
│ │ ├── store/ # Feature-specific state
│ │ └── index.ts # Feature entry point
│ ├── products/ # Products feature
│ └── checkout/ # Checkout feature
├── layouts/ # Application layouts
├── pages/ # Route pages
├── store/ # Root store configuration
├── App.tsx # Application root
└── main.tsx # Entry point
Use consistent naming conventions:
- Files: PascalCase for components, camelCase for utilities
- Components: PascalCase (e.g.,
Button.tsx
) - Hooks: camelCase with
use
prefix (e.g.,useAuth.ts
) - Context: PascalCase with
Context
suffix (e.g.,AuthContext.tsx
) - Constants: UPPER_SNAKE_CASE
- Types/Interfaces: PascalCase with descriptive names
- CSS Modules: Same name as component with
.module.css
extension
Track these key performance metrics:
- First Contentful Paint (FCP): Under 1.8s
- Largest Contentful Paint (LCP): Under 2.5s
- First Input Delay (FID): Under 100ms
- Cumulative Layout Shift (CLS): Under 0.1
- Time to Interactive (TTI): Under 3.8s
- Total Blocking Time (TBT): Under 200ms
Implement these optimization strategies:
- Code Splitting: Split code by routes and large components
- Lazy Loading: Defer loading of non-critical resources
- Tree Shaking: Eliminate unused code
- Bundle Analysis: Regularly analyze bundle size
- Resource Optimization: Optimize images and assets
- Caching Strategy: Implement effective caching
- Critical CSS: Inline critical CSS
- Web Vitals Monitoring: Monitor core web vitals
import React, { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
import Loading from './components/Loading';
// Lazy load route components
const Home = lazy(() => import('./pages/Home'));
const Products = lazy(() => import('./pages/Products'));
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
const Checkout = lazy(() => import('./pages/Checkout'));
const App = () => {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/checkout" element={<Checkout />} />
</Routes>
</Suspense>
);
};
Implement comprehensive testing across these levels:
- Unit Tests: Test individual components and functions
- Integration Tests: Test interactions between components
- Component Tests: Test component rendering and behavior
- End-to-End Tests: Test complete user flows
- Visual Regression Tests: Test UI appearance
- Performance Tests: Test loading and runtime performance
- Accessibility Tests: Test for accessibility compliance
Maintain these minimum test coverage levels:
- Core Components: 90% statement coverage
- Business Logic: 90% statement coverage
- Utility Functions: 95% statement coverage
- UI Components: Key interactions and states tested
- Critical User Flows: 100% end-to-end test coverage
Unit test example (Jest + React Testing Library):
// Button.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button component', () => {
test('renders with default props', () => {
render(<Button>Click me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeInTheDocument();
expect(button).toHaveClass('btn btn--primary btn--medium');
expect(button).not.toBeDisabled();
});
test('applies variant and size classes', () => {
render(<Button variant="secondary" size="large">Submit</Button>);
const button = screen.getByRole('button', { name: /submit/i });
expect(button).toHaveClass('btn btn--secondary btn--large');
});
test('handles click events', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByRole('button', { name: /click me/i }));
expect(handleClick).toHaveBeenCalledTimes(1);
});
test('disables the button when disabled prop is true', () => {
render(<Button disabled>Disabled</Button>);
const button = screen.getByRole('button', { name: /disabled/i });
expect(button).toBeDisabled();
expect(button).toHaveClass('btn--disabled');
});
});
Standardize on these build tools:
- Vite: For new React/Vue projects
- Next.js: For React applications requiring SSR/SSG
- Angular CLI: For Angular applications
- Webpack: For complex custom configurations
Implement these build optimizations:
- Environment-Specific Builds: Configure for dev, staging, production
- Minification: Minify JavaScript, CSS, and HTML
- Source Maps: Generate source maps for debugging
- Asset Optimization: Optimize and compress assets
- Chunking Strategy: Optimize chunk sizes and loading
- Cache Busting: Implement filename hashing
- Build Analysis: Analyze build output regularly
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { visualizer } from 'rollup-plugin-visualizer';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
react(),
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'robots.txt', 'apple-touch-icon.png'],
manifest: {
name: 'Bayat Application',
short_name: 'BayatApp',
theme_color: '#ffffff',
icons: [
// Icons configuration
]
}
}),
visualizer({
open: false,
gzipSize: true,
brotliSize: true,
})
],
build: {
target: 'es2015',
outDir: 'dist',
assetsDir: 'assets',
cssCodeSplit: true,
reportCompressedSize: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom', 'react-router-dom'],
// Additional chunk definitions
},
},
},
},
resolve: {
alias: {
'@': '/src',
},
},
});
Choose the appropriate CSS methodology based on project needs:
Methodology | Best For | Key Benefits |
---|---|---|
CSS Modules | Component-based apps | Local scope, composition |
Styled Components | React applications | Dynamic styling, theme support |
Tailwind CSS | Rapid development | Utility-first, consistency |
SCSS with BEM | Large applications | Structure, readability |
Implement a consistent design system:
- Design Tokens: Use design tokens for colors, spacing, typography
- Component Library: Build on a shared component library
- Theme Support: Implement light/dark and brand theming
- Responsive Design: Use a consistent approach to responsiveness
- Documentation: Maintain living documentation of the design system
// tailwind.config.js
const colors = require('tailwindcss/colors');
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
colors: {
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
// ... other shades
900: '#0c4a6e',
},
secondary: {
// Secondary color palette
},
// Other brand colors
},
fontFamily: {
sans: ['Inter var', 'sans-serif'],
display: ['Lexend', 'sans-serif'],
},
spacing: {
// Custom spacing values
},
borderRadius: {
// Custom border radius values
},
// Other theme extensions
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
};
Implement consistent routing patterns:
- Declarative Routes: Define routes declaratively
- Nested Routes: Use nested routes for related content
- Route Parameters: Use consistent parameter patterns
- Query Parameters: Use for filtering and sorting
- Route Guards: Implement authentication and authorization checks
- Code Splitting: Split code by route
- Route Transitions: Add smooth transitions between routes
Follow these URL structure guidelines:
- Use kebab-case for URL paths
- Use descriptive, resource-focused paths
- Follow RESTful patterns where appropriate
- Keep URLs reasonably short but descriptive
- Include language in URL for multilingual sites
- Use query parameters for filtering, not URL paths
Examples:
/products # Product listing
/products/category/electronics # Category filtering
/products/123-smartphone-x # Product detail with slug
/users/profile # User profile
/checkout/shipping # Multi-step process
// routes.tsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { lazy, Suspense } from 'react';
import MainLayout from './layouts/MainLayout';
import LoadingPage from './components/LoadingPage';
import ErrorBoundary from './components/ErrorBoundary';
import ProtectedRoute from './components/ProtectedRoute';
// Lazy-loaded route components
const Home = lazy(() => import('./pages/Home'));
const ProductList = lazy(() => import('./pages/ProductList'));
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
const UserProfile = lazy(() => import('./pages/UserProfile'));
const Checkout = lazy(() => import('./pages/Checkout'));
const NotFound = lazy(() => import('./pages/NotFound'));
const router = createBrowserRouter([
{
path: '/',
element: <MainLayout />,
errorElement: <ErrorBoundary />,
children: [
{ index: true, element: <Home /> },
{
path: 'products',
children: [
{ index: true, element: <ProductList /> },
{ path: ':productId', element: <ProductDetail /> }
]
},
{
path: 'profile',
element: (
<ProtectedRoute>
<UserProfile />
</ProtectedRoute>
)
},
{
path: 'checkout',
element: (
<ProtectedRoute>
<Checkout />
</ProtectedRoute>
)
},
{ path: '*', element: <NotFound /> }
]
}
]);
const Routes = () => (
<Suspense fallback={<LoadingPage />}>
<RouterProvider router={router} />
</Suspense>
);
export default Routes;
Implement a structured API client:
- Base Client: Create a configurable base API client
- Service Modules: Organize by domain/resource
- Request/Response Types: Define strong types
- Error Handling: Implement consistent error handling
- Caching Strategy: Define caching behavior
- Authentication: Handle auth tokens and refreshing
- Interceptors: Implement request/response interceptors
- Retry Logic: Add intelligent retry for failed requests
// api/client.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { getAuthToken, refreshToken } from '../auth/authService';
export class ApiClient {
private client: AxiosInstance;
private baseURL: string;
constructor(baseURL: string) {
this.baseURL = baseURL;
this.client = axios.create({
baseURL,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
this.setupInterceptors();
}
private setupInterceptors(): void {
// Request interceptor
this.client.interceptors.request.use(
(config) => {
const token = getAuthToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor
this.client.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
const originalRequest = error.config;
// Handle token refresh
if (error.response?.status === 401 && !originalRequest.headers['X-Retry']) {
originalRequest.headers['X-Retry'] = 'true';
try {
await refreshToken();
return this.client(originalRequest);
} catch (refreshError) {
// Handle auth refresh failure
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
}
// Generic request method
public async request<T>(config: AxiosRequestConfig): Promise<T> {
try {
const response: AxiosResponse<T> = await this.client(config);
return response.data;
} catch (error) {
this.handleError(error as AxiosError);
throw error;
}
}
// Convenience methods
public async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.request<T>({ ...config, method: 'GET', url });
}
public async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.request<T>({ ...config, method: 'POST', url, data });
}
public async put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.request<T>({ ...config, method: 'PUT', url, data });
}
public async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.request<T>({ ...config, method: 'DELETE', url });
}
private handleError(error: AxiosError): void {
// Centralized error handling
if (error.response) {
// Server responded with non-2xx status
console.error('API Error:', error.response.status, error.response.data);
} else if (error.request) {
// Request was made but no response received
console.error('Network Error:', error.message);
} else {
// Error in setting up the request
console.error('Request Error:', error.message);
}
}
}
// Create API instances
export const mainApi = new ApiClient(import.meta.env.VITE_API_URL);
Use these data fetching patterns:
- Custom Hooks: Encapsulate API calls in custom hooks
- SWR/React Query: Use data fetching libraries for caching
- Server State vs. UI State: Separate API data from UI state
- Optimistic Updates: Update UI before API responses
- Error Boundaries: Handle API errors gracefully
- Loading States: Show appropriate loading indicators
Implement these security measures:
- XSS Prevention: Sanitize data and use frameworks' protections
- CSRF Protection: Implement CSRF tokens
- Content Security Policy: Configure strict CSP
- Secure Authentication: Use secure auth practices
- HTTPS Only: Enforce HTTPS for all communication
- Sensitive Data: Don't store sensitive data in localStorage/sessionStorage
- Input Validation: Validate all user inputs
- Dependency Security: Regularly audit and update dependencies
- Feature Policy: Use feature policy headers
- Subresource Integrity: Verify third-party resources
- Token Storage: Store tokens securely (HttpOnly cookies preferred)
- Token Rotation: Implement token rotation
- Session Timeout: Implement automatic session timeouts
- Logout Functionality: Properly clear authentication state
- Auth State Management: Centralize auth state management
- Multi-factor Authentication: Support 2FA where appropriate
Ensure compliance with WCAG 2.1 AA standards:
- Perceivable: Information must be presentable to users in ways they can perceive
- Operable: User interface components must be operable
- Understandable: Information and operation must be understandable
- Robust: Content must be robust enough to be interpreted by a wide variety of user agents
Implement these accessibility features:
- Semantic HTML: Use proper HTML elements
- ARIA Attributes: Use ARIA when HTML semantics are insufficient
- Keyboard Navigation: Ensure full keyboard operability
- Focus Management: Implement proper focus management
- Color Contrast: Maintain sufficient color contrast
- Screen Reader Support: Test with screen readers
- Alternative Text: Provide alt text for images
- Form Labels: Associate labels with form elements
- Error Identification: Clearly identify form errors
Test accessibility using:
- Automated Tools: Lighthouse, axe, Wave
- Manual Testing: Keyboard navigation, screen readers
- Contrast Checkers: WCAG contrast checkers
- Accessibility Review: Regular expert review
Implement comprehensive internationalization:
- Translation System: Use a robust translation system
- Message Format: Support pluralization and formatting
- RTL Support: Support right-to-left languages
- Date/Time Format: Format dates and times by locale
- Number Format: Format numbers by locale
- Content Expansion: Allow for text expansion in translations
- Dynamic Content: Support translating dynamic content
// i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
supportedLngs: ['en', 'fr', 'es', 'de', 'ar'],
ns: ['common', 'auth', 'products', 'checkout'],
defaultNS: 'common',
interpolation: {
escapeValue: false, // React already escapes values
},
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
detection: {
order: ['querystring', 'cookie', 'localStorage', 'navigator'],
lookupQuerystring: 'lang',
lookupCookie: 'i18next',
lookupLocalStorage: 'i18nextLng',
caches: ['localStorage', 'cookie'],
},
react: {
useSuspense: true,
},
});
export default i18n;
Implement a comprehensive error handling strategy:
- Global Error Boundary: Catch uncaught errors
- API Error Handling: Standardize API error processing
- User Feedback: Provide clear error messages to users
- Error Logging: Log errors for debugging
- Retry Mechanism: Implement retries for transient failures
- Graceful Degradation: Maintain functionality when parts fail
- Error Recovery: Provide pathways to recover from errors
// ErrorBoundary.tsx
import React, { Component, ErrorInfo, ReactNode } from 'react';
import { logError } from '../services/errorLoggingService';
import ErrorFallback from './ErrorFallback';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
hasError: false,
error: null,
};
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
// Log error to service
logError(error, errorInfo);
}
render(): ReactNode {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback;
}
return <ErrorFallback error={this.state.error} />;
}
return this.props.children;
}
}
export default ErrorBoundary;
Implement comprehensive frontend monitoring:
- Error Tracking: Capture and report frontend errors
- Performance Monitoring: Track page load and interaction metrics
- User Behavior: Analyze user flows and interactions
- Session Recording: Record user sessions for debugging
- Feature Usage: Track feature adoption and usage
- Console Logs: Forward console errors to monitoring system
Standardize analytics implementation:
- Event Tracking: Define a consistent event taxonomy
- User Identification: Identify users across sessions
- Conversion Tracking: Track key conversion steps
- Custom Dimensions: Define business-specific dimensions
- Data Layer: Implement a structured data layer
- Privacy Compliance: Ensure GDPR/CCPA compliance
// monitoring.ts
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import { ErrorBoundary } from '@sentry/react';
export const initMonitoring = () => {
if (import.meta.env.PROD) {
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
integrations: [new BrowserTracing()],
tracesSampleRate: 0.2,
environment: import.meta.env.MODE,
release: `${import.meta.env.VITE_APP_NAME}@${import.meta.env.VITE_APP_VERSION}`,
beforeSend(event) {
// Clean sensitive data if needed
return event;
},
});
}
};
export { ErrorBoundary as SentryErrorBoundary };
// Custom analytics hooks
export const useAnalytics = () => {
const trackEvent = (category: string, action: string, label?: string, value?: number) => {
// Implementation with your analytics provider
if (window.gtag) {
window.gtag('event', action, {
event_category: category,
event_label: label,
value: value,
});
}
};
return { trackEvent };
};
Maintain these documentation types:
- Architecture Overview: High-level architecture documentation
- Component Documentation: Component API and usage
- Style Guide: Visual style guide and design system
- State Management: State structure and management patterns
- API Integration: API endpoints and data models
- Coding Standards: Frontend coding standards and patterns
Use these tools for documentation:
- Storybook: Component documentation and testing
- JSDoc/TSDoc: Code-level documentation
- Markdown: General documentation
- Diagrams: Architecture and flow diagrams
- README Files: Project and directory documentation
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import Button from './Button';
const meta: Meta<typeof Button> = {
title: 'UI/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
variant: {
control: { type: 'select' },
options: ['primary', 'secondary', 'tertiary']
},
size: {
control: { type: 'select' },
options: ['small', 'medium', 'large']
},
disabled: { control: 'boolean' },
onClick: { action: 'clicked' },
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
variant: 'primary',
children: 'Primary Button',
size: 'medium',
},
};
export const Secondary: Story = {
args: {
variant: 'secondary',
children: 'Secondary Button',
size: 'medium',
},
};
export const Small: Story = {
args: {
size: 'small',
children: 'Small Button',
},
};
export const Large: Story = {
args: {
size: 'large',
children: 'Large Button',
},
};
export const Disabled: Story = {
args: {
disabled: true,
children: 'Disabled Button',
},
};