Dwarves
Memo
Type ESC to close search bar

Design system integration in react

Design system integration in React involves creating a set of reusable, consistent, and easily maintainable components that reflect your app’s design guidelines. Integrating a design system helps ensure visual and functional consistency across your application while allowing for scalability as new components and features are added. Design systems often include UI components, design tokens, typography, colors, icons, spacing guidelines, and accessibility standards.

Key steps and concepts for design system integration in react

  1. Define the core design tokens (colors, typography, spacing, etc.)
  2. Build atomic components (buttons, inputs, typography)
  3. Create composable, flexible components
  4. Establish and use a component library framework (Storybook)
  5. Implement accessibility standards
  6. Use context and theming for adaptable designs

1. Define core design tokens

Design tokens are the fundamental building blocks of your design system. They represent design decisions like colors, typography, and spacing in a consistent, reusable format. By defining tokens, you create a single source of truth that makes updates and adjustments easy across the app.

Example: Design tokens in a JSON format

{
  "colors": {
    "primary": "#007bff",
    "secondary": "#6c757d",
    "background": "#f8f9fa",
    "text": "#212529"
  },
  "typography": {
    "fontFamily": "Arial, sans-serif",
    "fontSize": {
      "small": "12px",
      "medium": "16px",
      "large": "24px"
    }
  },
  "spacing": {
    "small": "4px",
    "medium": "8px",
    "large": "16px"
  }
}

You can then access and apply these tokens in your components, ensuring they follow consistent visual guidelines.

In a React component:

import tokens from './tokens.json'

const Button = ({ children }) => (
  <button
    style={{
      backgroundColor: tokens.colors.primary,
      color: tokens.colors.background,
      padding: tokens.spacing.medium,
      fontSize: tokens.typography.fontSize.medium,
      fontFamily: tokens.typography.fontFamily,
    }}
  >
    {children}
  </button>
)

2. Build atomic components

Atomic design breaks down UI elements into atoms, molecules, organisms, templates, and pages. This approach helps in building reusable, low-level components that can be combined and customized to create more complex components and layouts.

Atomic design hierarchy:

Example: Button component (atom)

const Button = ({ label, onClick, variant = 'primary' }) => {
  const styles = {
    primary: {
      backgroundColor: tokens.colors.primary,
      color: tokens.colors.background,
    },
    secondary: {
      backgroundColor: tokens.colors.secondary,
      color: tokens.colors.background,
    },
  }

  return (
    <button style={styles[variant]} onClick={onClick}>
      {label}
    </button>
  )
}

Example: Form component (molecule)

const FormField = ({ label, type = 'text', value, onChange }) => (
  <div>
    <label>{label}</label>
    <input type={type} value={value} onChange={onChange} />
  </div>
)

3. Create composable, flexible components

Design systems benefit from flexible components that can adapt to different use cases without being overly rigid. Use props and styled-system libraries (e.g., styled-components or emotion) to make your components customizable.

import styled from 'styled-components'
import tokens from './tokens.json'

const Button = styled.button`
  padding: ${tokens.spacing.medium};
  font-size: ${tokens.typography.fontSize.medium};
  color: ${({ variant }) => tokens.colors[variant]};
  background-color: ${({ bg }) => bg || tokens.colors.background};
`

This approach allows the Button component to be reusable, enabling different colors and backgrounds with minimal code.

4. Establish and use a component library framework (Storybook)

Storybook is a popular tool for creating isolated component libraries, documenting components, and allowing team members to interact with components outside of the application environment.

Set up Storybook

Install Storybook in your React project.

npx sb init

Write stories for components

Each component should have its own story, describing its appearance with various props and states.

// Example: Button.stories.js
import React from 'react'
import { Button } from './Button'

export default {
  title: 'Design system/Button',
  component: Button,
}

export const Primary = () => <Button variant="primary" label="Primary Button" />
export const Secondary = () => <Button variant="secondary" label="Secondary Button" />

Benefits:

5. Implement accessibility standards

Design systems must prioritize accessibility to create inclusive applications. Follow WCAG guidelines and use accessible components, ensuring that all users can interact with your app.

// Example: Accessible button with ARIA
const Button = ({ label, onClick, ariaLabel, variant = 'primary' }) => (
  <button onClick={onClick} style={{ backgroundColor: tokens.colors[variant] }} aria-label={ariaLabel}>
    {label}
  </button>
)
// Example: Focus management in a modal
import { useEffect, useRef } from 'react'

const Modal = ({ isOpen, onClose, children }) => {
  const closeButtonRef = useRef()

  useEffect(() => {
    if (isOpen) {
      closeButtonRef.current.focus()
    }
  }, [isOpen])

  return isOpen ? (
    <div role="dialog" aria-modal="true" tabIndex={-1}>
      <button ref={closeButtonRef} onClick={onClose}>
        Close
      </button>
      {children}
    </div>
  ) : null
}

Tools:

6. Use context and theming for adaptable designs

To support themes (e.g., dark and light modes), use context providers to pass theme values down to components. This enables consistent theming and allows the user to switch themes easily.

Example: Theming with context

  1. Create theme context:
import React, { createContext, useContext, useState } from 'react'
import tokens from './tokens.json'

const ThemeContext = createContext()

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light')

  const toggleTheme = () => setTheme((prev) => (prev === 'light' ? 'dark' : 'light'))

  const themeStyles = theme === 'light' ? tokens.light : tokens.dark

  return <ThemeContext.Provider value={{ theme, themeStyles, toggleTheme }}>{children}</ThemeContext.Provider>
}

export const useTheme = () => useContext(ThemeContext)
  1. Consume theme context in components:
const ThemedButton = ({ label }) => {
  const { themeStyles } = useTheme()
  return (
    <button
      style={{
        backgroundColor: themeStyles.colors.primary,
        color: themeStyles.colors.background,
      }}
    >
      {label}
    </button>
  )
}
  1. Switch themes in app:
function App() {
  const { toggleTheme } = useTheme()
  return (
    <div>
      <ThemedButton label="Click me" />
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  )
}

Summary

TechniquePurpose
Design tokensCentralize colors, typography, and spacing for consistent styling
Atomic componentsBuild scalable, reusable components from basic building blocks
Composable, flexible componentsEnsure flexibility for various use cases using props and dynamic styling
StorybookDocument, test, and showcase all components, ensuring design and functionality alignment
Accessibility standardsImprove usability for all users, following WCAG and ARIA best practices
Context and themingSupport adaptable designs, allowing for light and dark themes or other custom themes

By integrating these techniques, you can build a design system that is robust, adaptable, and consistent, making it easier to scale and maintain the UI over time.