Skip to content

PIE Design System

Comprehensive reference for visual style, component patterns, and implementation details across the PIE web client and desktop app. Use this document when building any new UI to ensure consistency.

For plugin widget UIs that run inside the sandboxed ===widget=== iframe, use the Widget UI Catalog (design tokens, components, and patterns aligned with the host app).


Table of Contents

  1. Brand Color Palette
  2. Typography
  3. Backgrounds and Surfaces
  4. Frosted Glass and Depth
  5. Borders and Dividers
  6. Shadows
  7. Scrollbars
  8. Navigation and Sidebar
  9. Buttons
  10. Inputs and Forms
  11. Cards
  12. Message Bubbles
  13. Icons
  14. Animations and Transitions
  15. Dark Mode
  16. Electron / Desktop Specifics
  17. Accessibility
  18. File Reference

Brand Color Palette

Primary Accent — PIE Pink

The brand color is a vibrant pink used for primary actions, active states, and accent highlights.

TokenHexUsage
pie-50 / accent-light#FFE5EFLight accent backgrounds, hover tints
pie-100#FFCCE0Subtle accent fills
pie-200#FF99C2
pie-300#FF66A3
pie-400#FF3385Hover state for primary buttons
pie-500 / accent#FF0066Primary accent, active icons, primary buttons
pie-600#E6005C
pie-700#CC0052
pie-800#990040
pie-900#66002B

Neutral Surfaces (Light Mode)

TokenHexUsage
background#FFFFFFPrimary content background, cards
background-secondary / surface-secondary#FAFAFAPage-level fallback, inset panels
background-tertiary / surface-tertiary#F5F5F5Hover fills, skeleton base, subtle containers

Neutral Surfaces (Dark Mode)

TokenHexUsage
dark-background#0A0A0FRoot page background
dark-background-secondary#111118Inset panels, sidebar fallback
dark-background-tertiary#1A1A24Hover fills, card backgrounds

Foreground / Text

TokenLightDarkUsage
foreground#171717#F0F0F5Primary body text
foreground-secondary#525252#8888A0Secondary/supporting text
foreground-muted#A3A3A3#525260Placeholder, disabled, tertiary text

Semantic Colors

TokenHexUsage
success#22C55ESuccess indicators, confirmation
error#EF4444Error states, destructive actions
warning#F59E0BWarning indicators

Borders

TokenLightDarkUsage
border#E5E5E5#262626Default border, dividers
border-subtle#D4D4D4#404040Stronger borders for emphasis

Within the frosted app shell, prefer translucent borders over opaque tokens:

  • Light: border-black/[0.06] or border-border/60
  • Dark: border-white/[0.06] or border-dark-border/60

Typography

Font Families

PurposeStackToken
UI text'Inter', system-ui, -apple-system, sans-seriffont-sans
Code / mono'JetBrains Mono', Menlo, Monaco, monospacefont-mono
Desktop notes editor-apple-system, BlinkMacSystemFont, 'Inter', sans-serifinline

Inter is loaded from Google Fonts at weights 400 (regular), 500 (medium), 600 (semibold), 700 (bold), and 800 (extrabold). JetBrains Mono is loaded at 400 and 500.

Font Features

Always enable these OpenType features globally on html:

css
font-feature-settings: 'cv11', 'ss01';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
  • cv11 gives Inter an alternate lowercase l that's easier to distinguish from 1 and I.
  • ss01 enables Inter's alternate digit forms for better readability.

Type Scale

NameSizeLine HeightLetter SpacingUsage
text-[10px]10pxtracking-[0.15em]--tracking-[0.2em]Section headers, labels, badges, timestamps
text-[12px]12pxSidebar nav items, compact lists, metadata
text-[13px]13pxrelaxedQuick Ask messages, inline content
text-xs0.75rem (12px)1remSmall supporting text
text-sm0.875rem (14px)1.25remDefault body text, inputs
text-base1rem (16px)1.5remStandard paragraph text
text-lg1.125rem (18px)1.75remPage titles in headers
text-xl--text-7xl1.5rem--4.5remvaries-0.02em to -0.03emHeadlines, hero text

Weight Conventions

WeightTokenUsage
400font-normalBody text, descriptions
500font-mediumNav items, sidebar labels, form labels
600font-semiboldSection headers, page titles
700font-boldEmphasis, hero headings
800font-extraboldMarketing headlines

