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
- Brand Color Palette
- Typography
- Backgrounds and Surfaces
- Frosted Glass and Depth
- Borders and Dividers
- Shadows
- Scrollbars
- Navigation and Sidebar
- Buttons
- Inputs and Forms
- Cards
- Message Bubbles
- Icons
- Animations and Transitions
- Dark Mode
- Electron / Desktop Specifics
- Accessibility
- 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.
| Token | Hex | Usage |
|---|---|---|
pie-50 / accent-light | #FFE5EF | Light accent backgrounds, hover tints |
pie-100 | #FFCCE0 | Subtle accent fills |
pie-200 | #FF99C2 | — |
pie-300 | #FF66A3 | — |
pie-400 | #FF3385 | Hover state for primary buttons |
pie-500 / accent | #FF0066 | Primary accent, active icons, primary buttons |
pie-600 | #E6005C | — |
pie-700 | #CC0052 | — |
pie-800 | #990040 | — |
pie-900 | #66002B | — |
Neutral Surfaces (Light Mode)
| Token | Hex | Usage |
|---|---|---|
background | #FFFFFF | Primary content background, cards |
background-secondary / surface-secondary | #FAFAFA | Page-level fallback, inset panels |
background-tertiary / surface-tertiary | #F5F5F5 | Hover fills, skeleton base, subtle containers |
Neutral Surfaces (Dark Mode)
| Token | Hex | Usage |
|---|---|---|
dark-background | #0A0A0F | Root page background |
dark-background-secondary | #111118 | Inset panels, sidebar fallback |
dark-background-tertiary | #1A1A24 | Hover fills, card backgrounds |
Foreground / Text
| Token | Light | Dark | Usage |
|---|---|---|---|
foreground | #171717 | #F0F0F5 | Primary body text |
foreground-secondary | #525252 | #8888A0 | Secondary/supporting text |
foreground-muted | #A3A3A3 | #525260 | Placeholder, disabled, tertiary text |
Semantic Colors
| Token | Hex | Usage |
|---|---|---|
success | #22C55E | Success indicators, confirmation |
error | #EF4444 | Error states, destructive actions |
warning | #F59E0B | Warning indicators |
Borders
| Token | Light | Dark | Usage |
|---|---|---|---|
border | #E5E5E5 | #262626 | Default border, dividers |
border-subtle | #D4D4D4 | #404040 | Stronger borders for emphasis |
Sidebar and Shell Borders
Within the frosted app shell, prefer translucent borders over opaque tokens:
- Light:
border-black/[0.06]orborder-border/60 - Dark:
border-white/[0.06]orborder-dark-border/60
Typography
Font Families
| Purpose | Stack | Token |
|---|---|---|
| UI text | 'Inter', system-ui, -apple-system, sans-serif | font-sans |
| Code / mono | 'JetBrains Mono', Menlo, Monaco, monospace | font-mono |
| Desktop notes editor | -apple-system, BlinkMacSystemFont, 'Inter', sans-serif | inline |
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:
font-feature-settings: 'cv11', 'ss01';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;cv11gives Inter an alternate lowercaselthat's easier to distinguish from1andI.ss01enables Inter's alternate digit forms for better readability.
Type Scale
| Name | Size | Line Height | Letter Spacing | Usage |
|---|---|---|---|---|
text-[10px] | 10px | — | tracking-[0.15em]--tracking-[0.2em] | Section headers, labels, badges, timestamps |
text-[12px] | 12px | — | — | Sidebar nav items, compact lists, metadata |
text-[13px] | 13px | relaxed | — | Quick Ask messages, inline content |
text-xs | 0.75rem (12px) | 1rem | — | Small supporting text |
text-sm | 0.875rem (14px) | 1.25rem | — | Default body text, inputs |
text-base | 1rem (16px) | 1.5rem | — | Standard paragraph text |
text-lg | 1.125rem (18px) | 1.75rem | — | Page titles in headers |
text-xl--text-7xl | 1.5rem--4.5rem | varies | -0.02em to -0.03em | Headlines, hero text |
Weight Conventions
| Weight | Token | Usage |
|---|---|---|
| 400 | font-normal | Body text, descriptions |
| 500 | font-medium | Nav items, sidebar labels, form labels |
| 600 | font-semibold | Section headers, page titles |
| 700 | font-bold | Emphasis, hero headings |
| 800 | font-extrabold | Marketing headlines |
Section Headers (Sidebar)
text-[10px] font-semibold uppercase tracking-[0.2em] text-gray-400 dark:text-gray-500Used 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:
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:
- Cyan glow — top-left corner,
rgba(56, 189, 248, 0.14), 32% spread - Pink glow — top-right corner,
rgba(244, 114, 182, 0.14), 28% spread - Cool gray base — vertical gradient from
#f7f8fbto#f2f4f8
Plus a ::before pseudo-element white wash:
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.07opacity (halved from light) - Pink glow at
0.07opacity - Base gradient from
#0f1016to#0a0a0f - White wash at
0.03opacity
When to Use Which Surface
| Context | Light | Dark | CSS Class |
|---|---|---|---|
| Authenticated root shell | Ambient gradient | Dark ambient gradient | .app-shell |
| Sidebar | Frosted translucent | Frosted dark translucent | .app-shell-sidebar |
| Content panel (headerless) | Frosted translucent | Frosted dark translucent | .app-shell-panel |
| Cards, modals | #FFFFFF | #111118 | .card or bg-background |
| Hover fills (in shell) | bg-white/60 | bg-white/[0.06] | inline |
| Hover fills (in cards) | bg-background-tertiary | bg-dark-background-tertiary | inline |
| Marketing/homepage | bg-white | — | not .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)
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)
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)
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(40px) saturate(180%);Glass Card (simple)
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(12px);Key Rules
- Always include
-webkit-backdrop-filteralongsidebackdrop-filterfor 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 0highlight 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/60In Opaque Content Areas
Use the full token:
border-border dark:border-dark-borderDividers
- 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
| Level | Shadow | Usage |
|---|---|---|
| None | — | Flat items, inactive states |
| Subtle | shadow-sm | Cards at rest, secondary buttons |
| Medium | shadow-md | Hovered cards, dropdowns |
| Active nav pill | shadow-[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 active | shadow-[0_4px_12px_rgba(15,23,42,0.05)] | Active session row (light) |
| Frosted panel | 0 12px 32px rgba(15,23,42,0.08) | .app-shell-panel |
| Primary button | 0 10px 24px rgba(255,0,102,0.22) | .quick-primary-btn |
| Mockup | 0 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:
inset 0 1px 0 rgba(255, 255, 255, 0.82) /* sidebar */
inset 0 1px 0 rgba(255, 255, 255, 0.92) /* panel */Dark mode:
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)
::-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:
.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
::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.12); border-radius: 3px; }Navigation and Sidebar
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)Nav Item Styling
All nav items use this base pattern:
gap-2.5 px-3 py-2.5 text-[12px] font-medium rounded-2xl transition-all duration-200Inactive 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-500Badge / 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-14fixed 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-lgDesktop gradient variant:
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-lgFrosted variant:
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-lgDanger (.btn-danger / .quick-danger-btn)
Standard:
bg-error text-white hover:bg-red-600 rounded-lgFrosted variant:
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-accentFocus States
focus:outline-none focus:ring-2 focus:ring-accent/50 focus:ring-offset-2The focus:ring-offset should use the current surface color to avoid a gap:
focus:ring-offset-background dark:focus:ring-offset-dark-backgroundCards
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-200Notification Card (.notif-card)
Frosted, iOS-style:
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-smAssistant Message (.message-assistant)
bg-background-tertiary dark:bg-dark-background-tertiary rounded-2xl rounded-bl-mdMention 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
| Context | Light | Dark |
|---|---|---|
| Active nav icon | text-accent (#FF0066) | text-accent |
| Inactive nav icon | text-gray-400 | text-gray-500 |
| Inline content icon | opacity-60 on currentColor | same |
| Destructive action | text-foreground-muted hover text-error | same |
| Success indicator | text-green-500 or text-green-600 | same |
| Status dot (connected) | bg-green-500 | same |
| Status dot (connecting) | bg-amber-400 animate-pulse | same |
| Status dot (disconnected) | bg-gray-400 | same |
App Icon Mask
For marketplace agent icons, use the iOS-style superellipse:
.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
| Token | Duration | Usage |
|---|---|---|
duration-fast / fast | 100ms | Micro-interactions, icon swaps |
duration-normal / normal | 200ms | Hover states, sidebar transitions |
duration-slow / slow | 300ms | Panel reveals, expanded content |
Named Animations
| Class | Effect | Duration | Usage |
|---|---|---|---|
.animate-fade-in | translateY(6px) + opacity | 0.25s ease-out | Content appearing |
.animate-fadeIn | scale(0.95) + opacity | 0.4s ease-out | Modal/card entrance |
.animate-pulse | Tailwind pulse | — | Typing cursor |
.animate-pulse-dot | opacity 1 to 0.4 | 2s ease-in-out infinite | Status indicators |
.jelly-bounce | scale bounce sequence | 0.6s ease-out | Install success icon |
.stagger-fade-in | translateY(8px) + opacity | 0.4s ease-out | List items (use animation-delay) |
.skeleton | gradient shimmer left-right | 1.5s ease-in-out infinite | Loading placeholders |
.processing-shimmer | gradient shimmer | 2s ease-in-out infinite | Processing states |
.hover-lift | translateY(-2px) + shadow | 200ms | Card hover uplift |
Siri-Style Aura (.siri-aura)
Rotating conic gradient ring around the input area during AI processing:
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
@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:
localStorage.getItem('darkMode')- 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:
#0f1016to#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)torgba(14, 14, 20, 0.80) - Panel:
rgba(17, 17, 24, 0.92)torgba(14, 14, 20, 0.84)
Dark Mode Sidebar Items
- Inactive:
text-gray-400, hovertext-gray-200, hoverbg-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:
.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
<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-2Color Contrast
- Primary text (
#171717on#FFFFFF): 15.4:1 ratio - Muted text (
#A3A3A3on#FFFFFF): 2.6:1 — used only for supporting/non-essential text - Accent (
#FF0066on#FFFFFF): 4.6:1 — meets AA for large text; use withfont-mediumor larger - Dark mode primary text (
#F0F0F5on#0A0A0F): 17.6:1
Selection
::selection {
background: rgba(255, 0, 102, 0.2); /* accent/20 */
}Reduced Motion
All animations respect prefers-reduced-motion: reduce (see Animations section).
File Reference
| File | Purpose |
|---|---|
client/tailwind.config.js | Color tokens, font families, type scale, animations, spacing |
client/src/styles/globals.css | Base styles, component classes (buttons, cards, shell, etc.), utility classes |
client/index.html | Google Fonts loading, initial body classes |
client/src/main.tsx | Dark mode toggle, Electron detection |
client/src/components/Layout.tsx | Authenticated app shell (sidebar + main) |
client/src/components/SessionSidebar.tsx | Sidebar nav items, session list, user menu |
client/src/pages/Chat.tsx | Chat page layout (independent shell) |
client/src/pages/Quick.tsx | Quick Ask (Electron vs web shell) |
desktop-app/index.html | Desktop Tailwind config, surface/pie tokens |
desktop-app/src/renderer/styles.css | Desktop frost classes, animations, scrollbars |
desktop-app/src/renderer/pages/settings-v2.ts | Desktop sidebar reference implementation |