Design System

Peraa

A minimal, token-based design system I created for building clean, accessible interfaces. Yes, I created a design system and use it for my portfolio, because Design Systems is what I do. (The system is actively maintained and expanded. A standalone public release is planned.)

Getting Started

Peraa is a lightweight design system built with vanilla CSS custom properties. It provides a comprehensive token system and reusable components for building consistent, accessible user interfaces. There is also a React and Tailwind CSS implementation.

Available Platforms

Peraa is available in three formats. Choose the one that fits your project:

HTML/CSS/JS

Vanilla implementation with CSS custom properties. Zero dependencies.

React

React components with props, TypeScript support, and composable patterns.

Tailwind CSS

Tailwind config preset with utility classes and component styles.

Installation

HTML
<!-- Include the stylesheet -->
<link rel="stylesheet" href="css/main.css">

<!-- Include the JavaScript (for theme toggle, tabs) -->
<script src="js/main.js"></script>
JSX
// Import Peraa styles in your app entry
import '@peraa/css/main.css';

// Import components
import { Button, Badge, Card, Tabs } from '@peraa/react';

// Use in your components
function App() {
  return (
    <Button intent="primary">Click me</Button>
  );
}
JavaScript
// tailwind.config.js
const peraaConfig = require('@peraa/tailwind/tailwind.config');

module.exports = {
  presets: [peraaConfig],
  content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
  // your customizations...
};

// In your CSS
@import '@peraa/tailwind/components.css';

Architecture

Peraa uses a two-tier token system:

  • Primitive Tokens: Raw values (colors, spacing, typography) that form the foundation
  • Semantic Tokens: Context-aware tokens that reference primitives and adapt to themes

Theme Support

Peraa supports light and dark modes out of the box. The theme automatically respects the user's system preference and can be toggled manually.

JavaScript
// Toggle theme programmatically
window.Peraa.ThemeManager.toggle();

// Set specific theme
window.Peraa.ThemeManager.setTheme('dark');
window.Peraa.ThemeManager.setTheme('light');

Components

Buttons

Primary, secondary, and tertiary button styles with multiple sizes.

Tabs

Horizontal tab navigation with keyboard support.

Timeline

Vertical sticky timeline for case study pages.

Cards

Content cards with image, title, and description.

Badges

Tags and skill badges for labeling content.

Image Placeholder

Clickable images with hover effects.

Buttons

Buttons trigger actions and events. Peraa provides three button intents for different levels of emphasis: primary, secondary, and tertiary.

HTML
<button class="peraa-btn peraa-btn--primary">
  Primary
</button>

<button class="peraa-btn peraa-btn--secondary">
  Secondary
</button>

<button class="peraa-btn peraa-btn--tertiary">
  Tertiary
</button>
JSX
import { Button } from '@peraa/react';

<Button intent="primary">Primary</Button>
<Button intent="secondary">Secondary</Button>
<Button intent="tertiary">Tertiary</Button>

// With additional props
<Button intent="primary" size="lg">
  Large Button
</Button>
<Button intent="primary" disabled>
  Disabled
</Button>
HTML
<button class="peraa-btn-primary">
  Primary
</button>

<button class="peraa-btn-secondary">
  Secondary
</button>

<button class="peraa-btn-tertiary">
  Tertiary
</button>

<!-- With sizes -->
<button class="peraa-btn-primary peraa-btn-sm">Small</button>
<button class="peraa-btn-primary peraa-btn-lg">Large</button>

Props / Classes

Class Description Required
.peraa-btn Base button styles Yes
.peraa-btn--primary High emphasis, solid background No
.peraa-btn--secondary Medium emphasis, outlined No
.peraa-btn--tertiary Low emphasis, text only No
.peraa-btn--sm Small size variant No
.peraa-btn--lg Large size variant No
.peraa-btn--icon-only Square button for icons No
.peraa-btn--full Full-width button No

Design Tokens