Section Headers (Sidebar)

text-[10px] font-semibold uppercase tracking-[0.2em] text-gray-400 dark:text-gray-500

Used for labels like "SETTINGS", "Pinned", "Private Chats", "Projects".


Backgrounds and Surfaces

App Shell Ambient Background

The authenticated app shell uses a layered gradient that gives a frosted macOS-like feel. This is the .app-shell class.

Light mode:

css
background:
  radial-gradient(circle at top left, rgba(56, 189, 248, 0.14), transparent 32%),
  radial-gradient(circle at top right, rgba(244, 114, 182, 0.14), transparent 28%),
  linear-gradient(180deg, #f7f8fb 0%, #f2f4f8 100%);

Three layers:

  1. Cyan glow — top-left corner, rgba(56, 189, 248, 0.14), 32% spread
  2. Pink glow — top-right corner, rgba(244, 114, 182, 0.14), 28% spread
  3. Cool gray base — vertical gradient from #f7f8fb to #f2f4f8

Plus a ::before pseudo-element white wash:

css
background: linear-gradient(180deg, rgba(255, 255, 255, 0.26), transparent 30%);

Dark mode: Same gradient positions, but drastically reduced opacity:

  • Cyan glow at 0.07 opacity (halved from light)
  • Pink glow at 0.07 opacity
  • Base gradient from #0f1016 to #0a0a0f
  • White wash at 0.03 opacity

When to Use Which Surface

ContextLightDarkCSS Class
Authenticated root shellAmbient gradientDark ambient gradient.app-shell
SidebarFrosted translucentFrosted dark translucent.app-shell-sidebar
Content panel (headerless)Frosted translucentFrosted dark translucent.app-shell-panel
Cards, modals#FFFFFF#111118.card or bg-background
Hover fills (in shell)bg-white/60bg-white/[0.06]inline
Hover fills (in cards)bg-background-tertiarybg-dark-background-tertiaryinline
Marketing/homepagebg-whitenot .app-shell

Frosted Glass and Depth

The app uses backdrop blur extensively to create depth. There are three tiers:

Tier 1: Shell Sidebar (.app-shell-sidebar)

css
background: linear-gradient(180deg, rgba(255,255,255,0.86) 0%, rgba(248,249,252,0.72) 100%);
backdrop-filter: blur(22px) saturate(1.12);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.82), 0 8px 24px rgba(15,23,42,0.05);

Tier 2: Shell Panel (.app-shell-panel)

css
background: linear-gradient(180deg, rgba(255,255,255,0.92) 0%, rgba(249,250,252,0.84) 100%);
backdrop-filter: blur(28px) saturate(1.18);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.92), 0 12px 32px rgba(15,23,42,0.08);

Tier 3: Notification Cards (.notif-card)

css
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(40px) saturate(180%);

Glass Card (simple)

css
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(12px);

Key Rules

  • Always include -webkit-backdrop-filter alongside backdrop-filter for Safari.
  • Translucent surfaces require the parent to have a visible background (gradient, image, or color) for the blur to have something to blur.
  • saturate() values above 1.0 make blurred content more vivid, mimicking macOS vibrancy.
  • The inset 0 1px 0 highlight simulates a thin light edge at the top of frosted panels.

Borders and Dividers

In the Frosted Shell

Use translucent borders that adapt to the frosted background:

border-black/[0.06]   (light mode)
border-white/[0.06]   (dark mode)

Alternatively, use token borders at reduced opacity:

border-border/60 dark:border-dark-border/60

In Opaque Content Areas

Use the full token:

border-border dark:border-dark-border

Dividers

  • Horizontal rule between sidebar sections: border-t border-black/[0.06] dark:border-white/[0.06]
  • Between cards or content blocks: border-b border-border dark:border-dark-border

Shadows

Elevation Levels

LevelShadowUsage
NoneFlat items, inactive states
Subtleshadow-smCards at rest, secondary buttons
Mediumshadow-mdHovered cards, dropdowns
Active nav pillshadow-[0_6px_18px_rgba(15,23,42,0.06)]Active sidebar item (light)
Active nav pill (dark)shadow-[0_6px_18px_rgba(0,0,0,0.2)]Active sidebar item (dark)
Session item activeshadow-[0_4px_12px_rgba(15,23,42,0.05)]Active session row (light)
Frosted panel0 12px 32px rgba(15,23,42,0.08).app-shell-panel
Primary button0 10px 24px rgba(255,0,102,0.22).quick-primary-btn
Mockup0 25px 50px -12px rgba(0,0,0,0.12)Marketing screenshots

