Skip to main content
The @proton/atoms package contains the foundational UI components built on atomic design principles. These are the lowest-level building blocks used throughout Proton applications.

Installation

yarn add @proton/atoms

Package Information

{
  "name": "@proton/atoms",
  "license": "GPL-3.0",
  "sideEffects": false,
  "type": "module",
  "main": "./src/index.ts"
}
Module Type: This package uses ES modules with explicit export paths defined in package.json.

Design Philosophy

Components are minimal, focused, and composable
  • Single responsibility
  • Minimal dependencies
  • Highly reusable
  • Type-safe APIs

Component Catalog

Button Components

Button

Primary button component with polymorphic rendering

ButtonLike

Make any element look and behave like a button

InlineLinkButton

Inline button styled as a link

Button Examples

import { Button } from '@proton/atoms/Button/Button';
import { ButtonLike } from '@proton/atoms/Button/ButtonLike';

// Standard button
<Button color="norm" shape="solid" size="medium">
  Click me
</Button>

// Button with different shapes
<Button shape="outline">Outline</Button>
<Button shape="ghost">Ghost</Button>
<Button shape="solid">Solid</Button>

// Button sizes
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>

// Polymorphic - render as a link
<ButtonLike as="a" href="/page" shape="solid">
  Link Button
</ButtonLike>
interface ButtonProps {
  // Colors
  color?: 'norm' | 'weak' | 'danger' | 'warning' | 'success' | 'info';
  
  // Shapes
  shape?: 'solid' | 'outline' | 'ghost' | 'underline';
  
  // Sizes
  size?: 'small' | 'medium' | 'large';
  
  // States
  loading?: boolean;
  disabled?: boolean;
  fullWidth?: boolean;
  
  // Icon
  icon?: ReactNode;
}

Input Components

import { Input } from '@proton/atoms/Input/Input';

<Input
  type="text"
  placeholder="Enter text"
  value={value}
  onChange={(e) => setValue(e.target.value)}
  error={error}
  disabled={disabled}
/>

// With icon
<Input
  prefix={<Icon name="magnifier" />}
  placeholder="Search..."
/>

Layout Components

import { Card } from '@proton/atoms/Card/Card';

<Card rounded bordered background="weak">
  <h3>Card Title</h3>
  <p>Card content goes here</p>
</Card>

Data Visualization

import { Donut } from '@proton/atoms/Donut/Donut';

<Donut
  segments={[
    { value: 30, color: 'var(--primary)' },
    { value: 50, color: 'var(--signal-success)' },
    { value: 20, color: 'var(--signal-warning)' },
  ]}
/>
import { CircleLoader } from '@proton/atoms/CircleLoader/CircleLoader';
import { ProtonLoader } from '@proton/atoms/ProtonLoader/ProtonLoader';

// Circle loader for progress
<CircleLoader size="large" />

// Branded Proton loader
<ProtonLoader />
import { Stepper } from '@proton/atoms/Stepper/Stepper';
import { Step } from '@proton/atoms/Stepper/Step';
import { StepIndicator } from '@proton/atoms/Stepper/StepIndicator';

<Stepper activeStep={1}>
  <Step>
    <StepIndicator>1</StepIndicator>
    Account setup
  </Step>
  <Step>
    <StepIndicator>2</StepIndicator>
    Verification
  </Step>
  <Step>
    <StepIndicator>3</StepIndicator>
    Complete
  </Step>
</Stepper>

Vertical Steps

import { VerticalSteps } from '@proton/atoms/VerticalSteps/VerticalSteps';
import { VerticalStep } from '@proton/atoms/VerticalSteps/VerticalStep';

<VerticalSteps>
  <VerticalStep status="complete">Step 1 complete</VerticalStep>
  <VerticalStep status="current">Step 2 in progress</VerticalStep>
  <VerticalStep status="upcoming">Step 3 upcoming</VerticalStep>
</VerticalSteps>

Badges & Indicators

Pill

Small status indicators

NotificationDot

Notification indicator dot

NotificationCounter

Numeric notification badge

CircledNumber

Number in a circle
import { Pill } from '@proton/atoms/Pill/Pill';
import { NotificationDot } from '@proton/atoms/NotificationDot/NotificationDot';
import { NotificationCounter } from '@proton/atoms/NotificationCounter/NotificationCounter';

<Pill color="success">Active</Pill>
<Pill color="danger">Error</Pill>
<Pill color="warning">Warning</Pill>

<NotificationDot color="danger" />
<NotificationCounter count={5} />

Feedback Components

import { Banner } from '@proton/atoms/Banner/Banner';
import { Tooltip } from '@proton/atoms/Tooltip/Tooltip';

// Banners for important messages
<Banner color="danger" icon="triangle-exclamation">
  Important warning message
</Banner>

<Banner color="info" icon="info-circle">
  Helpful information