Semantic Token Primitive Value Description
--peraa-interactive-primary-bg --peraa-color-purple-500 #6F1FAC Primary button background
--peraa-interactive-primary-bg-hover --peraa-color-purple-600 #5c1a8f Primary button hover
--peraa-interactive-primary-text --peraa-color-neutral-0 #ffffff Primary button text
--peraa-interactive-secondary-border --peraa-color-purple-500 #6F1FAC Secondary button border
--peraa-interactive-secondary-text --peraa-color-purple-500 #6F1FAC Secondary button text

Design Tokens

All design tokens used in the Peraa design system. Tokens are organized into two layers: primitives (raw values — colors, sizes, timing) and semantic tokens (contextual mappings — always prefer these in components).

Dark mode is the default

:root maps to the dark theme in tokens/_semantic.css. Light mode is opt-in via [data-theme="light"]. A blocking inline script in each page's <head> reads localStorage or prefers-color-scheme and sets data-theme synchronously, eliminating flash of unstyled content (FOUC) on load.

Color Primitives

Token Value Preview Description
--peraa-color-purple-50 #f5e9fc Lightest purple
--peraa-color-purple-100 #e5c7f7 Very light purple
--peraa-color-purple-200 #d3a1f1 Light purple
--peraa-color-purple-300 #bf79eb Medium light purple
--peraa-color-purple-400 #ab51e5 Medium purple
--peraa-color-purple-500 #6F1FAC Primary brand color
--peraa-color-purple-600 #5c1a8f Dark purple
--peraa-color-purple-700 #491572 Darker purple
--peraa-color-purple-800 #361055 Very dark purple
--peraa-color-purple-900 #230b38 Darkest purple

Neutral Colors

Token Value Preview Description
--peraa-color-neutral-0 #ffffff White
--peraa-color-neutral-50 #fafafa Off-white
--peraa-color-neutral-100 #f4f4f5 Light gray
--peraa-color-neutral-200 #e4e4e7 Border gray
--peraa-color-neutral-300 #d4d4d8 Medium light gray
--peraa-color-neutral-400 #a1a1aa Disabled text
--peraa-color-neutral-500 #71717a Tertiary text
--peraa-color-neutral-600 #52525b Secondary text
--peraa-color-neutral-700 #3f3f46 Dark gray
--peraa-color-neutral-800 #27272a Very dark gray
--peraa-color-neutral-900 #18181b Near black
--peraa-color-neutral-950 #09090b Darkest

Teal (Brand Accent)

The secondary brand palette. Used for ambient glows, interactive hover states, card accent borders, and CTAs that need to read as "active" without purple. The 400–600 range is the primary active zone.

TokenValuePreviewUsage
--peraa-color-teal-50#f0fdfaLightest tint — alert backgrounds
--peraa-color-teal-100#ccfbf1Success backgrounds
--peraa-color-teal-200#99f6e4Hover fills (light mode)
--peraa-color-teal-300#5eead4Subtle accents
--peraa-color-teal-400#2dd4bfActive accents in dark mode
--peraa-color-teal-500#14b8a6Brand accent — glow base, card hover borders
--peraa-color-teal-600#0d9488Pressed / active state
--peraa-color-teal-700#0f766eDark variant fills
--peraa-color-teal-800#115e59Deep background tints
--peraa-color-teal-900#134e4aDarkest tint
--peraa-color-teal-950#042f2eNear-black for overlays

Semantic Tokens

Semantic tokens are the layer between raw color values and your UI. Always prefer semantic tokens over primitives — they automatically respond to the active theme (light or dark) so components remain consistent without per-component media queries.

Surface

TokenLight valueDark valueDescription
--peraa-surface-page#fafafa#09090bMain page background. Dark mode carries ambient teal glow as background-image layers.
--peraa-surface-card#ffffff#18181bCard / panel background
--peraa-surface-elevated#ffffff#27272aDropdowns, tooltips, modals — above card level
--peraa-surface-subtle#f4f4f5#3f3f46Muted fills, hover states, tag backgrounds
--peraa-surface-overlayrgba(0,0,0,0.4)rgba(0,0,0,0.65)Modal / drawer scrim overlay

Text

