Claude
Skills
Sign in
Back

atomic-design-atoms

Included with Lifetime
$97 forever

Use when creating atomic-level UI components like buttons, inputs, labels, and icons. The smallest building blocks of a design system.

Design

What this skill does


# Atomic Design: Atoms

Master the creation of atomic components - the fundamental, indivisible building blocks of your design system. Atoms are the smallest functional units that cannot be broken down further without losing meaning.

## What Are Atoms?

Atoms are the basic UI elements that serve as the foundation for everything else in your design system. They are:

- **Indivisible**: Cannot be broken down into smaller functional units
- **Reusable**: Used throughout the application in various contexts
- **Stateless**: Typically controlled by parent components
- **Styled**: Implement design tokens for consistent appearance
- **Accessible**: Built with a11y in mind from the start

## Common Atom Types

### Interactive Atoms

- Buttons
- Links
- Inputs (text, checkbox, radio, select)
- Toggles/Switches
- Sliders

### Display Atoms

- Typography (headings, paragraphs, labels)
- Icons
- Images/Avatars
- Badges/Tags
- Dividers
- Spinners/Loaders

### Form Atoms

- Input fields
- Textareas
- Checkboxes
- Radio buttons
- Select dropdowns
- Labels

## Button Atom Example

### Basic Implementation

```typescript
// atoms/Button/Button.tsx
import React from 'react';
import type { ButtonHTMLAttributes } from 'react';
import styles from './Button.module.css';

export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'danger';
export type ButtonSize = 'sm' | 'md' | 'lg';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  /** Visual style variant */
  variant?: ButtonVariant;
  /** Size of the button */
  size?: ButtonSize;
  /** Full width button */
  fullWidth?: boolean;
  /** Loading state */
  isLoading?: boolean;
  /** Left icon */
  leftIcon?: React.ReactNode;
  /** Right icon */
  rightIcon?: React.ReactNode;
}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      variant = 'primary',
      size = 'md',
      fullWidth = false,
      isLoading = false,
      leftIcon,
      rightIcon,
      disabled,
      children,
      className,
      ...props
    },
    ref
  ) => {
    const classNames = [
      styles.button,
      styles[variant],
      styles[size],
      fullWidth && styles.fullWidth,
      isLoading && styles.loading,
      className,
    ]
      .filter(Boolean)
      .join(' ');

    return (
      <button
        ref={ref}
        className={classNames}
        disabled={disabled || isLoading}
        aria-busy={isLoading}
        {...props}
      >
        {isLoading ? (
          <span className={styles.spinner} aria-hidden="true" />
        ) : (
          <>
            {leftIcon && <span className={styles.leftIcon}>{leftIcon}</span>}
            <span className={styles.content}>{children}</span>
            {rightIcon && <span className={styles.rightIcon}>{rightIcon}</span>}
          </>
        )}
      </button>
    );
  }
);

Button.displayName = 'Button';
```

### Button Styles

```css
/* atoms/Button/Button.module.css */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  border-radius: 6px;
  font-weight: 500;
  cursor: pointer;
  transition: all 150ms ease-in-out;
  text-decoration: none;
}

.button:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Variants */
.primary {
  background-color: var(--color-primary-500);
  color: var(--color-white);
}

.primary:hover:not(:disabled) {
  background-color: var(--color-primary-600);
}

.secondary {
  background-color: transparent;
  color: var(--color-primary-500);
  border: 1px solid var(--color-primary-500);
}

.secondary:hover:not(:disabled) {
  background-color: var(--color-primary-50);
}

.tertiary {
  background-color: transparent;
  color: var(--color-primary-500);
}

.tertiary:hover:not(:disabled) {
  background-color: var(--color-primary-50);
}

.danger {
  background-color: var(--color-danger-500);
  color: var(--color-white);
}

.danger:hover:not(:disabled) {
  background-color: var(--color-danger-600);
}

/* Sizes */
.sm {
  padding: 6px 12px;
  font-size: 14px;
  min-height: 32px;
}

.md {
  padding: 8px 16px;
  font-size: 16px;
  min-height: 40px;
}

.lg {
  padding: 12px 24px;
  font-size: 18px;
  min-height: 48px;
}

/* Modifiers */
.fullWidth {
  width: 100%;
}

.loading {
  position: relative;
  color: transparent;
}

.spinner {
  position: absolute;
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.75s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Icon spacing */
.leftIcon,
.rightIcon {
  display: flex;
  align-items: center;
}
```

