🍕 Roman's Pizza Design System

Design tokens, Lucide icons, and component classes — one source of truth for all RP digital products.

v1.0 Lucide Icons Vanilla CSS Tailwind v4 ready Responsive Dark Mode

Gradients

All gradients follow a single rule: lightest colour at the top, darkest at the bottom. This creates natural visual hierarchy — the eye tracks downward through increasing weight — and anchors heavier UI elements (tables, footers, chart axes) with a darker, more grounded base.

Gradient tokens

↑ #0260c9 light ↓ #014085 dark

--g-blue

Buttons, hero cards, badges, CTAs

↑ #014085 light ↓ #010e1f dark

--g-sidebar

Sidebar nav, dark panels

↑ #f8faff lightest ↓ #e8eef8 subtle

Surface gradient

Inline use — subtle depth on cards

Direction rule

Correct — light top, dark bottom

Incorrect — dark top, light bottom

Usage in CSS

/* Use the token — never repeat the raw gradient */
.my-hero   { background: var(--gradient-brand-blue); }
.my-panel  { background: var(--gradient-sidebar); }

/* Custom one-off: always light → dark, top → bottom */
.my-banner {
  background: linear-gradient(180deg, #0260c9 0%, #014085 100%);
}

Colours

Brand

--color-brand-blue
#014085
--color-brand-blue-dark
#012d5e
--color-brand-blue-light
#0260c9
--color-brand-red
#ed1c24
--color-brand-red-dark
#c41820

Semantic Status

success
success-light
warning
warning-light
error
error-light
info
info-light

Rating Scale

rating-5
rating-4
rating-3
rating-2
rating-1

Typography

All typography values are defined as CSS custom properties in src/tokens/typography.css. Components reference these tokens — never use raw pixel or rem values.

Font Size Scale

--font-size-4xl · 2.5rem Empty State Icon
--font-size-3xl · 1.875rem Stat Card Value
--font-size-2xl · 1.375rem Page Header
--font-size-xl · 1.25rem Modal Title
--font-size-lg · 1rem Card Heading
--font-size-md-alt · 0.9375rem Button Large / Empty State
--font-size-md · 0.875rem Body text — Inter is the typeface across all RP products.
--font-size-base · 0.8125rem Button, Input, Card default
--font-size-sm-alt · 0.8rem Bar chart labels
--font-size-sm · 0.75rem Small / muted text
--font-size-xs · 0.6875rem LABEL CAPS
--font-size-2xs · 0.625rem Minimum text size (10px)

Font Weights

TokenValuePreview
--font-weight-regular400Regular weight text
--font-weight-medium500Medium weight text
--font-weight-semibold600Semibold weight text
--font-weight-bold700Bold weight text
--font-weight-extrabold800Extrabold weight text

Line Heights & Letter Spacing

TokenValueUse
--line-height-tight1.15Stat values, large headings
--line-height-snug1.2Buttons, compact text
--line-height-normal1.4General content
--line-height-base1.5Body text default
--line-height-relaxed1.6Long-form text, descriptions
--letter-spacing-tight-0.02emLarge headings
--letter-spacing-normal0.01emBody text
--letter-spacing-wide0.04emButtons, badges
--letter-spacing-wider0.07emLabels, uppercase text
--letter-spacing-widest0.075emAll-caps headings

Spacing & Radii

Spacing Scale

--spacing-1
0.25rem · 4px
--spacing-2
0.5rem · 8px
--spacing-3
0.75rem · 12px
--spacing-4
1rem · 16px
--spacing-5
1.25rem · 20px
--spacing-6
1.5rem · 24px
--spacing-8
2rem · 32px
--spacing-12
3rem · 48px
--spacing-16
4rem · 64px

Semantic Spacing

TokenValueUse
--spacing-page2remOuter page padding
--spacing-section2remGap between major sections
--spacing-card1.375remInternal card padding

Radius Tokens

xs 4
sm 6
md 8
lg 12
xl 16
2xl 24
full

Shadows

xs
sm
md
lg
card
hover
pop
glow

Shadow Tokens

TokenLight ModeUse
--shadow-xs0 1px 2px 5% blackSubtle depth, swatches
--shadow-smDual layer, 6%+4%Default elevation
--shadow-mdDual layer, 5%+4%Raised elements
--shadow-lgDual layer, 5%+3%Prominent elements
--shadow-cardDual layer, 7%+5%Card default
--shadow-card-hoverDual layer, blue-tintedCard hover state
--shadow-popDeep dual layerModals, overlays
--shadow-glowBlue brand glowFocus rings, emphasis

Motion

TokenValue
--transition-fast150ms ease
--transition-base250ms ease
--ease-settlecubic-bezier(0.25, 0.46, 0.45, 0.94)
--ease-springcubic-bezier(0.175, 0.885, 0.32, 1.275)

Breakpoints

The design system uses three breakpoints. Desktop styles are the default — overrides are applied at narrower viewports via max-width queries. This ensures existing desktop appearance is never affected.

Breakpoint Architecture

NameQueryRangeMin Supported
Mobilemax-width: 767px0–767px375px
Tabletmin-width: 768px AND max-width: 1023px768–1023px768px
Desktopmin-width: 1024px1024px+1024px

Combined Queries

AliasQueryCovers
≤ Tabletmax-width: 1023pxMobile + Tablet
≥ Tabletmin-width: 768pxTablet + Desktop

Usage in CSS

/* Mobile only */
@media (width <= 767px) {
  .my-component { padding: 1rem; }
}

/* Tablet only */
@media (768px <= width <= 1023px) {
  .my-component { padding: 1.5rem; }
}

/* Mobile + Tablet (≤ 1023px) */
@media (width <= 1023px) {
  .my-grid { grid-template-columns: 1fr; }
}

Buttons

Badges

Active Pending Closed Draft Archived Brand
No dot variant

Cards

Stat Grid

Total Reviews

1,248

+12% vs last month

Avg Rating

4.3

out of 5

Promoters

68%

Semantic Tints

Success

Warning

Error

Info

Accent Left-Border

Success

Warning

Error

Chips

Positive 42 Negative 8 Neutral 17

Bar Charts

5
90072%
4
18815%
3
756%
2
504%
1
353%

Chart Cards

Chart card CSS components with live Chart.js examples. The grid switches from 2-col to 1-col on mobile/tablet. See docs/chart-integration.md for full configuration guide.

Revenue Over Time

+12%

Store Comparison

Rating Trends — All Branches

30 days 90 days

Chart Card Classes

<div class="rp-chart-grid">
  <div class="rp-chart-card">
    <div class="rp-chart-header">
      <h3 class="rp-chart-title">Revenue</h3>
    </div>
    <div class="rp-chart-canvas-wrap">
      <canvas id="myChart" role="img"
        aria-label="Line chart showing revenue"></canvas>
    </div>
  </div>
  <!-- .rp-chart-card--full spans both columns -->
</div>

Tables

BranchReviewsAvg RatingStatus
Sandton City3124.6Active
Rosebank1984.1Pending
Midrand87Archived

Inputs

Modals

Rate Your Order

This is the modal card component. Backdrop fade and slide-up animate on mount.

Empty States

No reviews yet

Once customers leave reviews they'll appear here. Share your link to get started.

Loading States

Utilities

Utility classes from src/components/utilities.css and src/components/responsive.css. These work as single-purpose helpers on any element.

Text Truncation — .rp-truncate

This is a very long text string that will be truncated with an ellipsis because the container is only 250px wide.

Container constrained to 250px — text truncated with ellipsis.

Tabular Numbers — .rp-tabular-nums

Without

1,111

8,888

4,204

With .rp-tabular-nums

1,111

8,888

4,204

Tabular nums align digits in columns — critical for stat cards, tables, and numeric lists.

Horizontal Scroll — .rp-scroll-x

Excellent Great food Fast delivery Average Cold pizza OK value Would order again Best in area

Horizontally scrollable container with touch momentum and thin scrollbar.

Responsive Visibility

ClassMobileTablet+
.rp-hide-mobileHiddenVisible
.rp-show-mobileVisibleHidden
.rp-stack-mobileStacks flex children verticallyNormal flex direction

Screen Reader Only — .sr-only

The .sr-only class visually hides an element while keeping it accessible to screen readers. Use for skip links, form labels, or additional context.

Icons

Why Lucide SVG icons exclusively

Screen reader clarity. Emojis are read aloud by screen readers with verbose, platform-dependent descriptions (e.g. “grinning face with big eyes”). Lucide SVGs accept an aria-label or are hidden with aria-hidden="true" when decorative, giving full control over the announcement.
Visual consistency across platforms. Emoji glyphs render differently on iOS, Android, Windows, and macOS — the same codepoint can produce wildly different shapes, colours, and sizes. SVG icons render pixel-identically everywhere.
Themeable with CSS. SVG strokes inherit currentColor, meaning icons respond to semantic colour tokens (--color-success, --color-error, etc.) and work correctly in dark mode without any extra markup.
Precise sizing and alignment. SVG width/height attributes map directly to the design grid. Emoji size is controlled by font-size and behaves inconsistently inside flex and grid layouts.
Brand safety. Emojis carry cultural and emotional connotations that vary by region and can undermine the professional tone of a business platform. A curated icon set keeps the interface neutral, functional, and on-brand.

Click any icon to copy its usage snippet. Install: npm i lucide or see How to Use.

Loading icons…

Dark Mode

Add data-theme="dark" to <html> (or any ancestor) to switch the full token set to dark values. No extra stylesheet needed — all tokens are overridden via CSS custom properties.

Theme Toggle Component

Click to toggle — persists via localStorage
<!-- Theme toggle component -->
<button class="rp-theme-toggle" aria-label="Toggle dark mode">
  <svg class="rp-theme-toggle__icon--light" data-lucide="moon" width="16" height="16"></svg>
  <svg class="rp-theme-toggle__icon--dark" data-lucide="sun" width="16" height="16"></svg>
</button>

Implementation

/* 1. Include theme-init.js in <head> (prevents FOUWT) */
<script src="theme-init.js"></script>

/* 2. Wire the toggle button */
const btn = document.querySelector('.rp-theme-toggle');
btn.addEventListener('click', () => {
  const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark';
  document.documentElement.dataset.theme = next;
  localStorage.setItem('rp-theme', next);
});

/* 3. CSS fallback (no JS): prefers-color-scheme in theme-fallback.css */
/* OS theme changes are auto-detected via matchMedia listener */

Light vs Dark preview

Light (default)

Total Reviews

1,248

Active Pending Issue

Dark

Total Reviews

1,248

Active Pending Issue

Token changes in dark mode

TokenLightDarkRole
--color-background#f0f4f8#0a0f1aPage background
--color-surface#ffffff#111827Card / panel background
--color-surface-dim#f8f9fa#1a2233Subtle surface / table header
--color-surface-container#f3f4f6#1e293bSecondary control background
--color-on-surface#0f172a#f1f5f9Primary text
--color-on-surface-muted#64748b#94a3b8Secondary / placeholder text
--color-outline#cbd5e1#334155Border — inputs
--color-outline-variant#e2e8f0#1e293bBorder — subtle / cards

Icon colour utilities

Apply these classes directly to any Lucide <svg>. Use the parent .icon-hover--* classes to change icon colour on hover.

.icon--blue
.icon--red
.icon--ok
.icon--warn
.icon--err
.icon--info
.icon--muted
.icon--white
<!-- Colour class on the svg -->
<svg data-lucide="check-circle" width="20" height="20" class="icon--ok"></svg>
<svg data-lucide="alert-triangle" width="20" height="20" class="icon--warn"></svg>

<!-- Icon button variants -->
<button class="icon-btn"><svg data-lucide="copy" width="16" height="16"></svg></button>
<button class="icon-btn icon-btn--blue"><svg data-lucide="download" width="16" height="16"></svg></button>
<button class="icon-btn icon-btn--danger"><svg data-lucide="trash-2" width="16" height="16"></svg></button>

<!-- Hover: change icon colour when parent is hovered -->
<a class="icon-hover--blue" href="/">
  <svg data-lucide="home" width="18" height="18"></svg> Home
</a>

Responsive Preview

Live preview of components at each breakpoint. Each frame loads a lightweight component sampler constrained to its breakpoint width.

Mobile 375px
Tablet 768px
Desktop ≥ 1024px

How to Use

CDN

<link rel="stylesheet" href="https://design.romanspizza.co.za/rp-style.css">
<script src="lucide.min.js"></script>
<script>lucide.createIcons();</script>

npm

npm install lucide rp-design-system

// In your JS entry:
import { createIcons, icons } from 'lucide';
import 'rp-design-system/dist/rp-style.css';
createIcons({ icons });

Icon usage

<!-- Add a data-lucide attribute, then call createIcons() once -->
<svg data-lucide="star" width="20" height="20"></svg>
<button class="rp-btn rp-btn--primary">
  <svg data-lucide="download" width="14" height="14"></svg>
  Download
</button>