TokenLight valueDark valueDescription
--peraa-text-primary#09090b#fafafaBody text, headings
--peraa-text-secondary#71717a#a1a1aaDescriptions, subheadings
--peraa-text-tertiary#a1a1aa#52525bPlaceholders, hints, disabled labels
--peraa-text-disabled#d4d4d8#3f3f46Fully disabled state text
--peraa-text-brand#6F1FAC#ab51e5Overlines, active nav links, brand callouts
--peraa-text-inverse#fafafa#09090bText on filled/inverted surfaces

Border

TokenLight valueDark valueDescription
--peraa-border-default#e4e4e7#27272aDefault dividers, card borders
--peraa-border-subtle#f4f4f5#18181bVery light separation
--peraa-border-strong#a1a1aa#52525bEmphasis borders
--peraa-border-brand#6F1FAC#ab51e5Brand-colored border
--peraa-border-focus#6F1FAC#ab51e5Keyboard focus ring

Interactive States

TokenLight valueDark valueUsage
--peraa-interactive-primary-bg#6F1FAC#6F1FACPrimary button fill
--peraa-interactive-primary-bg-hover#5c1a8f#ab51e5Primary button hover
--peraa-interactive-primary-text#ffffff#ffffffText on primary button
--peraa-interactive-secondary-bgtransparenttransparentSecondary button fill
--peraa-interactive-secondary-border#6F1FAC#ab51e5Secondary button border
--peraa-interactive-tertiary-text#6F1FAC#ab51e5Tertiary / text-only button

Spacing

Token Value Pixels Description
--peraa-spacing-10.25rem4pxExtra small
--peraa-spacing-1-50.375rem6pxForm gap, tight inline spacing
--peraa-spacing-20.5rem8pxSmall
--peraa-spacing-2-50.625rem10pxInput padding vertical
--peraa-spacing-30.75rem12pxMedium small
--peraa-spacing-41rem16pxBase
--peraa-spacing-51.25rem20pxMedium
--peraa-spacing-61.5rem24pxLarge
--peraa-spacing-82rem32pxExtra large
--peraa-spacing-102.5rem40px2X large
--peraa-spacing-123rem48px3X large
--peraa-spacing-164rem64px4X large
--peraa-spacing-205rem80px5X large
--peraa-spacing-246rem96px6X large

Typography

Token Value Description
--peraa-font-family-sans'Outfit', sans-serifPrimary UI font — all headings and body
--peraa-font-family-mono'JetBrains Mono', monospaceCode blocks, tokens, technical labels
--peraa-font-size-xs0.75rem (12px)Captions, badges, fine print
--peraa-font-size-sm0.875rem (14px)Small body, table cells
--peraa-font-size-base1rem (16px)Default body text
--peraa-font-size-lg1.125rem (18px)Lead / intro copy
--peraa-font-size-xl1.25rem (20px)Section subheadings
--peraa-font-size-2xl1.5rem (24px).peraa-heading-4
--peraa-font-size-3xl1.875rem (30px).peraa-heading-3
--peraa-font-size-4xl2.25rem (36px).peraa-heading-2
--peraa-font-size-5xl3rem (48px).peraa-heading-1
--peraa-font-size-6xl3.75rem (60px)Hero / display headings
--peraa-font-weight-regular400Body text, descriptions
--peraa-font-weight-medium500h3–h6, UI labels, overlines
--peraa-font-weight-semibold600h1–h2 (Direction A editorial style)
--peraa-font-weight-bold700Reserved — use sparingly for max emphasis

Border Radius

Token Value Preview Description
--peraa-radius-none 0 No radius
--peraa-radius-sm 0.25rem (4px) Small
--peraa-radius-md 0.5rem (8px) Medium (default)
--peraa-radius-lg 0.75rem (12px) Large
--peraa-radius-xl 1rem (16px) Extra large
--peraa-radius-2xl 1.5rem (24px) Large cards, modals
--peraa-radius-full 9999px Pill shape

Shadows

