Code Component Templates¶
Overview¶
Standardized code components that implement our brand system consistently across all digital touchpoints.
Component Structure¶
Base Component Template¶
/**
* @component ComponentName
* @description Brief description of component purpose
* @version 1.0.0
* @author Your Team
* @example
* <ComponentName
* variant="primary"
* size="medium"
* onClick={handleClick}
* />
*/
import React from 'react';
import { styled } from '@emotion/styled';
import { ComponentProps } from './types';
import { useTheme } from '../../hooks/useTheme';
import { BRAND_TOKENS } from '../../constants/brand';
const StyledComponent = styled.div<ComponentProps>`
/* Base styles using brand tokens */
font-family: ${BRAND_TOKENS.typography.fontFamily};
transition: all ${BRAND_TOKENS.animation.duration.fast} ${BRAND_TOKENS.animation.easing.default};
/* Variant styles */
${props => props.variant === 'primary' && `
background-color: ${BRAND_TOKENS.colors.primary};
color: ${BRAND_TOKENS.colors.white};
`}
`;
export const ComponentName: React.FC<ComponentProps> = ({
variant = 'primary',
size = 'medium',
children,
className,
...props
}) => {
const theme = useTheme();
return (
<StyledComponent
variant={variant}
size={size}
className={className}
{...props}
>
{children}
</StyledComponent>
);
};
// Default props
ComponentName.defaultProps = {
variant: 'primary',
size: 'medium',
};
Common Components¶
1. Brand Button¶
// BrandButton.tsx
import React from 'react';
import { styled } from '@emotion/styled';
import { BRAND_TOKENS } from '../../constants/brand';
interface BrandButtonProps {
variant?: 'primary' | 'secondary' | 'tertiary';
size?: 'small' | 'medium' | 'large';
fullWidth?: boolean;
disabled?: boolean;
loading?: boolean;
icon?: React.ReactNode;
children: React.ReactNode;
onClick?: () => void;
}
const StyledButton = styled.button<BrandButtonProps>`
/* Base styles */
display: inline-flex;
align-items: center;
justify-content: center;
gap: ${BRAND_TOKENS.spacing.xs};
font-family: ${BRAND_TOKENS.typography.fontFamily};
font-weight: ${BRAND_TOKENS.typography.fontWeight.medium};
border: none;
border-radius: ${BRAND_TOKENS.borderRadius.md};
cursor: pointer;
transition: all ${BRAND_TOKENS.animation.duration.fast} ${BRAND_TOKENS.animation.easing.default};
/* Size variants */
${props => {
switch (props.size) {
case 'small':
return `
padding: ${BRAND_TOKENS.spacing.xs} ${BRAND_TOKENS.spacing.sm};
font-size: ${BRAND_TOKENS.typography.fontSize.sm};
`;
case 'large':
return `
padding: ${BRAND_TOKENS.spacing.md} ${BRAND_TOKENS.spacing.xl};
font-size: ${BRAND_TOKENS.typography.fontSize.lg};
`;
default:
return `
padding: ${BRAND_TOKENS.spacing.sm} ${BRAND_TOKENS.spacing.lg};
font-size: ${BRAND_TOKENS.typography.fontSize.md};
`;
}
}}
/* Variant styles */
${props => {
switch (props.variant) {
case 'secondary':
return `
background-color: transparent;
color: ${BRAND_TOKENS.colors.primary};
border: 2px solid ${BRAND_TOKENS.colors.primary};
&:hover:not(:disabled) {
background-color: ${BRAND_TOKENS.colors.primary};
color: ${BRAND_TOKENS.colors.white};
}
`;
case 'tertiary':
return `
background-color: transparent;
color: ${BRAND_TOKENS.colors.primary};
&:hover:not(:disabled) {
background-color: ${BRAND_TOKENS.colors.gray.light};
}
`;
default:
return `
background-color: ${BRAND_TOKENS.colors.primary};
color: ${BRAND_TOKENS.colors.white};
&:hover:not(:disabled) {
background-color: ${BRAND_TOKENS.colors.primary.dark};
}
`;
}
}}
/* States */
${props => props.fullWidth && `width: 100%;`}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
&:focus-visible {
outline: 2px solid ${BRAND_TOKENS.colors.primary};
outline-offset: 2px;
}
`;
export const BrandButton: React.FC<BrandButtonProps> = ({
variant = 'primary',
size = 'medium',
fullWidth = false,
disabled = false,
loading = false,
icon,
children,
onClick,
}) => {
return (
<StyledButton
variant={variant}
size={size}
fullWidth={fullWidth}
disabled={disabled || loading}
onClick={onClick}
>
{loading ? <LoadingSpinner /> : icon}
{children}
</StyledButton>
);
};
2. Brand Card¶
// BrandCard.tsx
const StyledCard = styled.div<{ elevation?: number }>`
background-color: ${BRAND_TOKENS.colors.white};
border-radius: ${BRAND_TOKENS.borderRadius.lg};
box-shadow: ${props => BRAND_TOKENS.shadows[props.elevation || 1]};
overflow: hidden;
transition: box-shadow ${BRAND_TOKENS.animation.duration.normal} ${BRAND_TOKENS.animation.easing.default};
&:hover {
box-shadow: ${props => BRAND_TOKENS.shadows[props.elevation ? props.elevation + 1 : 2]};
}
`;
const CardHeader = styled.div`
padding: ${BRAND_TOKENS.spacing.lg};
border-bottom: 1px solid ${BRAND_TOKENS.colors.gray.light};
`;
const CardBody = styled.div`
padding: ${BRAND_TOKENS.spacing.lg};
`;
const CardFooter = styled.div`
padding: ${BRAND_TOKENS.spacing.lg};
border-top: 1px solid ${BRAND_TOKENS.colors.gray.light};
background-color: ${BRAND_TOKENS.colors.gray.lightest};
`;
3. Brand Input¶
// BrandInput.tsx
const InputWrapper = styled.div`
position: relative;
width: 100%;
`;
const StyledInput = styled.input<{ hasError?: boolean }>`
width: 100%;
padding: ${BRAND_TOKENS.spacing.sm} ${BRAND_TOKENS.spacing.md};
font-family: ${BRAND_TOKENS.typography.fontFamily};
font-size: ${BRAND_TOKENS.typography.fontSize.md};
border: 2px solid ${props =>
props.hasError ? BRAND_TOKENS.colors.error : BRAND_TOKENS.colors.gray.medium
};
border-radius: ${BRAND_TOKENS.borderRadius.md};
background-color: ${BRAND_TOKENS.colors.white};
transition: all ${BRAND_TOKENS.animation.duration.fast} ${BRAND_TOKENS.animation.easing.default};
&:focus {
outline: none;
border-color: ${props =>
props.hasError ? BRAND_TOKENS.colors.error : BRAND_TOKENS.colors.primary
};
box-shadow: 0 0 0 3px ${props =>
props.hasError ? BRAND_TOKENS.colors.error + '20' : BRAND_TOKENS.colors.primary + '20'
};
}
&::placeholder {
color: ${BRAND_TOKENS.colors.gray.medium};
}
&:disabled {
background-color: ${BRAND_TOKENS.colors.gray.lightest};
cursor: not-allowed;
}
`;
4. Brand Badge¶
// BrandBadge.tsx
const StyledBadge = styled.span<{ variant: string }>`
display: inline-flex;
align-items: center;
padding: ${BRAND_TOKENS.spacing.xxs} ${BRAND_TOKENS.spacing.xs};
font-size: ${BRAND_TOKENS.typography.fontSize.xs};
font-weight: ${BRAND_TOKENS.typography.fontWeight.medium};
border-radius: ${BRAND_TOKENS.borderRadius.full};
${props => {
const colors = {
primary: { bg: BRAND_TOKENS.colors.primary + '20', text: BRAND_TOKENS.colors.primary },
success: { bg: BRAND_TOKENS.colors.success + '20', text: BRAND_TOKENS.colors.success },
warning: { bg: BRAND_TOKENS.colors.warning + '20', text: BRAND_TOKENS.colors.warning },
error: { bg: BRAND_TOKENS.colors.error + '20', text: BRAND_TOKENS.colors.error },
neutral: { bg: BRAND_TOKENS.colors.gray.light, text: BRAND_TOKENS.colors.gray.dark },
};
const color = colors[props.variant] || colors.neutral;
return `
background-color: ${color.bg};
color: ${color.text};
`;
}}
`;
Brand Tokens¶
// constants/brand.ts
export const BRAND_TOKENS = {
colors: {
primary: {
DEFAULT: '#2563EB',
light: '#3B82F6',
dark: '#1E40AF',
},
secondary: {
DEFAULT: '#10B981',
light: '#34D399',
dark: '#059669',
},
gray: {
lightest: '#F9FAFB',
light: '#E5E7EB',
medium: '#9CA3AF',
dark: '#4B5563',
darkest: '#1F2937',
},
white: '#FFFFFF',
black: '#000000',
error: '#EF4444',
warning: '#F59E0B',
success: '#10B981',
info: '#3B82F6',
},
typography: {
fontFamily: '\'Inter\', -apple-system, BlinkMacSystemFont, sans-serif',
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
md: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
'3xl': '1.875rem',
'4xl': '2.25rem',
},
fontWeight: {
light: 300,
regular: 400,
medium: 500,
semibold: 600,
bold: 700,
},
lineHeight: {
tight: 1.25,
normal: 1.5,
relaxed: 1.75,
},
},
spacing: {
xxs: '0.25rem',
xs: '0.5rem',
sm: '0.75rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
'2xl': '3rem',
'3xl': '4rem',
},
borderRadius: {
sm: '0.25rem',
md: '0.375rem',
lg: '0.5rem',
xl: '0.75rem',
full: '9999px',
},
shadows: {
0: 'none',
1: '0 1px 3px rgba(0, 0, 0, 0.1)',
2: '0 4px 6px rgba(0, 0, 0, 0.1)',
3: '0 10px 15px rgba(0, 0, 0, 0.1)',
4: '0 20px 25px rgba(0, 0, 0, 0.1)',
},
animation: {
duration: {
fast: '150ms',
normal: '300ms',
slow: '500ms',
},
easing: {
default: 'cubic-bezier(0.4, 0, 0.2, 1)',
in: 'cubic-bezier(0.4, 0, 1, 1)',
out: 'cubic-bezier(0, 0, 0.2, 1)',
inOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
},
},
};
Utility Components¶
Loading Spinner¶
const SpinnerKeyframes = keyframes`
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
`;
const LoadingSpinner = styled.div<{ size?: string }>`
width: ${props => props.size || '20px'};
height: ${props => props.size || '20px'};
border: 2px solid ${BRAND_TOKENS.colors.gray.light};
border-top-color: ${BRAND_TOKENS.colors.primary};
border-radius: 50%;
animation: ${SpinnerKeyframes} 0.8s linear infinite;
`;
Error Message¶
const ErrorMessage = styled.div`
display: flex;
align-items: center;
gap: ${BRAND_TOKENS.spacing.xs};
padding: ${BRAND_TOKENS.spacing.sm};
background-color: ${BRAND_TOKENS.colors.error}20;
color: ${BRAND_TOKENS.colors.error};
border-radius: ${BRAND_TOKENS.borderRadius.md};
font-size: ${BRAND_TOKENS.typography.fontSize.sm};
svg {
flex-shrink: 0;
}
`;
Toast Notification¶
const Toast = styled.div<{ type: 'success' | 'error' | 'warning' | 'info' }>`
position: fixed;
bottom: ${BRAND_TOKENS.spacing.lg};
right: ${BRAND_TOKENS.spacing.lg};
padding: ${BRAND_TOKENS.spacing.md};
background-color: ${BRAND_TOKENS.colors.white};
border-radius: ${BRAND_TOKENS.borderRadius.lg};
box-shadow: ${BRAND_TOKENS.shadows[3]};
display: flex;
align-items: center;
gap: ${BRAND_TOKENS.spacing.sm};
min-width: 300px;
max-width: 500px;
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: ${props => BRAND_TOKENS.colors[props.type]};
border-radius: ${BRAND_TOKENS.borderRadius.lg} 0 0 ${BRAND_TOKENS.borderRadius.lg};
}
`;
Accessibility Patterns¶
Focus Management¶
// useFocusTrap.ts
export const useFocusTrap = (ref: RefObject<HTMLElement>) => {
useEffect(() => {
const element = ref.current;
if (!element) return;
const focusableElements = element.querySelectorAll(
'a[href], button, textarea, input[type="text"], input[type="radio"], input[type="checkbox"], select'
);
const firstFocusable = focusableElements[0] as HTMLElement;
const lastFocusable = focusableElements[focusableElements.length - 1] as HTMLElement;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === firstFocusable) {
lastFocusable.focus();
e.preventDefault();
} else if (!e.shiftKey && document.activeElement === lastFocusable) {
firstFocusable.focus();
e.preventDefault();
}
};
element.addEventListener('keydown', handleKeyDown);
firstFocusable?.focus();
return () => element.removeEventListener('keydown', handleKeyDown);
}, [ref]);
};
ARIA Patterns¶
// Accessible Modal
const Modal = ({ isOpen, onClose, title, children }) => {
const modalRef = useRef(null);
useFocusTrap(modalRef);
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-hidden={!isOpen}
ref={modalRef}
>
<h2 id="modal-title">{title}</h2>
<button
aria-label="Close modal"
onClick={onClose}
>
<CloseIcon />
</button>
{children}
</div>
);
};
Testing Templates¶
Component Test¶
// BrandButton.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { BrandButton } from './BrandButton';
describe('BrandButton', () => {
it('renders with correct text', () => {
render(<BrandButton>Click me</BrandButton>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('handles click events', () => {
const handleClick = jest.fn();
render(<BrandButton onClick={handleClick}>Click me</BrandButton>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('applies correct variant styles', () => {
const { rerender } = render(<BrandButton variant="primary">Button</BrandButton>);
expect(screen.getByText('Button')).toHaveStyle({
backgroundColor: BRAND_TOKENS.colors.primary
});
rerender(<BrandButton variant="secondary">Button</BrandButton>);
expect(screen.getByText('Button')).toHaveStyle({
backgroundColor: 'transparent'
});
});
});
Component Library v2.0
Last Updated: January 2024
Design System: design.company.com