Inset Highlights

Frosted panels use an inset highlight to simulate a lit top edge:

css
inset 0 1px 0 rgba(255, 255, 255, 0.82)   /* sidebar */
inset 0 1px 0 rgba(255, 255, 255, 0.92)   /* panel */

Dark mode:

css
inset 0 1px 0 rgba(255, 255, 255, 0.04)   /* sidebar */
inset 0 1px 0 rgba(255, 255, 255, 0.05)   /* panel */

Scrollbars

Global Default (Web Client)

css
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { @apply bg-border/50 rounded-full; }

Inside the App Shell

Narrower and more neutral to match the frosted aesthetic:

css
.app-shell ::-webkit-scrollbar { width: 5px; height: 5px; }
.app-shell ::-webkit-scrollbar-thumb { background: rgba(120, 120, 128, 0.28); border-radius: 3px; }
.app-shell ::-webkit-scrollbar-thumb:hover { background: rgba(120, 120, 128, 0.42); }

Desktop App

css
::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.12); border-radius: 3px; }

Expanded Sidebar Structure

aside.app-shell-sidebar  (frosted translucent background)
  div                    (transparent, no background override)
    Logo + collapse toggle
    Nav links            (rounded-2xl pill items)
    Search divider
    Session list         (scrollable, grouped)
    User footer          (avatar + menu)

All nav items use this base pattern:

gap-2.5 px-3 py-2.5 text-[12px] font-medium rounded-2xl transition-all duration-200

Inactive state:

text-gray-500 dark:text-gray-400
hover:text-gray-800 dark:hover:text-gray-200
hover:bg-white/60 dark:hover:bg-white/[0.06]

Active state (white elevated pill):

bg-white dark:bg-white/[0.1]
text-gray-900 dark:text-gray-100
shadow-[0_6px_18px_rgba(15,23,42,0.06)] dark:shadow-[0_6px_18px_rgba(0,0,0,0.2)]
border border-black/[0.04] dark:border-white/[0.06]

Active icon color: text-accent (the pink)

Inactive icon color: text-gray-400 dark:text-gray-500

Session Items

Same pattern as nav items but slightly lighter shadows:

  • Active: shadow-[0_4px_12px_rgba(15,23,42,0.05)]
  • Text size: text-[12px]
  • Shape: rounded-2xl

Section Headers

text-[10px] font-semibold uppercase tracking-[0.2em]
text-gray-400 dark:text-gray-500

Badge / Count Pills

px-1.5 py-0.5 text-[10px] font-medium rounded-full
bg-black/[0.04] dark:bg-white/[0.06]

Collapsed Sidebar

Same frosted background (inherited from aside), with:

  • w-14 fixed width
  • Icon-only buttons at p-2 rounded-xl
  • Active state: same white elevated card, rounded-xl

Buttons

Primary (.btn-primary / .quick-primary-btn)

Standard:

bg-accent text-white hover:bg-accent-hover shadow-sm hover:shadow-md rounded-lg

Desktop gradient variant:

css
background: linear-gradient(135deg, #ff0066 0%, #ff5b9b 100%);
box-shadow: 0 10px 24px rgba(255, 0, 102, 0.22);

Hover: filter: brightness(1.02), shadow intensifies.

Secondary (.btn-secondary / .quick-secondary-btn)

Standard:

bg-transparent border border-border hover:bg-background-tertiary rounded-lg

Frosted variant:

css
background: rgba(255, 255, 255, 0.72);
border: 1px solid rgba(15, 23, 42, 0.08);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);

Ghost (.btn-ghost)

bg-transparent hover:bg-background-tertiary rounded-lg

Danger (.btn-danger / .quick-danger-btn)

Standard:

bg-error text-white hover:bg-red-600 rounded-lg

Frosted variant:

css
background: rgba(255, 241, 242, 0.82);
border: 1px solid rgba(244, 63, 94, 0.16);
color: #e11d48;

Button Sizing

All buttons use px-4 py-2 font-medium as the base. Minimum touch target is 44px (min-h-touch min-w-touch).


Inputs and Forms

Text Input (.input)