Token Preview Description
--peraa-shadow-xs Extra small shadow
--peraa-shadow-sm Small shadow
--peraa-shadow-md Medium shadow
--peraa-shadow-lg Large shadow
--peraa-shadow-xl Extra large shadow
--peraa-shadow-2xl 2X large — modals, overlays

Line Height

TokenValueDescription
--peraa-line-height-tight1.2Large headings (h1, h2) — condensed for display use
--peraa-line-height-snug1.375Subheadings (h3–h4)
--peraa-line-height-normal1.5Body text — default reading rhythm
--peraa-line-height-relaxed1.625Long-form reading, articles
--peraa-line-height-loose2Spacious UI labels, table rows

Letter Spacing

Direction A (Premium Dark Editorial) uses negative tracking on large headings to tighten display type. Overlines use wide tracking to add visual contrast against heading copy.

TokenValueUsed on
--peraa-letter-spacing-tighter-0.05emReserved for extreme display use
--peraa-letter-spacing-tight-0.025em.peraa-heading-2 (Direction A)
--peraa-letter-spacing-normal0Body text, most UI elements
--peraa-letter-spacing-wide0.025emOverlines, small labels
--peraa-letter-spacing-wider0.05emALL CAPS labels
--peraa-letter-spacing-widest0.1emDecorative overlines at small sizes

Border Widths

TokenValueDescription
--peraa-border-width-00No border (use to override)
--peraa-border-width-11pxDefault — all cards, dividers, inputs
--peraa-border-width-22pxActive tab indicator, focus rings
--peraa-border-width-44pxAlert left-border accent, timeline tracks

Animation — Duration

TokenValueUse case
--peraa-duration-7575msInstant micro-interactions (checkbox tick)
--peraa-duration-100100msIcon state changes
--peraa-duration-150150msButton press feedback
--peraa-duration-200200msHover transitions (color, border)
--peraa-duration-300300msPanel reveals, tab content switches
--peraa-duration-500500msPage entrance animations, theme fade

Animation — Easing

TokenValueUse case
--peraa-ease-linearlinearProgress bars, loaders
--peraa-ease-incubic-bezier(0.4, 0, 1, 1)Elements leaving the screen
--peraa-ease-outcubic-bezier(0, 0, 0.2, 1)Elements entering the screen (default)
--peraa-ease-in-outcubic-bezier(0.4, 0, 0.2, 1)State toggles, flip cards, modals

Z-Index

Use only the defined Z layers — never use arbitrary values. Layers establish a clear stacking context that prevents unresolvable overlap bugs.

TokenValueIntended layer
--peraa-z-00Base / no stacking context needed
--peraa-z-1010Sticky elements — table headers, inline toolbars
--peraa-z-2020Dropdowns, floating cards
--peraa-z-3030Sticky site header / navigation bar
--peraa-z-4040Drawers, side panels
--peraa-z-5050Modals, dialogs
--peraa-z-100100Toasts, alerts, top-level notifications

Glass — Semantic Tokens

All values are defined per theme. Apply via the .peraa-glass component class — do not use these tokens directly in component styles unless you are building a glass variant.

TokenDark valueLight value
--peraa-glass-bgneutral-800 solidTranslucent white + teal/purple radial gradients
--peraa-glass-borderneutral-700rgba(255,255,255,0.75)
--peraa-glass-blurnoneblur(20px) saturate(1.4)
--peraa-glass-shadownoneLayered outer shadow + inner top highlight
--peraa-glass-radius--peraa-radius-xl (1rem)

Focus Ring

TokenDarkLight
--peraa-focus-ring-shadow0 0 0 3px rgba(purple-500, 0.2)0 0 0 3px rgba(purple-500, 0.15)

Status — Subtle Backgrounds

Low-opacity status backgrounds used for feedback banners and toast surfaces. Theme-aware.

TokenDescription
--peraa-status-success-bg-subtleMuted green tint — success feedback banners
--peraa-status-warning-bg-subtleMuted amber tint — warning feedback banners
--peraa-status-error-bg-subtleMuted red tint — error feedback banners
--peraa-status-info-bg-subtleMuted blue tint — info feedback banners