Airbnb Design System
Overview
The Airbnb homepage presents a meticulously calibrated balance between warm hospitality and modern digital efficiency. Built on a pristine {colors.canvas} white foundation, the design breathes through generous vertical rhythm while maintaining information density across destination grids and multi-segmented search interfaces. The brand's emotional core lives in {colors.primary} — a distinctive coral-pink that pulses through the circular search button, the logo mark, and interactive highlights, creating instant recognition without overwhelming the neutral-heavy palette.
The typographic voice is carried entirely by Airbnb Cereal VF, a variable font family that enables smooth weight interpolation across the system. This is not a design that relies on boldness for hierarchy; instead, it deploys size contrast ({typography.display-lg} at 28px/700 against {typography.body-md} at 14px/400) and the strategic use of {colors.body} gray for secondary information. Headlines feel confident but never aggressive, body text remains highly readable at 14px with a comfortable 1.43 line-height, and labels gain just enough weight (500) to guide without commanding.
Shape language is defined by the pill metaphor: the search bar wraps in a {rounded.lg} 32px radius, individual search segments balloon to {rounded.xl} 50px, and the primary action button becomes a perfect circle ({rounded.full}). This soft geometry extends to category tabs ({rounded.md} 20px) and modal surfaces, creating a visual vocabulary that feels approachable, human, and distinctly un-corporate. Even the cookie consent modal — typically a cold legal afterthought — adopts the same rounded warmth, framed by {rounded.DEFAULT} 8px corners and breathing space.
The page operates as a layered information system: a persistent top navigation anchors the experience, followed by an elevated search bar that acts as the page's gravitational center, then category filtration, then a dense destination grid. A modal overlay interrupts this flow for consent/cookie management, using {extensions.shadows.md} depth to separate itself from the underlying content. Each layer has its own interaction density — the nav is sparse and navigational, the search is complex and segmented, the grid is scannable and card-based.
Key Characteristics:
- Canvas is pure
{colors.canvas}white — no tints, no gradients, no texture. - Brand voltage concentrates in
{colors.primary}#ff385c on the search button, logo, and interactive accents. - All text uses Airbnb Cereal VF variable font, falling back to Circular then system fonts.
- Display headings hit
{typography.display-lg}at 28px/700 weight; body settles at{typography.body-md}14px/400. - Dominant radius is pill-shaped:
{rounded.lg}(32px) for containers,{rounded.full}(50%) for icon actions. - Spacing operates on an 8px-ish base unit with
{spacing.section}48px defining major vertical rhythm. - Secondary text fades to
{colors.body}#6c6c6g; never lighter than this for readability. - Shadows are rare but meaningful:
{extensions.shadows.sm}for cards,{extensions.shadows.md}for modals. - The search bar is the hero element — 66px tall, segmented, floating above the content flow.
Colors
Brand & Accent
- Primary (
{colors.primary}— #ff385c): Airbnb's signature coral-pink. Used exclusively for the search button background, logo mark, and high-affordance interactive elements. This is the only color that carries brand emotion. - Primary Hover (
{colors.primary-hover}— #e03151): Darkened coral for hover states on primary buttons and interactive elements. - Primary Active (
{colors.primary-active}— #da1249): Deepest press-state coral, used when a primary button is actively being clicked or tapped.
Surface
- Canvas (
{colors.canvas}— #ffffff): The absolute page background. Every section begins here. No tinting, no noise, no gradient washes. - Surface Soft (
{colors.surface-soft}— #f7f7f7): Barely-visible gray used for subtle section alternation or hovered row backgrounds. - Surface Elevated (
{colors.surface-elevated}— #f2f2f2): Light gray for icon button backgrounds, input hover states, and secondary surface areas. - Surface Strong (
{colors.surface-strong}— #dddddd): Medium gray for dividers, disabled-looking backgrounds, or strong hover feedback on neutral surfaces. - Surface Dark (
{colors.surface-dark}— #222222): Near-black used for modal primary buttons ("Accepter tout", "Uniquement les cookies nécessaires") where inverted treatment creates emphasis.
Text
- Ink (
{colors.ink}— #222222): The darkest text color, used for headlines, primary navigation links, search values, destination names, and any copy requiring maximum readability and emphasis. - Body (
{colors.body}— #6c6c6c): Medium-gray for secondary descriptions, destination subtitles ("Locations de villas", "Locations de vacances"), placeholder text, and supporting information that shouldn't compete with primary content. - Muted (
{colors.muted}— #717171): Slightly lighter gray for tertiary text — timestamps, metadata, or extremely de-emphasized labels. - On Primary (
{colors.on-primary}— #ffffff): Pure white text rendered atop{colors.primary}coral backgrounds (search button) or{colors.surface-dark}near-black buttons. Satisfies AA contrast requirements against both. - On Dark (
{colors.on-dark}— #ffffff): White text for any element sitting on{colors.surface-dark}or darker backgrounds.
Hairlines & Borders
- Hairline (
{colors.hairline}— #dddddd): Standard border color for dividing lines between search segments, card separators, and structural borders. Matches{colors.surface-strong}for cohesion. - Hairline Soft (
{colors.hairline-soft}— #ebebeb): Extremely subtle divider for situations needing even less visual weight than standard hairline — inner component boundaries or decorative lines. - Border Focus (
{colors.border-focus}— #222222): Used for focus ring outlines (the outer ring of the dual-ring focus indicator). Matches{colors.ink}for consistency.
Semantic
- Error (
{colors.error}— #da1249): Deep red-pink for error states, validation failures, or destructive actions. Note: this maps closely to{colors.primary-active}in hue but carries different semantic intent. - Link (
{colors.link}— #222222): Links use{colors.ink}rather than a distinct blue or underline. Differentiation comes from context and hover underlining behavior.
Typography
Font Family
The entire system runs on Airbnb Cereal VF, a custom variable font served directly from Airbnb's CDN (a0.muscache.com). This is a variable font (VF) supporting weight interpolation along a continuous axis, which means the design can specify exact weights like 500 or 700 rather than being limited to named instances. The font stack includes robust fallbacks: Circular (Airbnb's previous corporate face), then -apple-system, BlinkMacSystemFont, Roboto, and finally "Helvetica Neue" and sans-serif. Both upright and italic styles are loaded via separate @font-face declarations, along with localized subsets for Arabic, Cyrillic, Hebrew, Devanagari, Thai, and Greek scripts — indicating the system's global reach.
Cereal's personality is geometric-humanist: clean apertures, slightly rounded terminals, open counters that maintain readability at small sizes (14px body, 12px captions), and enough character in the letterforms to feel friendly without being casual. It avoids the cold precision of a Helvetica or the quirkiness of a serif — it's designed to disappear into the content while feeling specifically "Airbnb."
Hierarchy
| Token | Size | Weight | Line Height | Letter Spacing | Use |
|---|---|---|---|---|---|
| {typography.display-xl} | 42px | 700 | 1.43 | -0.02em | Hero-level headlines (not visible in this viewport, reserved for deeper pages) |
| {typography.display-lg} | 28px | 700 | 1.43 | 0 | Section headlines — "Des idées pour vos prochaines escapades" |
| {typography.title-md} | 22px | 500 | 1.18 | -0.44px | Modal titles — "Améliorez votre expérience"; sub-headlines |
| {typography.title-sm} | 16px | 600 | 1.5 | 0 | Card titles, emphasized labels |
| {typography.label-md} | 14px | 500 | 1.29 | 0 | Navigation links, active tabs, button text, search segment values |
| {typography.body-md} | 14px | 400 | 1.43 | 0 | Primary body text, destination names, search placeholders, modal body copy |
| {typography.body-sm} | 13px | 400 | 1.38 | 0 | Secondary body text, slightly smaller paragraphs |
| {typography.caption} | 12px | 400 | 1.33 | 0 | Search segment labels ("Destination", "Dates", "Voyageurs"), fine print |
| {typography.button} | 14px | 500 | 1.29 | 0 | Button text ("Devenir hôte", modal CTAs) |
| {typography.button-sm} | 12px | 600 | 1.33 | 0 | Small button/link text ("Gérer les préférences") |
Principles
Airbnb's typographic system trusts size and color contrast over weight escalation for establishing hierarchy. Notice that the gap between {typography.display-lg} (28px/700) and {typography.body-md} (14px/400) is a full 2x size difference — this alone creates clear headline/body separation without requiring bold body text or lightweight headlines. Weight is used sparingly: 700 appears only at display level, 500 marks labels and buttons, and 400 handles everything else. This restraint prevents visual shouting.
The {typography.title-md} role introduces a subtle negative letter-spacing (-0.44px) at 22px, tightening the characters slightly for a more composed, headline-like feel. This is the only role in the system with non-zero tracking, making it a signature moment for sub-headline text.
Color does heavy lifting: {colors.ink} for primary text, {colors.body} #6c6c6c for secondary (a ~55% brightness reduction), creating a clear information hierarchy before the reader even processes semantic meaning. Destination cards exemplify this: the city name ("Rouen", "Paris") renders in {colors.ink}, while the accommodation type ("Locations de villas") drops to {colors.body} — same font size, different color, immediate comprehension of what matters most.
Uppercase is essentially absent from the visible interface. Even the logo ("airbnb") is lowercase. This reinforces the approachable, peer-to-peer hospitality tone — this is not a bank or an airline; it's a friend recommending a place to stay.
Note on Font Substitutes
Airbnb Cereal VF is a proprietary font not available on Google Fonts. For implementations without access to Airbnb's hosted font files, the closest visual substitute is Plus Jakarta Sans (Google Fonts), which shares similar geometric-humanist proportions, open apertures, and excellent readability at small sizes. An alternative is Nunito Sans for a slightly warmer feel, or DM Sans for a more technical alternative. Define these as CSS variables:
:root {
--font-family: 'Plus Jakarta Sans', 'Airbnb Cereal VF', Circular, -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;
}
If using the actual Airbnb Cereal VF (recommended for production fidelity), reference the @font-face declarations from the extracted CSS which point to a0.muscache.com/airbnb/static/airbnb-dls-web/build/fonts/cereal-variable/.
Layout
Spacing System
- Base unit: Approximately 4px, with practical tokens at 2px increments below 16px and larger jumps above.
- Tokens:
{spacing.xxs}(2px): Micro-adjustments, tight internal gaps.{spacing.xs}(4px): Icon-to-text gaps, tiny spacers.{spacing.sm}(6px): Small internal component padding.{spacing.md}(8px): Common vertical rhythm unit, list item spacing.{spacing.base}(10px): Compact padding inside dense components.{spacing.lg}(12px): Standard internal padding for buttons and inputs.{spacing.xl}(16px): Card internal padding, comfortable margins.{spacing.2xl}(18px): Between related element groups.{spacing.3xl}(24px): Section-internal separation, major block spacing.{spacing.section}(48px): Vertical distance between major page sections (nav to search, search to categories).
- Section padding (vertical):
{spacing.section}— used between the navigation bar and the search container, and between the search bar and the category/filter row. - Card internal padding: Destination cards appear to use minimal internal padding (~8-12px) with spacing driven by the grid gap rather than card-level padding.
- Gutters: The main content container sits at approximately 1425px max-width with
{spacing.section}(48px) horizontal padding on the header, creating generous edge margins on widescreen displays.
Grid & Container
- Max content width: ~1425px for the main content area (observed from the
<main>and<header>container widths). - Density choice: Marketplace-dense. The destination grid packs 7 columns of city cards, each containing a bold name + gray subtitle. Information density is high because users are scanning for inspiration, not reading long-form content.
- Grid behavior: The destination grid uses a fluid 7-column layout at desktop width, with cards flowing left-to-right, top-to-bottom. Each card contains a city name in
{typography.label-md}and a subtitle in{typography.body-md}at{colors.body}. - Hero column split: Not a traditional hero in this viewport — the search bar spans nearly the full content width, centered horizontally within the 1425px constraint, floating above the category tabs and destination grid.
Whitespace Philosophy
The system practices generous macro-whitespace, tight micro-whitespace. Major sections breathe with 48px+ vertical gaps, but individual components (search segments, destination cards, category tabs) pack information efficiently with minimal internal padding. This creates a "scannable density" — users can quickly parse many options without feeling overwhelmed, because the surrounding space gives each grouping room to exist. It's editorial-airy at the section level, marketplace-dense at the component level.
Header Architecture
|--------------------------------------------------------------------------------|
| [Logo] Tout voir Logements Expériences Services Devenir hôte [👤] [≡] |
|--------------------------------------------------------------------------------|
| [Destination........] | [Dates.......] | [Voyageurs...] | (🔍) |
| Rechercher une... Quand ? Ajouter des ... |
|--------------------------------------------------------------------------------|
← 48px padding → ← max 1425px →
The navigation bar is 80px tall with the logo flush-left, navigation items centered (with "Logements" receiving active underline treatment), and utility actions (Devenir hôte button, profile icon, hamburger menu) flush-right. Below it, the search bar floats as a distinct elevated element spanning most of the content width, internally divided into three clickable segments plus a circular search button.
Hero Section / Search Area
|--------------------------------------------------------------------------------|
| |
| ┌─────────────────────────────────────────────────────────────────────┐ |
| │ 🌍 Destination 📅 Dates 👥 Voyageurs (🔍) │ |
| │ Rechercher une Quand ? Ajouter des... │ |
| └─────────────────────────────────────────────────────────────────────┘ |
| |
|================================================================================|
| Équipements recherchés Sur la côte Montagnes Sports d'hiver Activités |
|_______________________ |
| |
The search bar is the page's focal point — a 66px-tall pill-shaped container ({rounded.lg}) with three segments, each acting as an independent clickable target. The search button is a 48px diameter circle in {colors.primary} positioned at the right end. Below the bar, a horizontal tab/category row filters the destination grid.
Destination Grid
| Rouen Carcassonne Aix-en-Provence Narbonne |
| Locations de Locations de Location de Locations de |
| villas villas maisons de vacc... cottages |
| |
| Paris Tignes Lille Vienne |
| Location de Locations de Locations de Location de |
| maisons de vacc... vacances villas maisons de vacc... |
| |
| ... (continues in 7-column grid layout) |
Each destination card is text-only in this view (imagery likely appears on hover or in alternate renderings). Cards stack vertically with the city name prominent and the accommodation type subdued.
Modal Overlay (Cookie Consent)
| ┌──────────────────────────────────────────────────────┐ |
| │ Améliorerz votre expérience │ |
| │ │ |
| │ Nous utilisons des cookies et d'autres │ |
| │ technologies pour personnaliser notre contenu... │ |
| │ │ |
| │ [Accepter tout] [Uniquement les cookies │ |
| │ nécessaires] Gérer préférences │ |
| └──────────────────────────────────────────────────────┘ |
| ← 32px internal padding, {rounded.DEFAULT} corners, shadow md → |
The modal centers on screen with a semi-transparent dark backdrop (rgba(0,0,0,0.5)). Two dark primary buttons sit side-by-side with a text-only tertiary action.
Elevation & Depth
| Level | Treatment | Use |
|---|---|---|
| 0 (Flat) | No shadow, {colors.canvas} background | Base page surface, navigation bar, category tabs, destination cards |
| 1 (Raised) | {extensions.shadows.sm} — subtle 1px border + 8px blur shadow | Floating search bar, elevated cards |
| 2 (Modal) | {extensions.shadows.md} — 28px blur, higher opacity | Cookie consent modal, dropdown panels |
| 3 (Focus) | {extensions.shadows.focus-ring} — dual white/black ring | Keyboard focus indicator on interactive elements |
The elevation philosophy is minimalist-layered. Unlike material design systems that use pervasive shadows to indicate z-depth, Airbnb reserves shadows for truly floating elements (the search bar, the modal) and keeps everything else flat against the canvas. This creates a cleaner, more editorial feel where depth is meaningful rather than ubiquitous.
The search bar is the primary elevated element — it visually "hovers" above the category tabs and destination grid, signaling its importance as the page's primary interaction point. The modal uses a heavier shadow to ensure it feels decisively "on top" of the content beneath it, supported by the semi-transparent overlay.
Focus states employ a distinctive dual-ring technique: a 2px white inner ring followed by a 4px dark outer ring, creating a high-contrast indicator that works on both light and dark surfaces. This is more visible than a simple outline and aligns with WCAG 2.1 focus visibility requirements.
No decorative depth (gradients, photographic layers, pattern overlays) is present in the extracted CSS. The visual interest comes from typography, photography (in production contexts), and the strategic pop of {colors.primary}.
Shapes
Border Radius Scale
| Token | Value | Use |
|---|---|---|
| {rounded.sm} | 4px | Small internal elements, tags, compact chips |
| {rounded.DEFAULT} | 8px | Modal surfaces, cards, default container rounding |
| {rounded.md} | 20px | Category tabs, "Devenir hôte" button, modal buttons |
| {rounded.lg} | 32px | Search bar outer container, large pill-shaped wrappers |
| {rounded.xl} | 50px | Individual search segment pills |
| {rounded.pill} | 9999px | Theoretical maximum pill (for future use) |
| {rounded.full} | 50% | Perfect circles — search button, profile avatar, icon buttons |
The shape language is definitively pill-forward. Even elements that aren't fully circular lean toward extreme rounding: 20px on a ~40px-tall button creates a very soft shape, and 32px on a 66px-tall search bar makes it distinctly capsule-like. This softness is core to Airbnb's brand personality — approachable, human, friendly. Sharp corners (4px) are reserved for purely functional, non-interactive elements.
Photography Geometry
This particular viewport shows a text-heavy state of the destination grid without visible imagery. Based on Airbnb's established design patterns (and the presence of image-related classes in the CSS), photography in this system typically:
- Renders in 4:3 or 16:9 aspect ratios for listing cards.
- Uses
{rounded.sm}(4px) corner rounding on card images. - Appears full-bleed within card containers.
- Would use cover object-fit to fill the allocated space.
- Avatars (profile pictures) render as perfect circles (
{rounded.full}).
Components
No hover states documented. Hover behavior is unreliable to extract from a single screenshot. State variants live as separate entries in the
components:frontmatter (e.g.{component.button-primary-icon-hover},{component.search-segment-hover}); add hover styling at implementation time per your stack's conventions.
Buttons & Interactive Elements
{component.top-nav} — The persistent 80px-tall navigation bar anchoring the top of the viewport. Background {colors.canvas}, text rendered in {colors.ink} at {typography.body-md}. Contains the logo, navigation links, and utility actions. Has no visible border or shadow — it merges seamlessly with the page background. Horizontal padding of {spacing.section} (48px) on each side within the 1425px max-width constraint.
{component.nav-link} — Navigation items in the top bar ("Tout voir", "Logements", "Expériences", "Services"). Transparent background, {colors.ink} text at {typography.body-md}. The active link ("Logements") uses {component.nav-link-active} styling with {typography.label-md} weight (500) and a 2px bottom underline in {colors.ink}. Padding is implicit from the nav layout.
{component.button-ghost} — The "Devenir hôte" button in the top-right navigation area. Transparent background with {colors.ink} text at {typography.button} (14px/500), {rounded.md} (20px) corners, and {padding: "11px 12px"} internal spacing. On hover, transitions to {component.button-ghost-hover} with {colors.surface-elevated} background. No border, no shadow — relies on the rounded shape and text weight for affordance.
{component.button-primary-icon} — The circular search button (magnifying glass icon) at the right end of the search bar. Background {colors.primary}, icon/text color {colors.on-primary}, perfectly circular at {rounded.full} with {size: "48px"}. This is the single highest-voltage interactive element on the page — the coral circle draws the eye immediately. Hover state is {component.button-primary-icon-hover} with {colors.primary-hover}.
{component.button-icon-circle} — The profile/avatar button in the top-right corner (showing a generic user icon). Background {colors.surface-elevated}, {colors.ink} icon, {rounded.full} circle at 32px diameter. Hover transitions to {component.button-icon-circle-hover} with {colors.surface-strong} background. Adjacent to the hamburger menu icon button which shares identical styling.
Search System
{component.search-bar} — The hero interactive element: a 66px-tall, pill-shaped container ({rounded.lg} 32px) with {colors.canvas} background and subtle {extensions.shadows.sm} elevation. Internal padding of {padding: "14px 16px"}. Contains three clickable segments and the search button. This is visually the most complex component on the page, functioning as a multi-field form collapsed into a single bar.
{component.search-segment} — Individual click targets within the search bar: "Destination", "Dates", "Voyageurs". Each is a transparent pill ({rounded.xl} 50px) with {padding: "14px 24px"} internal spacing. Text renders in {colors.ink} at {typography.body-md}. The segment structure is two-line: a label in {component.search-segment-label} ({typography.caption} 12px) and a value in {component.search-segment-value} ({typography.body-md}). Hover triggers {component.search-segment-hover} with {colors.surface-soft} background fill. Segments are separated by thin vertical dividers in {colors.hairline}.
{component.search-button} — Identical to {component.button-primary-icon}; the coral circle that executes the search. Positioned absolutely at the right end of the search bar, overlapping the container edge slightly for visual integration.
Navigation & Filtering
{component.category-tab} — Horizontal filter tabs below the search bar: "Équipements recherchés", "Sur la côte", "Montagnes", etc. Transparent background, {colors.ink} text at {typography.label-md} (14px/500), {rounded.md} (20px) corners, {padding: "12px 16px"}. The active tab ("Équipements recherchés") displays {component.category-tab-underline} — a 2px-tall {colors.ink} line beneath the text. Tabs have no background change on active state; the underline is the sole indicator.
Content Components
{component.destination-card} — Individual entries in the 7-column destination grid. In this text-only rendering, each card shows a city name in {component.destination-card-title} ({typography.label-md} 14px/500, {colors.ink}) and an accommodation subtitle in {component.destination-card-subtitle} ({typography.body-md} 14px/400, {colors.body}). Minimal internal spacing, {rounded.sm} (4px) corners on the overall card boundary. Cards are tightly packed in a fluid grid with consistent vertical rhythm.
{component.section-header} — The "Des idées pour vos prochaines escapades" headline above the destination grid. Uses {typography.display-lg} (28px/700) in {colors.ink}. Left-aligned with the grid below it. Generous bottom margin (likely {spacing.lg} or {spacing.xl}) separating it from the category tabs.
Overlay & Modal Components
{component.modal-overlay} — Full-viewport scrim behind the cookie consent dialog. Background rgba(0, 0, 0, 0.5) — 50% opaque black. Captures all clicks and dismisses the modal when clicked (outside the modal surface).
{component.modal-surface} — The white card containing the cookie consent content. Background {colors.canvas}, {rounded.DEFAULT} (8px) corners, {padding: "32px"} internal spacing, {extensions.shadows.md} elevation for lift-off from the backdrop. Centered both horizontally and vertically within the viewport. Max-width constrained to approximately 550-600px for readability.
{component.modal-title} — "Améliorez votre expérience" heading inside the modal. {typography.title-md} (22px/500) in {colors.ink}, with -0.44px letter-spacing for the tightened headline feel.
{component.modal-body} — The explanatory paragraph about cookies and technologies. {typography.body-md} (14px/400) in {colors.body} #6c6c6c, providing enough contrast to read comfortably while de-emphasizing relative to the title and buttons. Contains inline links styled in {colors.link} with underline decoration.
{component.button-modal-primary} — "Accepter tout" button. Background {colors.surface-dark} (#222222), text {colors.on-dark} (#ffffff), {typography.button} (14px/500), {rounded.md} (20px), {padding: "14px 24px"}. High-weight dark button creates strong call-to-action affordance within the modal context.
{component.button-modal-secondary} — "Uniquement les cookies nécessaires" button. Visually identical to primary modal button — same dark background, same dimensions, same rounding. The two buttons sit side-by-side with a gap between them (likely {spacing.md} 8px or {spacing.base} 10px). Differentiation is purely textual.
{component.button-modal-tertiary} — "Gérer les préférences" link-button. Transparent background, {colors.ink} text at {typography.button-sm} (12px/600), {rounded.sm} (4px), {padding: "8px 12px"}. Positioned below or beside the primary buttons as a lower-commitment action. May show underline on hover.
{component.footer} — While not fully visible in this viewport, the footer component is defined with {colors.canvas} background, {colors.body} text color, and {typography.body-sm} (13px) sizing. Typically contains legal links, country/language selectors, and copyright information in a multi-column layout.
Do's and Don'ts
Do
- Use
{colors.primary}#ff385c sparingly — reserve it for the search button, logo, and one or two other high-affordance moments per screen. Overuse dilutes brand recognition. - Apply
{rounded.lg}(32px) or higher to the search bar and major container elements to maintain the pill-shaped design language that defines Airbnb's friendliness. - Let
{typography.body-md}(14px/400) carry the majority of your interface text — it's optimized for the Ceral typeface's readability at this size. - Use
{colors.body}#6c6c6c for secondary information (subtitles, descriptions, placeholders) to create clear hierarchy without changing font size or weight. - Implement the dual-ring focus indicator (
{extensions.shadows.focus-ring}) for keyboard accessibility — it's more visible than a standard outline and part of the brand's accessibility commitment. - Give major vertical sections
{spacing.section}(48px) of breathing room — the airy feel depends on this macro-whitespace. - Keep the search bar elevated with
{extensions.shadows.sm}so it floats visually above the content beneath it. - Use
{typography.caption}(12px) for labels inside search segments ("Destination", "Dates") — they're meant to be scanned, not read as primary content. - Render modal buttons in
{colors.surface-dark}with{colors.on-dark}text when you need strong CTA weight without using the brand coral. - Maintain
{rounded.full}(50%) for all circular icon buttons (search, profile, menu) — consistency in round elements reinforces the shape language.
Don't
- Don't apply
{colors.primary}to large background areas or text blocks — it's an accent color, not a surface color. Reserve coral for interactive elements and the logo. - Don't use font weights above 700 — the system tops out at
{typography.display-lg}28px/700. Heavier weights fight the friendly, approachable tone. - Don't sharpen border radii below
{rounded.sm}(4px) for user-facing elements — even "small" rounding should feel soft. Reserve sharp corners for purely structural dividers. - Don't add gradients, textures, or color washes to
{colors.canvas}— the white background is intentional and clean. If you need depth, use{extensions.shadows}or layering. - Don't let destination card titles drop to
{colors.body}— city names must stay in{colors.ink}for scanability. Only the subtitle (accommodation type) goes gray. - Don't expand the radius vocabulary beyond the defined scale — if you need something between
{rounded.md}(20px) and{rounded.lg}(32px), pick one; don't invent intermediate values. - Don't use a different font family for any Latin-script text — Airbnb Cereal VF (or its fallback stack) is mandatory for brand consistency.
- Don't remove the hover state background from search segments — the
{colors.surface-soft}fill on{component.search-segment-hover}provides critical clickability feedback. - Don't make modal shadows heavier than
{extensions.shadows.md}— the cookie consent modal already uses the heaviest shadow in the system; going beyond it looks broken. - Don't render section headlines below
{typography.display-lg}(28px) — "Des idées pour vos prochaines escapades" at this size establishes proper hierarchical distance from body text.
Motion & Animation
Transition Tokens
The extracted CSS reveals a sophisticated motion system with multiple easing curves and durations tuned to specific property types:
/* Color transitions - standard */
--transition-color: color 0.25s cubic-bezier(0.2, 0, 0, 1);
/* Opacity fades - fast snap */
--transition-opacity: opacity 0.175s cubic-bezier(0, 0, 1, 1);
/* Transform/ movement - standard ease-out */
--transform-transform: transform 0.25s cubic-bezier(0.2, 0, 0, 1);
/* Complex multi-property (buttons, interactive elements) */
--transition-interactive: box-shadow 0.2s cubic-bezier(0.2, 0, 0, 1),
transform 0.25s cubic-bezier(0.2, 0, 0, 1),
background-color 0.3s cubic-bezier(0.2, 0, 0, 1),
border-color 0.3s cubic-bezier(0.2, 0, 0, 1),
color 0.3s cubic-bezier(0.2, 0, 0, 1);
/* Focus/quick interactions */
--transition-focus: box-shadow 0.2s cubic-bezier(0.2, 0, 0, 1),
transform 0.1s cubic-bezier(0.2, 0, 0, 1),
color 0.1s cubic-bezier(0, 0, 1, 1) 0.033s;
/* Link underline animation */
--transition-link: text-decoration-thickness 0.3s cubic-bezier(0.2, 0, 0, 1),
box-shadow 0.3s cubic-bezier(0.2, 0, 0, 1),
background-color 0.3s cubic-bezier(0.2, 0, 0, 1);
/* Custom linear easing for specific animations */
--transition-custom: transform 0.451754s linear(0 0%, 0.185572 10%, 0.465306 20%,
0.682334 30%, 0.822325 40%,
0.904974 50%, 0.951289 60%,
0.976364 70%, 0.989612 80%,
0.996485 90%, 1 100%);
Keyframe Animations
Twenty keyframe animations were detected in the extracted CSS (named kf-s0j3wl through kf-mbjk4k using Airbnb's minified naming convention). Based on typical Airbnb UI patterns, these likely include:
- kf-s0j3wl / kf-h0qfv9 — Modal entrance/exit slide-and-fade (the cookie consent modal uses animated reveal).
- kf-1qnooym / kf-14dwuik — Search bar expansion animation (segments animate when the bar enters "expanded" or "focused" state).
- kf-12tey57 / kf-uo7jbc — Category tab underline sliding (active indicator animates between tabs).
- kf-ogm7kb / kf-1ma8klo — Button press scale (subtle shrink on mousedown/touchstart).
- kf-aozddt / kf-ng66tb — Fade-in for dynamically loaded content (destination cards appearing on scroll).
- kf-1i1njk0 / kf-17sxjk4 — Tooltip/popover reveal.
- kf-1c30mo4 / kf-1kfidyc — Icon rotation or state transitions.
- kf-ye8ntg / kf-slu2u8 — Shimmer/skeleton loading effects.
- kf-vzf8zx / kf-15xg541 — Staggered list item entrance (cards appearing sequentially).
- kf-1dhnwt1 / kf-mbjk4k — Overlay fade-in/out.
Note: Without runtime observation, specific keyframe contents are inferred from naming patterns and Airbnb's known animation library behavior.
Interaction Patterns
- Search bar focus expansion: When a user clicks any search segment, the bar likely animates to an expanded state (increased height, full-width presentation) using
--transition-custom's 450ms linear easing with custom control points. - Segment hover fill: Search segments transition background color from transparent to
{colors.surface-soft}over 200-300ms withcubic-bezier(0.2, 0, 0, 1)easing (standard ease-out). - Button micro-interactions: Icon buttons scale subtly (0.97-0.98) on press using
--transition-focustiming (100ms transform, very fast for tactile response). - Modal backdrop fade: The cookie consent overlay fades in over 175ms with
cubic-bezier(0, 0, 1, 1)(ease-in-out) for smooth appearance. - Tab indicator slide: The active category tab's 2px underline slides horizontally between tabs with 300ms timing, likely using
translateXtransforms. - Link underline grow: Navigation and inline links animate underline thickness from 0 to 1px (or 2px) on hover, using
text-decoration-thicknesstransition — a modern CSS technique avoiding pseudo-element hacks. - Staggered card entrance: Destination cards may fade-and-slide up sequentially as they enter the viewport (intersection-observer triggered), using the 450ms custom easing curve for organic-feeling reveals.
Imagery Style
While the current viewport shows a text-rendered destination grid, Airbnb's established imagery conventions inform the following expected patterns:
- Photography-first aesthetic: Airbnb is fundamentally a photography-driven product. Listing images, destination hero shots, and host photos dominate the visual experience outside this text-heavy viewport.
- Warm, natural lighting: Photography favors golden-hour warmth, interior coziness, and authentic environmental portraiture over sterile architectural shots.
- User-generated authenticity: Images look like real guest photos or professional listings — high quality but not overly staged. This reinforces the peer-to-peer trust model.
- Full-bleed card imagery: When present, listing/destination images occupy the full width of their card container with
{rounded.sm}(4px) corner clipping, creating a "window" effect into each location. - Avatar circles: Host profile pictures and user avatars always render as perfect circles (
{rounded.full}), typically 32-48px depending on context. - Icon illustrations: Decorative icons in the navigation (globe for "Tout voir", house for "Logements", hot air balloon for "Expériences", bell for "Services") use a custom illustrated style — slightly playful, outlined or softly filled, matching the
{colors.ink}or{colors.primary}palette. - No stock photography: The brand avoids obviously stock imagery in favor of real listings and authentic travel moments.
- Image absence in this view: The text-only destination grid suggests either a loading state, a reduced-motion preference, an A/B test variant, or a progressive-enhancement pattern where images load after text content.
Icon System
- Library: Custom SVG icons (no third-party library detected —
framework.iconLibsis empty in the extracted data). Airbnb maintains its own icon set. - Specific icons observed:
- Bélo/Airbnb Logo — The integrated "A" heart-symbol logo in
{colors.primary} - Globe — "Tout voir" navigation item (earth/world icon)
- House — "Logements" navigation item (house with roof)
- Hot Air Balloon — "Expériences" navigation item (balloon silhouette)
- Service Bell — "Services" navigation item (concierge bell)
- Search/Magnifying Glass — Inside the circular
{colors.primary}search button - User/Person — Profile menu button (head and shoulders silhouette)
- Hamburger Menu — Three horizontal lines for mobile/nav toggle
- Chevron/Arrow — Likely used in dropdown indicators (implied from search segment structure)
- Bélo/Airbnb Logo — The integrated "A" heart-symbol logo in
- Treatment: Icons are primarily stroke-based or simply-filled vector shapes. Color is almost always
currentColor, inheriting from the parent element'scolorproperty — this means they automatically match{colors.ink}in navigation,{colors.on-primary}on the search button, and adapt to hover states. Size varies by context: navigation icons are approximately 16-18px, the search icon is 20-22px, and the logo mark scales proportionally to its container. Alignment is optically centered with adjacent text, typically with 8-12px horizontal gap between icon and label.
Recommended Frontend Stack
- Framework: Next.js (React-based SSR/SSG, matches Airbnb's actual stack)
- Styling: CSS Modules or Styled Components (Airbnb uses a custom
atomic/in-memory CSS solution; CSS Modules provide closest
parity for token-driven development)
- Fonts: Airbnb Cereal VF via @font-face from a0.muscache.com CDN,
with Plus Jakarta Sans as Google Fonts fallback substitute
- Animation: Framer Motion (React) or GSAP for complex sequenced animations;
vanilla CSS transitions for hover/focus states per the
extracted transition tokens
- Icons: Custom SVG icon set (extract from Airbnb's DLS package or
recreate the 8-10 observed icons as inline SVGs)
- Component lib: Custom components (Airbnb builds its own DLS — Design Language
System — components; no third-party UI library matches the
pill-shaped, coral-accented aesthetic)
- Build: Webpack or Next.js built-in bundling with postcss for
automatic vendor-prefixing and minification
Responsive Behavior
Breakpoints
| Name | Width | Key Changes | |---|---|---| | Mobile | < 768px | Single column layout, hamburger menu replaces horizontal nav, search bar stacks vertically (segments become full-width stacked), destination grid collapses to 1-2 columns, modal becomes full-width with edge margins | | Tablet | 768px – 1024px | 2-3 column destination grid, compressed search bar, navigation may show abbreviated labels or icons-only | | Desktop | 1025px – 1440px | Full 7-column destination grid, complete horizontal navigation, search bar at full width with 3 segments visible, modal centered at ~550px width | | Wide | > 1440px | Content constrains to 1425px max-width with increased horizontal padding, destination grid may expand to 8 columns, extra whitespace on edges |
Touch Targets
{component.button-primary-icon}(search button): 48×48px — exceeds WCAG AAA 44×44 minimum ✅{component.button-icon-circle}(profile/menu): 32×32px — below 44×44 minimum, may need invisible padding expansion for mobile touch ⚠️{component.search-segment}(clickable area): Approximately 200×38px (estimated) — height is within the 66px bar with padding, easily tappable ✅{component.category-tab}: Height ~44px with horizontal padding — meets minimum ✅{component.button-ghost}("Devenir hôte"): ~14px font with 11px/12px padding — total height likely ~40-44px, borderline for mobile ⚠️{component.button-modal-primary}/{component.button-modal-secondary}: 14px padding + font = ~48px height — excellent touch target ✅
Collapsing Strategy
- Navigation: Horizontal nav items collapse into the hamburger menu below 768px. The logo remains visible and left-aligned. "Devenir hôte" may move into the menu or become a text link.
- Search bar: The 3-segment horizontal bar reflows into a vertically stacked layout on mobile — "Destination" field on top, "Dates" below, "Travelers" third, with the search button full-width at the bottom. Each segment becomes a full-width tap target.
- Destination grid: Flows from 7 columns (desktop) → 3 columns (tablet) → 2 columns (mobile landscape) → 1 column (mobile portrait). Card content remains the same but images (when present) increase in vertical size to maintain visual weight.
- Category tabs: Horizontal scrollable container (
overflow-x: auto) on mobile with snap points, or collapse into a "Filters" button that opens a bottom sheet. - Modal: On mobile, the cookie consent modal takes full width with 16-24px horizontal margins, stacking the two primary buttons vertically (instead of side-by-side) and moving "Gérer les préférences" below them.
- Hero spacing:
{spacing.section}(48px) vertical padding compresses to{spacing.3xl}(24px) or{spacing.xl}(16px) on mobile to reduce scroll depth.
Image Behavior
- Hero/destination images (when present in production): Scale down in width proportionally to grid columns, maintaining 4:3 or 16:9 aspect ratio with
object-fit: cover. On mobile single-column layout, images may increase to near-full viewport width. - Avatar images: Maintain 32-48px circle regardless of breakpoint — consistent sizing aids recognition.
- Icon illustrations: Remain fixed pixel size (16-24px) across breakpoints; they don't scale with text.
Iteration Guide
-
Set up the font infrastructure first. Add the
@font-facedeclarations for Airbnb Cereal VF (all language subsets) to your global CSS, referencing thea0.muscache.comCDN URLs from the extracted fontFaces array. Define--font-familyas a CSS variable holding the full fallback chain. If CDN access is restricted, substitute Plus Jakarta Sans from Google Fonts and note the deviation. -
Initialize design tokens from YAML frontmatter. Map every
{colors.x},{typography.x},{rounded.x}, and{spacing.x}value to CSS custom properties on:root. Build your component styles to reference only these tokens — never hardcode a hex code, px value, or font size that has a token equivalent. Run a script to verify zero hardcoded values in component files. -
Build the navigation shell. Implement
{component.top-nav}as an 80px fixed or sticky header at full viewport width with{spacing.section}horizontal padding inside a 1425px max-width container. Place the logo (SVG,{colors.primary}), navigation links ({component.nav-link}), ghost button ({component.button-ghost}), and icon buttons ({component.button-icon-circle}) according to the ASCII diagram. Ensure the active nav link gets the 2px underline via{component.category-tab-underline}. -
Construct the search bar as the hero component. Build
{component.search-bar}as a 66px-tall,{rounded.lg}(32px) container with{extensions.shadows.sm}elevation. Inside, create three{component.search-segment}children separated by 1px{colors.hairline}vertical dividers. Each segment needs a two-line internal layout: label ({typography.caption}) above value ({typography.body-md}). Attach{component.search-button}as an overlapping{rounded.full}circle in{colors.primary}at the right edge. -
Implement the category filter row. Below the search bar, build a horizontal scrolling/flex container of
{component.category-tab}elements. Style the active tab with the 2px underline ({component.category-tab-underline}). Use{spacing.lg}(12px) gap between tabs. -
Lay out the destination grid. Create a 7-column CSS Grid (or Flexbox with
flex-wrap) for desktop. Each{component.destination-card}contains a title in{typography.label-md}/{colors.ink}and subtitle in{typography.body-md}/{colors.body}. Use{spacing.md}(8px) or{spacing.lg}(12px) grid gap. Above the grid, place{component.section-header}using{typography.display-lg}. -
Add the modal overlay system. Implement
{component.modal-overlay}as a fixed-position full-viewport div withrgba(0,0,0,0.5)background. Center{component.modal-surface}(white,{rounded DEFAULT}, 32px padding,{extensions.shadows.md}) within it. Inside, build the title ({component.modal-title}), body text ({component.modal-body}with inline link styling), two side-by-side dark buttons ({component.button-modal-primary},{component.button-modal-secondary}), and the tertiary text button ({component.button-modal-tertiary}). -
Apply motion with the extracted transitions. Add
transitionproperties to interactive elements using the timing values from the Motion & Animation section. Color changes get 250-300ms, transforms get 250ms, opacity gets 175ms. Implement hover states for buttons, search segments, and links — these are NOT in the frontmatter (per the no-hover policy) but are essential for usable UI. -
Test responsive behavior at four breakpoints. Verify the nav collapses, search bar stacks vertically, grid reduces columns, and modal adapts its button layout. Check that all touch targets meet 44×44px minimum on mobile — expand
{component.button-icon-circle}and{component.button-ghost}with invisible padding if needed. -
Run accessibility audits. Verify focus rings use the dual-ring
{extensions.shadows.focus-ring}style. Check color contrast ratios:{colors.primary}on{colors.on-primary}passes AA (coral on white is ~4.2:1). Ensure the modal trap focus and supports Escape-to-close. Validate that search segments have properrole="button"orrole="group"semantics.
Font Setup for Implementation:
@font-face {
font-family: 'Airbnb Cereal VF';
src: url('https://a0.muscache.com/airbnb/static/airbnb-dls-web/build/fonts/cereal-variable/AirbnbCerealVF_W_Wght.8816d9e5c3b6a860636193e36b6ac4e4.woff2') format('woff2-variations');
font-style: normal;
font-display: swap;
}
/* Fallback if CDN unavailable */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
:root {
--font-family: 'Airbnb Cereal VF', 'Plus Jakarta Sans', Circular, -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;
}
Known Gaps
- Hover state styling: Per the no-hover policy, hover backgrounds, color shifts, and transform scales for buttons, links, search segments, and cards are documented as separate frontmatter entries but lack precise values beyond what's inferred from the active/pressed states and the transition tokens.
- Loading/skeleton states: No skeleton loader components, shimmer animation details, or pending-state treatments were extractable from the static screenshot. The keyframe names suggest shimmer exists (
kf-slu2u8,kf-vzf8zx) but their CSS content wasn't available. - Form validation states: Beyond the basic
{component.text-input-focus}indication, error messages, red borders, validation icons, and disabled-form-field styling are absent from this viewport. - Dark mode tokens: The entire specification documents the light theme only. Airbnb does support dark mode in production, but those color values (dark canvas, adjusted text colors, inverted surfaces) weren't extractable from this screenshot.
- Internationalization/RTL layout: The page is in French ("Rechercher une destination", "Devenir hôte"), suggesting i18n support, but RTL (right-to-left) layout adaptations for Arabic/Hebrew (fonts for which are loaded) aren't documented.
- Map view and listing cards: This viewport shows a text-only destination grid. Actual listing cards with images, pricing badges, star ratings, superhost badges, and heart/wishlist buttons aren't represented.
- Authenticated/user-specific surfaces: The profile dropdown menu, notification bell panel, messaging interface, and host dashboard are entirely absent — this spec covers only the unauthenticated/discovery homepage.
- Exact search expanded state: When a user clicks a search segment, the bar likely expands into a fuller form with date pickers, traveler counters, and location autocomplete. That expanded state's dimensions, internal layout, and animation choreography couldn't be captured from a static screenshot.
- Accessibility attributes: While focus rings are documented, ARIA roles, live region announcements, screen-reader-only text, and keyboard navigation order (tab sequence) are implementation details not visible in CSS/screenshot alone.
- CSS variable naming mismatches: The source uses minified/atomic class names (
fhzfs0e,atmk_h2mmj6, etc.) that don't semantically map to component names. Any automated extraction relying on class-name heuristics would fail — this spec manually reconciles those obfuscated names to semantic component tokens.