w-full px-3 py-2.5 rounded-lg
border border-border dark:border-dark-border
bg-background dark:bg-dark-background
text-foreground dark:text-dark-foreground
placeholder:text-foreground-muted
focus:ring-2 focus:ring-accent/50 focus:border-accent

Focus States

focus:outline-none focus:ring-2 focus:ring-accent/50 focus:ring-offset-2

The focus:ring-offset should use the current surface color to avoid a gap:

focus:ring-offset-background dark:focus:ring-offset-dark-background

Cards

Standard Card (.card)

bg-background dark:bg-dark-background-secondary
border border-border dark:border-dark-border
rounded-xl shadow-sm
hover:shadow-md transition-shadow duration-200

Notification Card (.notif-card)

Frosted, iOS-style:

css
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(40px) saturate(180%);
box-shadow: 0 0.5px 0 0 rgba(0,0,0,0.04), 0 1px 3px 0 rgba(0,0,0,0.04), 0 4px 12px 0 rgba(0,0,0,0.03);

Active press: transform: scale(0.985) with 0.12s ease.

Urgent variant: background: rgba(255, 240, 240, 0.8)

Floating Chat Column

When side panels are open, the chat column becomes a floating card:

rounded-[24px] border border-border/70 dark:border-dark-border/70
bg-background dark:bg-dark-background
shadow-[0_10px_30px_rgba(0,0,0,0.08)] dark:shadow-[0_18px_42px_rgba(0,0,0,0.28)]

Message Bubbles

User Message (.message-user)

bg-accent text-white rounded-2xl rounded-br-md max-w-[70%] ml-auto shadow-sm

Assistant Message (.message-assistant)

bg-background-tertiary dark:bg-dark-background-tertiary rounded-2xl rounded-bl-md

Mention Pills (.mention-pill)

inline-block px-1 py-0 rounded font-semibold text-accent
background: rgba(255, 0, 102, 0.1)

Inside user messages: text-white, background: rgba(255, 255, 255, 0.25)


Icons

Style

All icons use 24x24 SVG stroke icons at stroke-width="2" with stroke-linecap="round" and stroke-linejoin="round". Display size is typically w-4 h-4 (16px).

Color Conventions

