1373 lines
56 KiB
HTML
1373 lines
56 KiB
HTML
<!doctype html>
|
||||
|
|
<html lang="en">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="utf-8" />
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|||
|
|
<title>Airbnb — reference components</title>
|
|||
|
|
<meta
|
|||
|
|
name="description"
|
|||
|
|
content="Reference fixture for design-systems/airbnb. Every visible
|
|||
|
|
value comes from tokens.css; the page itself follows Airbnb's
|
|||
|
|
rules — Canvas White surfaces, a single Rausch accent capped
|
|||
|
|
at two visible uses per viewport, Cereal at weight 500 for
|
|||
|
|
body, the signature 14-20px soft-circle radii, the three-
|
|||
|
|
layer booking-panel elevation, and the 2px Ink Black focus
|
|||
|
|
ring that survives full-bleed photography."
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
/* :root paste — sourced verbatim from design-systems/airbnb/tokens.css. */
|
|||
|
|
:root {
|
|||
|
|
/* ─── Surface (3 levels) ────────────────────────────────────────
|
|||
|
|
* Canvas White is both the page and the card — Airbnb refuses a
|
|||
|
|
* tonal shift between the two. Cards earn their edges from the
|
|||
|
|
* Hairline Gray border and from full-bleed photography. The
|
|||
|
|
* tertiary tier (--surface-warm) is Soft Cloud, used only for
|
|||
|
|
* footer backgrounds, map-view wrappers, and middle-of-range
|
|||
|
|
* date cells in the date picker. */
|
|||
|
|
--bg: #ffffff;
|
|||
|
|
--surface: #ffffff;
|
|||
|
|
--surface-warm: #f7f7f7;
|
|||
|
|
|
|||
|
|
/* ─── Foreground ramp (4 levels) ────────────────────────────────
|
|||
|
|
* Ink Black (#222222) carries roughly ninety percent of every
|
|||
|
|
* page — every heading, every body paragraph, every nav label,
|
|||
|
|
* every price. The brand's near-black is never true #000000.
|
|||
|
|
* Charcoal (--fg-2) is a one-step-down emphasis tier reserved
|
|||
|
|
* for focused-input text. Ash Gray (--muted) is the secondary
|
|||
|
|
* label — "Cottage rentals" subtitles, muted footer links.
|
|||
|
|
* Mute Gray (--meta) is reserved for disabled buttons and
|
|||
|
|
* low-priority metadata. */
|
|||
|
|
--fg: #222222;
|
|||
|
|
--fg-2: #3f3f3f;
|
|||
|
|
--muted: #6a6a6a;
|
|||
|
|
--meta: #929292;
|
|||
|
|
|
|||
|
|
/* ─── Border (2 levels) ─────────────────────────────────────────
|
|||
|
|
* Hairline Gray (#dddddd) is the workhorse — every card-to-card
|
|||
|
|
* divider, every amenity-row separator, every footer-column
|
|||
|
|
* rule. The soft tier is a lighter hairline used to break up
|
|||
|
|
* dense list interiors without competing with the primary card
|
|||
|
|
* edge. */
|
|||
|
|
--border: #dddddd;
|
|||
|
|
--border-soft: #ebebeb;
|
|||
|
|
|
|||
|
|
/* ─── Accent ────────────────────────────────────────────────────
|
|||
|
|
* Rausch coral-pink — the single signature color. Reserve for
|
|||
|
|
* primary CTAs (Reserve, Search, Add dates), the active-tab
|
|||
|
|
* underline, the wishlist heart fill, and price emphasis. Hard
|
|||
|
|
* cap of two visible uses per viewport; if a third Rausch
|
|||
|
|
* element shows up, neutralize one. Plus Magenta (#92174d) and
|
|||
|
|
* Luxe Purple (#460479) are kept inline at their product-tier
|
|||
|
|
* surfaces, not promoted to shared accent slots. */
|
|||
|
|
--accent: #ff385c;
|
|||
|
|
--accent-on: #ffffff;
|
|||
|
|
--accent-hover: #e31c5f; /* hand-picked, between Rausch and Deep Rausch */
|
|||
|
|
--accent-active: #e00b41; /* Deep Rausch — DESIGN.md §2 pressed-state value */
|
|||
|
|
|
|||
|
|
/* ─── Semantic ──────────────────────────────────────────────────
|
|||
|
|
* Error Red is explicit from DESIGN.md §2; success and warn are
|
|||
|
|
* not specified by the brand and are bound to desaturated values
|
|||
|
|
* that survive the white canvas without competing with Rausch.
|
|||
|
|
* Keep total semantic-color pixels well under five percent of
|
|||
|
|
* any page — the brand almost never renders status. */
|
|||
|
|
--success: #008a05; /* hand-picked warm green — available/in-stock */
|
|||
|
|
--warn: #c47700; /* burnt amber — never tailwind yellow */
|
|||
|
|
--danger: #c13515; /* DESIGN.md §2 Error Red */
|
|||
|
|
|
|||
|
|
/* ─── Typography — fonts ────────────────────────────────────────
|
|||
|
|
* Airbnb Cereal VF is the proprietary face that carries every
|
|||
|
|
* label from 8px superscript to 28px section heading. The
|
|||
|
|
* documented fallback chain renders acceptably on macOS/iOS
|
|||
|
|
* where system-ui resolves to San Francisco. Display and body
|
|||
|
|
* share the same stack — the visual identity comes from the
|
|||
|
|
* family itself, never from typeface mixing. Mono is a generic
|
|||
|
|
* stack for kbd, tabular metrics, and the rare code label. */
|
|||
|
|
--font-display:
|
|||
|
|
"Airbnb Cereal VF", "Airbnb Cereal App", Circular,
|
|||
|
|
-apple-system, system-ui, "Helvetica Neue", Roboto, sans-serif;
|
|||
|
|
--font-body:
|
|||
|
|
"Airbnb Cereal VF", "Airbnb Cereal App", Circular,
|
|||
|
|
-apple-system, system-ui, "Helvetica Neue", Roboto, sans-serif;
|
|||
|
|
--font-mono:
|
|||
|
|
ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco,
|
|||
|
|
Consolas, monospace;
|
|||
|
|
|
|||
|
|
/* ─── Typography — type scale (px) ──────────────────────────────
|
|||
|
|
* Derived from DESIGN.md §3 Hierarchy table. The system clusters
|
|||
|
|
* tightly between 11 and 22px (the conversational range) and
|
|||
|
|
* jumps to 28px for section headings; the higher tiers are used
|
|||
|
|
* sparingly for the Guest Favorite rating lockup (44–56px) and
|
|||
|
|
* the rare hero numeral. No 400-regular: Cereal's body weight
|
|||
|
|
* is 500, which any consuming component should set explicitly. */
|
|||
|
|
--text-xs: 12px; /* micro / footer disclaimer */
|
|||
|
|
--text-sm: 14px; /* button default, link, caption */
|
|||
|
|
--text-base: 16px; /* body medium, button large, subtitle bold */
|
|||
|
|
--text-lg: 20px; /* listing title */
|
|||
|
|
--text-xl: 22px; /* subsection heading */
|
|||
|
|
--text-2xl: 28px; /* section heading */
|
|||
|
|
--text-3xl: 44px; /* Guest Favorite rating, hero numeral */
|
|||
|
|
--text-4xl: 56px; /* display ceiling — Guest Favorite max */
|
|||
|
|
|
|||
|
|
/* ─── Typography — leading & tracking ───────────────────────────
|
|||
|
|
* Tight for display (1.20 — chiseled headlines), generous for
|
|||
|
|
* body (1.43 — magazine reading comfort). Display tracking
|
|||
|
|
* compresses negatively at sizes ≥20px; body and caption hold
|
|||
|
|
* at zero. The two-mode rhythm is what gives Airbnb pages
|
|||
|
|
* their travel-magazine feel rather than a utility-app feel. */
|
|||
|
|
--leading-body: 1.43;
|
|||
|
|
--leading-tight: 1.2;
|
|||
|
|
--tracking-display: -0.02em; /* applied at sizes ≥20px only */
|
|||
|
|
|
|||
|
|
/* ─── Spacing — base scale ──────────────────────────────────────
|
|||
|
|
* 8px base unit per DESIGN.md §5. The brand uses fine-grained
|
|||
|
|
* off-grid values (5.5, 10, 11, 18.5, 22) for pixel-perfect
|
|||
|
|
* icon alignment, but the shared scale stays on the standard
|
|||
|
|
* 4px grid — off-grid spacing belongs inline at the component
|
|||
|
|
* level, not in shared tokens. */
|
|||
|
|
--space-1: 4px;
|
|||
|
|
--space-2: 8px;
|
|||
|
|
--space-3: 12px;
|
|||
|
|
--space-4: 16px;
|
|||
|
|
--space-5: 20px;
|
|||
|
|
--space-6: 24px;
|
|||
|
|
--space-8: 32px;
|
|||
|
|
--space-12: 48px;
|
|||
|
|
|
|||
|
|
/* ─── Section rhythm ────────────────────────────────────────────
|
|||
|
|
* Section padding ranges 48–64px on desktop, 24–32px on mobile
|
|||
|
|
* per DESIGN.md §5. We bind the upper bound on desktop because
|
|||
|
|
* Airbnb's content-dense pages benefit from generous breathing
|
|||
|
|
* room between content blocks; the lower phone bound keeps the
|
|||
|
|
* vertical rhythm tight on small screens. */
|
|||
|
|
--section-y-desktop: 64px;
|
|||
|
|
--section-y-tablet: 48px;
|
|||
|
|
--section-y-phone: 32px;
|
|||
|
|
|
|||
|
|
/* ─── Radius ────────────────────────────────────────────────────
|
|||
|
|
* The brand's signature soft-circle geometry. 14px (--radius-md)
|
|||
|
|
* is the workhorse — every listing card photograph, every
|
|||
|
|
* amenity badge, every generic content container lands here.
|
|||
|
|
* 20px (--radius-lg) is reserved for the booking panel and hero
|
|||
|
|
* photo frames; 8px (--radius-sm) is the small radius for
|
|||
|
|
* buttons, dropdowns, and inputs; --radius-pill (9999px) is the
|
|||
|
|
* system's signature round geometry, applied to every circular
|
|||
|
|
* icon button, every avatar, and the wishlist heart. */
|
|||
|
|
--radius-sm: 8px;
|
|||
|
|
--radius-md: 14px;
|
|||
|
|
--radius-lg: 20px;
|
|||
|
|
--radius-pill: 9999px;
|
|||
|
|
|
|||
|
|
/* ─── Elevation ─────────────────────────────────────────────────
|
|||
|
|
* Airbnb forbids single drop shadows; the system uses stacked
|
|||
|
|
* layered shadows or no shadow at all. Listing cards sit flat
|
|||
|
|
* on the canvas. Booking panels, modals, and dropdowns use the
|
|||
|
|
* signature three-layer stack — a 2% hairline ring, a 4% short
|
|||
|
|
* blur, and a 10% medium blur — which reads as one cohesive
|
|||
|
|
* lift while giving the perimeter a premium anti-aliased edge. */
|
|||
|
|
--elev-flat: none;
|
|||
|
|
--elev-ring: 0 0 0 1px var(--border);
|
|||
|
|
--elev-raised:
|
|||
|
|
rgba(0, 0, 0, 0.02) 0 0 0 1px,
|
|||
|
|
rgba(0, 0, 0, 0.04) 0 2px 6px 0,
|
|||
|
|
rgba(0, 0, 0, 0.1) 0 4px 8px 0;
|
|||
|
|
|
|||
|
|
/* ─── Focus ring ────────────────────────────────────────────────
|
|||
|
|
* 2px solid Ink Black, NOT the schema's accent-tinted default.
|
|||
|
|
* DESIGN.md §6 mandates this exact treatment so focus indicators
|
|||
|
|
* read cleanly against full-bleed colorful photography — a
|
|||
|
|
* Rausch-tinted ring would disappear against warm sunset shots
|
|||
|
|
* or sea-blue beach overlays. Pair with a 4px white separator
|
|||
|
|
* ring at circular-icon buttons that float on images. */
|
|||
|
|
--focus-ring: 0 0 0 2px var(--fg);
|
|||
|
|
|
|||
|
|
/* ─── Motion ────────────────────────────────────────────────────
|
|||
|
|
* DESIGN.md did not capture transition timings (static
|
|||
|
|
* extraction scope). We bind defaults consistent with the
|
|||
|
|
* brand's tactile feel: 150ms for micro-states (the famous
|
|||
|
|
* `transform: scale(0.92)` pressed-button rebound) and 200ms
|
|||
|
|
* for general state changes. Standard easing is the schema
|
|||
|
|
* default — short, purposeful, no overshoot. */
|
|||
|
|
--motion-fast: 150ms;
|
|||
|
|
--motion-base: 200ms;
|
|||
|
|
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
|||
|
|
|
|||
|
|
/* ─── Layout ────────────────────────────────────────────────────
|
|||
|
|
* Detail pages cap at 1280px per DESIGN.md §5; the homepage
|
|||
|
|
* listing grid lets the canvas breathe to 1760–1920px on
|
|||
|
|
* ultra-wide. We bind the detail-page width as the shared
|
|||
|
|
* default because that is the geometry agents will most often
|
|||
|
|
* generate (rooms, experiences, host profiles). Gutters
|
|||
|
|
* tighten progressively: 40px desktop, 24px tablet, 16px
|
|||
|
|
* phone per DESIGN.md §8 small-mobile clause. */
|
|||
|
|
--container-max: 1280px;
|
|||
|
|
--container-gutter-desktop: 40px;
|
|||
|
|
--container-gutter-tablet: 24px;
|
|||
|
|
--container-gutter-phone: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ─── Reset (minimal) ───────────────────────────────────────── */
|
|||
|
|
*, *::before, *::after { box-sizing: border-box; }
|
|||
|
|
html, body { margin: 0; padding: 0; }
|
|||
|
|
body {
|
|||
|
|
background: var(--bg);
|
|||
|
|
color: var(--fg);
|
|||
|
|
font-family: var(--font-body);
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
font-weight: 500; /* Cereal's body weight — DESIGN.md §3 */
|
|||
|
|
line-height: var(--leading-body);
|
|||
|
|
-webkit-font-smoothing: antialiased;
|
|||
|
|
-moz-osx-font-smoothing: grayscale;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ─── Layout primitives ─────────────────────────────────────── */
|
|||
|
|
.container {
|
|||
|
|
max-width: var(--container-max);
|
|||
|
|
margin-inline: auto;
|
|||
|
|
padding-inline: var(--container-gutter-desktop);
|
|||
|
|
}
|
|||
|
|
section {
|
|||
|
|
padding-block: var(--section-y-desktop);
|
|||
|
|
}
|
|||
|
|
section + section {
|
|||
|
|
border-top: 1px solid var(--border);
|
|||
|
|
}
|
|||
|
|
@media (max-width: 1023px) {
|
|||
|
|
.container { padding-inline: var(--container-gutter-tablet); }
|
|||
|
|
section { padding-block: var(--section-y-tablet); }
|
|||
|
|
}
|
|||
|
|
@media (max-width: 639px) {
|
|||
|
|
.container { padding-inline: var(--container-gutter-phone); }
|
|||
|
|
section { padding-block: var(--section-y-phone); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ─── Typography ─────────────────────────────────────────────
|
|||
|
|
* Display sizes ≥20px compress tracking negatively per
|
|||
|
|
* DESIGN.md §3 ("Negative tracking on display type only").
|
|||
|
|
* Body and caption sizes hold at zero tracking for
|
|||
|
|
* readability. Weights: 500 body, 600 emphasis, 700 display
|
|||
|
|
* — no 400 anywhere, no italic anywhere. */
|
|||
|
|
h1, h2, h3 {
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
line-height: var(--leading-tight);
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
h1 {
|
|||
|
|
font-size: var(--text-2xl);
|
|||
|
|
font-weight: 700;
|
|||
|
|
letter-spacing: var(--tracking-display);
|
|||
|
|
}
|
|||
|
|
h2 {
|
|||
|
|
font-size: var(--text-xl);
|
|||
|
|
font-weight: 500;
|
|||
|
|
/* 22px Subsection Heading: line-height 1.18 per DESIGN.md §Typography.
|
|||
|
|
Overrides the shared 1.20 (--leading-tight) set on h1/h2/h3 above. */
|
|||
|
|
line-height: 1.18;
|
|||
|
|
letter-spacing: var(--tracking-display);
|
|||
|
|
}
|
|||
|
|
h3 {
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
font-weight: 600;
|
|||
|
|
/* 16px Subtitle/Body size: line-height 1.25 per DESIGN.md §Typography.
|
|||
|
|
Overrides the shared 1.20 (--leading-tight) set on h1/h2/h3 above. */
|
|||
|
|
line-height: 1.25;
|
|||
|
|
}
|
|||
|
|
p { margin: 0; }
|
|||
|
|
|
|||
|
|
.lede {
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
line-height: var(--leading-body);
|
|||
|
|
color: var(--fg);
|
|||
|
|
}
|
|||
|
|
.body-muted { color: var(--muted); }
|
|||
|
|
.body-meta { color: var(--meta); font-size: var(--text-sm); }
|
|||
|
|
.body-sm { font-size: var(--text-sm); }
|
|||
|
|
|
|||
|
|
/* The single uppercase moment in the system, per DESIGN.md §3
|
|||
|
|
* Principles ("No all-caps except at 8px"). We elevate this
|
|||
|
|
* to a 12px eyebrow for fixture legibility, with tracking
|
|||
|
|
* well above the craft 0.06em floor. Used once per section. */
|
|||
|
|
.eyebrow {
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: var(--text-xs);
|
|||
|
|
font-weight: 700;
|
|||
|
|
line-height: 1.33;
|
|||
|
|
color: var(--fg);
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
letter-spacing: 0.08em;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stack-2 > * + * { margin-block-start: var(--space-2); }
|
|||
|
|
.stack-3 > * + * { margin-block-start: var(--space-3); }
|
|||
|
|
.stack-4 > * + * { margin-block-start: var(--space-4); }
|
|||
|
|
.stack-6 > * + * { margin-block-start: var(--space-6); }
|
|||
|
|
|
|||
|
|
/* ─── Buttons ────────────────────────────────────────────────
|
|||
|
|
* Primary CTA is filled Rausch on Canvas White text. Active/
|
|||
|
|
* pressed uses Deep Rausch as the published brand value
|
|||
|
|
* (DESIGN.md §4 Primary CTA) combined with the famous
|
|||
|
|
* `transform: scale(0.92)` rebound that gives the brand its
|
|||
|
|
* tactile feel. Secondary is a Hairline Gray outlined pill
|
|||
|
|
* over the canvas. Both share the 2px Ink Black focus ring
|
|||
|
|
* — see the #Bind 6 rationale in tokens.css. */
|
|||
|
|
.btn {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
gap: var(--space-2);
|
|||
|
|
padding: 14px 24px;
|
|||
|
|
border: 1px solid transparent;
|
|||
|
|
border-radius: var(--radius-sm);
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-weight: 500;
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
line-height: 1;
|
|||
|
|
cursor: pointer;
|
|||
|
|
text-decoration: none;
|
|||
|
|
transition:
|
|||
|
|
transform var(--motion-fast) var(--ease-standard),
|
|||
|
|
background-color var(--motion-fast) var(--ease-standard),
|
|||
|
|
border-color var(--motion-fast) var(--ease-standard),
|
|||
|
|
box-shadow var(--motion-fast) var(--ease-standard);
|
|||
|
|
}
|
|||
|
|
.btn:focus-visible {
|
|||
|
|
outline: none;
|
|||
|
|
box-shadow: var(--focus-ring);
|
|||
|
|
}
|
|||
|
|
.btn:active {
|
|||
|
|
transform: scale(0.96); /* approaching DESIGN.md §4's scale(0.92) */
|
|||
|
|
}
|
|||
|
|
.btn-primary {
|
|||
|
|
background: var(--accent);
|
|||
|
|
color: var(--accent-on);
|
|||
|
|
}
|
|||
|
|
.btn-primary:hover {
|
|||
|
|
background: var(--accent-hover);
|
|||
|
|
}
|
|||
|
|
.btn-primary:active {
|
|||
|
|
background: var(--accent-active);
|
|||
|
|
transform: scale(0.92);
|
|||
|
|
}
|
|||
|
|
.btn-secondary {
|
|||
|
|
background: var(--surface);
|
|||
|
|
color: var(--fg);
|
|||
|
|
border-color: var(--border);
|
|||
|
|
border-radius: var(--radius-lg); /* 20px pill — DESIGN.md §4 Secondary */
|
|||
|
|
}
|
|||
|
|
.btn-secondary:hover {
|
|||
|
|
border-color: var(--fg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Compact button — listing-card price row, secondary nav links */
|
|||
|
|
.btn-sm {
|
|||
|
|
padding: 10px 16px;
|
|||
|
|
font-size: var(--text-sm);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Circular icon button — the brand's signature geometry
|
|||
|
|
* (DESIGN.md §4 Icon-Only Circular Button). Used for back,
|
|||
|
|
* share, favorite, and carousel controls. The 4px white
|
|||
|
|
* separator ring on `:hover` is what separates the button
|
|||
|
|
* from colorful photography backgrounds. */
|
|||
|
|
.btn-icon {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
width: 32px;
|
|||
|
|
height: 32px;
|
|||
|
|
padding: 0;
|
|||
|
|
border: 1px solid var(--border);
|
|||
|
|
border-radius: var(--radius-pill);
|
|||
|
|
background: var(--surface);
|
|||
|
|
color: var(--fg);
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition:
|
|||
|
|
transform var(--motion-fast) var(--ease-standard),
|
|||
|
|
box-shadow var(--motion-fast) var(--ease-standard);
|
|||
|
|
}
|
|||
|
|
.btn-icon:hover {
|
|||
|
|
box-shadow: 0 0 0 4px var(--surface), var(--elev-ring);
|
|||
|
|
}
|
|||
|
|
.btn-icon:active {
|
|||
|
|
transform: scale(0.92);
|
|||
|
|
}
|
|||
|
|
.btn-icon:focus-visible {
|
|||
|
|
outline: none;
|
|||
|
|
box-shadow: var(--focus-ring);
|
|||
|
|
}
|
|||
|
|
.btn-icon svg { width: 16px; height: 16px; }
|
|||
|
|
|
|||
|
|
/* ─── Inputs ─────────────────────────────────────────────────
|
|||
|
|
* Generic text input per DESIGN.md §4 Text Input — 1px
|
|||
|
|
* Hairline Gray edge, 8px radius, 14px·16px padding. Focus
|
|||
|
|
* switches the edge to Ink Black and adds the 2px outer
|
|||
|
|
* ring. Error state swaps to Error Red on both border and
|
|||
|
|
* helper text. */
|
|||
|
|
.field {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-2);
|
|||
|
|
}
|
|||
|
|
.field label {
|
|||
|
|
font-size: var(--text-sm);
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--fg);
|
|||
|
|
}
|
|||
|
|
.field input,
|
|||
|
|
.field select {
|
|||
|
|
padding: 14px 16px;
|
|||
|
|
border-radius: var(--radius-sm);
|
|||
|
|
border: 1px solid var(--border);
|
|||
|
|
background: var(--surface);
|
|||
|
|
color: var(--fg);
|
|||
|
|
font-family: inherit;
|
|||
|
|
font-weight: 500;
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
outline: none;
|
|||
|
|
transition:
|
|||
|
|
border-color var(--motion-fast) var(--ease-standard),
|
|||
|
|
box-shadow var(--motion-fast) var(--ease-standard);
|
|||
|
|
}
|
|||
|
|
.field input:focus-visible,
|
|||
|
|
.field select:focus-visible {
|
|||
|
|
border-color: var(--fg);
|
|||
|
|
box-shadow: var(--focus-ring);
|
|||
|
|
color: var(--fg-2);
|
|||
|
|
}
|
|||
|
|
.field input::placeholder { color: var(--muted); }
|
|||
|
|
.field-help {
|
|||
|
|
font-size: var(--text-xs);
|
|||
|
|
color: var(--muted);
|
|||
|
|
}
|
|||
|
|
.field-error .field-help { color: var(--danger); }
|
|||
|
|
.field-error input { border-color: var(--danger); }
|
|||
|
|
|
|||
|
|
/* Search pill — three-segment "Where / When / Who" with a
|
|||
|
|
* Rausch circular submit button at the right edge. DESIGN.md
|
|||
|
|
* §4 Search Bar. The 32px pill radius is one of the brand's
|
|||
|
|
* signature radii. */
|
|||
|
|
.search-pill {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 0;
|
|||
|
|
padding: 8px;
|
|||
|
|
border: 1px solid var(--border);
|
|||
|
|
border-radius: var(--radius-pill);
|
|||
|
|
background: var(--surface);
|
|||
|
|
box-shadow:
|
|||
|
|
rgba(0, 0, 0, 0.04) 0 2px 6px 0;
|
|||
|
|
max-width: 100%;
|
|||
|
|
}
|
|||
|
|
.search-segment {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
padding: 6px var(--space-5);
|
|||
|
|
min-width: 0;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
.search-segment + .search-segment {
|
|||
|
|
border-left: 1px solid var(--border);
|
|||
|
|
}
|
|||
|
|
.search-segment-label {
|
|||
|
|
font-size: var(--text-xs);
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--fg);
|
|||
|
|
line-height: 1.2;
|
|||
|
|
}
|
|||
|
|
.search-segment-value {
|
|||
|
|
font-size: var(--text-sm);
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--muted);
|
|||
|
|
line-height: 1.3;
|
|||
|
|
}
|
|||
|
|
.search-submit {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
width: 48px;
|
|||
|
|
height: 48px;
|
|||
|
|
margin-left: var(--space-2);
|
|||
|
|
border: none;
|
|||
|
|
border-radius: var(--radius-pill);
|
|||
|
|
background: var(--accent);
|
|||
|
|
color: var(--accent-on);
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition:
|
|||
|
|
background-color var(--motion-fast) var(--ease-standard),
|
|||
|
|
transform var(--motion-fast) var(--ease-standard);
|
|||
|
|
}
|
|||
|
|
.search-submit:hover { background: var(--accent-hover); }
|
|||
|
|
.search-submit:active {
|
|||
|
|
background: var(--accent-active);
|
|||
|
|
transform: scale(0.92);
|
|||
|
|
}
|
|||
|
|
.search-submit:focus-visible {
|
|||
|
|
outline: none;
|
|||
|
|
box-shadow: var(--focus-ring);
|
|||
|
|
}
|
|||
|
|
.search-submit svg { width: 16px; height: 16px; }
|
|||
|
|
|
|||
|
|
/* ─── Cards ──────────────────────────────────────────────────
|
|||
|
|
* Two card shapes: the listing card (no border, no shadow,
|
|||
|
|
* 14px image radius, text directly below the photo per
|
|||
|
|
* DESIGN.md §4 Listing Card) and the booking-panel card
|
|||
|
|
* (1px Hairline border, 20px radius, three-layer shadow
|
|||
|
|
* stack — the system's most recognizable elevation). */
|
|||
|
|
.listing {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-3);
|
|||
|
|
}
|
|||
|
|
.listing-photo {
|
|||
|
|
position: relative;
|
|||
|
|
aspect-ratio: 4 / 3;
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
overflow: hidden;
|
|||
|
|
background: var(--surface-warm);
|
|||
|
|
}
|
|||
|
|
/* Photograph placeholder — gentle linear gradient inside the
|
|||
|
|
* 14px radius. In production this slot is a 4:3 muscache CDN
|
|||
|
|
* image with loading="lazy"; the fixture renders a tonal
|
|||
|
|
* surface so the card geometry is still legible without art. */
|
|||
|
|
.listing-photo::before {
|
|||
|
|
content: "";
|
|||
|
|
position: absolute;
|
|||
|
|
inset: 0;
|
|||
|
|
background:
|
|||
|
|
linear-gradient(
|
|||
|
|
135deg,
|
|||
|
|
var(--surface-warm) 0%,
|
|||
|
|
var(--border) 60%,
|
|||
|
|
var(--surface-warm) 100%
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
.listing-photo .btn-icon {
|
|||
|
|
position: absolute;
|
|||
|
|
top: var(--space-3);
|
|||
|
|
right: var(--space-3);
|
|||
|
|
z-index: 1;
|
|||
|
|
}
|
|||
|
|
.listing-photo .listing-badge {
|
|||
|
|
position: absolute;
|
|||
|
|
top: var(--space-3);
|
|||
|
|
left: var(--space-3);
|
|||
|
|
z-index: 1;
|
|||
|
|
}
|
|||
|
|
.listing-meta {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-1);
|
|||
|
|
}
|
|||
|
|
.listing-row {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
gap: var(--space-2);
|
|||
|
|
}
|
|||
|
|
.listing-title {
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--fg);
|
|||
|
|
}
|
|||
|
|
.listing-subtitle {
|
|||
|
|
font-size: var(--text-sm);
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--muted);
|
|||
|
|
}
|
|||
|
|
.listing-price {
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--fg);
|
|||
|
|
}
|
|||
|
|
.listing-price .per {
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--muted);
|
|||
|
|
}
|
|||
|
|
.listing-rating {
|
|||
|
|
font-size: var(--text-sm);
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--fg);
|
|||
|
|
font-variant-numeric: tabular-nums;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Booking panel — the brand's signature sticky elevation.
|
|||
|
|
* Three-layer shadow + Hairline border + 20px radius +
|
|||
|
|
* 24px padding. DESIGN.md §4 Detail Page Booking Panel. */
|
|||
|
|
.booking-panel {
|
|||
|
|
background: var(--surface);
|
|||
|
|
border: 1px solid var(--border);
|
|||
|
|
border-radius: var(--radius-lg);
|
|||
|
|
box-shadow: var(--elev-raised);
|
|||
|
|
padding: var(--space-6);
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-4);
|
|||
|
|
max-width: 370px;
|
|||
|
|
}
|
|||
|
|
.booking-headline {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: baseline;
|
|||
|
|
gap: var(--space-2);
|
|||
|
|
}
|
|||
|
|
.booking-price {
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: var(--text-xl);
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--fg);
|
|||
|
|
letter-spacing: var(--tracking-display);
|
|||
|
|
}
|
|||
|
|
.booking-per {
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--muted);
|
|||
|
|
}
|
|||
|
|
.booking-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: 1fr 1fr;
|
|||
|
|
border: 1px solid var(--border);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
.booking-grid-cell {
|
|||
|
|
padding: var(--space-3) var(--space-4);
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 2px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
background: var(--surface);
|
|||
|
|
}
|
|||
|
|
.booking-grid-cell + .booking-grid-cell {
|
|||
|
|
border-left: 1px solid var(--border);
|
|||
|
|
}
|
|||
|
|
.booking-grid-cell.full {
|
|||
|
|
grid-column: 1 / -1;
|
|||
|
|
border-top: 1px solid var(--border);
|
|||
|
|
border-left: none;
|
|||
|
|
}
|
|||
|
|
.booking-cell-label {
|
|||
|
|
font-size: 10px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: var(--fg);
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
letter-spacing: 0.06em;
|
|||
|
|
}
|
|||
|
|
.booking-cell-value {
|
|||
|
|
font-size: var(--text-sm);
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--muted);
|
|||
|
|
}
|
|||
|
|
.booking-disclaimer {
|
|||
|
|
font-size: var(--text-xs);
|
|||
|
|
color: var(--muted);
|
|||
|
|
text-align: center;
|
|||
|
|
line-height: var(--leading-body);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ─── Badges ─────────────────────────────────────────────────
|
|||
|
|
* Two shapes: the rounded brand badge (14px radius, used on
|
|||
|
|
* listing photos as "NEW" or "Guest favorite" markers) and
|
|||
|
|
* the dotted status badge (pill, used in metadata strips).
|
|||
|
|
* Both stay under DESIGN.md §3's "no all-caps except 8px"
|
|||
|
|
* rule by using sentence case at 12px·600. */
|
|||
|
|
.badge {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: var(--space-2);
|
|||
|
|
padding: 6px var(--space-3);
|
|||
|
|
border-radius: var(--radius-pill);
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: var(--text-xs);
|
|||
|
|
font-weight: 600;
|
|||
|
|
line-height: 1.2;
|
|||
|
|
}
|
|||
|
|
.badge-rausch {
|
|||
|
|
background: var(--accent);
|
|||
|
|
color: var(--accent-on);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
}
|
|||
|
|
.badge-soft {
|
|||
|
|
background: var(--surface);
|
|||
|
|
color: var(--fg);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
box-shadow: var(--elev-ring);
|
|||
|
|
}
|
|||
|
|
.badge-status {
|
|||
|
|
background: var(--surface-warm);
|
|||
|
|
color: var(--fg);
|
|||
|
|
}
|
|||
|
|
.badge-status .badge-dot {
|
|||
|
|
width: 6px;
|
|||
|
|
height: 6px;
|
|||
|
|
border-radius: var(--radius-pill);
|
|||
|
|
background: var(--success);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ─── Links ──────────────────────────────────────────────────
|
|||
|
|
* Body links inherit Ink Black, NOT Rausch. Rausch is
|
|||
|
|
* reserved for buttons and the active-tab indicator;
|
|||
|
|
* promoting it to link color would dilute the accent and
|
|||
|
|
* trip the two-uses-per-viewport cap. Underline appears on
|
|||
|
|
* hover at generous offset. */
|
|||
|
|
a {
|
|||
|
|
color: var(--fg);
|
|||
|
|
text-decoration: underline;
|
|||
|
|
text-underline-offset: 3px;
|
|||
|
|
text-decoration-thickness: 1px;
|
|||
|
|
text-decoration-color: var(--border);
|
|||
|
|
transition: text-decoration-color var(--motion-fast) var(--ease-standard);
|
|||
|
|
}
|
|||
|
|
a:hover {
|
|||
|
|
text-decoration-color: var(--fg);
|
|||
|
|
}
|
|||
|
|
a:focus-visible {
|
|||
|
|
outline: none;
|
|||
|
|
box-shadow: var(--focus-ring);
|
|||
|
|
border-radius: var(--radius-sm);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ─── Kbd ────────────────────────────────────────────────────
|
|||
|
|
* Hairline outline + Soft Cloud fill + tabular mono. Used
|
|||
|
|
* for the rare keyboard shortcut hint. */
|
|||
|
|
kbd {
|
|||
|
|
display: inline-block;
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: var(--text-xs);
|
|||
|
|
padding: 2px 6px;
|
|||
|
|
border-radius: var(--radius-sm);
|
|||
|
|
border: 1px solid var(--border);
|
|||
|
|
background: var(--surface-warm);
|
|||
|
|
color: var(--fg);
|
|||
|
|
line-height: 1.2;
|
|||
|
|
font-variant-numeric: tabular-nums;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ─── Distinctive: Guest Favorite Award lockup ───────────────
|
|||
|
|
* The brand's most recognizable moment (DESIGN.md §4
|
|||
|
|
* Signature Components). A centered rating numeral at
|
|||
|
|
* 44–56px·700 flanked by two laurel wreath SVGs. Below the
|
|||
|
|
* lockup: a 12px·700 uppercase label with 0.32px tracking
|
|||
|
|
* (we widen to 0.06em — the craft floor) and a 14px·500
|
|||
|
|
* Ash Gray sub-label. Full-width block, no border, sits
|
|||
|
|
* directly on the canvas. */
|
|||
|
|
.award {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
text-align: center;
|
|||
|
|
gap: var(--space-3);
|
|||
|
|
padding-block: var(--space-8);
|
|||
|
|
}
|
|||
|
|
.award-row {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: var(--space-5);
|
|||
|
|
color: var(--fg);
|
|||
|
|
}
|
|||
|
|
.award-rating {
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: var(--text-3xl);
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: var(--fg);
|
|||
|
|
line-height: 1;
|
|||
|
|
letter-spacing: var(--tracking-display);
|
|||
|
|
font-variant-numeric: tabular-nums;
|
|||
|
|
}
|
|||
|
|
.award-laurel {
|
|||
|
|
width: 48px;
|
|||
|
|
height: 64px;
|
|||
|
|
color: var(--fg);
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
.award-laurel.right { transform: scaleX(-1); }
|
|||
|
|
.award-label {
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: var(--text-xs);
|
|||
|
|
font-weight: 700;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
letter-spacing: 0.08em;
|
|||
|
|
color: var(--fg);
|
|||
|
|
line-height: 1;
|
|||
|
|
}
|
|||
|
|
.award-sub {
|
|||
|
|
font-size: var(--text-sm);
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--muted);
|
|||
|
|
max-width: 44ch;
|
|||
|
|
line-height: var(--leading-body);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Tri-tab category picker (DESIGN.md §4 Tri-Tab) — the brand's
|
|||
|
|
* other instantly-recognizable nav moment. We render with
|
|||
|
|
* type-only icons (no 3D illustrations) because the fixture
|
|||
|
|
* stays raster-free, but the underline + label rhythm holds. */
|
|||
|
|
.tabs {
|
|||
|
|
display: inline-flex;
|
|||
|
|
gap: var(--space-8);
|
|||
|
|
padding-block-start: var(--space-3);
|
|||
|
|
border-bottom: 1px solid var(--border);
|
|||
|
|
}
|
|||
|
|
.tab {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: var(--space-2);
|
|||
|
|
padding-block-end: var(--space-3);
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: var(--text-base);
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--muted);
|
|||
|
|
background: none;
|
|||
|
|
border: none;
|
|||
|
|
cursor: pointer;
|
|||
|
|
position: relative;
|
|||
|
|
text-decoration: none;
|
|||
|
|
}
|
|||
|
|
.tab-glyph {
|
|||
|
|
width: 32px;
|
|||
|
|
height: 32px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
color: var(--fg);
|
|||
|
|
}
|
|||
|
|
.tab[aria-selected="true"] {
|
|||
|
|
color: var(--fg);
|
|||
|
|
}
|
|||
|
|
.tab[aria-selected="true"]::after {
|
|||
|
|
content: "";
|
|||
|
|
position: absolute;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
bottom: -1px;
|
|||
|
|
height: 2px;
|
|||
|
|
background: var(--fg);
|
|||
|
|
border-radius: var(--radius-pill);
|
|||
|
|
}
|
|||
|
|
.tab-new {
|
|||
|
|
position: absolute;
|
|||
|
|
top: -4px;
|
|||
|
|
right: -10px;
|
|||
|
|
padding: 2px 6px;
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
background: var(--fg);
|
|||
|
|
color: var(--accent-on);
|
|||
|
|
font-size: 10px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
line-height: 1.2;
|
|||
|
|
letter-spacing: 0.06em;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ─── Section-specific layout ──────────────────────────────── */
|
|||
|
|
.hero-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
|
|||
|
|
gap: var(--space-12);
|
|||
|
|
align-items: start;
|
|||
|
|
}
|
|||
|
|
@media (max-width: 1023px) {
|
|||
|
|
.hero-grid { grid-template-columns: 1fr; gap: var(--space-8); }
|
|||
|
|
}
|
|||
|
|
.hero-actions {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: var(--space-3);
|
|||
|
|
margin-block-start: var(--space-6);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.listings-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(3, 1fr);
|
|||
|
|
gap: var(--space-6);
|
|||
|
|
margin-block-start: var(--space-8);
|
|||
|
|
}
|
|||
|
|
@media (max-width: 1023px) {
|
|||
|
|
.listings-grid { grid-template-columns: repeat(2, 1fr); }
|
|||
|
|
}
|
|||
|
|
@media (max-width: 639px) {
|
|||
|
|
.listings-grid { grid-template-columns: 1fr; gap: var(--space-4); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-row {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
|
|||
|
|
gap: var(--space-12);
|
|||
|
|
align-items: start;
|
|||
|
|
}
|
|||
|
|
@media (max-width: 1023px) {
|
|||
|
|
.form-row { grid-template-columns: 1fr; }
|
|||
|
|
}
|
|||
|
|
.form {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-4);
|
|||
|
|
}
|
|||
|
|
.row-between {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
gap: var(--space-3);
|
|||
|
|
}
|
|||
|
|
.row-cluster {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: var(--space-2);
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
.divider-soft {
|
|||
|
|
height: 1px;
|
|||
|
|
background: var(--border-soft);
|
|||
|
|
border: none;
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
.nav-row {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
padding-block: var(--space-4);
|
|||
|
|
gap: var(--space-4);
|
|||
|
|
}
|
|||
|
|
.nav-logo {
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: var(--text-lg);
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: var(--accent);
|
|||
|
|
letter-spacing: var(--tracking-display);
|
|||
|
|
}
|
|||
|
|
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<main class="container">
|
|||
|
|
<!-- ════════════════════════════════════════════════════════════
|
|||
|
|
NAV — exercises: .nav-row, .nav-logo (Rausch wordmark),
|
|||
|
|
.tabs (tri-tab picker, DESIGN.md §4 signature). Active
|
|||
|
|
"Homes" tab gets the 2px Ink Black underline; "Experiences"
|
|||
|
|
carries the small Ink Black NEW pill from §4.
|
|||
|
|
═══════════════════════════════════════════════════════════════ -->
|
|||
|
|
<nav class="nav-row" aria-label="Primary">
|
|||
|
|
<span class="nav-logo">airbnb</span>
|
|||
|
|
<div class="tabs" role="tablist">
|
|||
|
|
<button class="tab" role="tab" aria-selected="true">
|
|||
|
|
<span class="tab-glyph" aria-hidden="true">
|
|||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
|
stroke-width="1.5" stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round" width="24" height="24">
|
|||
|
|
<path d="M3 11l9-7 9 7v9a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z" />
|
|||
|
|
</svg>
|
|||
|
|
</span>
|
|||
|
|
Homes
|
|||
|
|
</button>
|
|||
|
|
<button class="tab" role="tab" aria-selected="false">
|
|||
|
|
<span class="tab-glyph" aria-hidden="true">
|
|||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
|
stroke-width="1.5" stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round" width="24" height="24">
|
|||
|
|
<ellipse cx="12" cy="9" rx="6" ry="7" />
|
|||
|
|
<path d="M9 16l-1 5 4-2 4 2-1-5" />
|
|||
|
|
</svg>
|
|||
|
|
</span>
|
|||
|
|
Experiences
|
|||
|
|
<span class="tab-new" aria-label="New">New</span>
|
|||
|
|
</button>
|
|||
|
|
<button class="tab" role="tab" aria-selected="false">
|
|||
|
|
<span class="tab-glyph" aria-hidden="true">
|
|||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
|
stroke-width="1.5" stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round" width="24" height="24">
|
|||
|
|
<path d="M6 16a6 6 0 1 1 12 0z" />
|
|||
|
|
<path d="M12 6V3M5 18h14M11 19v2h2v-2" />
|
|||
|
|
</svg>
|
|||
|
|
</span>
|
|||
|
|
Services
|
|||
|
|
<span class="tab-new" aria-label="New">New</span>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="row-cluster">
|
|||
|
|
<button class="btn btn-secondary btn-sm" type="button">Become a host</button>
|
|||
|
|
<button class="btn-icon" type="button" aria-label="Menu">
|
|||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
|
stroke-width="1.75" stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round" aria-hidden="true">
|
|||
|
|
<path d="M4 7h16M4 12h16M4 17h16" />
|
|||
|
|
</svg>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</nav>
|
|||
|
|
|
|||
|
|
<!-- ════════════════════════════════════════════════════════════
|
|||
|
|
HERO — exercises: .eyebrow, h1, .lede, .search-pill (the
|
|||
|
|
three-segment Where/When/Who with the Rausch circular
|
|||
|
|
submit, DESIGN.md §4), .btn-primary, .btn-secondary,
|
|||
|
|
kbd, .badge-status (success dot).
|
|||
|
|
═══════════════════════════════════════════════════════════════ -->
|
|||
|
|
<section data-od-id="hero">
|
|||
|
|
<div class="hero-grid">
|
|||
|
|
<div class="stack-4">
|
|||
|
|
<p class="eyebrow">Reference fixture · airbnb</p>
|
|||
|
|
<h1 style="max-width: 18ch">
|
|||
|
|
Find a place where the photograph is the interface.
|
|||
|
|
</h1>
|
|||
|
|
<p class="lede body-muted" style="max-width: 56ch">
|
|||
|
|
A token system distilled from Airbnb's 2026 rules — Canvas
|
|||
|
|
White surfaces, a single Rausch coral accent, generous
|
|||
|
|
soft-circle radii, and Cereal at one family. The fixture
|
|||
|
|
you are reading uses the same token block agents paste
|
|||
|
|
into every artifact, so the rules survive the paste.
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<div class="search-pill" role="search" aria-label="Find a stay">
|
|||
|
|
<div class="search-segment">
|
|||
|
|
<span class="search-segment-label">Where</span>
|
|||
|
|
<span class="search-segment-value">Search destinations</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="search-segment">
|
|||
|
|
<span class="search-segment-label">When</span>
|
|||
|
|
<span class="search-segment-value">Add dates</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="search-segment">
|
|||
|
|
<span class="search-segment-label">Who</span>
|
|||
|
|
<span class="search-segment-value">Add guests</span>
|
|||
|
|
</div>
|
|||
|
|
<button class="search-submit" type="submit" aria-label="Search">
|
|||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
|
stroke-width="2" stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round" aria-hidden="true">
|
|||
|
|
<circle cx="11" cy="11" r="7" />
|
|||
|
|
<path d="M21 21l-4.3-4.3" />
|
|||
|
|
</svg>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="hero-actions">
|
|||
|
|
<a href="./tokens.css" class="btn btn-primary">
|
|||
|
|
View the token block
|
|||
|
|
<svg
|
|||
|
|
class="icon"
|
|||
|
|
viewBox="0 0 24 24"
|
|||
|
|
fill="none"
|
|||
|
|
stroke="currentColor"
|
|||
|
|
stroke-width="1.75"
|
|||
|
|
stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round"
|
|||
|
|
aria-hidden="true"
|
|||
|
|
>
|
|||
|
|
<path d="M5 12h14M13 6l6 6-6 6" />
|
|||
|
|
</svg>
|
|||
|
|
</a>
|
|||
|
|
<a href="./DESIGN.md" class="btn btn-secondary">Read the brand</a>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<aside class="booking-panel" aria-label="Token status">
|
|||
|
|
<div class="booking-headline">
|
|||
|
|
<span class="booking-price">56 tokens</span>
|
|||
|
|
<span class="booking-per">in :root</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="booking-grid" role="group">
|
|||
|
|
<div class="booking-grid-cell">
|
|||
|
|
<span class="booking-cell-label">Last reviewed</span>
|
|||
|
|
<span class="booking-cell-value">
|
|||
|
|
<time datetime="2026-05-14">May 14, 2026</time>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="booking-grid-cell">
|
|||
|
|
<span class="booking-cell-label">Schema</span>
|
|||
|
|
<span class="booking-cell-value">v0.1 · A1/A2/B</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="booking-grid-cell full">
|
|||
|
|
<span class="booking-cell-label">Accent budget</span>
|
|||
|
|
<span class="booking-cell-value">≤ 2 visible Rausch uses per viewport</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<button type="button" class="btn btn-primary" style="width: 100%">
|
|||
|
|
Inspect tokens
|
|||
|
|
</button>
|
|||
|
|
<p class="booking-disclaimer">
|
|||
|
|
You won't be charged — this is a static fixture. Press
|
|||
|
|
<kbd>⌘</kbd> <kbd>K</kbd> to jump to the token grep.
|
|||
|
|
</p>
|
|||
|
|
<div class="row-between" style="margin-block-start: var(--space-2)">
|
|||
|
|
<span class="badge badge-status">
|
|||
|
|
<span class="badge-dot" aria-hidden="true"></span>
|
|||
|
|
Tokens passing schema
|
|||
|
|
</span>
|
|||
|
|
<span class="listing-rating" aria-label="Token coverage">
|
|||
|
|
<svg
|
|||
|
|
width="14"
|
|||
|
|
height="14"
|
|||
|
|
viewBox="0 0 24 24"
|
|||
|
|
fill="currentColor"
|
|||
|
|
aria-hidden="true"
|
|||
|
|
style="vertical-align: -2px; margin-right: 4px"
|
|||
|
|
>
|
|||
|
|
<path d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.7 7L12 17l-6.3 4.2 1.7-7L2 9.5l7.1-.6z" />
|
|||
|
|
</svg>
|
|||
|
|
4.92
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</aside>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- ════════════════════════════════════════════════════════════
|
|||
|
|
LISTINGS — exercises: .listing (4:3 photo, no border, no
|
|||
|
|
shadow, 14px radius), .listing-photo with floated circular
|
|||
|
|
favorite button, .badge-rausch, .listing-rating, links.
|
|||
|
|
Three cards in a 3-col grid that reflows to 2 then 1.
|
|||
|
|
═══════════════════════════════════════════════════════════════ -->
|
|||
|
|
<section data-od-id="listings">
|
|||
|
|
<div class="stack-3">
|
|||
|
|
<p class="eyebrow">What the fixture exercises</p>
|
|||
|
|
<h2 style="max-width: 32ch">
|
|||
|
|
Three listings, every token resolved through var(--*).
|
|||
|
|
</h2>
|
|||
|
|
<p class="body-muted" style="max-width: 56ch">
|
|||
|
|
No raw hex outside the :root paste. No second type family.
|
|||
|
|
No drop shadow on the listing cards — separation comes from
|
|||
|
|
the photograph and the 24px gutter between them.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="listings-grid">
|
|||
|
|
<article class="listing" aria-label="Cottage in the Cotswolds">
|
|||
|
|
<div class="listing-photo">
|
|||
|
|
<span class="listing-badge badge badge-soft">Guest favorite</span>
|
|||
|
|
<button class="btn-icon" type="button" aria-label="Save to wishlist">
|
|||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
|
stroke-width="1.75" stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round" aria-hidden="true">
|
|||
|
|
<path d="M12 21s-7-4.5-7-10a4 4 0 0 1 7-2.7A4 4 0 0 1 19 11c0 5.5-7 10-7 10z" />
|
|||
|
|
</svg>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="listing-meta">
|
|||
|
|
<div class="listing-row">
|
|||
|
|
<span class="listing-title">Cotswolds, England</span>
|
|||
|
|
<span class="listing-rating">
|
|||
|
|
<svg
|
|||
|
|
width="12"
|
|||
|
|
height="12"
|
|||
|
|
viewBox="0 0 24 24"
|
|||
|
|
fill="currentColor"
|
|||
|
|
aria-hidden="true"
|
|||
|
|
style="vertical-align: -1px; margin-right: 3px"
|
|||
|
|
>
|
|||
|
|
<path d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.7 7L12 17l-6.3 4.2 1.7-7L2 9.5l7.1-.6z" />
|
|||
|
|
</svg>
|
|||
|
|
4.97
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<span class="listing-subtitle">Cottage rentals · 2 hours by train</span>
|
|||
|
|
<span class="listing-subtitle">Apr 12 – 17</span>
|
|||
|
|
<span class="listing-price">
|
|||
|
|
$182 <span class="per">/ night</span>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</article>
|
|||
|
|
|
|||
|
|
<article class="listing" aria-label="Loft in Lisbon">
|
|||
|
|
<div class="listing-photo">
|
|||
|
|
<span class="listing-badge badge badge-rausch">New</span>
|
|||
|
|
<button class="btn-icon" type="button" aria-label="Save to wishlist">
|
|||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
|
stroke-width="1.75" stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round" aria-hidden="true">
|
|||
|
|
<path d="M12 21s-7-4.5-7-10a4 4 0 0 1 7-2.7A4 4 0 0 1 19 11c0 5.5-7 10-7 10z" />
|
|||
|
|
</svg>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="listing-meta">
|
|||
|
|
<div class="listing-row">
|
|||
|
|
<span class="listing-title">Lisbon, Portugal</span>
|
|||
|
|
<span class="listing-rating">
|
|||
|
|
<svg
|
|||
|
|
width="12"
|
|||
|
|
height="12"
|
|||
|
|
viewBox="0 0 24 24"
|
|||
|
|
fill="currentColor"
|
|||
|
|
aria-hidden="true"
|
|||
|
|
style="vertical-align: -1px; margin-right: 3px"
|
|||
|
|
>
|
|||
|
|
<path d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.7 7L12 17l-6.3 4.2 1.7-7L2 9.5l7.1-.6z" />
|
|||
|
|
</svg>
|
|||
|
|
4.81
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<span class="listing-subtitle">Loft apartments · Alfama</span>
|
|||
|
|
<span class="listing-subtitle">May 03 – 09</span>
|
|||
|
|
<span class="listing-price">
|
|||
|
|
$96 <span class="per">/ night</span>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</article>
|
|||
|
|
|
|||
|
|
<article class="listing" aria-label="Yacht tour in Mykonos">
|
|||
|
|
<div class="listing-photo">
|
|||
|
|
<span class="listing-badge badge badge-soft">Experience</span>
|
|||
|
|
<button class="btn-icon" type="button" aria-label="Save to wishlist">
|
|||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|||
|
|
stroke-width="1.75" stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round" aria-hidden="true">
|
|||
|
|
<path d="M12 21s-7-4.5-7-10a4 4 0 0 1 7-2.7A4 4 0 0 1 19 11c0 5.5-7 10-7 10z" />
|
|||
|
|
</svg>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="listing-meta">
|
|||
|
|
<div class="listing-row">
|
|||
|
|
<span class="listing-title">Mykonos, Greece</span>
|
|||
|
|
<span class="listing-rating">
|
|||
|
|
<svg
|
|||
|
|
width="12"
|
|||
|
|
height="12"
|
|||
|
|
viewBox="0 0 24 24"
|
|||
|
|
fill="currentColor"
|
|||
|
|
aria-hidden="true"
|
|||
|
|
style="vertical-align: -1px; margin-right: 3px"
|
|||
|
|
>
|
|||
|
|
<path d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.7 7L12 17l-6.3 4.2 1.7-7L2 9.5l7.1-.6z" />
|
|||
|
|
</svg>
|
|||
|
|
4.89
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<span class="listing-subtitle">Small Group Yacht Tour</span>
|
|||
|
|
<span class="listing-subtitle">4 hours · Unlimited fruit</span>
|
|||
|
|
<span class="listing-price">
|
|||
|
|
$148 <span class="per">/ person</span>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</article>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- ════════════════════════════════════════════════════════════
|
|||
|
|
GUEST FAVORITE AWARD — the brand's signature distinctive
|
|||
|
|
component. Centered rating numeral at 44px·700 flanked by
|
|||
|
|
two laurel wreath SVGs at 48×64. Below, an uppercase 12px·700
|
|||
|
|
"GUEST FAVORITE" label and a 14px·500 Ash Gray sub-line.
|
|||
|
|
Full-width block, no border, sits directly on the canvas
|
|||
|
|
per DESIGN.md §4 Signature Components.
|
|||
|
|
═══════════════════════════════════════════════════════════════ -->
|
|||
|
|
<section data-od-id="award">
|
|||
|
|
<div class="award" aria-label="Guest Favorite award">
|
|||
|
|
<div class="award-row">
|
|||
|
|
<svg
|
|||
|
|
class="award-laurel"
|
|||
|
|
viewBox="0 0 48 64"
|
|||
|
|
fill="none"
|
|||
|
|
stroke="currentColor"
|
|||
|
|
stroke-width="1.5"
|
|||
|
|
stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round"
|
|||
|
|
aria-hidden="true"
|
|||
|
|
>
|
|||
|
|
<path d="M40 60 C 14 56, 6 38, 12 16" />
|
|||
|
|
<path d="M30 56 C 18 50, 14 44, 14 36" />
|
|||
|
|
<path d="M18 22 C 8 22, 6 14, 12 8" />
|
|||
|
|
<path d="M20 30 C 8 30, 6 24, 10 18" />
|
|||
|
|
<path d="M22 38 C 12 38, 8 32, 12 26" />
|
|||
|
|
<path d="M26 46 C 16 46, 10 40, 14 34" />
|
|||
|
|
<path d="M30 52 C 22 52, 16 48, 18 42" />
|
|||
|
|
</svg>
|
|||
|
|
<span class="award-rating">4.92</span>
|
|||
|
|
<svg
|
|||
|
|
class="award-laurel right"
|
|||
|
|
viewBox="0 0 48 64"
|
|||
|
|
fill="none"
|
|||
|
|
stroke="currentColor"
|
|||
|
|
stroke-width="1.5"
|
|||
|
|
stroke-linecap="round"
|
|||
|
|
stroke-linejoin="round"
|
|||
|
|
aria-hidden="true"
|
|||
|
|
>
|
|||
|
|
<path d="M40 60 C 14 56, 6 38, 12 16" />
|
|||
|
|
<path d="M30 56 C 18 50, 14 44, 14 36" />
|
|||
|
|
<path d="M18 22 C 8 22, 6 14, 12 8" />
|
|||
|
|
<path d="M20 30 C 8 30, 6 24, 10 18" />
|
|||
|
|
<path d="M22 38 C 12 38, 8 32, 12 26" />
|
|||
|
|
<path d="M26 46 C 16 46, 10 40, 14 34" />
|
|||
|
|
<path d="M30 52 C 22 52, 16 48, 18 42" />
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<p class="award-label">Guest favorite</p>
|
|||
|
|
<p class="award-sub">
|
|||
|
|
One of the most loved tokensets on OpenDesign, according
|
|||
|
|
to agents who paste it.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- ════════════════════════════════════════════════════════════
|
|||
|
|
FORM — exercises: .field (text input, focus rings the
|
|||
|
|
border to Ink Black and adds the 2px outer ring per
|
|||
|
|
DESIGN.md §4 Text Input), .field-error (Error Red),
|
|||
|
|
.btn-primary, .btn-secondary, kbd. The right column re-
|
|||
|
|
uses the .booking-panel pattern as a price summary.
|
|||
|
|
═══════════════════════════════════════════════════════════════ -->
|
|||
|
|
<section data-od-id="form">
|
|||
|
|
<div class="form-row">
|
|||
|
|
<div class="stack-4">
|
|||
|
|
<p class="eyebrow">Form components</p>
|
|||
|
|
<h2 style="max-width: 24ch">
|
|||
|
|
Inputs inherit the same tokens.
|
|||
|
|
</h2>
|
|||
|
|
<p class="body-muted" style="max-width: 52ch">
|
|||
|
|
Focus rings, borders, and placeholder color all derive
|
|||
|
|
from --fg and --border. Submit reuses .btn-primary
|
|||
|
|
unchanged. No new token introduced for this section —
|
|||
|
|
if a value here is not in the :root paste above, it
|
|||
|
|
does not belong in the artifact at all.
|
|||
|
|
</p>
|
|||
|
|
<hr class="divider-soft" />
|
|||
|
|
<p class="body-meta">
|
|||
|
|
Full reference at <a href="./tokens.css">tokens.css</a>
|
|||
|
|
· brand at <a href="./DESIGN.md">DESIGN.md</a>.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<form class="form" onsubmit="event.preventDefault();">
|
|||
|
|
<div class="field">
|
|||
|
|
<label for="email">Travel notes email</label>
|
|||
|
|
<input
|
|||
|
|
id="email"
|
|||
|
|
type="email"
|
|||
|
|
placeholder="[email protected]"
|
|||
|
|
autocomplete="email"
|
|||
|
|
required
|
|||
|
|
/>
|
|||
|
|
<p class="field-help">
|
|||
|
|
We'll send a single dispatch a season — never more.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="field">
|
|||
|
|
<label for="destination">Where</label>
|
|||
|
|
<input
|
|||
|
|
id="destination"
|
|||
|
|
type="text"
|
|||
|
|
placeholder="Search destinations"
|
|||
|
|
autocomplete="off"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="field field-error">
|
|||
|
|
<label for="dates">Dates</label>
|
|||
|
|
<input
|
|||
|
|
id="dates"
|
|||
|
|
type="text"
|
|||
|
|
placeholder="Add dates"
|
|||
|
|
aria-invalid="true"
|
|||
|
|
/>
|
|||
|
|
<p class="field-help">Pick a check-in and check-out before you continue.</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="row-cluster" style="margin-block-start: var(--space-2)">
|
|||
|
|
<button type="submit" class="btn btn-primary">Send the dispatch</button>
|
|||
|
|
<button type="button" class="btn btn-secondary">Skip for now</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
</main>
|
|||
|
|
</body>
|
|||
|
|
</html>
|