Skip to main content

Building a Design System: UI Components for danieljoffe.com

An image of a jellyfish in the dark
Sep 4, 20252 min readReact, TypeScript, Storybook, Accessibility, Component Library, Tailwind CSS

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.

Button — VariantsOpen in Storybook

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().

Input — DefaultOpen in Storybook

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.

Grid — Two ColumnsOpen in Storybook

Utility Components

Loading

A loading spinner component for async states. Provides visual feedback during data fetching or processing.

Loading SpinnerOpen in Storybook

A portal-based modal component controlled via global state. Renders content above the main application with proper focus management.

Modal — DefaultOpen in Storybook

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 2
  • Feedback components (alerts, tooltips, badges) Done! See Part 2
  • Layout primitives (cards, tabs, stacks) Done! See Part 2
  • Dark mode support Done!
  • 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.