ContextLightDark
Active nav icontext-accent (#FF0066)text-accent
Inactive nav icontext-gray-400text-gray-500
Inline content iconopacity-60 on currentColorsame
Destructive actiontext-foreground-muted hover text-errorsame
Success indicatortext-green-500 or text-green-600same
Status dot (connected)bg-green-500same
Status dot (connecting)bg-amber-400 animate-pulsesame
Status dot (disconnected)bg-gray-400same

App Icon Mask

For marketplace agent icons, use the iOS-style superellipse:

css
.app-icon-mask {
  -webkit-mask-image: url("data:image/svg+xml,...rect rx='22' ry='22'...");
  mask-size: cover;
}

Animations and Transitions

Global Theme Transition

All elements transition background-color and border-color with 150ms ease for smooth dark mode toggling.

Standard Durations

TokenDurationUsage
duration-fast / fast100msMicro-interactions, icon swaps
duration-normal / normal200msHover states, sidebar transitions
duration-slow / slow300msPanel reveals, expanded content

Named Animations

ClassEffectDurationUsage
.animate-fade-intranslateY(6px) + opacity0.25s ease-outContent appearing
.animate-fadeInscale(0.95) + opacity0.4s ease-outModal/card entrance
.animate-pulseTailwind pulseTyping cursor
.animate-pulse-dotopacity 1 to 0.42s ease-in-out infiniteStatus indicators
.jelly-bouncescale bounce sequence0.6s ease-outInstall success icon
.stagger-fade-intranslateY(8px) + opacity0.4s ease-outList items (use animation-delay)
.skeletongradient shimmer left-right1.5s ease-in-out infiniteLoading placeholders
.processing-shimmergradient shimmer2s ease-in-out infiniteProcessing states
.hover-lifttranslateY(-2px) + shadow200msCard hover uplift

Siri-Style Aura (.siri-aura)

Rotating conic gradient ring around the input area during AI processing:

css
conic-gradient(from 120deg at 50% 50%,
  rgba(56, 189, 248, 0.55),   /* cyan */
  rgba(167, 139, 250, 0.65),  /* purple */
  rgba(244, 114, 182, 0.55),  /* pink */
  rgba(52, 211, 153, 0.5),    /* green */
  rgba(56, 189, 248, 0.55)    /* cyan repeat */
);
animation: siri-spin 5s linear infinite;
filter: blur(14px);

When waiting: adds siri-breathe (opacity 0.35 to 0.65 + scale pulse at 2.2s).

Reduced Motion

css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

Dark Mode

Activation

Dark mode is toggled via the .dark class on <html>, controlled by:

  1. localStorage.getItem('darkMode')
  2. Fallback to prefers-color-scheme: dark

Pattern

All dark mode styles use the .dark ancestor selector pattern. In Tailwind classes: dark:bg-dark-background. In raw CSS: .dark .my-class { ... }.

Dark Mode Ambient Shell

The app shell gradient dims the accent glows to ~7% opacity and uses deep dark base colors:

  • Base: #0f1016 to #0a0a0f
  • White wash: rgba(255, 255, 255, 0.03)

Frosted surfaces in dark mode use translucent dark fills:

  • Sidebar: rgba(17, 17, 24, 0.88) to rgba(14, 14, 20, 0.80)
  • Panel: rgba(17, 17, 24, 0.92) to rgba(14, 14, 20, 0.84)

Dark Mode Sidebar Items

  • Inactive: text-gray-400, hover text-gray-200, hover bg-white/[0.06]
  • Active: bg-white/[0.1], text-gray-100, shadow-[0_6px_18px_rgba(0,0,0,0.2)], border-white/[0.06]

Electron / Desktop Specifics

Traffic Light Clearance

When running in Electron (detected via navigator.userAgent.includes('Electron')), the pie-desktop class is added to <html>. This triggers:

css
.pie-desktop body::before {
  content: '';
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 38px;
  z-index: 9999;
  -webkit-app-region: drag;
}

.pie-desktop aside,
.pie-desktop main {
  padding-top: 38px;
}

Drag Regions

  • .drag-region / -webkit-app-region: drag — enables window dragging
  • .no-drag / -webkit-app-region: no-drag — prevents dragging on interactive elements

Every button, input, toggle, and scrollable area inside a drag region must have .no-drag or equivalent.

Desktop Sidebar Chrome

The desktop app's settings sidebar uses:

w-[228px] border-r border-black/[0.06] bg-white/[0.34] backdrop-blur-[24px]

With a 72px drag region header for the macOS traffic lights.

Desktop Body Defaults

html
<body class="bg-surface-secondary text-gray-900 overflow-hidden select-none">

overflow-hidden and select-none are appropriate for the desktop app but should NOT be used on the web client.


Accessibility

Touch Targets

Minimum 44x44px for all interactive elements:

min-h-touch (44px)
min-w-touch (44px)

Focus Visibility

focus:outline-none focus:ring-2 focus:ring-accent/50 focus:ring-offset-2

Color Contrast

  • Primary text (#171717 on #FFFFFF): 15.4:1 ratio
  • Muted text (#A3A3A3 on #FFFFFF): 2.6:1 — used only for supporting/non-essential text
  • Accent (#FF0066 on #FFFFFF): 4.6:1 — meets AA for large text; use with font-medium or larger
  • Dark mode primary text (#F0F0F5 on #0A0A0F): 17.6:1

Selection

css
::selection {
  background: rgba(255, 0, 102, 0.2);  /* accent/20 */
}

Reduced Motion

All animations respect prefers-reduced-motion: reduce (see Animations section).


File Reference

FilePurpose
client/tailwind.config.jsColor tokens, font families, type scale, animations, spacing
client/src/styles/globals.cssBase styles, component classes (buttons, cards, shell, etc.), utility classes
client/index.htmlGoogle Fonts loading, initial body classes
client/src/main.tsxDark mode toggle, Electron detection
client/src/components/Layout.tsxAuthenticated app shell (sidebar + main)
client/src/components/SessionSidebar.tsxSidebar nav items, session list, user menu
client/src/pages/Chat.tsxChat page layout (independent shell)
client/src/pages/Quick.tsxQuick Ask (Electron vs web shell)
desktop-app/index.htmlDesktop Tailwind config, surface/pie tokens
desktop-app/src/renderer/styles.cssDesktop frost classes, animations, scrollbars
desktop-app/src/renderer/pages/settings-v2.tsDesktop sidebar reference implementation

Built with VitePress