## Input Atom Example

```typescript
// atoms/Input/Input.tsx
import React from 'react';
import type { InputHTMLAttributes } from 'react';
import styles from './Input.module.css';

export type InputSize = 'sm' | 'md' | 'lg';

export interface InputProps
  extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
  /** Size variant */
  size?: InputSize;
  /** Error state */
  hasError?: boolean;
  /** Left addon element */
  leftAddon?: React.ReactNode;
  /** Right addon element */
  rightAddon?: React.ReactNode;
}

export const Input = React.forwardRef<HTMLInputElement, InputProps>(
  (
    {
      size = 'md',
      hasError = false,
      leftAddon,
      rightAddon,
      disabled,
      className,
      ...props
    },
    ref
  ) => {
    const wrapperClasses = [
      styles.wrapper,
      styles[size],
      hasError && styles.error,
      disabled && styles.disabled,
      className,
    ]
      .filter(Boolean)
      .join(' ');

    return (
      <div className={wrapperClasses}>
        {leftAddon && <span className={styles.leftAddon}>{leftAddon}</span>}
        <input
          ref={ref}
          className={styles.input}
          disabled={disabled}
          aria-invalid={hasError}
          {...props}
        />
        {rightAddon && <span className={styles.rightAddon}>{rightAddon}</span>}
      </div>
    );
  }
);

Input.displayName = 'Input';
```

```css
/* atoms/Input/Input.module.css */
.wrapper {
  display: flex;
  align-items: center;
  border: 1px solid var(--color-neutral-300);
  border-radius: 6px;
  background-color: var(--color-white);
  transition: border-color 150ms, box-shadow 150ms;
}

.wrapper:focus-within {
  border-color: var(--color-primary-500);
  box-shadow: 0 0 0 3px var(--color-primary-100);
}

.input {
  flex: 1;
  border: none;
  background: transparent;
  outline: none;
  width: 100%;
}

.input::placeholder {
  color: var(--color-neutral-400);
}

/* Error state */
.error {
  border-color: var(--color-danger-500);
}

.error:focus-within {
  border-color: var(--color-danger-500);
  box-shadow: 0 0 0 3px var(--color-danger-100);
}

/* Disabled state */
.disabled {
  background-color: var(--color-neutral-100);
  cursor: not-allowed;
}

.disabled .input {
  cursor: not-allowed;
}

/* Sizes */
.sm {
  min-height: 32px;
}

.sm .input {
  padding: 6px 12px;
  font-size: 14px;
}

.md {
  min-height: 40px;
}

.md .input {
  padding: 8px 12px;
  font-size: 16px;
}

.lg {
  min-height: 48px;
}

.lg .input {
  padding: 12px 16px;
  font-size: 18px;
}

/* Addons */
.leftAddon,
.rightAddon {
  display: flex;
  align-items: center;
  padding: 0 12px;
  color: var(--color-neutral-500);
}
```

## Label Atom Example

```typescript
// atoms/Label/Label.tsx
import React from 'react';
import type { LabelHTMLAttributes } from 'react';
import styles from './Label.module.css';

export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
  /** Indicates required field */
  required?: boolean;
  /** Disabled state styling */
  disabled?: boolean;
}

export const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
  ({ required = false, disabled = false, children, className, ...props }, ref) => {
    const classNames = [
      styles.labe

Related in Design