</Banner>

// Tooltips
<Tooltip title="Helpful explanation">
  <button>Hover me</button>
</Tooltip>

Interactive Controls

import { Slider } from '@proton/atoms/Slider/Slider';
import { SliderMark } from '@proton/atoms/Slider/SliderMark';

<Slider
  min={0}
  max={100}
  value={value}
  onChange={setValue}
  marks={
    <>
      <SliderMark value={0}>0</SliderMark>
      <SliderMark value={50}>50</SliderMark>
      <SliderMark value={100}>100</SliderMark>
    </>
  }
/>

User Interface

import { Avatar } from '@proton/atoms/Avatar/Avatar';
import { UserAvatar } from '@proton/atoms/UserAvatar/UserAvatar';
import { getAccentColorForUsername } from '@proton/atoms/UserAvatar/getAccentColorForUsername';
import { Kbd } from '@proton/atoms/Kbd/Kbd';
import { Vr } from '@proton/atoms/Vr/Vr';

// Avatars
<Avatar>JD</Avatar>
<UserAvatar name="John Doe" email="[email protected]" />

// Keyboard shortcuts
<span>Press <Kbd>Ctrl</Kbd> + <Kbd>S</Kbd> to save</span>

// Vertical divider
<Vr />

Dashboard Components

import { DashboardGrid } from '@proton/atoms/DashboardGrid/DashboardGrid';
import { DashboardCard } from '@proton/atoms/DashboardCard/DashboardCard';

<DashboardGrid>
  <DashboardCard>
    <h3>Storage</h3>
    <p>50 GB used</p>
  </DashboardCard>
  
  <DashboardCard>
    <h3>Messages</h3>
    <p>1,234 emails</p>
  </DashboardCard>
</DashboardGrid>
import { Href } from '@proton/atoms/Href/Href';

// Type-safe external links
<Href href="https://proton.me">Visit Proton</Href>

// Opens in new tab with security
<Href href="https://example.com" target="_blank">
  External link
</Href>

Import Pattern

Explicit Imports: Use the full import path for optimal tree-shaking:
import { Button } from '@proton/atoms/Button/Button';
import { Tooltip } from '@proton/atoms/Tooltip/Tooltip';
Package.json exports define exact paths:
{
  "exports": {
    "./Button/Button": "./src/Button/Button.tsx",
    "./Tooltip/Tooltip": "./src/Tooltip/Tooltip.tsx"
  }
}

Styling

Components use CSS classes from @proton/styles:
// Components have design tokens
<Button color="norm">  // Uses --interaction-norm color
<Button color="danger">  // Uses --signal-danger color

// Custom styling via className
<Button className="my-custom-class">
  Custom styled button
</Button>

Creating Atoms

The package includes a generator for new atoms:
yarn workspace @proton/atoms create-atom
This creates the component structure:
MyAtom/
  ├── MyAtom.tsx
  ├── MyAtom.scss
  └── MyAtom.test.tsx

Testing

# Run tests
yarn workspace @proton/atoms test

# Watch mode
yarn workspace @proton/atoms test:watch

# Coverage
yarn workspace @proton/atoms test:ci

Testing Example

import { render, screen } from '@testing-library/react';
import { Button } from '@proton/atoms/Button/Button';

test('renders button', () => {
  render(<Button>Click me</Button>);
  expect(screen.getByRole('button')).toHaveTextContent('Click me');
});

Dependencies

{
  "@proton/colors": "workspace:^",
  "@proton/hooks": "workspace:^",
  "@proton/icons": "workspace:^",
  "@proton/styles": "workspace:^",
  "@proton/utils": "workspace:^"
}

Best Practices

Composition over Customization: Compose atoms to build complex components rather than heavily customizing individual atoms.
Polymorphic Props: Use the as prop to render components with different HTML elements:
<ButtonLike as="a" href="/link">Link as button</ButtonLike>
<ButtonLike as="div" onClick={handler}>Div as button</ButtonLike>
Accessibility: All atoms include proper ARIA attributes. Ensure you maintain accessibility when composing them.

TypeScript Support

Full TypeScript support with polymorphic types:
import type { ButtonProps } from '@proton/atoms/Button/Button';
import type { PolymorphicComponentProps } from '@proton/react-polymorphic-types';

// Polymorphic button that can be rendered as any element
type MyButtonProps<E extends React.ElementType> = 
  PolymorphicComponentProps<E, ButtonProps>;

Design Tokens

Components use CSS custom properties from the design system:
// Color tokens
--interaction-norm
--interaction-norm-hover
--interaction-norm-active
--signal-danger
--signal-warning
--signal-success
--signal-info

// Spacing tokens
--space-1
--space-2
--space-4

// Border radius
--border-radius-sm
--border-radius-md
--border-radius-lg

Build docs developers (and LLMs) love