
Casper Design System
- 255 installs
- 11 repo stars
- Updated August 4, 2026
- casper-studios/casper-marketplace
casper-design-system is a Claude Code agent skill that generates consistent production-grade SaaS UI using shadcn/ui, Tailwind CSS v4, and React with Vite for developers building dashboards, internal tools, and client ap
About
An internal design system that enforces consistent, production-grade SaaS UI using shadcn/ui, Tailwind CSS v4, and React with required component and theme references. A developer uses it whenever generating on-brand Casper Studios interfaces, dashboards, or prototypes.
- Built on shadcn/ui, Tailwind CSS v4, and React (Vite)
- Required references for reusable components and copy-in theme tokens
Casper Design System by the numbers
- 255 all-time installs (skills.sh)
- Ranked #866 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/casper-studios/casper-marketplace --skill casper-design-systemAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 255 |
|---|---|
| repo stars | ★ 11 |
| Last updated | August 4, 2026 |
| Repository | casper-studios/casper-marketplace ↗ |
How do you generate consistent SaaS UI with Casper design system?
Generates consistent SaaS UI following Casper Studios' internal design system built on shadcn/ui, Tailwind CSS v4, and React (Vite).
Who is it for?
Developers building Casper Studios SaaS dashboards, internal tools, or client prototypes who need shadcn/ui and Tailwind v4 consistency.
Skip if: Non-React stacks, projects without shadcn/ui, or teams that need a framework-agnostic design token export only.
When should I use this skill?
The user builds React UI, mentions Casper style or our design system, or needs dashboards and prototypes matching Casper visual language.
What you get
On-brand React components and pages using shadcn/ui, theme.css tokens, platform layout patterns, and correct logo variants.
- Branded React components
- theme.css token file
- Platform-specific page layouts
By the numbers
- Includes 4 Casper Studios logo SVG variants in assets/
- Requires 2 universal reference files plus 1 platform-specific layout file
Files
Casper Studios Design System
A clean, elevated SaaS design system built on shadcn/ui, Tailwind CSS v4, and React (Vite). Every interface generated for Casper Studios — whether a client demo, internal tool, or quick prototype — must follow these rules to maintain a consistent, professional visual identity across the team.
Before generating any UI code, read this file completely and the reference files listed below. You MUST read the reference files — they contain rules and code examples that are required for correct output. Skipping them will produce incorrect, off-brand UI.
Required for EVERY project:
- `references/components.md` — ALWAYS read. Reusable pieces: stat cards, list items, filter bars, kanban boards, profile cards, product cards, activity feeds, toast notifications, form validation states. Required whenever building UI elements inside a layout.
- `references/theme.css` — ALWAYS read. Tailwind CSS v4 theme tokens. Copy this file into your project as-is.
- `assets/` — Contains Casper Studios logo SVGs in 4 variants (default, variant, mono-black, mono-white). Use the correct variant based on background color — see the Logo section below.
Required based on platform:
- `references/web-layouts.md` — MUST read when the project is a web application. Web-specific responsive rules + code examples: app shell, sidebar nav, dashboard grid, data table page, page header.
- `references/mobile.md` — MUST read when the project is a mobile application. Mobile-specific rules + code examples: device frame, top bar, bottom tab navigation, form patterns, pinned-button layout, list views, card stacks, full screen compositions, contextual actions (menus + bottom sheets).
Non-negotiable: Do not generate UI without reading the platform reference file first. If you are unsure whether the project is web or mobile, ask the user before proceeding.
---
Before You Generate: Required Context
Before producing any UI code, confirm the following with the user. If their prompt already answers these clearly, proceed without asking. If not, ask before generating anything.
1. Platform — Is this a web application or a mobile application? (Determines which reference file to follow.) 2. Dark mode — Should the interface support dark mode, or is it light mode only? (Determines whether to implement .dark class overrides, use bg-neutral-0 for surfaces, and include the mono-white logo variant.)
Do NOT assume defaults for these. If the user says "build me a dashboard," you don't know if it's web or mobile, or if it needs dark mode. Ask.
---
Summary
The Casper aesthetic is clean authority — a professional SaaS style that feels premium without trying too hard. It uses generous whitespace, a restrained purple accent, and soft rounded surfaces to create interfaces that feel trustworthy and modern. Think Linear meets Notion: structured, breathable, quietly confident.
---
Core Principles
1. Whitespace is a feature. Generous padding, breathing room between sections. Never cram. 2. One accent, used sparingly. Brand purple (#5900FF) appears on active states, primary buttons, and key CTAs — nowhere else. If everything is purple, nothing is. 3. Rounded but not bubbly. 10px default radius for cards (shadcn default). Buttons and inputs use 8px. Feels modern without feeling like a toy. 4. Flat with depth hints. No heavy shadows. Use shadow-sm for cards, shadow-md for popovers. Never use shadow-lg on in-page elements. 5. Content over chrome. The UI should disappear. Users notice the data, not the design.
---
Tech Stack
- React (Vite) with TypeScript
- Tailwind CSS v4 — Use the theme file at
references/theme.cssas Casper brand overrides - shadcn/ui — Initialize a standard shadcn/ui project first (
shadcn init), then layer Casper brand tokens fromreferences/theme.csson top. The shadcn semantic layer (bg-background,text-foreground,bg-primary,border-border, etc.) is the base — Casper's theme.css adds brand colors, typography, shadows, and spacing on top of it, not as a replacement. Use shadcn components directly. Do NOT create custom base components that duplicate shadcn functionality - Lucide React — Icon library. Always use Lucide, never Heroicons or FontAwesome
- Fonts —
DM Sanswithsans-serifas fallback for all UI text. Load via Google Fonts or bundle
---
Color System
The palette is intentionally restrained. Most of the UI is neutral gray + white, with purple as a sharp accent.
Usage Rules
| Role | Token | Hex | When to use |
|---|---|---|---|
| Brand accent | brand-500 | #5900FF | Active nav items, primary buttons, links, focus rings |
| Brand subtle | brand-50 | #EEE5FF | Active nav background, selected row highlight, hover tints |
| Brand light | brand-100 | #DECCFF | Icon circle backgrounds, soft tag fills |
| Default text | neutral-950 | #0A0A0A | Page titles, headings |
| Body text | neutral-900 | #171717 | Primary body text |
| Subtext | neutral-500 | #737373 | Metadata, timestamps, secondary labels |
| Borders | neutral-200 | #E5E5E5 | Card borders, dividers, table lines |
| Surface | neutral-50 | #FAFAFA | Page background behind cards |
| Card surface | neutral-0 | #FFFFFF | Card backgrounds, panels (use bg-neutral-0 for dark mode compatibility — see Dark Mode section) |
Semantic Colors
Use these ONLY for status indicators, badges, and contextual feedback — never as decorative accents.
- Success —
success-500(#22C55E) for badges/icons,success-50for pill backgrounds - Error —
error-500(#EF4444) for badges/icons,error-50for pill backgrounds - Warning —
warning-500(#F59E0B) for badges/icons,warning-50for pill backgrounds
What NOT to do
- Do NOT use brand purple for backgrounds on large surfaces
- Do NOT use semantic colors decoratively
- Do NOT introduce new colors. If you need a new shade, use the neutral scale
- Do NOT use opacity-based colors when a token exists (e.g., don't do
text-black/50, useneutral-500)
---
Typography
All text is set in DM Sans with sans-serif as fallback (font-family: 'DM Sans', sans-serif). Monospace (font-mono) is acceptable for code blocks, data labels, and IDs only.
Scale
| Style | Size | Weight | Line Height | Use |
|---|---|---|---|---|
| Heading 1 | 30px | 500 | 36px | Page titles only. One per view. |
| Heading 2 | 20px | 500 | 24px | Section titles within a page |
| Heading 3 | 16px | 500 | 20px | Card titles, subsection labels |
| Body | 14px | 400 | 20px | Default paragraph and UI text |
| Body Bold | 14px | 500 | 20px | Emphasis within body text, table headers |
| Caption | 12px | 400 | 16px | Timestamps, helper text, metadata |
| Caption Bold | 12px | 500 | 16px | Badge labels, small category tags |
Rules
- Headings are always
mediumweight (500), never bold (700) - NEVER use all-caps except for tiny metadata labels (e.g., "STATUS", "OWNER" in table column headers) set at caption size
- Links use
brand-500color with no underline by default, underline on hover - Do NOT vary font size beyond this scale. If something feels wrong, adjust spacing not font size
---
Spacing & Layout
Spacing Scale
Use Tailwind's default spacing scale. Key values:
4px(p-1) — icon padding, tight gaps8px(p-2) — badge padding, small gaps12px(p-3) — input padding, card internal gaps16px(p-4) — card padding, section gaps24px(p-6) — between cards, content sections32px(p-8) — major section separation48px(p-12) — page-level padding on large screens
Layout Rules
- Page background:
neutral-50(#FAFAFA) - Content is always organized inside Cards (white background, border, rounded)
- Maximum content width:
1280pxcentered - On pages with a sidebar, sidebar is
240pxfixed width - Main content area uses remaining space with
24pxpadding
---
Shadows & Elevation
The design is predominantly flat. Shadows are used to indicate layers, not to add decoration.
| Token | Use |
|---|---|
shadow-sm | Cards, inputs at rest |
shadow-md | Dropdown menus, popovers, tooltips |
shadow-lg | Modals, command palettes, overlays ONLY |
shadow-overlay | Semantic alias for shadow-lg — identical value. Use on modals/sheets so the intent reads clearly in code |
NEVER apply shadow-lg (or its alias shadow-overlay) to cards or in-page elements. These are reserved for floating layers only.
---
Border Radius
The theme file (references/theme.css) uses the shadcn/ui radius system — a single --radius base variable in :root that controls the entire scale via calc(). This is mapped into Tailwind classes via @theme inline. No Tailwind v4 defaults are overridden.
| Token | Tailwind Class | Default Value | Use |
|---|---|---|---|
--radius-sm | rounded-sm | 6px | Inner elements, nav items, small nested controls |
--radius-md | rounded-md | 8px | Buttons, inputs, popovers, tooltips, chart tooltips |
--radius-lg | rounded-lg | 10px | Cards, panels, large containers |
--radius-xl | rounded-xl | 14px | Modal containers, dialogs, hero cards |
| (Tailwind built-in) | rounded-full | 9999px | Badges, pills, avatars, icon circles |
The base value --radius: 0.625rem (10px) is the shadcn default. To make the entire UI sharper or rounder, change this single value — all tokens recalculate automatically.
Cards always use rounded-lg (10px). Buttons and inputs always use rounded-md (8px). Nested elements inside cards should use rounded-sm (6px) for non-interactive elements to maintain visual hierarchy — the inner radius should always be smaller than the outer.
---
Iconography
- Use Lucide React exclusively. Import as:
import { IconName } from "lucide-react" - Default icon size:
16pxfor inline,20pxfor standalone - Icon color follows its text context (e.g.,
neutral-500for subtext,brand-500for active) - For icons inside circular backgrounds (common in lists and dashboards):
- Circle:
40pxdiameter,brand-50orbrand-100background,rounded-full - Icon:
20px,brand-500color - For semantic contexts, swap to the matching semantic color pair (e.g.,
error-50bg +error-500icon)
---
Casper Studios Logo
The Casper Studios logo has four variants stored in assets/. Use the correct variant based on the background it sits on:
| Variant | File | When to use |
|---|---|---|
| Default (full color) | assets/logo-on-white-default.svg | Light/white backgrounds. Gradient icon + purple "CASPER" + dark "STUDIOS". This is the primary logo — use it whenever possible. |
| Variant (full color) | assets/logo-on-white-variant.svg | Light/white backgrounds when you want all-black text instead of purple + dark gray. Same gradient icon. |
| Mono Black | assets/logo-mono-black.svg | Light backgrounds where color is unavailable (e.g., print, grayscale contexts). Grayscale icon + near-black text. |
| Mono White | assets/logo-mono-white.svg | Dark backgrounds only. Gray icon + white text. Use this whenever the logo sits on a dark surface (dark nav bars, dark hero sections, overlays, dark mode). |
Rules
- Light mode (default): Use
logo-on-white-default.svgorlogo-on-white-variant.svg - Dark mode or dark surfaces: Use
logo-mono-white.svg— never place the default or black logo on a dark background - The logo should appear in the sidebar header (web) or top bar (mobile) at the sizes defined in those patterns
- Do NOT resize the logo disproportionately — maintain the original aspect ratio
- Do NOT place the logo on busy backgrounds or low-contrast surfaces. If contrast is insufficient, use the mono variant that provides the best visibility
- Minimum clear space around the logo:
8pxon all sides
---
shadcn/ui Component Theming
Use shadcn/ui components as your base layer. Theme them using the CSS variables in references/theme.css. Here is how specific components should be configured:
Button
- Primary:
brand-500bg,text-white(literal white, not a token),rounded-md. Hover:brand-600. - Secondary: White bg,
neutral-200border,neutral-900text,rounded-md. Hover:neutral-50bg. - Ghost: No bg, no border.
neutral-600text,rounded-md. Hover:neutral-100bg. - Destructive:
error-500bg,text-white,rounded-md. - All buttons:
rounded-md(8px),14pxfont. - Default height:
48px(h-12). Use this on standalone pages, forms, modals, and any top-level content area. - Compact height:
36px(h-9). Use when the button lives inside a smaller container — cards, panels, table rows, sidebar nav, filter bars, inline actions. The tighter context calls for a tighter control.
Badge
rounded-full(pill shape). Height22px. Caption-bold text (12px, 500 weight).- Semantic badges: Use pastel bg + darker text. E.g., success badge =
success-50bg,success-700text. - Neutral badge:
neutral-100bg,neutral-700text. - Brand badge:
brand-50bg,brand-700text. - Always include a small dot or icon before the label when indicating status.
Card
- White background (
bg-neutral-0).1pxneutral-200border.rounded-lg(10px).shadow-sm. - Internal padding:
16pxminimum,24pxfor spacious cards. - Card headers:
Heading 3(16px/500) with optional "View all" link aligned right. - Separate header from content with a
1pxneutral-200divider.
Table
- Use shadcn
<Table>. No outer border on the table itself — let the parent Card provide the container. - Column headers: Caption-bold (12px/500),
neutral-500color, uppercase. - Row height:
48-56px. Rows separated by1pxneutral-200bottom border. - Row hover:
neutral-50background. - No alternating row colors.
Input / Textarea
rounded-md(8px).1pxneutral-200border.neutral-50bg or white bg.- Focus:
2pxbrand-500ring (use Tailwindring-2 ring-brand-500). - Labels MUST be visible and external — render a
<label>element above every input, never inside it. Labels use14px/400 inneutral-900. The gap between label and input is6px(space-y-1.5). - Placeholder text MUST be de-emphasized —
neutral-400color, normal weight (400), short hint text (e.g., "e.g. john@email.com"). Placeholders are supplementary hints, not labels. - Select inputs follow the same pattern: visible label above, de-emphasized placeholder inside.
- Spacing between fields:
16px(space-y-4orgap-4). - Default height:
48px(h-12). Use for inputs on standalone pages, forms, modals, and any top-level content area. - Compact height:
36px(h-9). Use when the input lives inside a smaller container — cards, panels, table rows, filter bars, inline search fields. Same logic as compact buttons.
Sidebar (App Shell)
See references/web-layouts.md for the full sidebar code pattern. Key specs:
- Width:
240px. White background. Right border:1pxneutral-200. - Nav items:
36pxheight (compact — inside sidebar panel),rounded-sm(6px),12pxleft padding. - Default:
neutral-600text. Active:brand-50bg,brand-500text,font-weight: 500. Hover:neutral-100bg. - Group labels: Caption (12px/400),
neutral-400,24pxtop margin between groups. - On mobile: Sidebar collapses to a
Sheet(slide-in from left).
Tabs
- Use shadcn
<Tabs>. Underline variant. - Active tab:
brand-500bottom border (2px),neutral-900text. - Inactive tab: no border,
neutral-500text. Hover:neutral-900text.
Dialog / Sheet
- Overlay:
black/50opacity. - Container: white bg,
rounded-xl(14px),shadow-overlay. - Always include a close button (X icon) top-right.
---
Composite Patterns
For rules and code examples, read the appropriate reference file:
- `references/web-layouts.md` — Responsive rules, App Shell, Sidebar Navigation, Dashboard Grid, Data Table Page, Page Header
- `references/components.md` — Stat Card, List Item Row, Filter Bar, Kanban Board, Profile/Discovery Card, Product Card, Activity Feed Item, Toast Notifications, Form Validation States
- `references/mobile.md` — Mobile rules, Device Frame Shell, Mobile Top Bar, Bottom Tab Navigation, Mobile Form Layout, Pinned Bottom Button, Mobile List View, Mobile Card Stack, Full Screen Composition, Contextual Actions
---
Web vs Mobile Context
Every project is either a web application or a mobile application — the layout approach is fundamentally different. You MUST have already read the appropriate platform reference file (as instructed at the top of this document) before reaching this point. If you haven't, stop and read it now:
- Web application →
references/web-layouts.md(responsive breakpoints, sidebar behavior, layout patterns) - Mobile application (native app, iOS, Android, phone-based experience) →
references/mobile.md(device frame, touch targets, pinned buttons, navigation, mobile-specific rules)
Do NOT treat a mobile app as a responsive web page. Mobile apps render inside a device frame and follow entirely different navigation, spacing, and interaction patterns.
---
Image Placeholders
When no real image is available, use soft gradient mesh backgrounds — NOT gray boxes. These should feel like abstract art, not loading states.
Gradient recipes (CSS linear-gradient or radial-gradient combos):
- Mint/Teal:
linear-gradient(135deg, #a8edea 0%, #fed6e3 50%, #a8edea 100%) - Peach/Coral:
linear-gradient(135deg, #f6d5c5 0%, #e8b4b8 50%, #d4a0a0 100%) - Purple/Pink:
linear-gradient(135deg, #c3b1e1 0%, #f0c4d0 50%, #e0aed0 100%) - Teal/Emerald:
linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)
Apply rounded-lg to image containers. These are placeholders — they should look intentional and beautiful.
---
Transitions & Animations
Every state change should feel smooth and intentional — no hard cuts. This applies across both web and mobile:
- Interactive elements (buttons, inputs, cards): Use
transition-colorsortransition-allwith Tailwind's default duration (~150ms). State changes like hover, focus, and active should never feel instantaneous. - Page transitions: When navigating between views, apply a subtle slide or fade. A
150–300msease-out transition keeps things feeling responsive without sluggish. - Modals, Dialogs, Sheets: Always animate in and out. Sheets slide from their edge (left, right, bottom), dialogs/modals fade + scale up slightly from ~95% to 100%. The overlay backdrop should fade in (
opacity 0→50%), not appear instantly. - Lists and content loading: When new items appear (e.g., after a fetch), a subtle fade-in or stagger is preferred over a hard pop-in.
Nothing on screen should ever just "appear" or "disappear" — every visual change gets a brief, smooth transition.
---
Empty, Error & Loading States
Every screen has at least three faces: populated, empty, and broken. AI-generated interfaces almost always show only the happy path with fake data. A polished interface acknowledges the other two.
Empty State
When a list, table, or content area has no data yet (first-time use, zero results, cleared filters):
- Center a Lucide icon (
48px,neutral-300) + a short heading (Heading 3) + one line of body text (neutral-500) vertically in the content area - Optionally include a primary action button below the text (e.g., "Create your first project")
- Do NOT show an empty table with headers and no rows — that looks like a bug, not a feature
- Do NOT use illustrations or complex graphics. Keep it text + icon. Minimal.
Error State
When something goes wrong (network failure, permission denied, server error):
- Same centered layout as empty state, but use
error-500for the icon color - Icon: contextual —
WifiOfffor network errors,ShieldXfor permission,AlertTriangleas a generic fallback - Heading: brief, human-readable ("Something went wrong", "Couldn't load projects")
- Body: one sentence explaining what to do ("Check your connection and try again")
- Include a "Retry" button (
secondaryvariant) when the action is retryable
Loading State
When content is being fetched:
- For initial page loads: Show a single centered spinner (
Loader2icon withanimate-spin,24px,neutral-400). No skeleton screens unless explicitly requested — they add complexity without clarifying the design - For inline updates (e.g., submitting a form, loading more items): Swap the trigger button's label to a spinner + "Loading…" and disable the button
- For pull-to-refresh or lazy loading: A small spinner at the top or bottom of the list,
neutral-400
The anti-pattern rule "No animated skeletons or shimmer effects in static mockups" still stands — but a simple animate-spin on a loader icon is fine for interactive interfaces.
---
Form Validation & Error Feedback
Inputs need to clearly communicate when something is wrong. This applies to both web and mobile. For code examples, see the Form Validation States section in references/components.md.
Behavioral Rules
- Validate on blur (when the user leaves the field), not on every keystroke — keystroke validation feels aggressive
- Show errors inline, directly below the relevant field. Never collect errors at the top of the form — users shouldn't have to hunt
- When the user corrects the input and the field is valid, remove the error state immediately (on change, not on blur)
- If the form is submitted with errors, scroll to the first invalid field and focus it
Success Confirmation (Post-Submit)
After a successful form submission, provide clear feedback. Don't just silently navigate away:
- Option A: Toast notification confirming the action + navigate to the next logical screen
- Option B: Inline success message replacing the form content (useful for single-purpose screens like "Reset password")
---
Toast Notifications
Transient feedback messages that confirm actions, surface errors, or provide information. Use shadcn's Sonner toast component. For code examples and the full variant table, see the Toast Notification section in references/components.md.
Positioning & Behavior
- Web: Bottom-right corner,
16pxfrom the edge - Mobile: Top-center, below the status bar / top bar area
- Duration:
4000msdefault,6000msfor messages with an action link. Error toasts should persist until dismissed - Stacking: Max 3 visible at once. New toasts push older ones up (web) or down (mobile)
- Animation: Slide in from the edge + fade. Slide out + fade on dismiss. Should feel like the Transitions & Animations section — smooth, never instant
---
Data Visualization Palette
When rendering charts (Recharts, Chart.js, etc.), use this ordered color sequence so data visualizations feel native to the brand. The palette starts with the brand purple and fans out through distinguishable hues:
| Order | Name | Hex | Use |
|---|---|---|---|
| 1 | Brand | #5900FF | Primary data series, single-metric charts |
| 2 | Teal | #14B8A6 | Secondary series |
| 3 | Amber | #F59E0B | Tertiary series |
| 4 | Rose | #F43F5E | Fourth series, or "negative" in comparisons |
| 5 | Sky | #0EA5E9 | Fifth series |
| 6 | Lime | #84CC16 | Sixth series |
Rules
- For single-metric charts (one bar, one line, one donut), always use
Brand(#5900FF) - For two-series comparisons, use
Brand+Teal - Apply colors in order — don't skip or shuffle. Consistency across charts makes dashboards feel cohesive
- Use
10%opacity fills for area charts (e.g.,#5900FF1Afor brand area fill) - Gridlines:
neutral-200. Axis labels:neutral-500, caption size (12px). Axis lines:neutral-300 - Tooltips: White bg,
shadow-md,rounded-md,neutral-200border — same treatment as popovers - Never use brand purple for "negative" or "declining" values — use Rose for that. Purple is always neutral-to-positive
---
Dark Mode
Dark mode is off by default. Only implement it when explicitly requested by the user or client. When dark mode is requested, follow these rules — do NOT improvise an inverted palette.
How It Works
The theme file (references/theme.css) includes a .dark class block that overrides CSS custom properties. Adding class="dark" to <html> or a wrapper element flips the entire color system without changing any component code. The neutral scale inverts so existing utilities (text-neutral-900, bg-neutral-50, border-neutral-200) automatically produce the correct dark-mode values.
Token Behavior in Dark Mode
You don't change token names in your code — the .dark override changes the values behind them. Here's what each token resolves to:
| Token (unchanged in code) | Light Mode Value | Dark Mode Value | Notes |
|---|---|---|---|
bg-neutral-50 | #FAFAFA (light gray) | #171717 (near-black) | Page background |
bg-neutral-0 | #FFFFFF (white) | #0A0A0A (near-black) | Card surfaces |
border-neutral-200 | #E5E5E5 (light gray) | #404040 (dark gray) | Borders / dividers |
text-neutral-950 | #0A0A0A (near-black) | #FFFFFF (white) | Page titles |
text-neutral-900 | #171717 (near-black) | #FAFAFA (near-white) | Body text |
text-neutral-500 | #737373 (mid-gray) | #737373 (mid-gray) | Stays the same — mid-range |
text-neutral-400 | #A3A3A3 (light gray) | #A3A3A3 (stays same) | Subtext — readable on dark |
bg-brand-500 | #5900FF | #7A33FF (lighter) | Brighter for contrast on dark bg |
bg-brand-50 | #EEE5FF (light purple) | #120033 (very dark purple) | Tinted backgrounds invert |
bg-white vs bg-neutral-0 — Critical Distinction
Tailwind's built-in white and black are NOT overridden in dark mode. They always resolve to literal #FFFFFF and #000000.
- Use `text-white` when you mean actual white — e.g., button text on a
brand-500background. This stays white in both modes. ✅ - Use `bg-neutral-0` (not
bg-white) for surfaces that should invert in dark mode — e.g., card backgrounds, sidebars, top bars.bg-neutral-0maps to#FFFFFFin light and#0A0A0Ain dark. ✅ - Use `bg-neutral-50` for page backgrounds. Maps to
#FAFAFAin light,#171717in dark. ✅
If you use bg-white on a card, it will stay bright white in dark mode — blinding. Use bg-neutral-0 instead.
Shadows in Dark Mode
Shadows are nearly invisible on dark surfaces. The .dark block increases shadow opacity so elevation is still perceptible, but cards should primarily rely on their neutral-200 border (which maps to a subtle dark gray divider in dark mode) for definition.
Logo
Use assets/logo-mono-white.svg — the only variant approved for dark surfaces.
What NOT to Do in Dark Mode
- Do NOT manually set dark colors in component code — rely on the
.darkclass override - Do NOT use
bg-whitefor surfaces — usebg-neutral-0so they invert properly - Do NOT use
brand-500for large tinted surfaces — usebrand-50(which maps to a dark purple in dark mode) - Do NOT change spacing, radius, or typography — the spatial system is mode-independent
---
Anti-Patterns (Do NOT)
- No gradients on buttons. Flat solid colors only.
- No colored page backgrounds. Background is always
neutral-50orneutral-0. - No heavy borders. Max
1pxfor structural borders. Never 2px+. - No rounded-full on cards. Cards are
rounded-lg(10px), never circles. - No custom fonts. DM Sans / sans-serif only. Monospace for code.
- Prefer icon + label navigation on desktop. Icon-only sidebars are acceptable if a tooltip with the label appears on hover or focus.
- No dark mode unless explicitly requested. Default is always light.
- No animated skeletons or shimmer effects in static mockups.
- No drop shadows on text. Ever.
- No border-radius mixing. Don't use the same radius token at different hierarchy levels — e.g.,
rounded-lgcards should containrounded-smchildren, notrounded-lgchildren. The inner radius should always be smaller than the outer. - No floating labels or placeholder-as-label. Labels must always be visible above inputs. Placeholders are hints, not labels — they vanish on focus and users lose context.
- No emojis in the UI. Icons only. The only exception is user-generated content — if a user typed an emoji, display it. But never add emojis to labels, headings, buttons, nav items, placeholders, or any system-generated text.
---
Checklist Before Output
Before delivering any UI code, verify:
- [ ] Uses
DM Sansfont family (withsans-seriffallback) - [ ] Brand purple only on interactive/active elements
- [ ] Cards have
bg-neutral-0+neutral-200border +rounded-lg+shadow-sm - [ ] No unauthorized colors, fonts, or shadows
- [ ] All icons from Lucide React
- [ ] shadcn components used where available (not custom recreations)
- [ ] Spacing feels generous — nothing cramped
- [ ] Page background is
neutral-50, not white - [ ] Empty states are handled (not just the happy path with fake data)
- [ ] Form inputs show error styling on validation failure (red border + message below)
- [ ] Actions provide feedback via toast or inline confirmation
- [ ] Charts (if present) use the data visualization palette in order
- [ ] All form inputs have visible external labels (not inside the input)
- [ ] Placeholder text is de-emphasized (
neutral-400, normal weight) - [ ] Casper Studios logo uses the correct variant for the background (default on light, mono-white on dark)
- [ ] If dark mode was requested:
.darkclass applied, logo uses mono-white variant, surfaces usebg-neutral-0(notbg-white) - [ ] Border-radius: buttons/inputs use
rounded-md(8px), cards userounded-lg(10px), nav items/inner elements userounded-sm(6px) - [ ] Button/input heights:
48px(h-12) by default,36px(h-9) when inside cards, panels, tables, or other compact containers
Additional checks for Web Applications:
- [ ] Sidebar follows the 240px / grouped nav / active state pattern
- [ ] Responsive: works at 1280px, 768px, and 375px
Additional checks for Mobile Applications:
- [ ] Rendered inside iPhone 16 device frame (393×852, 40px outer radius, 8px bezel)
- [ ] Primary action button is pinned to bottom with safe area padding
- [ ] Navigation uses bottom tab bar, not sidebar
- [ ] Buttons and inputs are
48pxheight (same as global default — no mobile override needed) - [ ] Most tap targets are 44×44px or larger (smaller is OK in dense UI, but not the default)
- [ ] Single-column layout only — no multi-column grids
- [ ] Typography uses mobile scale (H1: 24px, H2: 18px)
<svg xmlns="http://www.w3.org/2000/svg" width="271" height="59" fill="none" viewBox="0 0 271 59"><g clip-path="url(#a)"><rect width="56" height="56" x="1" y="1.145" fill="url(#b)" rx="28"/><g filter="url(#c)"><path fill="#fff" d="M13.36 27.021c0-9.385 7.607-16.993 16.992-16.993s16.993 7.608 16.993 16.993v7.724c0 5.12-4.15 9.27-9.268 9.27h-23.1c-.068 0-.102 0-.13-.002a1.545 1.545 0 0 1-1.487-1.486V27.021"/><path fill="url(#d)" fill-opacity=".2" d="M13.36 27.021c0-9.385 7.607-16.993 16.992-16.993s16.993 7.608 16.993 16.993v7.724c0 5.12-4.15 9.27-9.268 9.27h-23.1c-.068 0-.102 0-.13-.002a1.545 1.545 0 0 1-1.487-1.486V27.021"/><g filter="url(#e)"><rect width="4.634" height="11.972" x="29.965" y="19.297" fill="#000" rx="2.158"/></g><g filter="url(#f)"><rect width="4.634" height="11.972" x="37.303" y="19.297" fill="#000" rx="2.158"/></g></g></g><g fill="#070707" clip-path="url(#g)"><path d="M81.148 15.908q0 1.39.915 2.135.949.745 2.541.745 1.424 0 2.1-.474.68-.474 1.153-1.423.44-.88 1.186-.881h7.318q.305 0 .509.237a.64.64 0 0 1 .237.508q0 2.067-1.39 4.066-1.388 1.965-4.235 3.287-2.811 1.287-6.878 1.287-3.626 0-6.471-1.084-2.812-1.118-4.473-3.388Q72 18.619 72 15.197v-3.998q0-3.423 1.66-5.693 1.66-2.304 4.473-3.388Q80.979 1 84.604 1q4.066 0 6.878 1.321 2.847 1.288 4.236 3.287 1.389 1.965 1.389 4.032a.75.75 0 0 1-.237.542.7.7 0 0 1-.509.203h-7.318q-.745 0-1.186-.88-.474-.949-1.152-1.424-.678-.474-2.1-.474-1.594 0-2.542.745-.915.746-.915 2.135zM108.279 23.905q-.44 1.15-1.525 1.151h-6.403a.8.8 0 0 1-.543-.203.8.8 0 0 1-.203-.542l.034-.203 7.386-21.651q.136-.44.509-.78.372-.338 1.016-.338h9.419q.644 0 1.017.339t.508.779l7.386 21.65.034.204a.75.75 0 0 1-.237.542.7.7 0 0 1-.508.203h-6.404q-1.084 0-1.525-1.152l-.847-2.371h-8.267zm4.981-16.027-2.406 7.048h4.811zM143.914 9.945q3.728.406 5.964 1.321 2.27.915 3.286 2.406 1.017 1.49 1.017 3.727 0 2.44-1.593 4.27-1.559 1.794-4.337 2.778-2.778.948-6.336.948-3.964 0-6.709-.982t-4.099-2.61q-1.356-1.66-1.356-3.625 0-.338.204-.542a.86.86 0 0 1 .576-.203h6.946q.711 0 1.152.44.576.542 1.287.746.712.17 1.999.17 3.219 0 3.219-.983 0-.407-.44-.678-.407-.305-1.491-.508-1.052-.238-3.117-.475-4.643-.541-7.116-2.304-2.473-1.796-2.473-5.048 0-2.304 1.389-4.066t3.93-2.744Q138.392 1 141.78 1q3.523 0 6.166 1.118 2.643 1.084 4.032 2.71 1.389 1.627 1.389 3.186a.68.68 0 0 1-.237.542q-.202.203-.576.203h-7.284q-.577 0-1.051-.407a2.9 2.9 0 0 0-.881-.542q-.508-.203-1.558-.203-2.27 0-2.27.949 0 .508.88.813.882.27 3.524.576M174.399 1.339q3.186 0 5.557 1.084t3.625 2.948 1.254 4.167q0 3.795-2.372 5.964-2.371 2.134-8.064 2.134h-4.472v6.506q0 .373-.271.643a.88.88 0 0 1-.644.271h-7.285a.88.88 0 0 1-.643-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .643-.271zm-.135 10.029q.813 0 1.287-.474.475-.51.475-1.322 0-.847-.475-1.389-.474-.542-1.287-.542h-4.303v3.727zM210.814 18.45q.372 0 .644.27.27.272.271.644v4.778a.88.88 0 0 1-.271.643.88.88 0 0 1-.644.271H189.4a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .644-.271h21.075a.88.88 0 0 1 .644.27q.271.272.271.645V7.03q0 .373-.271.644a.88.88 0 0 1-.644.27h-13.18v2.136h11.317q.372 0 .643.27.271.272.271.644V15.4q0 .372-.271.644a.88.88 0 0 1-.643.27h-11.317v2.135zM242.18 24.006q.068.135.068.305a.75.75 0 0 1-.237.542.7.7 0 0 1-.509.203h-7.691q-.474 0-.881-.237a1.4 1.4 0 0 1-.542-.61l-2.982-6.708h-2.846v6.64q0 .375-.271.644a.88.88 0 0 1-.644.271h-7.318a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .644-.271h13.18q3.05 0 5.32.982 2.304.983 3.523 2.847t1.22 4.37q0 4.608-3.558 6.675zm-11.385-12.773q.746 0 1.152-.475.407-.508.407-1.254 0-.745-.407-1.287-.372-.576-1.152-.576h-4.235v3.592zM86.163 41.84q3.726.407 5.963 1.322 2.27.915 3.287 2.405t1.016 3.727q0 2.44-1.592 4.27-1.56 1.795-4.337 2.778-2.78.948-6.336.949-3.965 0-6.709-.983t-4.1-2.609Q72 52.039 72 50.074q0-.34.203-.542a.86.86 0 0 1 .576-.204h6.946q.712 0 1.152.44.576.543 1.288.746.71.17 1.999.17 3.219 0 3.219-.983 0-.407-.44-.678-.408-.304-1.492-.508-1.05-.237-3.117-.474-4.641-.543-7.115-2.304-2.475-1.796-2.474-5.049 0-2.304 1.39-4.066t3.93-2.744q2.575-.983 5.963-.983 3.524 0 6.167 1.118 2.643 1.085 4.032 2.711t1.389 3.185q0 .34-.237.542-.204.204-.576.203h-7.285q-.576 0-1.05-.406a2.9 2.9 0 0 0-.881-.542q-.509-.204-1.559-.204-2.27 0-2.27.949 0 .508.881.813.88.27 3.524.576M124.881 33.234a.88.88 0 0 1 .644.271q.271.27.271.644v5.455q0 .372-.271.644a.88.88 0 0 1-.644.27h-7.284v15.519a.88.88 0 0 1-.271.644.88.88 0 0 1-.644.27h-6.98a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V40.519h-7.284a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644v-5.455q0-.373.271-.644a.88.88 0 0 1 .644-.27zM142.085 57.29q-5.693 0-9.047-2.473-3.32-2.473-3.32-7.725V34.149q0-.373.271-.644a.88.88 0 0 1 .643-.27h7.082q.372 0 .644.27.27.27.271.644v13.485q0 1.39.914 2.135.95.745 2.542.745t2.507-.745q.948-.746.949-2.135V34.15q0-.373.271-.644a.88.88 0 0 1 .643-.27h7.082q.372 0 .644.27.27.27.271.644v12.943q0 5.251-3.355 7.725-3.32 2.474-9.012 2.474M170.876 33.234q5.725 0 9.148 2.474 3.456 2.472 3.456 7.657v3.456q0 5.286-3.422 7.725-3.389 2.406-9.182 2.406h-10.809a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644V34.149q0-.373.271-.644a.88.88 0 0 1 .644-.27zm.169 17.11q1.593 0 2.507-.745.95-.78.949-2.168v-4.676q0-1.39-.949-2.135-.915-.78-2.507-.779h-3.083v10.504zM204.478 50.006h6.133q.373 0 .643.27.271.272.271.645v5.116q0 .372-.271.644a.88.88 0 0 1-.643.27h-21.482a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V50.92q0-.373.271-.644a.88.88 0 0 1 .644-.271h6.201V40.18h-6.201a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644V34.15q0-.373.271-.644a.88.88 0 0 1 .644-.27h21.482q.373 0 .643.27.271.27.271.644v5.116q0 .373-.271.644a.88.88 0 0 1-.643.271h-6.133zM241.367 47.16q0 5.184-3.456 7.657-3.423 2.474-9.148 2.474-5.727 0-9.183-2.474-3.422-2.473-3.422-7.725v-3.998q0-3.456 1.627-5.726 1.66-2.304 4.506-3.389t6.472-1.084q3.624 0 6.437 1.084 2.847 1.084 4.507 3.355t1.66 5.692zm-16.06.644q0 1.389.914 2.134.95.745 2.542.745t2.507-.745q.948-.78.949-2.168v-5.354q0-1.389-.949-2.134-.915-.78-2.507-.78t-2.542.746q-.915.745-.914 2.134zM259.519 41.84q3.726.407 5.963 1.322 2.27.915 3.287 2.405t1.016 3.727q0 2.44-1.592 4.27-1.559 1.795-4.337 2.778-2.78.948-6.336.949-3.964 0-6.709-.983t-4.1-2.609q-1.355-1.66-1.355-3.625 0-.34.203-.542a.86.86 0 0 1 .576-.204h6.946q.711 0 1.152.44.576.543 1.288.746.71.17 1.999.17 3.219 0 3.218-.983 0-.407-.44-.678-.406-.304-1.491-.508-1.05-.237-3.117-.474-4.642-.543-7.115-2.304-2.474-1.796-2.474-5.049 0-2.304 1.389-4.066t3.931-2.744q2.575-.983 5.963-.983 3.524 0 6.167 1.118 2.643 1.085 4.032 2.711t1.389 3.185a.68.68 0 0 1-.237.542q-.204.204-.576.203h-7.285q-.576 0-1.05-.406a2.9 2.9 0 0 0-.881-.542q-.509-.204-1.559-.204-2.27 0-2.27.949 0 .508.881.813.88.27 3.524.576"/></g><defs><filter id="c" width="72.607" height="72.607" x="-5.951" y="-2.717" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="6.566"/><feGaussianBlur stdDeviation="9.655"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.19 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150421"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4.634"/><feGaussianBlur stdDeviation="2.897"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/><feBlend in2="effect1_dropShadow_226_150421" result="effect2_dropShadow_226_150421"/><feBlend in="SourceGraphic" in2="effect2_dropShadow_226_150421" result="shape"/></filter><filter id="e" width="6.792" height="14.13" x="28.617" y="18.758" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.27" dy=".539"/><feGaussianBlur stdDeviation=".539"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150421"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_226_150421" result="shape"/></filter><filter id="f" width="6.792" height="14.13" x="35.955" y="18.758" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.27" dy=".539"/><feGaussianBlur stdDeviation=".539"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150421"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_226_150421" result="shape"/></filter><clipPath id="a"><rect width="56" height="56" x="1" y="1.145" fill="#fff" rx="28"/></clipPath><clipPath id="g"><path fill="#fff" d="M72 1h197.785v56.291H72z"/></clipPath><radialGradient id="d" cx="0" cy="0" r="1" gradientTransform="matrix(-55.7093 65.4487 -65.4487 -51.3841 77.478 -21.434)" gradientUnits="userSpaceOnUse"><stop stop-opacity=".25"/><stop offset=".547" stop-color="#f5f5f5"/><stop offset="1" stop-color="#666"/></radialGradient><linearGradient id="b" x1="48.31" x2="10.269" y1="1.145" y2="46.138" gradientUnits="userSpaceOnUse"><stop stop-color="#878787"/><stop offset="1"/></linearGradient></defs></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="59" fill="none" viewBox="0 0 271 59"><g clip-path="url(#a)"><rect width="56" height="56" x="1" y="1.145" fill="#acacac" rx="28"/><g filter="url(#b)"><path fill="#fff" d="M13.36 27.021c0-9.385 7.607-16.993 16.992-16.993s16.993 7.608 16.993 16.993v7.724c0 5.12-4.15 9.27-9.268 9.27h-23.1c-.068 0-.102 0-.13-.002a1.545 1.545 0 0 1-1.487-1.486V27.021"/><g filter="url(#c)"><rect width="4.634" height="11.972" x="29.965" y="19.297" fill="#000" rx="2.158"/></g><g filter="url(#d)"><rect width="4.634" height="11.972" x="37.303" y="19.297" fill="#000" rx="2.158"/></g></g></g><g fill="#fff" clip-path="url(#e)"><path d="M81.148 15.908q0 1.39.915 2.135.949.745 2.541.745 1.424 0 2.1-.474.68-.474 1.153-1.423.44-.88 1.186-.881h7.318q.305 0 .509.237a.64.64 0 0 1 .237.508q0 2.067-1.39 4.066-1.388 1.965-4.235 3.287-2.811 1.287-6.878 1.287-3.626 0-6.471-1.084-2.812-1.118-4.473-3.388Q72 18.619 72 15.197v-3.998q0-3.423 1.66-5.693 1.66-2.304 4.473-3.388Q80.979 1 84.604 1q4.066 0 6.878 1.321 2.847 1.288 4.236 3.287 1.389 1.965 1.389 4.032a.75.75 0 0 1-.237.542.7.7 0 0 1-.509.203h-7.318q-.745 0-1.186-.88-.474-.949-1.152-1.424-.678-.474-2.1-.474-1.594 0-2.542.745-.915.746-.915 2.135zM108.279 23.905q-.44 1.15-1.525 1.151h-6.403a.8.8 0 0 1-.543-.203.8.8 0 0 1-.203-.542l.034-.203 7.386-21.651q.136-.44.509-.78.372-.338 1.016-.338h9.419q.644 0 1.017.339t.508.779l7.386 21.65.034.204a.75.75 0 0 1-.237.542.7.7 0 0 1-.508.203h-6.404q-1.084 0-1.525-1.152l-.847-2.371h-8.267zm4.981-16.027-2.406 7.048h4.811zM143.914 9.945q3.728.406 5.964 1.321 2.27.915 3.286 2.406 1.017 1.49 1.017 3.727 0 2.44-1.593 4.27-1.559 1.794-4.337 2.778-2.778.948-6.336.948-3.964 0-6.709-.982t-4.099-2.61q-1.356-1.66-1.356-3.625 0-.338.204-.542a.86.86 0 0 1 .576-.203h6.946q.711 0 1.152.44.576.542 1.287.746.712.17 1.999.17 3.219 0 3.219-.983 0-.407-.44-.678-.407-.305-1.491-.508-1.052-.238-3.117-.475-4.643-.541-7.116-2.304-2.473-1.796-2.473-5.048 0-2.304 1.389-4.066t3.93-2.744Q138.392 1 141.78 1q3.523 0 6.166 1.118 2.643 1.084 4.032 2.71 1.389 1.627 1.389 3.186a.68.68 0 0 1-.237.542q-.202.203-.576.203h-7.284q-.577 0-1.051-.407a2.9 2.9 0 0 0-.881-.542q-.508-.203-1.558-.203-2.27 0-2.27.949 0 .508.88.813.882.27 3.524.576M174.399 1.339q3.186 0 5.557 1.084t3.625 2.948 1.254 4.167q0 3.795-2.372 5.964-2.371 2.134-8.064 2.134h-4.472v6.506q0 .373-.271.643a.88.88 0 0 1-.644.271h-7.285a.88.88 0 0 1-.643-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .643-.271zm-.135 10.029q.813 0 1.287-.474.475-.51.475-1.322 0-.847-.475-1.389-.474-.542-1.287-.542h-4.303v3.727zM210.814 18.45q.372 0 .644.27.27.272.271.644v4.778a.88.88 0 0 1-.271.643.88.88 0 0 1-.644.271H189.4a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .644-.271h21.075a.88.88 0 0 1 .644.27q.271.272.271.645V7.03q0 .373-.271.644a.88.88 0 0 1-.644.27h-13.18v2.136h11.317q.372 0 .643.27.271.272.271.644V15.4q0 .372-.271.644a.88.88 0 0 1-.643.27h-11.317v2.135zM242.18 24.006q.068.135.068.305a.75.75 0 0 1-.237.542.7.7 0 0 1-.509.203h-7.691q-.474 0-.881-.237a1.4 1.4 0 0 1-.542-.61l-2.982-6.708h-2.846v6.64q0 .375-.271.644a.88.88 0 0 1-.644.271h-7.318a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .644-.271h13.18q3.05 0 5.32.982 2.304.983 3.523 2.847t1.22 4.37q0 4.608-3.558 6.675zm-11.385-12.773q.746 0 1.152-.475.407-.508.407-1.254 0-.745-.407-1.287-.372-.576-1.152-.576h-4.235v3.592zM86.163 41.84q3.726.407 5.963 1.322 2.27.915 3.287 2.405t1.016 3.727q0 2.44-1.592 4.27-1.56 1.795-4.337 2.778-2.78.948-6.336.949-3.965 0-6.709-.983t-4.1-2.609Q72 52.039 72 50.074q0-.34.203-.542a.86.86 0 0 1 .576-.204h6.946q.712 0 1.152.44.576.543 1.288.746.71.17 1.999.17 3.219 0 3.219-.983 0-.407-.44-.678-.408-.304-1.492-.508-1.05-.237-3.117-.474-4.641-.543-7.115-2.304-2.475-1.796-2.474-5.049 0-2.304 1.39-4.066t3.93-2.744q2.575-.983 5.963-.983 3.524 0 6.167 1.118 2.643 1.085 4.032 2.711t1.389 3.185q0 .34-.237.542-.204.204-.576.203h-7.285q-.576 0-1.05-.406a2.9 2.9 0 0 0-.881-.542q-.509-.204-1.559-.204-2.27 0-2.27.949 0 .508.881.813.88.27 3.524.576M124.881 33.234a.88.88 0 0 1 .644.271q.271.27.271.644v5.455q0 .372-.271.644a.88.88 0 0 1-.644.27h-7.284v15.519a.88.88 0 0 1-.271.644.88.88 0 0 1-.644.27h-6.98a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V40.519h-7.284a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644v-5.455q0-.373.271-.644a.88.88 0 0 1 .644-.27zM142.085 57.29q-5.693 0-9.047-2.473-3.32-2.473-3.32-7.725V34.149q0-.373.271-.644a.88.88 0 0 1 .643-.27h7.082q.372 0 .644.27.27.27.271.644v13.485q0 1.39.914 2.135.95.745 2.542.745t2.507-.745q.948-.746.949-2.135V34.15q0-.373.271-.644a.88.88 0 0 1 .643-.27h7.082q.372 0 .644.27.27.27.271.644v12.943q0 5.251-3.355 7.725-3.32 2.474-9.012 2.474M170.876 33.234q5.725 0 9.148 2.474 3.456 2.472 3.456 7.657v3.456q0 5.286-3.422 7.725-3.389 2.406-9.182 2.406h-10.809a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644V34.149q0-.373.271-.644a.88.88 0 0 1 .644-.27zm.169 17.11q1.593 0 2.507-.745.95-.78.949-2.168v-4.676q0-1.39-.949-2.135-.915-.78-2.507-.779h-3.083v10.504zM204.478 50.006h6.133q.373 0 .643.27.271.272.271.645v5.116q0 .372-.271.644a.88.88 0 0 1-.643.27h-21.482a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V50.92q0-.373.271-.644a.88.88 0 0 1 .644-.271h6.201V40.18h-6.201a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644V34.15q0-.373.271-.644a.88.88 0 0 1 .644-.27h21.482q.373 0 .643.27.271.27.271.644v5.116q0 .373-.271.644a.88.88 0 0 1-.643.271h-6.133zM241.367 47.16q0 5.184-3.456 7.657-3.423 2.474-9.148 2.474-5.727 0-9.183-2.474-3.422-2.473-3.422-7.725v-3.998q0-3.456 1.627-5.726 1.66-2.304 4.506-3.389t6.472-1.084q3.624 0 6.437 1.084 2.847 1.084 4.507 3.355t1.66 5.692zm-16.06.644q0 1.389.914 2.134.95.745 2.542.745t2.507-.745q.948-.78.949-2.168v-5.354q0-1.389-.949-2.134-.915-.78-2.507-.78t-2.542.746q-.915.745-.914 2.134zM259.519 41.84q3.726.407 5.963 1.322 2.27.915 3.287 2.405t1.016 3.727q0 2.44-1.592 4.27-1.559 1.795-4.337 2.778-2.78.948-6.336.949-3.964 0-6.709-.983t-4.1-2.609q-1.355-1.66-1.355-3.625 0-.34.203-.542a.86.86 0 0 1 .576-.204h6.946q.711 0 1.152.44.576.543 1.288.746.71.17 1.999.17 3.219 0 3.218-.983 0-.407-.44-.678-.406-.304-1.491-.508-1.05-.237-3.117-.474-4.642-.543-7.115-2.304-2.474-1.796-2.474-5.049 0-2.304 1.389-4.066t3.931-2.744q2.575-.983 5.963-.983 3.524 0 6.167 1.118 2.643 1.085 4.032 2.711t1.389 3.185a.68.68 0 0 1-.237.542q-.204.204-.576.203h-7.285q-.576 0-1.05-.406a2.9 2.9 0 0 0-.881-.542q-.509-.204-1.559-.204-2.27 0-2.27.949 0 .508.881.813.88.27 3.524.576"/></g><defs><filter id="b" width="72.607" height="72.607" x="-5.951" y="-2.717" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="6.566"/><feGaussianBlur stdDeviation="9.655"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.19 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150432"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4.634"/><feGaussianBlur stdDeviation="2.897"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/><feBlend in2="effect1_dropShadow_226_150432" result="effect2_dropShadow_226_150432"/><feBlend in="SourceGraphic" in2="effect2_dropShadow_226_150432" result="shape"/></filter><filter id="c" width="6.792" height="14.13" x="28.617" y="18.758" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.27" dy=".539"/><feGaussianBlur stdDeviation=".539"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150432"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_226_150432" result="shape"/></filter><filter id="d" width="6.792" height="14.13" x="35.955" y="18.758" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.27" dy=".539"/><feGaussianBlur stdDeviation=".539"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150432"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_226_150432" result="shape"/></filter><clipPath id="a"><rect width="56" height="56" x="1" y="1.145" fill="#fff" rx="28"/></clipPath><clipPath id="e"><path fill="#fff" d="M72 1h197.785v56.291H72z"/></clipPath></defs></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="59" fill="none" viewBox="0 0 271 59"><g clip-path="url(#a)"><rect width="56" height="56" x="1" y="1.145" fill="url(#b)" rx="28"/><g filter="url(#c)"><path fill="#fff" d="M13.36 27.021c0-9.385 7.607-16.993 16.992-16.993s16.993 7.608 16.993 16.993v7.724c0 5.12-4.15 9.27-9.268 9.27h-23.1c-.068 0-.102 0-.13-.002a1.545 1.545 0 0 1-1.487-1.486V27.021"/><path fill="url(#d)" fill-opacity=".2" d="M13.36 27.021c0-9.385 7.607-16.993 16.992-16.993s16.993 7.608 16.993 16.993v7.724c0 5.12-4.15 9.27-9.268 9.27h-23.1c-.068 0-.102 0-.13-.002a1.545 1.545 0 0 1-1.487-1.486V27.021"/><g filter="url(#e)"><rect width="4.634" height="11.972" x="29.965" y="19.297" fill="#5900ff" rx="2.158"/></g><g filter="url(#f)"><rect width="4.634" height="11.972" x="37.303" y="19.297" fill="#5900ff" rx="2.158"/></g></g></g><g clip-path="url(#g)"><path fill="#5900ff" d="M81.148 15.908q0 1.39.915 2.135.949.745 2.541.745 1.424 0 2.1-.474.68-.474 1.153-1.423.44-.88 1.186-.881h7.318q.305 0 .509.237a.64.64 0 0 1 .237.508q0 2.067-1.39 4.066-1.388 1.965-4.235 3.287-2.811 1.287-6.878 1.287-3.626 0-6.471-1.084-2.812-1.118-4.473-3.388Q72 18.619 72 15.197v-3.998q0-3.423 1.66-5.693 1.66-2.304 4.473-3.388Q80.979 1 84.604 1q4.066 0 6.878 1.321 2.847 1.288 4.236 3.287 1.389 1.965 1.389 4.032a.75.75 0 0 1-.237.542.7.7 0 0 1-.509.203h-7.318q-.745 0-1.186-.88-.474-.949-1.152-1.424-.678-.474-2.1-.474-1.594 0-2.542.745-.915.746-.915 2.135zM108.279 23.905q-.44 1.15-1.525 1.151h-6.403a.8.8 0 0 1-.543-.203.8.8 0 0 1-.203-.542l.034-.203 7.386-21.651q.136-.44.509-.78.372-.338 1.016-.338h9.419q.644 0 1.017.339t.508.779l7.386 21.65.034.204a.75.75 0 0 1-.237.542.7.7 0 0 1-.508.203h-6.404q-1.084 0-1.525-1.152l-.847-2.371h-8.267zm4.981-16.027-2.406 7.048h4.811zM143.914 9.945q3.728.406 5.964 1.321 2.27.915 3.286 2.406 1.017 1.49 1.017 3.727 0 2.44-1.593 4.27-1.559 1.794-4.337 2.778-2.778.948-6.336.948-3.964 0-6.709-.982t-4.099-2.61q-1.356-1.66-1.356-3.625 0-.338.204-.542a.86.86 0 0 1 .576-.203h6.946q.711 0 1.152.44.576.542 1.287.746.712.17 1.999.17 3.219 0 3.219-.983 0-.407-.44-.678-.407-.305-1.491-.508-1.052-.238-3.117-.475-4.643-.541-7.116-2.304-2.473-1.796-2.473-5.048 0-2.304 1.389-4.066t3.93-2.744Q138.392 1 141.78 1q3.523 0 6.166 1.118 2.643 1.084 4.032 2.71 1.389 1.627 1.389 3.186a.68.68 0 0 1-.237.542q-.202.203-.576.203h-7.284q-.577 0-1.051-.407a2.9 2.9 0 0 0-.881-.542q-.508-.203-1.558-.203-2.27 0-2.27.949 0 .508.88.813.882.27 3.524.576M174.399 1.339q3.186 0 5.557 1.084t3.625 2.948 1.254 4.167q0 3.795-2.372 5.964-2.371 2.134-8.064 2.134h-4.472v6.506q0 .373-.271.643a.88.88 0 0 1-.644.271h-7.285a.88.88 0 0 1-.643-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .643-.271zm-.135 10.029q.813 0 1.287-.474.475-.51.475-1.322 0-.847-.475-1.389-.474-.542-1.287-.542h-4.303v3.727zM210.814 18.45q.372 0 .644.27.27.272.271.644v4.778a.88.88 0 0 1-.271.643.88.88 0 0 1-.644.271H189.4a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .644-.271h21.075a.88.88 0 0 1 .644.27q.271.272.271.645V7.03q0 .373-.271.644a.88.88 0 0 1-.644.27h-13.18v2.136h11.317q.372 0 .643.27.271.272.271.644V15.4q0 .372-.271.644a.88.88 0 0 1-.643.27h-11.317v2.135zM242.18 24.006q.068.135.068.305a.75.75 0 0 1-.237.542.7.7 0 0 1-.509.203h-7.691q-.474 0-.881-.237a1.4 1.4 0 0 1-.542-.61l-2.982-6.708h-2.846v6.64q0 .375-.271.644a.88.88 0 0 1-.644.271h-7.318a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .644-.271h13.18q3.05 0 5.32.982 2.304.983 3.523 2.847t1.22 4.37q0 4.608-3.558 6.675zm-11.385-12.773q.746 0 1.152-.475.407-.508.407-1.254 0-.745-.407-1.287-.372-.576-1.152-.576h-4.235v3.592z"/><path fill="#2f3946" d="M86.163 41.84q3.726.407 5.963 1.322 2.27.915 3.287 2.405t1.016 3.727q0 2.44-1.592 4.27-1.56 1.795-4.337 2.778-2.78.948-6.336.949-3.965 0-6.709-.983t-4.1-2.609Q72 52.039 72 50.074q0-.34.203-.542a.86.86 0 0 1 .576-.204h6.946q.712 0 1.152.44.576.543 1.288.746.71.17 1.999.17 3.219 0 3.219-.983 0-.407-.44-.678-.408-.304-1.492-.508-1.05-.237-3.117-.474-4.641-.543-7.115-2.304-2.475-1.796-2.474-5.049 0-2.304 1.39-4.066t3.93-2.744q2.575-.983 5.963-.983 3.524 0 6.167 1.118 2.643 1.085 4.032 2.711t1.389 3.185q0 .34-.237.542-.204.204-.576.203h-7.285q-.576 0-1.05-.406a2.9 2.9 0 0 0-.881-.542q-.509-.204-1.559-.204-2.27 0-2.27.949 0 .508.881.813.88.27 3.524.576M124.881 33.234a.88.88 0 0 1 .644.271q.271.27.271.644v5.455q0 .372-.271.644a.88.88 0 0 1-.644.27h-7.284v15.519a.88.88 0 0 1-.271.644.88.88 0 0 1-.644.27h-6.98a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V40.519h-7.284a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644v-5.455q0-.373.271-.644a.88.88 0 0 1 .644-.27zM142.085 57.29q-5.693 0-9.047-2.473-3.32-2.473-3.32-7.725V34.149q0-.373.271-.644a.88.88 0 0 1 .643-.27h7.082q.372 0 .644.27.27.27.271.644v13.485q0 1.39.914 2.135.95.745 2.542.745t2.507-.745q.948-.746.949-2.135V34.15q0-.373.271-.644a.88.88 0 0 1 .643-.27h7.082q.372 0 .644.27.27.27.271.644v12.943q0 5.251-3.355 7.725-3.32 2.474-9.012 2.474M170.876 33.234q5.725 0 9.148 2.474 3.456 2.472 3.456 7.657v3.456q0 5.286-3.422 7.725-3.389 2.406-9.182 2.406h-10.809a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644V34.149q0-.373.271-.644a.88.88 0 0 1 .644-.27zm.169 17.11q1.593 0 2.507-.745.95-.78.949-2.168v-4.676q0-1.39-.949-2.135-.915-.78-2.507-.779h-3.083v10.504zM204.478 50.006h6.133q.373 0 .643.27.271.272.271.645v5.116q0 .372-.271.644a.88.88 0 0 1-.643.27h-21.482a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V50.92q0-.373.271-.644a.88.88 0 0 1 .644-.271h6.201V40.18h-6.201a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644V34.15q0-.373.271-.644a.88.88 0 0 1 .644-.27h21.482q.373 0 .643.27.271.27.271.644v5.116q0 .373-.271.644a.88.88 0 0 1-.643.271h-6.133zM241.367 47.16q0 5.184-3.456 7.657-3.423 2.474-9.148 2.474-5.727 0-9.183-2.474-3.422-2.473-3.422-7.725v-3.998q0-3.456 1.627-5.726 1.66-2.304 4.506-3.389t6.472-1.084q3.624 0 6.437 1.084 2.847 1.084 4.507 3.355t1.66 5.692zm-16.06.644q0 1.389.914 2.134.95.745 2.542.745t2.507-.745q.948-.78.949-2.168v-5.354q0-1.389-.949-2.134-.915-.78-2.507-.78t-2.542.746q-.915.745-.914 2.134zM259.519 41.84q3.726.407 5.963 1.322 2.27.915 3.287 2.405t1.016 3.727q0 2.44-1.592 4.27-1.559 1.795-4.337 2.778-2.78.948-6.336.949-3.964 0-6.709-.983t-4.1-2.609q-1.355-1.66-1.355-3.625 0-.34.203-.542a.86.86 0 0 1 .576-.204h6.946q.711 0 1.152.44.576.543 1.288.746.71.17 1.999.17 3.219 0 3.218-.983 0-.407-.44-.678-.406-.304-1.491-.508-1.05-.237-3.117-.474-4.642-.543-7.115-2.304-2.474-1.796-2.474-5.049 0-2.304 1.389-4.066t3.931-2.744q2.575-.983 5.963-.983 3.524 0 6.167 1.118 2.643 1.085 4.032 2.711t1.389 3.185a.68.68 0 0 1-.237.542q-.204.204-.576.203h-7.285q-.576 0-1.05-.406a2.9 2.9 0 0 0-.881-.542q-.509-.204-1.559-.204-2.27 0-2.27.949 0 .508.881.813.88.27 3.524.576"/></g><defs><filter id="c" width="72.607" height="72.607" x="-5.951" y="-2.717" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="6.566"/><feGaussianBlur stdDeviation="9.655"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.19 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_222_7096"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4.634"/><feGaussianBlur stdDeviation="2.897"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/><feBlend in2="effect1_dropShadow_222_7096" result="effect2_dropShadow_222_7096"/><feBlend in="SourceGraphic" in2="effect2_dropShadow_222_7096" result="shape"/></filter><filter id="e" width="6.792" height="14.13" x="28.617" y="18.758" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.27" dy=".539"/><feGaussianBlur stdDeviation=".539"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_222_7096"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_222_7096" result="shape"/></filter><filter id="f" width="6.792" height="14.13" x="35.955" y="18.758" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.27" dy=".539"/><feGaussianBlur stdDeviation=".539"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_222_7096"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_222_7096" result="shape"/></filter><clipPath id="a"><rect width="56" height="56" x="1" y="1.145" fill="#fff" rx="28"/></clipPath><clipPath id="g"><path fill="#fff" d="M72 1h197.785v56.291H72z"/></clipPath><radialGradient id="d" cx="0" cy="0" r="1" gradientTransform="matrix(-55.7093 65.4487 -65.4487 -51.3841 77.478 -21.434)" gradientUnits="userSpaceOnUse"><stop stop-opacity=".25"/><stop offset=".547" stop-color="#f5f5f5"/><stop offset="1" stop-color="#5900ff"/></radialGradient><linearGradient id="b" x1="48.31" x2="10.269" y1="1.145" y2="46.138" gradientUnits="userSpaceOnUse"><stop stop-color="#f0f"/><stop offset="1" stop-color="#5900ff"/></linearGradient></defs></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="59" fill="none" viewBox="0 0 271 59"><g clip-path="url(#a)"><rect width="56" height="56" x="1" y="1.145" fill="url(#b)" rx="28"/><g filter="url(#c)"><path fill="#fff" d="M13.36 27.021c0-9.385 7.607-16.993 16.992-16.993s16.993 7.608 16.993 16.993v7.724c0 5.12-4.15 9.27-9.268 9.27h-23.1c-.068 0-.102 0-.13-.002a1.545 1.545 0 0 1-1.487-1.486V27.021"/><path fill="url(#d)" fill-opacity=".2" d="M13.36 27.021c0-9.385 7.607-16.993 16.992-16.993s16.993 7.608 16.993 16.993v7.724c0 5.12-4.15 9.27-9.268 9.27h-23.1c-.068 0-.102 0-.13-.002a1.545 1.545 0 0 1-1.487-1.486V27.021"/><g filter="url(#e)"><rect width="4.634" height="11.972" x="29.965" y="19.297" fill="#5900ff" rx="2.158"/></g><g filter="url(#f)"><rect width="4.634" height="11.972" x="37.303" y="19.297" fill="#5900ff" rx="2.158"/></g></g></g><g fill="#000" clip-path="url(#g)"><path d="M81.148 15.908q0 1.39.915 2.135.949.745 2.541.745 1.424 0 2.1-.474.68-.474 1.153-1.423.44-.88 1.186-.881h7.318q.305 0 .509.237a.64.64 0 0 1 .237.508q0 2.067-1.39 4.066-1.388 1.965-4.235 3.287-2.811 1.287-6.878 1.287-3.626 0-6.471-1.084-2.812-1.118-4.473-3.388Q72 18.619 72 15.197v-3.998q0-3.423 1.66-5.693 1.66-2.304 4.473-3.388Q80.979 1 84.604 1q4.066 0 6.878 1.321 2.847 1.288 4.236 3.287 1.389 1.965 1.389 4.032a.75.75 0 0 1-.237.542.7.7 0 0 1-.509.203h-7.318q-.745 0-1.186-.88-.474-.949-1.152-1.424-.678-.474-2.1-.474-1.594 0-2.542.745-.915.746-.915 2.135zM108.279 23.905q-.44 1.15-1.525 1.151h-6.403a.8.8 0 0 1-.543-.203.8.8 0 0 1-.203-.542l.034-.203 7.386-21.651q.136-.44.509-.78.372-.338 1.016-.338h9.419q.644 0 1.017.339t.508.779l7.386 21.65.034.204a.75.75 0 0 1-.237.542.7.7 0 0 1-.508.203h-6.404q-1.084 0-1.525-1.152l-.847-2.371h-8.267zm4.981-16.027-2.406 7.048h4.811zM143.914 9.945q3.728.406 5.964 1.321 2.27.915 3.286 2.406 1.017 1.49 1.017 3.727 0 2.44-1.593 4.27-1.559 1.794-4.337 2.778-2.778.948-6.336.948-3.964 0-6.709-.982t-4.099-2.61q-1.356-1.66-1.356-3.625 0-.338.204-.542a.86.86 0 0 1 .576-.203h6.946q.711 0 1.152.44.576.542 1.287.746.712.17 1.999.17 3.219 0 3.219-.983 0-.407-.44-.678-.407-.305-1.491-.508-1.052-.238-3.117-.475-4.643-.541-7.116-2.304-2.473-1.796-2.473-5.048 0-2.304 1.389-4.066t3.93-2.744Q138.392 1 141.78 1q3.523 0 6.166 1.118 2.643 1.084 4.032 2.71 1.389 1.627 1.389 3.186a.68.68 0 0 1-.237.542q-.202.203-.576.203h-7.284q-.577 0-1.051-.407a2.9 2.9 0 0 0-.881-.542q-.508-.203-1.558-.203-2.27 0-2.27.949 0 .508.88.813.882.27 3.524.576M174.399 1.339q3.186 0 5.557 1.084t3.625 2.948 1.254 4.167q0 3.795-2.372 5.964-2.371 2.134-8.064 2.134h-4.472v6.506q0 .373-.271.643a.88.88 0 0 1-.644.271h-7.285a.88.88 0 0 1-.643-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .643-.271zm-.135 10.029q.813 0 1.287-.474.475-.51.475-1.322 0-.847-.475-1.389-.474-.542-1.287-.542h-4.303v3.727zM210.814 18.45q.372 0 .644.27.27.272.271.644v4.778a.88.88 0 0 1-.271.643.88.88 0 0 1-.644.271H189.4a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .644-.271h21.075a.88.88 0 0 1 .644.27q.271.272.271.645V7.03q0 .373-.271.644a.88.88 0 0 1-.644.27h-13.18v2.136h11.317q.372 0 .643.27.271.272.271.644V15.4q0 .372-.271.644a.88.88 0 0 1-.643.27h-11.317v2.135zM242.18 24.006q.068.135.068.305a.75.75 0 0 1-.237.542.7.7 0 0 1-.509.203h-7.691q-.474 0-.881-.237a1.4 1.4 0 0 1-.542-.61l-2.982-6.708h-2.846v6.64q0 .375-.271.644a.88.88 0 0 1-.644.271h-7.318a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V2.254q0-.373.271-.644a.88.88 0 0 1 .644-.271h13.18q3.05 0 5.32.982 2.304.983 3.523 2.847t1.22 4.37q0 4.608-3.558 6.675zm-11.385-12.773q.746 0 1.152-.475.407-.508.407-1.254 0-.745-.407-1.287-.372-.576-1.152-.576h-4.235v3.592zM86.163 41.84q3.726.407 5.963 1.322 2.27.915 3.287 2.405t1.016 3.727q0 2.44-1.592 4.27-1.56 1.795-4.337 2.778-2.78.948-6.336.949-3.965 0-6.709-.983t-4.1-2.609Q72 52.039 72 50.074q0-.34.203-.542a.86.86 0 0 1 .576-.204h6.946q.712 0 1.152.44.576.543 1.288.746.71.17 1.999.17 3.219 0 3.219-.983 0-.407-.44-.678-.408-.304-1.492-.508-1.05-.237-3.117-.474-4.641-.543-7.115-2.304-2.475-1.796-2.474-5.049 0-2.304 1.39-4.066t3.93-2.744q2.575-.983 5.963-.983 3.524 0 6.167 1.118 2.643 1.085 4.032 2.711t1.389 3.185q0 .34-.237.542-.204.204-.576.203h-7.285q-.576 0-1.05-.406a2.9 2.9 0 0 0-.881-.542q-.509-.204-1.559-.204-2.27 0-2.27.949 0 .508.881.813.88.27 3.524.576M124.881 33.234a.88.88 0 0 1 .644.271q.271.27.271.644v5.455q0 .372-.271.644a.88.88 0 0 1-.644.27h-7.284v15.519a.88.88 0 0 1-.271.644.88.88 0 0 1-.644.27h-6.98a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V40.519h-7.284a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644v-5.455q0-.373.271-.644a.88.88 0 0 1 .644-.27zM142.085 57.29q-5.693 0-9.047-2.473-3.32-2.473-3.32-7.725V34.149q0-.373.271-.644a.88.88 0 0 1 .643-.27h7.082q.372 0 .644.27.27.27.271.644v13.485q0 1.39.914 2.135.95.745 2.542.745t2.507-.745q.948-.746.949-2.135V34.15q0-.373.271-.644a.88.88 0 0 1 .643-.27h7.082q.372 0 .644.27.27.27.271.644v12.943q0 5.251-3.355 7.725-3.32 2.474-9.012 2.474M170.876 33.234q5.725 0 9.148 2.474 3.456 2.472 3.456 7.657v3.456q0 5.286-3.422 7.725-3.389 2.406-9.182 2.406h-10.809a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644V34.149q0-.373.271-.644a.88.88 0 0 1 .644-.27zm.169 17.11q1.593 0 2.507-.745.95-.78.949-2.168v-4.676q0-1.39-.949-2.135-.915-.78-2.507-.779h-3.083v10.504zM204.478 50.006h6.133q.373 0 .643.27.271.272.271.645v5.116q0 .372-.271.644a.88.88 0 0 1-.643.27h-21.482a.88.88 0 0 1-.644-.27.88.88 0 0 1-.271-.644V50.92q0-.373.271-.644a.88.88 0 0 1 .644-.271h6.201V40.18h-6.201a.88.88 0 0 1-.644-.271.88.88 0 0 1-.271-.644V34.15q0-.373.271-.644a.88.88 0 0 1 .644-.27h21.482q.373 0 .643.27.271.27.271.644v5.116q0 .373-.271.644a.88.88 0 0 1-.643.271h-6.133zM241.367 47.16q0 5.184-3.456 7.657-3.423 2.474-9.148 2.474-5.727 0-9.183-2.474-3.422-2.473-3.422-7.725v-3.998q0-3.456 1.627-5.726 1.66-2.304 4.506-3.389t6.472-1.084q3.624 0 6.437 1.084 2.847 1.084 4.507 3.355t1.66 5.692zm-16.06.644q0 1.389.914 2.134.95.745 2.542.745t2.507-.745q.948-.78.949-2.168v-5.354q0-1.389-.949-2.134-.915-.78-2.507-.78t-2.542.746q-.915.745-.914 2.134zM259.519 41.84q3.726.407 5.963 1.322 2.27.915 3.287 2.405t1.016 3.727q0 2.44-1.592 4.27-1.559 1.795-4.337 2.778-2.78.948-6.336.949-3.964 0-6.709-.983t-4.1-2.609q-1.355-1.66-1.355-3.625 0-.34.203-.542a.86.86 0 0 1 .576-.204h6.946q.711 0 1.152.44.576.543 1.288.746.71.17 1.999.17 3.219 0 3.218-.983 0-.407-.44-.678-.406-.304-1.491-.508-1.05-.237-3.117-.474-4.642-.543-7.115-2.304-2.474-1.796-2.474-5.049 0-2.304 1.389-4.066t3.931-2.744q2.575-.983 5.963-.983 3.524 0 6.167 1.118 2.643 1.085 4.032 2.711t1.389 3.185a.68.68 0 0 1-.237.542q-.204.204-.576.203h-7.285q-.576 0-1.05-.406a2.9 2.9 0 0 0-.881-.542q-.509-.204-1.559-.204-2.27 0-2.27.949 0 .508.881.813.88.27 3.524.576"/></g><defs><filter id="c" width="72.607" height="72.607" x="-5.951" y="-2.717" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="6.566"/><feGaussianBlur stdDeviation="9.655"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.19 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150443"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4.634"/><feGaussianBlur stdDeviation="2.897"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/><feBlend in2="effect1_dropShadow_226_150443" result="effect2_dropShadow_226_150443"/><feBlend in="SourceGraphic" in2="effect2_dropShadow_226_150443" result="shape"/></filter><filter id="e" width="6.792" height="14.13" x="28.617" y="18.758" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.27" dy=".539"/><feGaussianBlur stdDeviation=".539"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150443"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_226_150443" result="shape"/></filter><filter id="f" width="6.792" height="14.13" x="35.955" y="18.758" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.27" dy=".539"/><feGaussianBlur stdDeviation=".539"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_226_150443"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_226_150443" result="shape"/></filter><clipPath id="a"><rect width="56" height="56" x="1" y="1.145" fill="#fff" rx="28"/></clipPath><clipPath id="g"><path fill="#fff" d="M72 1h197.785v56.291H72z"/></clipPath><radialGradient id="d" cx="0" cy="0" r="1" gradientTransform="matrix(-55.7093 65.4487 -65.4487 -51.3841 77.478 -21.434)" gradientUnits="userSpaceOnUse"><stop stop-opacity=".25"/><stop offset=".547" stop-color="#f5f5f5"/><stop offset="1" stop-color="#5900ff"/></radialGradient><linearGradient id="b" x1="48.31" x2="10.269" y1="1.145" y2="46.138" gradientUnits="userSpaceOnUse"><stop stop-color="#f0f"/><stop offset="1" stop-color="#5900ff"/></linearGradient></defs></svg>Component Patterns
Reusable composite components built from shadcn/ui primitives. These are the building blocks that live inside layout structures.
---
Table of Contents
1. Stat Card 2. List Item Row 3. Filter Bar 4. Kanban Board 5. Profile / Discovery Card 6. Product Card 7. Activity Feed Item 8. Toast Notification 9. Form Validation States
---
Stat Card
A card showing a key metric with an optional semantic accent.
Variants
Pastel accent background:
<Card className="bg-success-50 border-success-200">
<CardContent className="p-4 flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-success-100 flex items-center justify-center">
<Users className="w-5 h-5 text-success-600" />
</div>
<div>
<p className="text-sm font-medium text-neutral-900">5 new members</p>
<p className="text-xs text-neutral-500">1 onboarding now</p>
</div>
</CardContent>
</Card>Neutral with colored number:
<Card>
<CardContent className="p-4 flex items-center gap-3">
<span className="text-2xl font-medium text-brand-500">3</span>
<div className="flex items-center gap-2">
<CheckCircle className="w-4 h-4 text-success-500" />
<span className="text-sm text-neutral-700">Verified</span>
</div>
</CardContent>
</Card>Stat cards commonly appear in a horizontal row (scrollable on mobile):
<div className="flex gap-4 overflow-x-auto pb-2">
<StatCard />
<StatCard />
<StatCard />
</div>---
List Item Row
A horizontal row used in to-do lists, event lists, activity feeds, and file lists.
Anatomy
┌─────────────────────────────────────────────────────┐
│ [Icon Circle] Title Today │
│ Description / subtitle │
└─────────────────────────────────────────────────────┘Code
<div className="flex items-center gap-3 py-3 px-4 hover:bg-neutral-50 transition-colors">
{/* Icon in circle */}
<div className="w-10 h-10 rounded-full bg-brand-100 flex items-center justify-center shrink-0">
<ClipboardCheck className="w-5 h-5 text-brand-500" />
</div>
{/* Content */}
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-neutral-900 truncate">{title}</p>
<p className="text-xs text-neutral-500 truncate">{subtitle}</p>
</div>
{/* Metadata */}
<span className="text-xs text-neutral-400 shrink-0">{timestamp}</span>
</div>Separate rows with divide-y divide-neutral-200 on the parent container.
---
Filter Bar
A horizontal bar of pill-shaped filter chips, commonly used above tables and list views.
Chip Variants
With count badge:
<button className={cn(
"flex items-center gap-2 px-3 py-1.5 rounded-full text-sm border transition-colors",
isActive
? "bg-brand-50 border-brand-200 text-brand-700"
: "bg-white border-neutral-200 text-neutral-700 hover:bg-neutral-50"
)}>
<span>{label}</span>
<span className={cn(
"text-xs font-medium rounded-full px-1.5 py-0.5",
isActive ? "bg-brand-100 text-brand-600" : "bg-neutral-100 text-neutral-500"
)}>
{count}
</span>
</button>Layout
<div className="flex gap-2 overflow-x-auto pb-2">
{filters.map(f => <FilterChip key={f.id} {...f} />)}
</div>On mobile, this scrolls horizontally with -mx-4 px-4 to extend past container padding.
---
Kanban Board
A column-based layout for pipelines, lead tracking, project stages.
Structure
┌─────────────┬─────────────┬─────────────┐
│ New [+]│ Qualified[+]│ Meeting [+]│
├─────────────┼─────────────┼─────────────┤
│ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │
│ │ Card 1 │ │ │ Card 4 │ │ │ Card 7 │ │
│ └─────────┘ │ └─────────┘ │ └─────────┘ │
│ ┌─────────┐ │ ┌─────────┐ │ │
│ │ Card 2 │ │ │ Card 5 │ │ │
│ └─────────┘ │ └─────────┘ │ │
└─────────────┴─────────────┴─────────────┘Column
<div className="flex-1 min-w-[280px]">
{/* Column header */}
<div className="flex items-center justify-between mb-3">
<h3 className="text-sm font-medium text-neutral-900">{title}</h3>
<button className="text-neutral-400 hover:text-neutral-600">
<Plus className="w-4 h-4" />
</button>
</div>
{/* Cards */}
<div className="flex flex-col gap-3">
{items.map(item => <KanbanCard key={item.id} {...item} />)}
</div>
</div>Kanban Card
<Card className="p-4">
<div className="flex items-center gap-3 mb-3">
<Avatar className="w-8 h-8"><AvatarImage src={item.avatar} /></Avatar>
<div>
<p className="text-sm font-medium text-neutral-900">{item.name}</p>
<p className="text-xs text-neutral-500">{item.company}</p>
</div>
</div>
<div className="flex items-center gap-2">
<Badge variant="outline" className="text-xs bg-success-50 text-success-700 border-success-200">
{item.date}
</Badge>
<span className="text-xs text-neutral-500">{item.value}</span>
</div>
</Card>Container
<div className="flex gap-6 overflow-x-auto">
{columns.map(col => <KanbanColumn key={col.id} {...col} />)}
</div>---
Profile / Discovery Card
Used for freelancer profiles, team members, or user listings.
Anatomy
┌───────────────────────────────────┐
│ [Gradient image placeholder] │
│ ┌──────┐ │
│ │Avatar│ │
│ └──────┘ │
│ [star] 4.8 (20) New York, NY │
│ Laura Adams │
│ [Available] [bolt] │
│ UI/UX Design Expert │
│ │
│ [$80-$120/hr] │
│ [UI/UX Designer] [Graphic Design] │
│ [+5 Specializations] │
└───────────────────────────────────┘Key Details
- Top section: Gradient placeholder image (see Image Placeholders in SKILL.md)
- Avatar overlaps the image bottom edge by ~50%
- Name in Body Bold, role in Body/neutral-500
- Availability badge: success-50 bg, success-700 text
- Price range in a standalone pill: brand-50 bg, brand-700 text
- Skill tags: neutral-100 bg, neutral-700 text, rounded-full
- Overflow tag: "+N Specializations" in same pill style
---
Product Card
For e-commerce or marketplace item displays.
Anatomy
┌───────────────────────┬──────────────────────┐
│ │ Brand Name │
│ [Product image / │ Product Title │
│ gradient placeholder]│ [stars] (146) │
│ │ $18.00 │
│ │ │
│ │ [Type: Whole ▾] │
│ │ [Size: 12oz ▾] │
│ │ Qty: [- 1 +] │
│ │ │
│ │ [Add to cart] ← primary │
│ │ [Buy now] ← secondary │
└───────────────────────┴──────────────────────┘Key Details
- Image takes up ~50% width on desktop, full width on mobile (stacked)
- Price in Heading 2 weight
- Selectors use shadcn
Selectcomponent - Quantity uses a custom stepper (secondary button styles)
- Primary CTA: brand-500 button, full width
- Secondary CTA: brand-50 bg, brand-700 text, full width
---
Activity Feed Item
A compact row for event feeds, notification lists, or changelog entries.
<div className="flex items-start gap-3 py-3">
<div className="w-8 h-8 rounded-full bg-error-50 flex items-center justify-center shrink-0">
<Calendar className="w-4 h-4 text-error-500" />
</div>
<div className="flex-1">
<p className="text-sm text-neutral-900">
<span className="font-medium">Department Offsite</span>
</p>
<p className="text-xs text-neutral-500">Monday, Nov 13, 2023</p>
</div>
<span className="text-xs text-neutral-400 shrink-0">All-day</span>
</div>---
Toast Notification
Transient feedback using shadcn's Sonner toast. Confirms actions, surfaces errors, or provides info.
Success Toast
<div className="flex items-center gap-3 p-4 bg-white border border-neutral-200 border-l-4 border-l-success-200 rounded-sm shadow-md">
<CheckCircle className="w-5 h-5 text-success-500 shrink-0" />
<div className="flex-1 min-w-0">
<p className="text-sm text-neutral-900">Project created successfully</p>
</div>
<a href="#" className="text-sm text-brand-500 shrink-0">View</a>
<button className="text-neutral-400 hover:text-neutral-600 shrink-0">
<X className="w-4 h-4" />
</button>
</div>Error Toast
<div className="flex items-center gap-3 p-4 bg-white border border-neutral-200 border-l-4 border-l-error-200 rounded-sm shadow-md">
<XCircle className="w-5 h-5 text-error-500 shrink-0" />
<div className="flex-1 min-w-0">
<p className="text-sm text-neutral-900">Couldn't save changes — try again</p>
</div>
<button className="text-sm text-brand-500 shrink-0">Retry</button>
<button className="text-neutral-400 hover:text-neutral-600 shrink-0">
<X className="w-4 h-4" />
</button>
</div>Variants
| Variant | Icon | Left Border | Use |
|---|---|---|---|
| Success | CheckCircle / success-500 | success-200 | Action completed |
| Error | XCircle / error-500 | error-200 | Action failed |
| Info | Info / brand-500 | brand-200 | Neutral updates |
| Warning | AlertTriangle / warning-500 | warning-200 | Non-blocking caution |
Rules
- Body text:
14px,neutral-900. One sentence max - Optional action link:
brand-500, inline ("Undo", "View", "Retry") - Close button:
Xicon,neutral-400 - Positioning: bottom-right on web, top-center on mobile
- Duration:
4000msdefault,6000mswith action link. Error toasts persist until dismissed
---
Form Validation States
Inline error and success feedback for form inputs.
Input with Error
<div className="space-y-1.5">
<label className="text-sm text-neutral-900">Email address</label>
<input
type="email"
value="not-an-email"
className="w-full h-9 px-3 rounded-md border border-error-500 bg-white text-sm text-neutral-900 focus:outline-none focus:ring-2 focus:ring-error-500"
/>
<div className="flex items-center gap-1">
<AlertCircle className="w-3.5 h-3.5 text-error-500 shrink-0" />
<p className="text-xs text-error-500">Please enter a valid email address</p>
</div>
</div>Rules
- Error border: Swap
neutral-200→error-500 - Error ring: Swap
brand-500→error-500 - Error message: Caption size (12px),
error-500, below the input withspace-y-1 - Label stays neutral: Don't color the label red — the border and message are enough signal
- Border radius: Inputs always use
rounded-md(8px), even in error state - Validate on blur, not on keystroke. Remove error on change when corrected
- On submit with errors: Scroll to and focus the first invalid field
Mobile Application Patterns
Code examples, rules, and structural patterns for mobile app interfaces. This file is self-contained — read it whenever the project is a mobile application, native app, iOS app, Android app, or any variant that implies a phone-based experience.
Why this file exists: Most AI code-generation tools default to web layouts. When someone says "mobile app," they don't want a responsive webpage — they want something that looks like it's running on a phone. The device frame, the touch-sized inputs, the pinned bottom button — these details are what separate "shrunk-down website" from "this actually feels like an app."
Dark mode note: The code examples below usebg-whitefor surfaces (top bars, tab bars, cards). This is correct for light mode (the default). If dark mode is requested, swapbg-whitetobg-neutral-0on all surfaces so they invert correctly via the.darkclass. See the Dark Mode section in SKILL.md for full guidance.
---
Mobile Rules
These rules govern every mobile app output. The code examples in the sections below implement these rules — refer to them for copy-paste patterns.
Device Frame (Non-Negotiable)
Every mobile app output MUST be rendered inside a device frame that simulates an iPhone 16:
- Viewport size:
393pxwide ×852pxtall — always, no exceptions - Outer frame:
border-radius: 40px,border: 8px solid #1a1a1a(simulates the phone bezel) - Inner content area:
border-radius: 20px,overflow-y: auto,overflow-x: hidden - Page behind the phone: Use
neutral-100or a subtle gradient so the device frame stands out as an object on the canvas - Center the device frame horizontally on the page
Mobile Form Inputs
All global form input rules from SKILL.md apply (visible external labels, de-emphasized placeholders, label-above pattern, rounded-md border-radius). On mobile, inputs use the global default height of 48px (h-12) — no override needed. When inputs appear inside cards or compact containers, use 36px (h-9) per the compact sizing rule.
Mobile Button Height
All buttons in mobile contexts use the global default height of 48px (h-12) with rounded-md (8px) border-radius. When buttons appear inside cards, panels, or compact containers, use 36px (h-9) per the compact sizing rule. This applies to all button variants: primary, secondary, ghost, and destructive.
Primary Action Button (Pinned to Bottom)
The main CTA on every mobile screen MUST be pinned to the bottom of the viewport for ergonomic thumb reachability:
- Position: Place the button container as the last child in the flex column layout (after the
flex-1 overflow-y-autocontent area), withshrink-0so it never collapses. Use16pxhorizontal padding and16pxbottom padding - Width: Full width minus padding
- Safe area: Add
pb-6(24px) below the button to account for the home indicator on modern iPhones - Keyboard behavior: When a text input is focused, the button should remain visible above the keyboard. The
shrink-0flex child approach inside aflex flex-colcontainer achieves this naturally - Visual separation: Add
border-t border-neutral-200or ashadow-mdon the pinned container so scrollable content doesn't visually bleed into the button area
Mobile Navigation
Mobile apps do NOT use a persistent sidebar visible alongside content. The primary navigation pattern is a bottom tab bar:
- Bottom tab bar: 5 tabs max, icon + label,
56pxheight, pinned to bottom above the safe area - Active tab:
brand-500icon + text. Inactive:neutral-400 - Top bar: Screen title, optional back arrow left, optional action icon right. Height:
44px - The tab bar sits below the primary action button's safe area — they should never overlap
When there are more than 5 top-level destinations: Use a slide-out navigation panel triggered by a hamburger icon in the top bar. This panel overlays the content as a modal-style Sheet (slides in from the left, black/50 overlay behind it). It is not a persistent sidebar — it opens on demand and closes when the user taps away or selects a destination. The bottom tab bar can still hold the 4–5 most important items, with a "More" tab or the hamburger providing access to the rest.
Mobile Typography
Phone screens are physically smaller — the web heading scale feels oversized:
- Heading 1:
24px(instead of 30px) - Heading 2:
18px(instead of 20px) - All other sizes remain unchanged
Mobile Spacing
- Page padding:
16pxhorizontal,12pxtop (below the top bar) - Card internal padding:
16px - Between cards/sections:
12px - Density target: think iOS Settings app — snug but breathable
Contextual Actions (Menus & Bottom Sheets)
When a button or trigger presents multiple options on mobile, use one of two patterns depending on the complexity:
Contextual Menu — for short, simple option lists (3–6 actions like "Edit", "Share", "Delete"). Use shadcn's DropdownMenu component. It appears adjacent to the trigger, floating above content. Keep it minimal — if the options need descriptions, icons with labels, or scrolling, use a bottom sheet instead.
Bottom Sheet — for richer option sets, confirmations, or any content that benefits from more space. The sheet slides up from the bottom of the device frame with these specs:
- Scrim:
black/50overlay behind the sheet, withbackdrop-blur-smto blur the content underneath - Max height:
85%of the device frame height — the sheet should never cover the full screen - Content hugging: If the sheet content is shorter than 85%, the sheet should only be as tall as its content (plus padding). Don't force it to 85% when it doesn't need to be
- Border radius:
24pxon the top-left and top-right corners (rounded-t-[24px]), square on the bottom - Drag indicator: A small
40px × 4pxrounded pill centered at the top of the sheet (neutral-300) so users know they can swipe to dismiss - Animation: Slides up from below with a
200–300msease-out transition - Dismissal: Tap the scrim or swipe down to close
Mobile Anti-Patterns
- No persistent sidebar navigation — use bottom tabs as primary nav; a slide-out panel overlay is fine for overflow destinations
- No horizontal scroll tables — transform into card/list views
- Prefer generous tap targets — aim for
44px × 44pxor larger on interactive elements. Smaller targets are acceptable in dense UI (e.g., inline icons, compact lists) but should be the exception, not the default - No multi-column grids — single column only
---
Table of Contents — Code Examples
1. Device Frame Shell 2. Mobile Top Bar 3. Bottom Tab Navigation 4. Mobile Form Layout 5. Pinned Bottom Button 6. Mobile List View 7. Mobile Card Stack 8. Full Screen Composition 9. Contextual Actions
---
Device Frame Shell
The outermost wrapper that makes the output look like an actual phone. This is the first thing you render — everything else goes inside it.
{/* Page background — the "desk" the phone sits on */}
<div className="min-h-screen bg-neutral-100 flex items-center justify-center p-8">
{/* Phone bezel */}
<div className="relative w-[393px] h-[852px] rounded-[40px] border-[8px] border-[#1a1a1a] overflow-hidden shadow-xl">
{/* Status bar notch area — visual only */}
<div className="h-[54px] bg-white flex items-end justify-center pb-1">
<div className="w-[126px] h-[34px] bg-[#1a1a1a] rounded-full" />
</div>
{/* Scrollable content area */}
<div className="h-[calc(100%-54px)] overflow-y-auto overflow-x-hidden bg-neutral-50 flex flex-col">
{/* App content goes here */}
</div>
</div>
</div>Key Details
- The
w-[393px] h-[852px]is the iPhone 16 viewport — do not change these values - The
rounded-[40px]on the outer frame mimics the phone's physical corner radius - The inner content gets
rounded-[20px]where it meets the bezel at the top/bottom edges - The Dynamic Island / notch is a decorative element — a
126×34pxblack pill centered at the top - The scrollable area is everything below the notch, using
flex flex-colso sticky elements work correctly
---
Mobile Top Bar
The navigation bar at the top of each screen. Sits just below the status bar / notch area.
Standard Top Bar (Screen Title)
<header className="h-11 px-4 flex items-center justify-between bg-white border-b border-neutral-200 shrink-0">
<h1 className="text-lg font-medium text-neutral-950">Screen Title</h1>
<button className="w-10 h-10 flex items-center justify-center">
<Bell className="w-5 h-5 text-neutral-600" />
</button>
</header>Top Bar with Back Navigation
<header className="h-11 px-4 flex items-center gap-3 bg-white border-b border-neutral-200 shrink-0">
<button className="w-10 h-10 flex items-center justify-center -ml-2">
<ChevronLeft className="w-5 h-5 text-neutral-900" />
</button>
<h1 className="text-lg font-medium text-neutral-950 flex-1">Detail View</h1>
</header>Rules
- Height:
44px(h-11) - Always
shrink-0so it doesn't collapse in flex layouts - Background: white with bottom border
- Back button:
ChevronLefticon, not an arrow — follows iOS convention - Title:
18pxfont-medium (this is the mobile H2 scale)
---
Bottom Tab Navigation
The primary navigation pattern for mobile apps. Pinned to the bottom of the device frame.
<nav className="shrink-0 bg-white border-t border-neutral-200 px-2 pb-6 pt-2">
<div className="flex items-center justify-around">
{tabs.map(tab => (
<button
key={tab.id}
className="flex flex-col items-center gap-1 py-1 px-3 min-w-[56px]"
>
<tab.icon className={cn(
"w-5 h-5",
tab.active ? "text-brand-500" : "text-neutral-400"
)} />
<span className={cn(
"text-[10px]",
tab.active ? "text-brand-500 font-medium" : "text-neutral-400"
)}>
{tab.label}
</span>
</button>
))}
</div>
</nav>Rules
- Maximum 5 tabs
pb-6on the nav provides the home indicator safe area (24px)- Active state:
brand-500icon + label,font-mediumon label - Inactive state:
neutral-400icon + label - Icon size:
20px - Label size:
10px— smaller than the standard caption to avoid crowding - Each tab target area should aim for at least
44×44pxfor comfortable tapping - Always use
shrink-0so the tab bar doesn't get pushed out of view
When More Than 5 Destinations Are Needed
If the app has more than 5 top-level sections, use a slide-out navigation panel alongside the bottom tabs. The bottom tabs hold the 4–5 most important destinations, and a hamburger icon in the top bar (or a "More" tab) opens a Sheet from the left that overlays the content:
<Sheet>
<SheetTrigger asChild>
<button className="w-10 h-10 flex items-center justify-center">
<Menu className="w-5 h-5 text-neutral-900" />
</button>
</SheetTrigger>
<SheetContent side="left" className="w-72 p-0">
<div className="pt-14 px-4 space-y-1">
{allNavItems.map(item => (
<button
key={item.id}
className={cn(
"flex items-center gap-3 w-full px-3 h-11 rounded-sm text-sm",
item.active
? "bg-brand-50 text-brand-500 font-medium"
: "text-neutral-600 active:bg-neutral-100"
)}
>
<item.icon className="w-5 h-5" />
<span>{item.label}</span>
</button>
))}
</div>
</SheetContent>
</Sheet>This panel is modal-style — it overlays the content with a black/50 backdrop and closes when the user taps outside or selects a destination. It is not a persistent sidebar visible alongside content.
---
Mobile Form Layout
Forms on mobile need extra care. Every field must be self-describing without interaction.
Single Field
<div className="space-y-1.5">
<label className="text-sm font-normal text-neutral-900">
Email address
</label>
<input
type="email"
placeholder="e.g. john@email.com"
className="w-full h-12 px-3 rounded-sm border border-neutral-200 bg-white text-sm text-neutral-900 placeholder:text-neutral-400 placeholder:font-normal focus:outline-none focus:ring-2 focus:ring-brand-500"
/>
</div>Select Field
<div className="space-y-1.5">
<label className="text-sm font-normal text-neutral-900">
Country
</label>
<select className="w-full h-12 px-3 rounded-sm border border-neutral-200 bg-white text-sm text-neutral-900 appearance-none focus:outline-none focus:ring-2 focus:ring-brand-500">
<option value="" disabled selected className="text-neutral-400">Select a country</option>
<option value="us">United States</option>
<option value="co">Colombia</option>
</select>
</div>Full Form Example
<div className="flex-1 overflow-y-auto px-4 py-3">
<div className="space-y-4">
{/* Field 1 */}
<div className="space-y-1.5">
<label className="text-sm text-neutral-900">Full name</label>
<input
type="text"
placeholder="e.g. Oscar Gonzalez"
className="w-full h-12 px-3 rounded-sm border border-neutral-200 bg-white text-sm placeholder:text-neutral-400 focus:ring-2 focus:ring-brand-500 focus:outline-none"
/>
</div>
{/* Field 2 */}
<div className="space-y-1.5">
<label className="text-sm text-neutral-900">Email</label>
<input
type="email"
placeholder="e.g. oscar@casper.studio"
className="w-full h-12 px-3 rounded-sm border border-neutral-200 bg-white text-sm placeholder:text-neutral-400 focus:ring-2 focus:ring-brand-500 focus:outline-none"
/>
</div>
{/* Field 3 - Select */}
<div className="space-y-1.5">
<label className="text-sm text-neutral-900">Role</label>
<select className="w-full h-12 px-3 rounded-sm border border-neutral-200 bg-white text-sm focus:ring-2 focus:ring-brand-500 focus:outline-none">
<option value="" disabled selected>Select role</option>
<option>Designer</option>
<option>Developer</option>
<option>Product Manager</option>
</select>
</div>
</div>
</div>Rules
- Label is always a separate element above the input —
space-y-1.5(6px) gap between label and input - Labels:
14px,font-normal(400),neutral-900 - Placeholders:
neutral-400,font-normal— must feel like a faint hint, not a label - Input height:
48px(h-12) minimum for comfortable touch targets - Between fields:
16pxgap (space-y-4) - Inputs use
rounded-sm(6px /radius-sm)
---
Pinned Bottom Button
The primary CTA that sticks to the bottom of the screen so the user's thumb can always reach it.
Basic Pinned Button
{/* This goes at the bottom of the flex column, after the scrollable content */}
<div className="shrink-0 px-4 pt-3 pb-6 bg-white border-t border-neutral-200">
<button className="w-full h-12 bg-brand-500 text-white text-sm font-medium rounded-sm hover:bg-brand-600 active:bg-brand-700 transition-colors">
Continue
</button>
</div>With Secondary Action
<div className="shrink-0 px-4 pt-3 pb-6 bg-white border-t border-neutral-200 space-y-2">
<button className="w-full h-12 bg-brand-500 text-white text-sm font-medium rounded-sm hover:bg-brand-600 active:bg-brand-700 transition-colors">
Submit
</button>
<button className="w-full h-12 bg-white border border-neutral-200 text-neutral-900 text-sm font-medium rounded-sm active:bg-neutral-50 transition-colors">
Save as Draft
</button>
</div>Rules
- Container:
shrink-0so it never collapses, white background, top border for visual separation pb-6(24px) accounts for the home indicator safe areapt-3(12px) breathing room above the button- Button:
h-12(48px), full width,brand-500background,rounded-sm - Use
active:states for press feedback — darken the background slightly on press.hover:states also work (mice and styluses exist on mobile) - If there's a secondary button, stack vertically with
space-y-2
---
Mobile List View
Tables don't work on mobile — transform them into tappable list rows.
List Row
<button className="w-full flex items-center gap-3 px-4 py-3 active:bg-neutral-50 transition-colors">
{/* Leading element — icon or avatar */}
<div className="w-10 h-10 rounded-full bg-brand-100 flex items-center justify-center shrink-0">
<FileText className="w-5 h-5 text-brand-500" />
</div>
{/* Content */}
<div className="flex-1 min-w-0 text-left">
<p className="text-sm font-medium text-neutral-900 truncate">Document Title</p>
<p className="text-xs text-neutral-500 truncate">Updated 2 hours ago</p>
</div>
{/* Trailing element — chevron or badge */}
<ChevronRight className="w-4 h-4 text-neutral-400 shrink-0" />
</button>List Container
<div className="bg-white rounded-lg border border-neutral-200 divide-y divide-neutral-200 overflow-hidden">
<ListRow />
<ListRow />
<ListRow />
</div>Rules
- Rows are full-width
buttonelements for tap handling - Minimum row height:
56px(frompy-3+ content) - Use
active:bg-neutral-50for press feedback - Trailing chevron (
ChevronRight) signals the row is tappable - Wrap list in a Card-like container with
rounded-lganddivide-y
---
Mobile Card Stack
The default layout for dashboards and content pages on mobile — vertically stacked cards.
<div className="px-4 py-3 space-y-3">
{/* Stat row — horizontal scroll */}
<div className="flex gap-3 overflow-x-auto -mx-4 px-4 pb-1">
<div className="shrink-0 w-[140px] bg-white rounded-lg border border-neutral-200 p-3">
<p className="text-xs text-neutral-500">Active Users</p>
<p className="text-xl font-medium text-neutral-950 mt-1">2,847</p>
</div>
<div className="shrink-0 w-[140px] bg-white rounded-lg border border-neutral-200 p-3">
<p className="text-xs text-neutral-500">Revenue</p>
<p className="text-xl font-medium text-neutral-950 mt-1">$12.4k</p>
</div>
<div className="shrink-0 w-[140px] bg-white rounded-lg border border-neutral-200 p-3">
<p className="text-xs text-neutral-500">Conversion</p>
<p className="text-xl font-medium text-neutral-950 mt-1">3.2%</p>
</div>
</div>
{/* Full-width content card */}
<div className="bg-white rounded-lg border border-neutral-200 p-4">
<h3 className="text-base font-medium text-neutral-950 mb-3">Recent Activity</h3>
{/* Card content */}
</div>
</div>Rules
- Cards stack vertically with
space-y-3(12px gaps) - Stat cards in a horizontal scroll row: fixed width,
overflow-x-auto, negative margin trick for edge-to-edge scroll - Full-width cards:
rounded-lg(10px /radius-lg) - No multi-column layouts — everything is single column
---
Full Screen Composition
How all the pieces come together in a complete mobile app screen.
Example: Form Screen
<div className="min-h-screen bg-neutral-100 flex items-center justify-center p-8">
<div className="relative w-[393px] h-[852px] rounded-[40px] border-[8px] border-[#1a1a1a] overflow-hidden shadow-xl">
{/* Dynamic Island */}
<div className="h-[54px] bg-white flex items-end justify-center pb-1">
<div className="w-[126px] h-[34px] bg-[#1a1a1a] rounded-full" />
</div>
{/* App content */}
<div className="h-[calc(100%-54px)] flex flex-col bg-neutral-50">
{/* Top bar */}
<header className="h-11 px-4 flex items-center gap-3 bg-white border-b border-neutral-200 shrink-0">
<button className="w-10 h-10 flex items-center justify-center -ml-2">
<ChevronLeft className="w-5 h-5 text-neutral-900" />
</button>
<h1 className="text-lg font-medium text-neutral-950">Create Account</h1>
</header>
{/* Scrollable form area */}
<div className="flex-1 overflow-y-auto px-4 py-4">
<div className="space-y-4">
<div className="space-y-1.5">
<label className="text-sm text-neutral-900">Full name</label>
<input type="text" placeholder="e.g. Oscar Gonzalez" className="w-full h-12 px-3 rounded-sm border border-neutral-200 bg-white text-sm placeholder:text-neutral-400 focus:ring-2 focus:ring-brand-500 focus:outline-none" />
</div>
<div className="space-y-1.5">
<label className="text-sm text-neutral-900">Email</label>
<input type="email" placeholder="e.g. oscar@casper.studio" className="w-full h-12 px-3 rounded-sm border border-neutral-200 bg-white text-sm placeholder:text-neutral-400 focus:ring-2 focus:ring-brand-500 focus:outline-none" />
</div>
<div className="space-y-1.5">
<label className="text-sm text-neutral-900">Password</label>
<input type="password" placeholder="At least 8 characters" className="w-full h-12 px-3 rounded-sm border border-neutral-200 bg-white text-sm placeholder:text-neutral-400 focus:ring-2 focus:ring-brand-500 focus:outline-none" />
</div>
</div>
</div>
{/* Pinned bottom button */}
<div className="shrink-0 px-4 pt-3 pb-6 bg-white border-t border-neutral-200">
<button className="w-full h-12 bg-brand-500 text-white text-sm font-medium rounded-sm hover:bg-brand-600 active:bg-brand-700 transition-colors">
Create Account
</button>
</div>
</div>
</div>
</div>Example: List Screen with Bottom Tabs
<div className="min-h-screen bg-neutral-100 flex items-center justify-center p-8">
<div className="relative w-[393px] h-[852px] rounded-[40px] border-[8px] border-[#1a1a1a] overflow-hidden shadow-xl">
{/* Dynamic Island */}
<div className="h-[54px] bg-white flex items-end justify-center pb-1">
<div className="w-[126px] h-[34px] bg-[#1a1a1a] rounded-full" />
</div>
{/* App content */}
<div className="h-[calc(100%-54px)] flex flex-col bg-neutral-50">
{/* Top bar */}
<header className="h-11 px-4 flex items-center justify-between bg-white border-b border-neutral-200 shrink-0">
<h1 className="text-[24px] font-medium text-neutral-950">Projects</h1>
<button className="w-10 h-10 flex items-center justify-center">
<Plus className="w-5 h-5 text-brand-500" />
</button>
</header>
{/* Scrollable list */}
<div className="flex-1 overflow-y-auto px-4 py-3">
<div className="bg-white rounded-lg border border-neutral-200 divide-y divide-neutral-200 overflow-hidden">
{/* List rows */}
<button className="w-full flex items-center gap-3 px-4 py-3 active:bg-neutral-50">
<div className="w-10 h-10 rounded-full bg-brand-100 flex items-center justify-center shrink-0">
<Folder className="w-5 h-5 text-brand-500" />
</div>
<div className="flex-1 min-w-0 text-left">
<p className="text-sm font-medium text-neutral-900 truncate">Client Portal Redesign</p>
<p className="text-xs text-neutral-500">Updated 2h ago</p>
</div>
<ChevronRight className="w-4 h-4 text-neutral-400 shrink-0" />
</button>
{/* More rows... */}
</div>
</div>
{/* Bottom tab bar */}
<nav className="shrink-0 bg-white border-t border-neutral-200 px-2 pb-6 pt-2">
<div className="flex items-center justify-around">
<button className="flex flex-col items-center gap-1 py-1 px-3">
<Home className="w-5 h-5 text-brand-500" />
<span className="text-[10px] text-brand-500 font-medium">Home</span>
</button>
<button className="flex flex-col items-center gap-1 py-1 px-3">
<Search className="w-5 h-5 text-neutral-400" />
<span className="text-[10px] text-neutral-400">Search</span>
</button>
<button className="flex flex-col items-center gap-1 py-1 px-3">
<Bell className="w-5 h-5 text-neutral-400" />
<span className="text-[10px] text-neutral-400">Alerts</span>
</button>
<button className="flex flex-col items-center gap-1 py-1 px-3">
<User className="w-5 h-5 text-neutral-400" />
<span className="text-[10px] text-neutral-400">Profile</span>
</button>
</div>
</nav>
</div>
</div>
</div>Composition Rules
The flex column layout (flex flex-col) is what makes everything work: 1. Top bar — shrink-0, stays at top 2. Content area — flex-1 overflow-y-auto, takes remaining space and scrolls 3. Bottom element (pinned button OR tab bar) — shrink-0, stays at bottom
This structure ensures the top and bottom elements are always visible while the middle content scrolls. When both a pinned button and tab bar are needed (rare), the button sits above the tab bar, both with shrink-0.
---
Contextual Actions
When a trigger presents multiple options, choose between a contextual menu or a bottom sheet.
Contextual Menu
For short option lists (3–6 simple actions). Uses shadcn's DropdownMenu:
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button className="w-10 h-10 flex items-center justify-center rounded-sm active:bg-neutral-100">
<MoreVertical className="w-5 h-5 text-neutral-600" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48 rounded-xl shadow-md border border-neutral-200">
<DropdownMenuItem className="flex items-center gap-2 px-3 py-2.5 text-sm">
<Edit className="w-4 h-4 text-neutral-500" />
<span>Edit</span>
</DropdownMenuItem>
<DropdownMenuItem className="flex items-center gap-2 px-3 py-2.5 text-sm">
<Share className="w-4 h-4 text-neutral-500" />
<span>Share</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="flex items-center gap-2 px-3 py-2.5 text-sm text-error-500">
<Trash className="w-4 h-4" />
<span>Delete</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Bottom Sheet
For richer content, longer option lists, or anything needing more space. Slides up from the bottom with a scrim + blur.
{/* Scrim overlay — blurs and darkens the background */}
{isOpen && (
<div
className="absolute inset-0 bg-black/50 backdrop-blur-sm z-40 transition-opacity duration-300"
onClick={onClose}
/>
)}
{/* Bottom sheet */}
<div className={cn(
"absolute bottom-0 left-0 right-0 z-50 bg-white rounded-t-[24px] transition-transform duration-300 ease-out",
isOpen ? "translate-y-0" : "translate-y-full"
)}
style={{ maxHeight: '85%' }}
>
{/* Drag indicator */}
<div className="flex justify-center pt-3 pb-2">
<div className="w-10 h-1 rounded-full bg-neutral-300" />
</div>
{/* Sheet content — scrollable if needed */}
<div className="overflow-y-auto px-4 pb-8" style={{ maxHeight: 'calc(85vh - 40px)' }}>
<h3 className="text-lg font-medium text-neutral-950 mb-4">Select an option</h3>
<div className="space-y-1">
<button className="w-full flex items-center gap-3 px-3 py-3 rounded-xl text-sm text-neutral-900 active:bg-neutral-50">
<Star className="w-5 h-5 text-neutral-500" />
<span>Add to favorites</span>
</button>
<button className="w-full flex items-center gap-3 px-3 py-3 rounded-xl text-sm text-neutral-900 active:bg-neutral-50">
<Copy className="w-5 h-5 text-neutral-500" />
<span>Duplicate</span>
</button>
<button className="w-full flex items-center gap-3 px-3 py-3 rounded-xl text-sm text-neutral-900 active:bg-neutral-50">
<Archive className="w-5 h-5 text-neutral-500" />
<span>Archive</span>
</button>
</div>
</div>
</div>Rules
- Contextual menu: Use for 3–6 simple, label-only actions. Appears next to the trigger.
- Bottom sheet: Use when options need icons + descriptions, when there are more than 6 options, or when the content is richer (forms, confirmations, previews).
- Scrim:
bg-black/50withbackdrop-blur-sm— both darkens and blurs the background. - Max height:
85%of the device frame. The sheet hugs its content if shorter — never force it taller than it needs to be. - Drag indicator:
40px × 4pxpill inneutral-300, centered at the top. - Corner radius:
rounded-t-[24px](24px) on top corners only. - Animation:
translate-ytransition,200–300ms,ease-out. - Dismissal: Tap scrim or swipe down.
/*
* Casper Studios — Design System Brand Overrides
* Tailwind CSS v4
*
* IMPORTANT: This file contains Casper-specific brand tokens (colors,
* typography, shadows, spacing). It is NOT a replacement for your
* project's globals.css or the shadcn/ui setup.
*
* Setup order:
* 1. Initialize a standard shadcn/ui project (shadcn init)
* 2. This gives you the shadcn semantic layer (--background,
* --primary, --border, @theme inline mappings, tw-animate-css, etc.)
* 3. Import or merge the tokens below into your globals.css
* alongside the shadcn defaults — they extend, not replace.
*
* The shadcn semantic variables (bg-background, text-foreground,
* bg-primary, text-primary-foreground, border-border, etc.) come
* from the shadcn setup itself. This file adds the Casper brand
* palette, typography scale, shadows, and spacing on top.
*/
@theme {
/* ─── Brand Purple ─── */
--color-brand-50: rgb(238, 229, 255);
--color-brand-100: rgb(222, 204, 255);
--color-brand-200: rgb(189, 153, 255);
--color-brand-300: rgb(156, 102, 255);
--color-brand-400: rgb(122, 51, 255);
--color-brand-500: rgb(89, 0, 255);
--color-brand-600: rgb(71, 0, 204);
--color-brand-700: rgb(54, 0, 153);
--color-brand-800: rgb(36, 0, 102);
--color-brand-900: rgb(18, 0, 51);
/* ─── Neutrals ─── */
--color-neutral-0: rgb(255, 255, 255);
--color-neutral-50: rgb(250, 250, 250);
--color-neutral-100: rgb(245, 245, 245);
--color-neutral-200: rgb(229, 229, 229);
--color-neutral-300: rgb(212, 212, 212);
--color-neutral-400: rgb(163, 163, 163);
--color-neutral-500: rgb(115, 115, 115);
--color-neutral-600: rgb(82, 82, 82);
--color-neutral-700: rgb(64, 64, 64);
--color-neutral-800: rgb(38, 38, 38);
--color-neutral-900: rgb(23, 23, 23);
--color-neutral-950: rgb(10, 10, 10);
/* ─── Error (Red) ─── */
--color-error-50: rgb(254, 242, 242);
--color-error-100: rgb(254, 226, 226);
--color-error-200: rgb(254, 202, 202);
--color-error-300: rgb(252, 165, 165);
--color-error-400: rgb(248, 113, 113);
--color-error-500: rgb(239, 68, 68);
--color-error-600: rgb(220, 38, 38);
--color-error-700: rgb(185, 28, 28);
--color-error-800: rgb(153, 27, 27);
--color-error-900: rgb(127, 29, 29);
/* ─── Success (Green) ─── */
--color-success-50: rgb(240, 253, 244);
--color-success-100: rgb(220, 252, 231);
--color-success-200: rgb(187, 247, 208);
--color-success-300: rgb(134, 239, 172);
--color-success-400: rgb(74, 222, 128);
--color-success-500: rgb(34, 197, 94);
--color-success-600: rgb(22, 163, 74);
--color-success-700: rgb(21, 128, 61);
--color-success-800: rgb(22, 101, 52);
--color-success-900: rgb(20, 83, 45);
/* ─── Warning (Amber) ─── */
--color-warning-50: rgb(255, 251, 235);
--color-warning-100: rgb(254, 243, 199);
--color-warning-200: rgb(253, 230, 138);
--color-warning-300: rgb(252, 211, 77);
--color-warning-400: rgb(251, 191, 36);
--color-warning-500: rgb(245, 158, 11);
--color-warning-600: rgb(217, 119, 6);
--color-warning-700: rgb(180, 83, 9);
--color-warning-800: rgb(146, 64, 14);
--color-warning-900: rgb(120, 53, 15);
/* ─── Semantic Aliases ─── */
--color-default-font: rgb(23, 23, 23);
--color-subtext-color: rgb(115, 115, 115);
--color-neutral-border: rgb(229, 229, 229);
--color-page-background: rgb(250, 250, 250);
--color-card-background: rgb(255, 255, 255);
/* ─── Typography ─── */
--text-caption: 12px;
--text-caption--font-weight: 400;
--text-caption--letter-spacing: 0em;
--text-caption--line-height: 16px;
--text-caption-bold: 12px;
--text-caption-bold--font-weight: 500;
--text-caption-bold--letter-spacing: 0em;
--text-caption-bold--line-height: 16px;
--text-body: 14px;
--text-body--font-weight: 400;
--text-body--letter-spacing: 0em;
--text-body--line-height: 20px;
--text-body-bold: 14px;
--text-body-bold--font-weight: 500;
--text-body-bold--letter-spacing: 0em;
--text-body-bold--line-height: 20px;
--text-heading-3: 16px;
--text-heading-3--font-weight: 500;
--text-heading-3--letter-spacing: 0em;
--text-heading-3--line-height: 20px;
--text-heading-2: 20px;
--text-heading-2--font-weight: 500;
--text-heading-2--letter-spacing: 0em;
--text-heading-2--line-height: 24px;
--text-heading-1: 30px;
--text-heading-1--font-weight: 500;
--text-heading-1--letter-spacing: 0em;
--text-heading-1--line-height: 36px;
/* ─── Mobile Typography Overrides ─── */
--text-mobile-heading-1: 24px;
--text-mobile-heading-1--font-weight: 500;
--text-mobile-heading-1--letter-spacing: 0em;
--text-mobile-heading-1--line-height: 30px;
--text-mobile-heading-2: 18px;
--text-mobile-heading-2--font-weight: 500;
--text-mobile-heading-2--letter-spacing: 0em;
--text-mobile-heading-2--line-height: 24px;
--text-monospace-body: 14px;
--text-monospace-body--font-weight: 400;
--text-monospace-body--letter-spacing: 0em;
--text-monospace-body--line-height: 20px;
/* ─── Font Families ─── */
--font-caption: "DM Sans", sans-serif;
--font-caption-bold: "DM Sans", sans-serif;
--font-body: "DM Sans", sans-serif;
--font-body-bold: "DM Sans", sans-serif;
--font-heading-3: "DM Sans", sans-serif;
--font-heading-2: "DM Sans", sans-serif;
--font-heading-1: "DM Sans", sans-serif;
--font-monospace-body: monospace;
/* ─── Border Radius ───
Uses the shadcn/ui radius system. A single `--radius` base variable in
:root controls the entire scale via calc(). The @theme inline block
below maps these into Tailwind utility classes.
No Tailwind defaults are overridden — this is the shadcn pattern. */
/* ─── Shadows ─── */
--shadow-sm: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);
--shadow-default: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);
--shadow-md:
0px 4px 16px -2px rgba(0, 0, 0, 0.08), 0px 2px 4px -1px rgba(0, 0, 0, 0.08);
--shadow-lg:
0px 12px 32px -4px rgba(0, 0, 0, 0.08), 0px 4px 8px -2px rgba(0, 0, 0, 0.08);
/* shadow-overlay is intentionally identical to shadow-lg — it's a semantic
alias so code reads "shadow-overlay" on modals/sheets for clarity. */
--shadow-overlay:
0px 12px 32px -4px rgba(0, 0, 0, 0.08), 0px 4px 8px -2px rgba(0, 0, 0, 0.08);
/* ─── Extended Spacing ─── */
--spacing-112: 28rem;
--spacing-144: 36rem;
--spacing-192: 48rem;
--spacing-256: 64rem;
--spacing-320: 80rem;
}
/* ─── Dark Mode Overrides ─── */
/* Apply by adding class="dark" to <html> or a wrapper element.
The neutral scale inverts so existing utility classes (text-neutral-900,
bg-neutral-50, border-neutral-200, etc.) automatically adapt. Brand and
semantic colors shift to dark-friendly tints.
IMPORTANT: `white` and `black` are NOT overridden here. Tailwind's
built-in `bg-white`, `text-white`, `bg-black`, `text-black` always
resolve to literal white/black regardless of mode. This is intentional —
use `bg-white`/`text-white` when you mean actual white (e.g., button
text on a colored bg). Use semantic tokens like `bg-neutral-0` or
`bg-neutral-50` for surfaces that should invert in dark mode. */
.dark {
/* Neutrals — inverted scale */
--color-neutral-0: rgb(10, 10, 10);
--color-neutral-50: rgb(23, 23, 23);
--color-neutral-100: rgb(38, 38, 38);
--color-neutral-200: rgb(64, 64, 64);
--color-neutral-300: rgb(82, 82, 82);
--color-neutral-400: rgb(163, 163, 163);
--color-neutral-500: rgb(115, 115, 115);
--color-neutral-600: rgb(212, 212, 212);
--color-neutral-700: rgb(229, 229, 229);
--color-neutral-800: rgb(245, 245, 245);
--color-neutral-900: rgb(250, 250, 250);
--color-neutral-950: rgb(255, 255, 255);
/* Brand — lighten accent, darken tint backgrounds */
--color-brand-50: rgb(18, 0, 51);
--color-brand-100: rgb(36, 0, 102);
--color-brand-500: rgb(122, 51, 255);
/* Semantic — darken pastel backgrounds, keep icon/badge colors */
--color-error-50: rgb(30, 8, 8);
--color-success-50: rgb(8, 30, 14);
--color-warning-50: rgb(30, 24, 8);
/* Aliases */
--color-page-background: rgb(10, 10, 10);
--color-card-background: rgb(23, 23, 23);
--color-default-font: rgb(250, 250, 250);
--color-subtext-color: rgb(163, 163, 163);
--color-neutral-border: rgb(64, 64, 64);
/* Shadows — increased opacity for visibility on dark backgrounds */
--shadow-sm: 0px 1px 2px 0px rgba(0, 0, 0, 0.2);
--shadow-default: 0px 1px 2px 0px rgba(0, 0, 0, 0.2);
--shadow-md:
0px 4px 16px -2px rgba(0, 0, 0, 0.3), 0px 2px 4px -1px rgba(0, 0, 0, 0.2);
--shadow-lg:
0px 12px 32px -4px rgba(0, 0, 0, 0.4), 0px 4px 8px -2px rgba(0, 0, 0, 0.3);
--shadow-overlay:
0px 12px 32px -4px rgba(0, 0, 0, 0.4), 0px 4px 8px -2px rgba(0, 0, 0, 0.3);
}
/* ─── Container ─── */
@utility container {
padding-left: 16px;
padding-right: 16px;
@media (width >= theme(--breakpoint-sm)) {
padding-left: calc((100vw + 16px - 640px) / 2);
padding-right: calc((100vw + 16px - 640px) / 2);
}
@media (width >= theme(--breakpoint-md)) {
padding-left: calc((100vw + 16px - 768px) / 2);
padding-right: calc((100vw + 16px - 768px) / 2);
}
@media (width >= theme(--breakpoint-lg)) {
padding-left: calc((100vw + 16px - 1024px) / 2);
padding-right: calc((100vw + 16px - 1024px) / 2);
}
@media (width >= theme(--breakpoint-xl)) {
padding-left: calc((100vw + 16px - 1280px) / 2);
padding-right: calc((100vw + 16px - 1280px) / 2);
}
@media (width >= theme(--breakpoint-2xl)) {
padding-left: calc((100vw + 16px - 1536px) / 2);
padding-right: calc((100vw + 16px - 1536px) / 2);
}
}
@custom-variant mobile (@media (max-width: 767px));
/* ─── shadcn/ui Radius System ───
A single --radius variable controls the entire border-radius scale.
Change this one value and all components adjust proportionally.
Default: 0.625rem (10px) — the shadcn default. */
:root {
--radius: 0.625rem;
}
@theme inline {
--radius-sm: calc(
var(--radius) - 4px
); /* 6px — inputs, small buttons, inner elements */
--radius-md: calc(var(--radius) - 2px); /* 8px — popovers, tooltips */
--radius-lg: var(--radius); /* 10px — cards, panels, large containers */
--radius-xl: calc(
var(--radius) + 4px
); /* 14px — modals, hero cards, dialogs */
}
Related skills
How it compares
Pick casper-design-system when Casper-branded shadcn/ui output matters more than generic frontend-design aesthetic freedom.
FAQ
Which reference files must casper-design-system read first?
casper-design-system requires references/components.md and references/theme.css for every project, plus references/web-layouts.md for web apps or references/mobile.md for mobile apps. Skipping these files produces off-brand UI according to the SKILL.md.
What tech stack does casper-design-system target?
casper-design-system targets React with Vite and TypeScript, Tailwind CSS v4 using references/theme.css tokens, and shadcn/ui components initialized via shadcn init. Lucide React is the required icon library.