
Cal.com is a Next.js (App Router) monorepo built with TypeScript. The UI layer relies on Tailwind CSS v4 (with the @import "tailwindcss" pattern), supplemented by Radix UI primitives, Class Variance Authority (CVA) for variant management, and tailwindcss-radix for state-driven utilities. Components are distributed from a shared packages/ui workspace package. The platform ships a desktop app via ToDesktop, embeddable scheduling atoms via packages/platform/atoms, and a shadow-DOM-safe embed bundle โ all consuming the same design-token layer.
Cal.com uses two custom variable typefaces it designed in-house, referenced via CSS custom properties:
--font-cal) โ display / heading face. A geometric sans-serif with a distinctive rounded "a". Used for hero text and marketing headings.--font-sans) โ UI body face. High-legibility, optimised for dense scheduling interfaces.Font smoothing is enabled globally (-webkit-font-smoothing: antialiased, text-rendering: optimizeLegibility). Button text is consistently text-sm font-medium (14 px, weight 500). Scale variants in Button: xs โ 12 px, sm/base โ 14 px, lg โ 14 px with larger padding.
Cal.com uses a semantic HSL token system with two tiers:
--cal-bg, --cal-text, --cal-border with four intensity levels each: emphasis / default / subtle / muted.-subtle (tinted backgrounds) and -emphasis (vivid accent) variants.The brand color in light mode is near-black (hsl(221, 39%, 11%)), and flips to white in dark mode โ enabling a single --cal-brand variable to power both themes without conditional logic at the component level. Visualization palette covers 7 distinct hues for charts and calendars.
Spacing follows the default Tailwind 4-point grid (1 unit = 0.25 rem = 4 px). Padding conventions observed in components: buttons use px-2.5 py-2 (base), px-3 py-2.5 (lg), p-2 (xs). Layout sections use 4 / 6 / 8 / 12 / 16-unit steps. A custom stack-y-_ utility recreates Tailwind v3 space-y-_ semantics for backwards compatibility during the v4 migration.
Buttons default to variant="button", color="primary", size="base", and rounded-[10px] (custom 10 px radius, overriding the global --radius). CVA drives all variant combinations. Key component patterns:
primary (brand-filled), secondary (outline), minimal (ghost), destructive (red outline).button, icon, fab (floating action, responsive).shadow-button-solid-brand-focused box-shadow tokens instead of native browser outlines.--brand-color.The app uses a sidebar + main content layout pattern with a draggable header region on the desktop app. Max-width containers are used for the marketing site. Mobile breakpoint hides chat FABs and adjusts Beacon/Zendesk/Intercom widgets at 768 px. The booking UI is a responsive date picker / time slot grid. RTL (right-to-left) support is implemented via html[dir="rtl"] overrides (e.g., phone flag dropdown repositioning).
/\* ============================================================
Cal.com Design Token CSS Variables
Source: packages/config/theme/tokens.css
============================================================ \*/
:root,
:host {
/_ --- Border Radius --- _/
--radius: 0.25rem; /_ 4px โ default _/
--radius-sm: 0.125rem; /_ 2px _/
--radius-md: 0.375rem; /_ 6px _/
--radius-lg: 0.5rem; /_ 8px _/
--radius-xl: 0.75rem; /_ 12px _/
--radius-2xl: 1rem; /_ 16px _/
--radius-3xl: 1.5rem; /_ 24px _/
--radius-none: 0px;
/_ Button component uses rounded-[10px] = 10px, not a token _/
/_ --- Background: Standard --- _/
--cal-bg: hsla(0, 0%, 100%, 1); /_ white _/
--cal-bg-emphasis: hsla(220, 13%, 91%, 1); /_ light gray _/
--cal-bg-subtle: hsla(220, 14%, 94%, 1); /_ off-white _/
--cal-bg-muted: hsla(210, 20%, 97%, 1); /_ near-white _/
--cal-bg-inverted: hsla(210, 30%, 4%, 1); /_ near-black _/
/_ --- Background: Primary --- _/
--cal-bg-primary: hsla(214, 30%, 16%, 1);
--cal-bg-primary-emphasis: hsla(220, 6%, 25%, 1);
--cal-bg-primary-muted: hsla(220, 14%, 94%, 1);
/_ --- Background: Semantic --- _/
--cal-bg-semantic-info-subtle: hsla(212, 88%, 97%, 1);
--cal-bg-semantic-info-emphasis: hsla(236, 80%, 25%, 1);
--cal-bg-semantic-success-subtle: hsla(167, 54%, 93%, 1);
--cal-bg-semantic-success-emphasis: hsla(158, 74%, 38%, 1);
--cal-bg-semantic-attention-subtle: hsla(34, 100%, 92%, 1);
--cal-bg-semantic-attention-emphasis: hsla(15, 79%, 34%, 1);
--cal-bg-semantic-error-subtle: hsla(0, 93%, 94%, 1);
--cal-bg-semantic-error-emphasis: hsla(0, 63%, 24%, 1);
/_ --- Background: Visualization (7-hue palette) --- _/
--cal-bg-visualization-1-subtle: hsla(326, 78%, 95%, 1);
--cal-bg-visualization-1-emphasis: hsla(330, 81%, 60%, 1); /_ pink _/
--cal-bg-visualization-2-subtle: hsla(256, 86%, 91%, 1);
--cal-bg-visualization-2-emphasis: hsla(256, 85%, 57%, 1); /_ purple _/
--cal-bg-visualization-3-subtle: hsla(217, 87%, 91%, 1);
--cal-bg-visualization-3-emphasis: hsla(235, 100%, 63%, 1);/_ blue _/
--cal-bg-visualization-4-subtle: hsla(167, 54%, 93%, 1);
--cal-bg-visualization-4-emphasis: hsla(158, 74%, 38%, 1); /_ green _/
--cal-bg-visualization-5-subtle: hsla(55, 97%, 88%, 1);
--cal-bg-visualization-5-emphasis: hsla(45, 93%, 47%, 1); /_ yellow _/
--cal-bg-visualization-6-subtle: hsla(34, 100%, 92%, 1);
--cal-bg-visualization-6-emphasis: hsla(25, 95%, 53%, 1); /_ orange _/
--cal-bg-visualization-7-subtle: hsla(0, 96%, 89%, 1);
--cal-bg-visualization-7-emphasis: hsla(0, 84%, 60%, 1); /_ red _/
/_ --- Legacy Background Components --- _/
--cal-bg-info: hsla(221, 91%, 93%, 1);
--cal-bg-success: hsla(142, 76%, 94%, 1);
--cal-bg-attention: hsla(33, 100%, 92%, 1);
--cal-bg-error: hsla(3, 66%, 93%, 1);
--cal-bg-dark-error: hsla(2, 55%, 30%, 1);
/_ --- Borders --- _/
--cal-border: hsla(216, 12%, 84%, 1);
--cal-border-emphasis: hsla(218, 11%, 65%, 1);
--cal-border-subtle: hsla(220, 13%, 91%, 1);
--cal-border-muted: hsla(220, 14%, 94%, 1);
--cal-border-error: hsla(0, 96%, 89%, 1);
--cal-border-booker: var(--cal-border-subtle);
/_ --- Text / Content --- _/
--cal-text: hsla(220, 6%, 25%, 1); /_ body text _/
--cal-text-emphasis: hsla(210, 30%, 4%, 1); /_ headings _/
--cal-text-subtle: hsla(220, 9%, 46%, 1); /_ secondary text _/
--cal-text-muted: hsla(218, 11%, 65%, 1); /_ placeholder _/
--cal-text-inverted: hsla(0, 0%, 100%, 1); /_ on dark bg _/
/_ Text Semantic _/
--cal-text-semantic-info: hsla(236, 80%, 25%, 1);
--cal-text-semantic-success: hsla(150, 84%, 22%, 1);
--cal-text-semantic-attention: hsla(15, 79%, 34%, 1);
--cal-text-semantic-error: hsla(0, 63%, 24%, 1);
/_ --- Brand --- _/
--cal-brand: hsla(221, 39%, 11%, 1); /_ near-black _/
--cal-brand-emphasis: hsla(0, 0%, 6%, 1); /_ darker black _/
--cal-brand-text: hsla(0, 0%, 100%, 1); /_ white on brand _/
/_ --- Box Shadows --- _/
--shadow-dropdown: 0px 5px 20px 0px rgba(0,0,0,0.10),
0px 10px 40px 0px rgba(0,0,0,0.03);
--shadow-elevation-low: 0px 1px 1px 0px rgba(0,0,0,0.07),
0px 1px 2px 0px rgba(0,0,0,0.08),
0px 2px 2px 0px rgba(0,0,0,0.10),
0px 0px 8px 0px rgba(0,0,0,0.05);
--shadow-button-solid-brand-default:
0px 2px 3px 0px rgba(0,0,0,0.06),
0px 1px 1px 0px rgba(0,0,0,0.08),
1px 4px 8px 0px rgba(0,0,0,0.12),
0px 2px 0.4px 0px rgba(255,255,255,0.12) inset,
0px -3px 2px 0px rgba(0,0,0,0.04) inset;
--shadow-button-solid-brand-hover:
0px 1px 1px 0px rgba(0,0,0,0.10),
0px 2px 3px 0px rgba(0,0,0,0.08),
1px 4px 8px 0px rgba(0,0,0,0.12),
0px -3px 2px 0px rgba(0,0,0,0.10) inset,
0px 2px 0.4px 0px rgba(255,255,255,0.24) inset;
--shadow-button-solid-brand-active:
0px 3px 1px 0px rgba(0,0,0,0.10) inset,
0px 0px 2px 0px rgba(0,0,0,0.10) inset;
--shadow-button-solid-brand-focused:
0px 0px 0px 1px rgba(255,255,255,0.40),
0px 0px 0px 2px rgba(0,0,0,0.20),
0px 1px 1px 0px rgba(0,0,0,0.10),
0px 2px 3px 0px rgba(0,0,0,0.08),
1px 4px 8px 0px rgba(0,0,0,0.12),
0px -3px 2px 0px rgba(0,0,0,0.10) inset,
0px 2px 0.4px 0px rgba(255,255,255,0.24) inset;
--shadow-outline-gray-rested: 0px 2px 3px 0px rgba(0,0,0,0.03),
0px 2px 2px -1px rgba(0,0,0,0.03);
--shadow-outline-gray-focused: 0px 0px 0px 1px rgba(255,255,255,0.20),
0px 0px 0px 2px rgba(0,0,0,0.10);
/_ --- Animations --- _/
--animate-fade-in-up: fade-in-up 600ms var(--animation-delay, 0ms)
cubic-bezier(0.21, 1.02, 0.73, 1) forwards;
--animate-spinning: spinning 0.75s linear infinite;
--animate-drawer-slide-left-and-fade:
drawerSlideLeftAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
/\* ============================================================
DARK MODE OVERRIDES (.dark class on )
============================================================ \*/
.dark {
/_ Background Standard _/
--cal-bg: hsla(0, 0%, 6%, 1); /_ #0F0F0F _/
--cal-bg-emphasis: hsla(0, 0%, 25%, 1); /_ dark gray _/
--cal-bg-subtle: hsla(0, 0%, 15%, 1); /_ #262626 _/
--cal-bg-muted: hsla(0, 0%, 9%, 1); /_ #171717 _/
--cal-bg-inverted: hsla(0, 0%, 98%, 1); /_ near-white _/
/_ Background Primary _/
--cal-bg-primary: hsla(0, 0%, 96%, 1);
--cal-bg-primary-emphasis: hsla(0, 0%, 64%, 1);
--cal-bg-primary-muted: hsla(0, 0%, 15%, 1);
/_ Background Semantic โ dark _/
--cal-bg-semantic-info-subtle: hsla(236, 80%, 8%, 1);
--cal-bg-semantic-info-emphasis: hsla(229, 90%, 74%, 1);
--cal-bg-semantic-success-subtle: hsla(148, 88%, 16%, 1);
--cal-bg-semantic-success-emphasis: hsla(158, 74%, 38%, 1);
--cal-bg-semantic-attention-subtle: hsla(21, 86%, 8%, 1);
--cal-bg-semantic-attention-emphasis: hsla(27, 96%, 61%, 1);
--cal-bg-semantic-error-subtle: hsla(0, 70%, 8%, 1);
--cal-bg-semantic-error-emphasis: hsla(0, 91%, 71%, 1);
/_ Borders โ dark _/
--cal-border: hsla(0, 0%, 30%, 1);
--cal-border-emphasis: hsla(0, 0%, 45%, 1);
--cal-border-subtle: hsla(0, 0%, 15%, 1);
--cal-border-muted: hsla(0, 0%, 9%, 1);
--cal-border-error: hsla(0, 63%, 24%, 1);
/_ Text โ dark _/
--cal-text: hsla(0, 0%, 83%, 1);
--cal-text-emphasis: hsla(0, 0%, 98%, 1);
--cal-text-subtle: hsla(0, 0%, 64%, 1);
--cal-text-muted: hsla(0, 0%, 64%, 1);
--cal-text-inverted: hsla(0, 0%, 0%, 1);
--cal-text-semantic-info: hsla(229, 90%, 74%, 1);
--cal-text-semantic-success: hsla(161, 49%, 54%, 1);
--cal-text-semantic-attention: hsla(27, 96%, 61%, 1);
--cal-text-semantic-error: hsla(0, 91%, 71%, 1);
/_ Brand โ dark (inverted!) _/
--cal-brand: hsla(0, 0%, 100%, 1); /_ white _/
--cal-brand-emphasis: hsla(218, 11%, 65%, 1);
--cal-brand-text: hsla(0, 0%, 0%, 1); /_ black on brand _/
}
| Token | Light Value | Usage |
|---|---|---|
--cal-brand | hsl(221, 39%, 11%) โ near-black | Primary CTA background, brand identity |
--cal-brand-text | hsl(0, 0%, 100%) โ white | Text/icons on brand-colored surfaces |
--cal-brand-emphasis | hsl(0, 0%, 6%) โ near-black | Hover state on brand button |
--cal-bg | hsl(0, 0%, 100%) โ white | Default page background |
--cal-bg-emphasis | hsl(220, 13%, 91%) | Hover/active surface highlights |
--cal-bg-subtle | hsl(220, 14%, 94%) | Sidebar, card backgrounds |
--cal-bg-muted | hsl(210, 20%, 97%) | Input backgrounds, code blocks |
--cal-bg-inverted | hsl(210, 30%, 4%) | Dark overlays, tooltips on light bg |
--cal-text | hsl(220, 6%, 25%) | Body text |
--cal-text-emphasis | hsl(210, 30%, 4%) | Headings, high-contrast labels |
--cal-text-subtle | hsl(220, 9%, 46%) | Secondary / helper text |
--cal-text-muted | hsl(218, 11%, 65%) | Placeholder text, disabled labels |
--cal-text-inverted | hsl(0, 0%, 100%) | Text on dark/inverted backgrounds |
--cal-border | hsl(216, 12%, 84%) | Default input/card borders |
--cal-border-emphasis | hsl(218, 11%, 65%) | Focused/active borders |
--cal-border-subtle | hsl(220, 13%, 91%) | Dividers, subtle separators |
--cal-bg-semantic-info-subtle | hsl(212, 88%, 97%) | Info banner background |
--cal-bg-semantic-success-emphasis | hsl(158, 74%, 38%) | Success badge/icon fill |
--cal-bg-semantic-attention-subtle | hsl(34, 100%, 92%) | Warning banner background |
--cal-bg-semantic-error-subtle | hsl(0, 93%, 94%) | Error banner background |
--cal-bg-visualization-1-emphasis | hsl(330, 81%, 60%) โ pink | Chart series 1 |
--cal-bg-visualization-2-emphasis | hsl(256, 85%, 57%) โ purple | Chart series 2 |
--cal-bg-visualization-3-emphasis | hsl(235, 100%, 63%) โ blue | Chart series 3 |
--cal-bg-visualization-4-emphasis | hsl(158, 74%, 38%) โ green | Chart series 4 |
--cal-bg-visualization-5-emphasis | hsl(45, 93%, 47%) โ yellow | Chart series 5 |
--cal-bg-visualization-6-emphasis | hsl(25, 95%, 53%) โ orange | Chart series 6 |
--cal-bg-visualization-7-emphasis | hsl(0, 84%, 60%) โ red | Chart series 7 |
| Token | Value | Usage |
|---|---|---|
| Button xs padding | p-2 (8px) | Extra-small buttons |
| Button sm padding | px-2 py-1.5 (8px / 6px) | Small buttons |
| Button base padding | px-2.5 py-2 (10px / 8px) | Default buttons |
| Button lg padding | px-3 py-2.5 (12px / 10px) | Large buttons |
| Base spacing unit | 4px (0.25rem) | Tailwind 4-point grid |
| Mobile breakpoint | 768px | Hide chat FABs, responsive layouts |
| Token | Value | Usage |
|---|---|---|
--font-cal | Cal Sans (variable) | Display headings, marketing hero text |
--font-sans | Cal Sans UI / Inter (variable) | UI body text, form labels, buttons |
| Button font size | text-sm (14px) | All button sizes (xs uses 12px) |
| Button font weight | font-medium (500) | All buttons |
| Font smoothing | antialiased + optimizeLegibility | Applied globally on body |
| Token | Value | Usage |
|---|---|---|
--radius | 0.25rem (4px) | Default global radius |
--radius-sm | 0.125rem (2px) | Small chips, tags |
--radius-md | 0.375rem (6px) | Inputs, small cards |
--radius-lg | 0.5rem (8px) | Cards, modals |
--radius-xl | 0.75rem (12px) | Large panels |
--radius-2xl | 1rem (16px) | Feature cards |
--radius-3xl | 1.5rem (24px) | Hero sections, pills |
| Button radius (hardcoded) | 10px (rounded-[10px]) | All standard button shapes |
| FAB button radius | 9999px โ md: 10px | Floating action button (mobile full-circle) |
| Token | Value | Usage |
|---|---|---|
--shadow-dropdown | 5px/10px layered rgba blacks | Dropdown menus, popovers |
--shadow-elevation-low | 4-layer subtle black rgba | Cards, floating elements |
--shadow-button-solid-brand-default | Layered + inset highlight | Primary button resting state |
--shadow-button-solid-brand-hover | Elevated + stronger inset | Primary button hover |
--shadow-button-solid-brand-active | Deep inset only | Primary button pressed |
--shadow-button-solid-brand-focused | Outer ring + inner glow | Primary button keyboard focus |
--shadow-outline-gray-rested | Soft 2px spread | Secondary button resting |
--shadow-outline-gray-focused | White ring + black ring | Secondary button keyboard focus |
--shadow-switch-thumb | Tiny dual-layer | Toggle switch thumb |
Framework & Tooling
@import "tailwindcss" entry and @theme inline blocks for token-to-utility mapping.<Icon name="..." /> wrapper component..todesktop-platform-darwin, -win32, -linux).Theme Support
.dark class on <html> โ no media-query-only approach.rgba(255,255,255,0.8) / rgba(0,0,0,0.6) frosted glass backgrounds on macOS via transparent windows.tokens.css file, ensuring full design parity for the embeddable booking widget.Data Confidence
packages/config/theme/tokens.css in the public GitHub repo (main branch, July 2025).packages/ui/components/button/Button.tsx), including all CVA variant definitions.--font-cal, --font-sans) and the homepage footer referencing "Cal Sans UI & Text โ Our own variable typeface for user interface design."tailwind-preset.js file returned empty from GitHub raw.Notable Patterns
sm/md/lg โ they are named by interaction state (-rested, -hover, -active, -focused) and component type (solid-brand, outline-gray, outline-red). This creates highly consistent micro-interactions.emphasis / default / subtle / muted) mirror the background and text scales โ a cohesive tri-scale system across all three property types.html[dir="rtl"] overrides.stack-y-_ utility layer recreates Tailwind v3 space-y-_ behavior to ease the v3 โ v4 migration.

.webp)


.webp)













SaaSFrame subscriptions are sold on Lemon Squeezy, a secured online selling platform for creators.
SaaSFrame gets updated all the time! Every new update and version will immediately be made available to all SaaSFrame users.
You can generate an invoice on your Lemon Squeezy portal once you purchased SaaSFrame. You will find the link in the order confirmation email.
You can cancel your membership subscription at any moment on your profile page.โ
If you're not satisfied, you can ask for a refund within 7 days. Just send me an email at antoine[at]unmake[dot]io
You can send me an email at antoine[at]unmake[dot]io, I'll be happy to help with any questions.