Building a Design System: UI Components for danieljoffe.com
When I built this portfolio, I wanted more than a pile of half-baked components thrown together; I wanted a design system that would actually scale. Here's what I built and why it matters, starting with the foundational components.
Business Impact
- Established foundational design system components used across the portfolio site
- Reduced UI development time for new pages by providing pre-built, tested primitives
- Ensured consistent visual language and accessibility standards across all components
Form Components
Button
A flexible button with three variants (primary, secondary, icon) and three sizes. It uses React.forwardRef for proper ref forwarding, and accessibility is built in with ARIA attributes and keyboard navigation.
Input
A polymorphic form component that renders as either an <input> or a <textarea>. It handles validation states, error messages, and success feedback, with proper ARIA relationships wired up through React.useId().
Layout Components
Container
A simple but essential wrapper that centers content and provides consistent spacing. Used on almost every page.
Grid
A responsive grid layout for displaying content cards. Automatically adjusts columns based on viewport size.
Utility Components
Loading
A loading spinner component for async states. Provides visual feedback during data fetching or processing.
Modal
A portal-based modal component controlled via global state. Renders content above the main application with proper focus management.
Design Principles
Accessibility First Every component starts with accessibility: ARIA attributes, keyboard navigation, and screen reader compatibility. None of that is an afterthought; it's the foundation.
Type Safety Full TypeScript support catches errors at compile time, and proper prop interfaces plus strict type checking have saved me countless debugging hours.
Consistency via Tailwind Tailwind CSS gives me a consistent design language, a neutral palette with blue accents, so I'm not making one-off spacing or typography choices on every page.
Key Takeaways
- Always use labels; they're essential for accessibility
- Meaningful error messages help users understand what actually went wrong
- Test keyboard navigation, because if you can't use it with a keyboard, it's broken
- Semantic HTML matters, so reach for the right element for the job
What's Next
More input types (select, checkbox, switch)Done! See Part 2Feedback components (alerts, tooltips, badges)Done! See Part 2Layout primitives (cards, tabs, stacks)Done! See Part 2Dark mode supportDone!- Animation integration
- Expanded test coverage
Building these taught me that a primitive you can trust to be accessible and typed is one less thing you have to second-guess on every new page.
