imitation
← all crawls

Luxury Balinese Villa in St. John | Indo House

https://www.indohousestjohn.com/the-villa

Discover Indo House: a 4,500 sq ft oceanfront villa blending Balinese design with Caribbean tranquility. Infinity pool, chef's kitchen, private suites.

indohousestjohn.com screenshot

Visuals

Colors

Primary
#18242d
Accent
#cbaf7b
Background
#ffffff
Text Primary
#030303
Text Secondary
#333333

Typography

Aa
Display
Cormorant Garamond
56px / w700
Aa
Heading
Cormorant Garamond
31.2px / w700
Aa
Body
system-ui
16px / w400
Aa
Label
system-ui
14px / w600

Spacing

Xs
6px
Sm
8px
Md
10px
Lg
12px
Xl
16px

Radius

Sm
8px
Md
12px
Lg
16px
Pill
9999px

Mode

Light mode
Background
Sample text on canvas
Bg#ffffff
Text#030303
Muted#333333

Motion

Levelsubtle
Duration200ms
Easingcubic-bezier(0.165, 0.84, 0.44, 1)

Stack

Frameworkwebflow
Iconscustom

Full Palette · 24 tokens

Brand
accent
#cbaf7b
accent-dark
#b09a6f
accent-hover
#bda06e
accent-subtle
rgba(155, 137, 103, 0.1)
primary
#18242d
primary-hover
#0f171e
Surface
canvas
#ffffff
surface
#ffffff
surface-dark
#030303
surface-wash
rgba(15, 19, 38, 0.05)
Text
body
#333333
ink
#030303
muted
rgba(3, 3, 3, 0.6)
muted-dense
rgba(3, 3, 3, 0.75)
muted-strong
rgba(3, 3, 3, 0.7)
on-accent
#000000
on-dark
#ffffff
on-primary
#ffffff
on-surface
#030303
Hairlines & Borders
hairline
rgba(3, 3, 3, 0.1)
hairline-strong
rgba(3, 3, 3, 0.2)
Semantic
link
#0000ee
link-visited
#551a8b
Other
body-secondary
#222222

Typography Roles · 9

body-md
Aa
16px / w400
body-sm
Aa
14.4px / w400
button
Aa
14.4px / w400
display-lg
Aa
48.8px / w700
display-xl
Aa
56px / w700
label-md
Aa
14px / w600
nav-link
Aa
16px / w600
title-md
Aa
31.2px / w700
title-sm
Aa
24.96px / w700

Components · 13

button-accent
backgroundColor{colors.accent}
padding14.4px 28.8px
rounded{rounded.DEFAULT}
textColor{colors.on-accent}
typography{typography.button}
button-accent-hover
backgroundColor{colors.accent-hover}
rounded{rounded.DEFAULT}
textColor{colors.on-accent}
button-outline
backgroundColorrgba(0, 0, 0, 0)
padding14px 28px
rounded{rounded.pill}
textColor{colors.on-dark}
typography{typography.button}
card
backgroundColor{colors.surface}
padding{spacing.section-xl} {spacing.xl}
rounded{rounded.lg}
textColor{colors.ink}
typography{typography.body-md}
card-label
backgroundColortransparent
textColor{colors.accent-dark}
typography{typography.label-md}
footer
backgroundColor{colors.canvas}
padding{spacing.section-xl} {spacing.xl}
textColor{colors.muted}
typography{typography.body-sm}
hero-band
backgroundColor{colors.surface-dark}
textColor{colors.on-dark}
typography{typography.display-lg}
hero-headline
backgroundColortransparent
textColor{colors.on-dark}
typography{typography.display-lg}
hero-stat-badge
backgroundColorrgba(0, 0, 0, 0)
padding8px 16px
rounded{rounded.pill}
textColor{colors.muted}
typography{typography.body-sm}
nav-dropdown-toggle
backgroundColorrgba(0, 0, 0, 0)
padding16px 40px 16px 16px
rounded{rounded.none}
textColor{colors.body-secondary}
typography{typography.nav-link}
nav-link-tertiary
backgroundColorrgba(0, 0, 0, 0)
padding10px 18px
rounded{rounded.sm}
textColor{colors.ink}
typography{typography.nav-link}
section-container
backgroundColortransparent
padding{spacing.section-xl} 0
textColor{colors.ink}
top-nav
backgroundColor{colors.canvas}
height80px
padding{spacing.xl} {spacing.xl}
textColor{colors.body-secondary}
typography{typography.nav-link}

design.md

Indo House Design System

Overview

Indo House presents itself as a digital sanctuary — a luxury villa brand that channels the architectural serenity of Balinese design into a web experience defined by warm restraint and photographic immersion. The visual language is built on a near-monochromatic foundation of deep charcoal ink ({colors.ink}) against pure white canvas ({colors.canvas}), punctuated by a single warm sandstone accent ({colors.accent}) that evokes sun-bleached teak and limestone. This is not a system that shouts; it whispers through generous whitespace, editorial serif display type, and imagery that occupies the full viewport.

The brand's voltage lives in measured doses: the {colors.accent} sand tone appears primarily on the primary CTA ({component.button-accent}), where its warmth invites action without demanding attention. Elsewhere, a translucent wash of the same hue ({colors.accent-subtle}) provides subtle surface differentiation. The typography makes its strongest statement through Cormorant Garamond — a high-contrast serif deployed at bold weights ({typography.display-lg}, {typography.title-md}) for headlines, creating an immediate association with boutique hospitality and architectural publishing. Body copy retreats to a neutral system stack ({typography.body-md}), allowing the photography and display type to carry the emotional load.

Shape language is consistently soft: {rounded.DEFAULT} (12px) anchors buttons and cards, while {rounded.lg} (16px) gives container cards a gentler feel. The hero stat badges go fully to {rounded.pill}, creating capsule shapes that float ethereally over the immersive photograph. The navigation is notably sharp-edged ({rounded.none}) for dropdown toggles, creating a subtle tension between the soft UI elements below and the precise utility of wayfinding.

Two sub-systems emerge: the hero immersion dialect (dark overlay, white text, full-bleed photography, floating pill badges) and the editorial content dialect (white background, ink text, sand-accented labels, generously padded cards). The reserve flow appears to be a third micro-dialect with its outlined button treatment.

Key Characteristics:

  • Pure white {colors.canvas} foundation with {colors.ink} near-black as primary text — zero color noise in the reading surface
  • Single warm accent {colors.accent} (#cbaf7b) reserved for CTAs and label highlights, never used as a background wash at full opacity
  • Cormorant Garamond at 700 weight for all display and title roles, with tight negative letter-spacing (-0.488px at {typography.display-lg} size)
  • System UI stack for all body, label, and interface text — no secondary serif in running copy
  • Rounded corners clustered at 12px–16px; only stat badges and outline buttons use {rounded.pill}
  • Hero section uses full-viewport photography with darkened overlay and centered white serif headline
  • Navigation is transparent over hero, switching to white background on scroll (implied by architecture)
  • Transition easing follows cubic-bezier(0.165, 0.84, 0.44, 1) — a deceleration curve with slight overshoot, giving interactions a premium "settling" feel
  • Section vertical rhythm anchored at {spacing.section-xl} (48px) with internal component spacing at {spacing.xl} (16px)

Colors

Brand & Accent

  • Primary ({colors.primary} — #18242d): Deep charcoal-blue used for structural emphasis, dark mode surfaces, and high-contrast text elements. Appears in muted opacity variants for overlay treatments.
  • Primary Hover ({colors.primary-hover} — #0f171e): Darker shade for interactive states on primary-colored surfaces.
  • Accent ({colors.accent} — #cbaf7b): Warm sandstone/gold tone — the sole brand color carrying emotional warmth. Used exclusively on {component.button-accent} and {component.card-label} elements.
  • Accent Hover ({colors.accent-hover} — #bda06e): Deepened sand for hover states on accent buttons.
  • Accent Subtle ({colors.accent-subtle} — rgba(155, 137, 103, 0.1)): 10% opacity wash of the accent, used for very light background tints on secondary surfaces.
  • Accent Dark ({colors.accent-dark} — #b09a6f): Muted gold-brown for label text (h4-level elements), providing hierarchical distinction without competing with the primary accent.

Surface

  • Canvas ({colors.canvas} — #ffffff): Pure white — the dominant surface across navigation, content sections, and card backgrounds.
  • Surface ({colors.surface} — #ffffff): Identical to canvas in this light-mode implementation; maintained as a semantic token for future dark-mode divergence.
  • Surface Wash ({colors.surface-wash} — rgba(15, 19, 38, 0.05)): Extremely subtle cool-gray tint at 5% opacity for alternating band backgrounds or elevated card differentiation.
  • Surface Dark ({colors.surface-dark} — #030303): Near-black used as the hero image overlay and potential footer background.

Text & Ink

  • Ink ({colors.ink} — #030303): The darkest text color — not quite pure black, softened by 3 units per channel for screen readability. Used for headlines and primary body copy.
  • Body ({colors.body} — #333333): Standard body text at roughly 20% brightness — the workhorse for paragraphs and descriptions.
  • Body Secondary ({colors.body-secondary} — #222222): Slightly darker than body, used specifically for navigation links requiring more weight than body text.
  • Muted ({colors.muted} — rgba(3, 3, 3, 0.6)): 60% opacity black for secondary information — stat badge labels, captions, metadata.
  • Muted Strong ({colors.muted-strong} — rgba(3, 3, 3, 0.7)): 70% opacity for slightly more prominent secondary text.
  • Muted Dense ({colors.muted-dense} — rgba(3, 3, 3, 0.75)): 75% opacity for the densest secondary text tier.
  • On Primary ({colors.on-primary} — #ffffff): White text for readability against {colors.primary} dark backgrounds.
  • On Accent ({colors.on-accent} — #000000): Black text on {colors.accent} sandstone buttons — maintains AA contrast against the warm background.
  • On Dark ({colors.on-dark} — #ffffff): White text for any element overlapping {colors.surface-dark} or hero imagery.
  • On Surface ({colors.on-surface} — #030303): Alias for {colors.ink} — default text color on standard surfaces.

Hairlines & Borders

  • Hairline ({colors.hairline} — rgba(3, 3, 3, 0.1)): 10% opacity borders for subtle dividers, input outlines, and card edges.
  • Hairline Strong ({colors.hairline-strong} — rgba(3, 3, 3, 0.2)): 20% opacity for borders needing slightly more visibility.

Semantic

  • Link ({colors.link} — #0000ee): Standard web blue for unvisited hyperlinks in body copy.
  • Link Visited ({colors.link-visited} — #551a8b): Traditional visited purple for browser-default link states.

Typography

Font Family

The type system is built on a two-face pairing with stark role separation. Cormorant Garamond — a contemporary serif with high contrast between thick and thin strokes — owns all expressive typography: the hero headline ({typography.display-lg}), section titles ({typography.title-md}, {typography.title-sm}), and any text meant to evoke editorial luxury or architectural permanence. It is loaded from Google Fonts at weights 300–700, though the design exclusively uses the 700 weight in extracted instances.

All functional text — body copy, navigation, buttons, labels, metadata — uses the system UI stack: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif. This ensures native rendering fidelity across macOS, Windows, iOS, and Android while keeping interface elements visually neutral. The font stack includes proprietary references to "Lyon" and "Whitney 2" in the site's @font-face declarations, suggesting a Webflow template origin, but these do not appear in computed styles for visible elements.

Hierarchy

| Token | Size | Weight | Line Height | Letter Spacing | Use | |---|---|---|---|---|---| | {typography.display-xl} | 56px | 700 | 1.04 | -0.56px | Reserved for future larger hero contexts | | {typography.display-lg} | 48.8px | 700 | 1.04 | -0.488px | Hero headline — white on dark photo overlay | | {typography.title-md} | 31.2px | 700 | 1.04 | -0.312px | Section headings (h2) in content areas | | {typography.title-sm} | 24.96px | 700 | 1.04 | -0.2496px | Subsection headings (h3) | | {typography.label-md} | 14px | 600 | 1.714 | 0 | Category labels, h4-style accents in {colors.accent-dark} | | {typography.body-md} | 16px | 400 | 1.5 | 0 | Primary body copy, paragraphs | | {typography.body-sm} | 14.4px | 400 | 1.5 | 0 | Button text, secondary body, compact copy | | {typography.button} | 14.4px | 400 | 1.4 | 0 | Button label text specifically | | {typography.nav-link} | 16px | 600 | 1.5 | 0 | Navigation items, tertiary links |

Principles

The typography system trusts size and family to carry hierarchy almost exclusively. Cormorant Garamond at 48.8px ({typography.display-lg}) is immediately recognizable as the hero voice — there is no bold-vs-extrabold variation within the serif; it is always 700 weight. The negative letter-spacing (approximately -1% of font size at each level) tightens the elegant serif characters, preventing the loose feeling that large serif type can impart on screens. Line height is remarkably tight at 1.04 (essentially 104% of font size) for all display and title roles, creating a compact, editorial column that feels like a magazine spread rather than a web page.

Body text in the system stack uses a comfortable 1.5 line height at 16px ({typography.body-md}), adhering to accessibility norms for paragraph readability. Navigation links step up to 600 weight at the same 16px size ({typography.nav-link}), creating distinction from body text without size changes. Labels use both size reduction (14px) and weight increase (600) plus the {colors.accent-dark} color to create a distinct "metadata" tier.

Notably, the system avoids italics, uppercase transformations, and letter-spacing expansion entirely in extracted instances. The voice is straightforward: bold serif for impact, neutral sans-serif for information.

Note on Font Substitutes

Cormorant Garamond is available through Google Fonts and requires no substitution. For environments where Google Fonts cannot be loaded, Playfair Display (also Google Fonts) offers the closest match in character width, stroke contrast, and serif style. Define the CSS variable --font-display: 'Cormorant Garamond', 'Playfair Display', Georgia, serif; for the display role. The system UI stack needs no substitution — it is the native fallback chain.

Layout

Spacing System

  • Base unit: 3px (the smallest extracted spacing value, appearing as {spacing.xxs}).
  • Tokens: {spacing.xxs} (3px — micro-adjustments, icon gaps), {spacing.xs} (6px — tight internal padding), {spacing.sm} (8px — small gaps), {spacing.md} (10px — button internal padding), {spacing.base} (11px — rare fine-tuning), {spacing.lg} (12px — card internal spacing), {spacing.xl} (16px — standard padding, gutter baseline), {spacing.xxl} (20px — generous internal padding), {spacing.section} (29px — component-to-component rhythm), {spacing.section-lg} (32px — section vertical padding), {spacing.section-xl} (48px — major section breaks).
  • Section padding (vertical): {spacing.section-xl} — used on {component.section-container} and implied on {component.hero-band}.
  • Card internal padding: {spacing.section-xl} {spacing.xl} (48px horizontal, variable vertical) for {component.card}.
  • Gutters: 16px ({{spacing.xl}}) on navigation container; content appears centered in a 1440px max-width viewport with implicit auto margins.

Grid & Container

The page operates within a 1440px max-width container — a widescreen layout choice appropriate for immersive hospitality photography. Content is center-aligned within this constraint. The hero section breaks free of conventional grid, using full-viewport-width imagery with centered text overlay (no multi-column grid in hero). Below the fold, the section container ({component.section-container}) maintains the 1440px width with 48px vertical padding.

The layout philosophy is editorial-single-column for content sections — no sidebar, no card grids visible in the extracted above-fold structure. This suggests a scrolling narrative format (common in villa/hotel sites) where each section receives full width and the photography carries the visual complexity.

Whitespace Philosophy

Whitespace is abundant and intentional. The hero allows the photograph to breathe with only a centered headline and single CTA — no text columns flanking the image. Section padding at 48px ({spacing.section-xl}) creates clear visual breathing room between content blocks. Internal component padding never crowds below 16px ({spacing.xl}). This is an editorial-airy density: the system trusts photography and display type to fill space, reserving text density for the body-copy sections below the immersive header.

Header Architecture

|--------------------------------------------------------------------------------|
| INDOHOUSE     Explore ▼          Plan ▼                    Instagram  [Reserve] |
|--------------------------------------------------------------------------------|
  ↑logo         ↑dropdown          ↑dropdown                  ↑link       ↑CTA
                 (16px 40px 16px 16px pad)                   (10px 18px pad)
                                                                                 |
<-- 1440px max-width container, 16px horizontal padding -->

The navigation bar sits at 80px total height ({component.top-nav}), with the logo flush left, two dropdown toggles with generous right-padding (40px) accommodating chevron icons, and the Instagram link + Reserve CTA flushed right. The Reserve button uses the {colors.accent} background with 12px radius.

Hero Section

|================================================================================|
|                                                                                |
|           █████████████████████ FULL-VIEWPORT PHOTOGRAPH ████████████████████|
|           █                                                              █     |
|           █                                                              █     |
|           █            "Balinese Tranquility                              █     |
|          █              on Caribbean Shores"                              █     |
|           █                                                              █     |
|          █                     [ Reserve ]                                █     |
|           █                                                              █     |
|           █        (👤 8 guests)  (🛏 4 bedrooms)  (🚿 5 baths)           █     |
|           █                                                              █     |
|           ████████████████████████████████████████████████████████████████     |
|================================================================================|
                         ↑display-lg white serif   ↑button-outline    ↑stat-badges
                                                  (pill, transparent bg)

The hero is a full-viewport immersive photograph (villa interior with Balinese architectural details — woven ceiling, pendant lamps, stone staircase). A dark overlay (implied by {colors.surface-dark} context) ensures the white {typography.display-lg} headline remains readable. The headline is center-aligned both horizontally and vertically in the viewport. Below it, a single {component.button-outline} (transparent background, white text, pill shape) provides the primary conversion action. At the bottom, three {component.hero-stat-badge} pills float with muted text showing property specs.

Elevation & Depth

| Level | Treatment | Use | |---|---|---| | 0 — Flat | No shadow, {colors.canvas} background | Base surfaces: nav, sections, cards | | 1 — Inset | {shadows.inset-light} — white 1px inner glow | Subtle input or button pressed state indication | | 2 — Overlay | Dark semi-transparent layer ({colors.surface-dark} at varying opacities) | Hero photo text legibility, modal backdrops | | 3 — Floating | Implicit elevation via {colors.surface-wash} tint | Cards differentiated from canvas |

The elevation philosophy is intentionally flat. This is not a material-design system with cast shadows and floating cards. Depth comes from two sources only: photographic depth (the hero image itself provides all the visual complexity the eye needs) and color-block layering (the dark hero overlay, the subtle surface wash on alternating sections). The single detected shadow — an inset white glow — suggests it may be used for focus rings or active states on inputs/buttons rather than for elevation.

There are no drop shadows on cards, no z-index stacking beyond the nav-and-hero relationship, no floating panels. The luxury here comes from restraint: the design refuses to add artificial depth when the photography already provides it.

Decorative Depth

The hero photograph is the decorative depth element. It features natural lighting from woven pendant lamps, textural contrast between smooth stone and rattan, and atmospheric perspective through the villa's open floor plan. The design does not add gradient overlays, parallax effects, or animated particles — the architecture in the photograph is the decoration.

Shapes

Border Radius Scale

| Token | Value | Use | |---|---|---| | {rounded.none} | 0px | Navigation dropdown toggles — sharp, utilitarian | | {rounded.sm} | 8px | Tertiary nav links ({component.nav-link-tertiary}) | | {rounded.DEFAULT} | 12px | Primary button ({component.button-accent}), standard cards | | {rounded.md} | 12px | Alias for DEFAULT — card containers | | {rounded.lg} | 16px | Larger card surfaces, content containers | | {rounded.xl} | 20px | Reserved for future larger containers | | {rounded.pill} | 9999px | Hero stat badges, outlined CTA buttons |

Photography Geometry

The hero photograph is full-viewport width and height with no border radius — it bleeds edge-to-edge. The image aspect ratio appears to be approximately 16:9 or wider (landscape architectural photography). No corner rounding is applied to the photograph itself; if it were contained in a card, it would likely use {rounded.lg} (16px) to match the card's shape language, but in the hero context it is unconstrained. Future gallery thumbnails or room preview images would likely adopt the {rounded.lg} radius to maintain consistency with the card system.

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-accent-hover}, future focus states); add hover styling at implementation time per your stack's conventions.

Buttons & CTAs

{component.button-accent} — The primary conversion button throughout the site. Features the warm {colors.accent} (#cbaf7b) background with black text ({colors.on-accent}) for strong contrast. Uses {typography.button} (14.4px, weight 400) in the system font stack — notably not bold, letting the color carry the emphasis. Rounded at {rounded.DEFAULT} (12px) with generous padding of 14.4px × 28.8px (vertical matches font size, horizontal is 2× vertical). Appears in the navigation bar as "Reserve" and likely reappears in booking flows. Hover state documented as {component.button-accent-hover} with deepened {colors.accent-hover} background.

{component.button-outline} — The ghost/outline variant used in the hero overlay context. Transparent background with {colors.on-dark} (white) text, {typography.button} sizing, and {rounded.pill} (fully rounded) shape. Padding approximately 14px × 28px. This button is designed to float over photography without blocking the visual — the outline (likely 1px {colors.hairline} or white at low opacity) defines the hit area while remaining ethereal. No background-color state variant documented; hover likely inverts to filled white or adds background opacity.

{component.nav-dropdown-toggle} — The "Explore" and "Plan" navigation items that trigger dropdown menus. Transparent background, {colors.body-secondary} text (#222222) at {typography.nav-link} (16px, 600 weight). Notably sharp-edged ({rounded.none}) despite the system's general roundness — this creates a subtle "utility vs. beauty" distinction. Asymmetric padding: 16px on top/bottom/left, 40px on right to accommodate a dropdown chevron icon. These are div elements with w-dropdown-toggle class (Webflow nomenclature).

{component.nav-link-tertiary} — The "Instagram" link in the navigation. Transparent background, {colors.ink} text, same typography as dropdown toggles but with {rounded.sm} (8px) corners and smaller padding (10px × 18px). Functions as a quiet utility link — present but not competing with the primary CTA.

Navigation

{component.top-nav} — The persistent header bar spanning full viewport width at 80px height. White ({colors.canvas}) background with {colors.body-secondary} text. Contains the INDOHOUSE wordmark (left), dropdown toggles (center-left), and the Instagram link + Reserve CTA (right). Padding is 16px horizontally within the 1440px container. On scroll, this likely transitions to a solid white background with subtle shadow (not captured in static extract); in its initial hero-overlapping state, it may be transparent or semi-transparent.

Hero & Immersive Elements

{component.hero-band} — The full-viewport hero section. Background is effectively {colors.surface-dark} (near-black) due to the photograph + overlay combination. Text color is {colors.on-dark} (white). Typography is {typography.display-lg} (Cormorant Garamond, 48.8px, 700 weight). This is not a traditional "band" with padding — it is a full-screen photographic canvas with centered overlay text.

{component.hero-headline} — The "Balinese Tranquility on Caribbean Shores" h1 element. White text, {typography.display-lg}, centered both axes within the hero. Negative letter-spacing of -0.488px tightens the elegant serif characters. Line height at 1.04 keeps the two-line headline compact.

{component.hero-stat-badge} — The floating pill-shaped indicators at the hero bottom ("8 guests", "4 bedrooms", "5 baths"). Transparent background with {colors.muted} text (60% black) at {typography.body-sm} size. {rounded.pill} geometry creates the capsule shape. Padding approximately 8px × 16px. These badges include small icons (user, bed, bath) preceding the text — the icons are likely 16px SVGs in the currentColor.

Content Sections

{component.section-container} — The standard content wrapper below the hero. 1440px max-width, 48px vertical padding ({spacing.section-xl}), transparent background. This is the structural container into which editorial content, amenity lists, gallery grids, and booking forms would be placed.

{component.card} — A content card surface (observed in CSS though not visible in the above-fold screenshot). White ({colors.surface}) background, {colors.ink} text, {typography.body-md} for content. Rounded at {rounded.lg} (16px) with 48px × 16px internal padding. Likely used for feature highlights, room descriptions, or testimonial blocks in sections below the fold.

{component.card-label} — A label component for card headers or section categories. Transparent background, {colors.accent-dark} (#b09a6f) text color, {typography.label-md} (14px, 600 weight). This muted gold color creates a hierarchical "tag" appearance without competing with the primary {colors.accent} CTA.

Footer

{component.footer} — Implied footer component (architecture suggests its presence below content sections). White ({colors.canvas}) background, {colors.muted} text color for secondary information, {typography.body-sm} sizing. Generous padding at {spacing.section-xl} (48px). Would contain legal links, contact info, and social media follow icons.

Do's and Don'ts

Do

  • Do use {typography.display-lg} (Cormorant Garamond, 48.8px, 700 weight) for the hero headline and {typography.title-md} (31.2px, 700 weight) for all section headings — maintain the serif-bold-only hierarchy.
  • Do reserve {colors.accent} (#cbaf7b) exclusively for the primary CTA button and label accents; never use it as a background wash at full opacity.
  • Do apply {rounded.pill} (9999px) to hero stat badges and outline buttons, but use {rounded.DEFAULT} (12px) for the filled accent button.
  • Do set display and title typography to line-height: 1.04 with negative letter-spacing proportional to font size (~-1%) — this tight setting is signature to the editorial feel.
  • Do allow hero photographs to occupy full viewport width and height with no border radius; constrain photos within cards to {rounded.lg} (16px).
  • Do use {colors.muted} (60% opacity black) for secondary text like stat badge labels and captions — avoid introducing new grays.
  • Do maintain the 1440px max-width container for content sections with 48px vertical rhythm between major blocks.
  • Do keep button text at weight 400 (not bold) for {component.button-accent} — the color carries the emphasis, matching the brand's restrained voice.
  • Do use the cubic-bezier(0.165, 0.84, 0.44, 1) easing for all transitions — this curve's slight overshoot conveys premium physicality.
  • Do pair the system UI font stack with {colors.ink} or {colors.body} for all body copy, reserving Cormorant Garamond exclusively for display roles.

Don't

  • Don't introduce additional brand colors beyond {colors.primary} and {colors.accent} — the two-color discipline is central to the serene aesthetic.
  • Don't apply border-radius to navigation dropdown toggles — their {rounded.none} (sharp) treatment is intentional, separating utility from beauty.
  • Don't use font weights below 400 for body text or below 700 for display text — the system has no "light" or "medium" voice.
  • Don't add drop shadows to cards or elevate surfaces with cast shadows — the flat, color-block depth strategy is deliberate.
  • Don't expand letter-spacing on any typography role (all values are 0 or negative) — tracking expansion would fight the tight editorial feel.
  • Don't use {colors.accent-dark} (#b09a6f) for CTAs — it is strictly a label/tag color with insufficient contrast for actionable elements.
  • Don't place more than one CTA in the hero section — the single [ Reserve ] outline button (or its filled accent variant) should stand alone.
  • Don't round hero photographs — they must bleed edge-to-edge without corner softening.
  • Don't substitute Playfair Display for Cormorant Garamond unless Google Fonts is unavailable — the character widths and stroke contrasts differ meaningfully.
  • Don't reduce section padding below {spacing.section-xl} (48px) between major content blocks — the airy rhythm is non-negotiable.

Motion & Animation

Transition Tokens

--transition-base: all 0.3s ease;
--transition-interactive: border-color 0.2s, color 0.2s, background-color 0.2s cubic-bezier(0.165, 0.84, 0.44, 1), box-shadow 0.2s;

Two distinct transition patterns were extracted:

  1. all 0.3s ease — A broad, forgiving transition likely applied to generic layout shifts or menu animations. The ease keyword (equivalent to ease-in-out) provides symmetrical acceleration and deceleration.

  2. border-color 0.2s, color 0.2s, background-color 0.2s cubic-bezier(0.165, 0.84, 0.44, 1), box-shadow 0.2s — A precise, property-specific transition for interactive elements (buttons, links, inputs). The 200ms duration is snappy but not jarring. The custom cubic-bezier curve is notable: it decelerates aggressively (0.84 control point) with a slight overshoot (0.44 final point exceeding 0.5), creating a "settling" physical sensation — like a high-end door latch engaging. This curve is the signature motion token of the brand's interactivity.

Keyframe Animations

  • spin — Detected in the keyframes list, typically used for loading indicators or icon rotation (e.g., a refresh spinner or dropdown chevron animation).

Interaction Patterns

  • Dropdown reveal: Navigation "Explore" and "Plan" toggles trigger Webflow-style dropdown panels (w-dropdown-toggle / w-dropdown-list). These animate with the base 0.3s ease transition.
  • Button state changes: The {component.button-accent} transitions background-color through the cubic-bezier(0.165, 0.84, 0.44, 1) curve over 200ms, creating a smooth color shift to {colors.accent-hover} on interaction.
  • Mobile menu toggle: The uui-navbar02_menu-button (hamburger icon) triggers a class change (w--open) that applies overflow: hidden to the body, indicating a full-screen mobile nav overlay with its own enter/exit animation.
  • Focus rings: Inputs and interactive elements likely gain the {shadows.inset-light} (white inset glow) or a subtle box-shadow on focus, transitioning over 200ms.
  • Scroll-triggered nav opacity: While not explicitly in the extracted CSS, the navigation architecture (Webflow navbar02 pattern) strongly implies the header transitions from transparent-over-hero to solid-white-on-scroll, likely with a background-color opacity transition.

No explicit parallax, marquee, or scroll-triggered fade-up animations were detected in the CSS — the motion philosophy is functional minimalism: transitions exist to smooth state changes, not to entertain.

Imagery Style

  • Photography-first approach: The hero is dominated by a full-viewport interior photograph — imagery is the primary visual element, not a supporting illustration.
  • Architectural interior subject matter: The featured image shows a Balinese-inspired villa interior with woven rattan ceiling panels, organic-shaped pendant lamps (likely bamboo or woven fiber), natural stone wall cladding, a floating wooden staircase, and tropical hardwood furnishings.
  • Warm natural lighting: The photograph uses ambient interior lighting (visible lamp glow) combined with natural daylight, creating a golden-warm color temperature that harmonizes with {colors.accent}.
  • Depth through perspective: The image captures deep spatial layers — kitchen foreground, dining mid-ground, living area and staircase background — creating immersive depth without digital effects.
  • Textural richness: Woven textures (ceiling, lamp shades, chair backs), smooth stone, polished wood, and glass surfaces provide tactile visual interest.
  • No illustration or 3D render: All imagery appears to be professional architectural photography — no flat illustrations, isometric graphics, or 3D visualizations detected.
  • Color grading: Photographs appear minimally graded, relying on natural material colors (warm woods, cream stone, natural fibers) that complement the sandstone {colors.accent} palette.
  • Landscape orientation: Hero image is widescreen (~16:9 or wider), appropriate for showcasing interior breadth and architectural lines.
  • Human scale absence: The photograph focuses on architecture and furnishings without people, emphasizing the space as a sanctuary awaiting the guest.
  • Future imagery expectations: Gallery sections would likely continue the architectural photography style — villa exteriors, pool/ocean views, bedroom details, bathroom materials — all in the same warm, natural-light aesthetic.

Icon System

  • Library: Custom SVG icons (no detected icon font library such as Lucide, Heroicons, or Material Icons in the framework detection).
  • Specific icons observed:
    • Chevron/down-arrow (navigation dropdown indicators)
    • User/person icon (hero stat badge: "8 guests")
    • Bed icon (hero stat badge: "4 bedrooms")
    • Bath/shower icon (hero stat badge: "5 baths")
    • Hamburger/menu icon (mobile nav toggle, implied by w-nav-button)
    • Instagram glyph (external link indicator or actual platform icon)
  • Treatment: Icons appear to use currentColor for fill/stroke, inheriting the parent element's text color. In hero stat badges, they render in {colors.muted} (60% black). Size is likely 16px or 18px to align with {typography.body-sm} (14.4px) text.
  • Alignment: Icons are inline-flex with text, vertically centered, with a small gap (probably {spacing.xs} = 6px) between icon and label text.
  • Stroke vs. fill: Stat badge icons (user, bed, bath) appear to be outline/stroke-based for lightweight appearance; the dropdown chevron is likely a simple polygon fill.

Recommended Frontend Stack

- Framework:      Next.js 14+ (App Router) or Astro (for static/marketing-heavy sites like this)
- Styling:        Tailwind CSS v4 with custom theme extension (or CSS Modules if staying close to Webflow output)
- Fonts:          Google Fonts: Cormorant Garamond (weights 300,400,500,600,700); system-ui for body (native stack, no external needed)
- Animation:      Framer Motion for scroll-triggered reveals and layout animations; CSS transitions for hover/focus states
- Icons:          Custom SVG components (React/Framer) or Lucide icons with custom stroke-width matching the site's delicate weight
- Component lib:  None (custom components) or Radix UI for accessible dropdowns/nav patterns
- CMS:           Sanity or Contentful (if this is a multi-villa portfolio site)

Rationale: The site was built in Webflow (detected from w-dropdown, w-nav, w-inline-block classes and @font-face declarations referencing Webflow CDN assets). For a rebuild or extension, Next.js or Astro provides the static-marketing optimization and image pipeline needed for full-viewport photography. Tailwind CSS maps naturally to the token system defined in this spec. Framer Motion handles the scroll-triggered animations that the current CSS hints at but doesn't explicitly define.

Responsive Behavior

Breakpoints

| Name | Width | Key Changes | |---|---|---| | Mobile | < 768px | Nav collapses to hamburger; hero headline scales down; stat badges wrap or stack; Reserve button becomes full-width | | Tablet | 768px – 1024px | Nav may show condensed links; container padding reduces; hero text size intermediate | | Desktop | 1025px – 1440px | Full layout as documented: 1440px container, full nav, desktop hero | | Wide | > 1440px | Container centers with auto margins; photography may scale slightly beyond 1440px for ultra-wide displays |

Touch Targets

  • {component.button-accent}: 14.4px × 28.8px padding + border + text ≈ 48px+ height — exceeds WCAG AAA 44×44 minimum ✓
  • {component.button-outline}: Similar dimensions to accent button — compliant ✓
  • {component.nav-dropdown-toggle}: 16px × (40px+text) padding — the 40px right padding ensures adequate touch area for the chevron target ✓
  • {component.nav-link-tertiary}: 10px × 18px padding — potentially tight at ~42px height; recommend increasing to 12px × 20px for mobile compliance
  • {component.hero-stat-badge}: 8px × 16px padding — approximately 32-36px tall; may need vertical padding increase to 12px for reliable 44px touch target on mobile

Collapsing Strategy

  • Navigation: The uui-navbar02_menu-button (hamburger) replaces the horizontal nav on mobile. The body:has(.w--open) { overflow: hidden } rule confirms a full-screen overlay menu that locks body scroll when open.
  • Hero headline: {typography.display-lg} (48.8px) likely scales down to {typography.title-md} (31.2px) or {typography.title-sm} (24.96px) on mobile using responsive font-size (clamp() or media queries). The negative letter-spacing should scale proportionally.
  • Hero stat badges: Three badges (guests, bedrooms, baths) likely wrap to multiple lines or stack vertically on narrow viewports, maintaining {rounded.pill} shape individually.
  • CTA buttons: The Reserve button in nav likely moves into the mobile menu or becomes a fixed-bottom bar on mobile. The hero outline button remains centered but may increase tap area.
  • Photography: Hero image maintains full-viewport width but may use object-position: center with object-fit: cover to ensure the focal point (staircase/living area) remains visible regardless of crop.
  • Container padding: The 16px horizontal padding on nav likely reduces to 12px or remains; 48px section vertical padding likely reduces to 32px ({spacing.section-lg}) or 24px on mobile.

Image Behavior

  • Hero photograph: Uses width: 100%; height: 100vh; object-fit: cover; object-position: center to fill the viewport at all breakpoints. On mobile portrait orientation, the image crops sides to maintain height focus.
  • Gallery thumbnails (future): Would likely use aspect-ratio: 16/10 or aspect-ratio: 4/3 with {rounded.lg} (16px) corners, scaling down in grid columns from 3-col (desktop) → 2-col (tablet) → 1-col (mobile).
  • Avatar/profile images (future): Would use {rounded.pill} (circle) or {rounded.lg} depending on context, sized at 48px–64px.

Iteration Guide

  1. Initialize with the token system. Create a design-tokens.ts (or Tailwind config theme.extend) file containing every value from the YAML frontmatter: all {colors.*}, {typography.*}, {rounded.*}, and {spacing.*} tokens. Reference these tokens everywhere — never hard-code a hex, px value, or font size that has a token definition.

  2. Build the hero first. Implement {component.hero-band} as a full-viewport section with a <img> or <div> background at 100vh, a dark overlay div (background: {colors.surface-dark} at ~40-60% opacity), and the centered {component.hero-headline} using {typography.display-lg}. Add {component.button-outline} and three {component.hero-stat-badge} elements. Get the immersive feel right before building anything else — this is the brand's first impression.

  3. Implement the navigation. Build {component.top-nav} as a fixed or sticky 80px bar with the INDOHOUSE wordmark in {typography.nav-link} (or a custom logo lockup), two {component.nav-dropdown-toggle} elements with asymmetric padding (16px/40px), the {component.nav-link-tertiary} Instagram link, and {component.button-accent} Reserve CTA. Implement mobile hamburger behavior with the w--open body-lock pattern.

  4. Create the component library. Build each component from the components: frontmatter as a reusable React/Vue/Svelte component (or CSS class). Start with {component.button-accent} and {component.button-outline}, then {component.card}, {component.card-label}, {component.section-container}, and {component.hero-stat-badge}. Ensure every component accepts props for its documented tokens (backgroundColor, textColor, typography, rounded, padding) so variants are data-driven.

  5. Apply the typography hierarchy. Set up CSS (or Tailwind utilities) for every {typography.*} role. Ensure Cormorant Garamond loads from Google Fonts before paint (use font-display: swap with a preload hint). Verify that display/title roles use weight 700 with negative letter-spacing, and body roles use weight 400 with normal spacing.

  6. Wire up the transitions. Apply --transition-interactive (the 200ms cubic-bezier) to all buttons, links, and inputs for their background-color, color, border-color, and box-shadow properties. Apply --transition-base (300ms ease) to layout shifts, dropdown reveals, and menu animations.

  7. Build content sections below the fold. Using {component.section-container} (1440px max-width, 48px vertical padding), construct the editorial sections: villa description with {typography.body-md}, amenity cards using {component.card} with {component.card-label} headers, possibly a gallery grid, and {component.footer}. Maintain the 48px section rhythm religiously.

  8. Test responsive behavior. At each breakpoint (mobile <768px, tablet 768-1024px, desktop 1025-1440px), verify: nav collapses correctly, hero headline scales (use clamp(24px, 5vw, 48.8px) for fluid responsiveness), stat badges reflow, buttons remain touch-target compliant (44px minimum), and container padding adjusts.

  9. Run the linter. Execute npx @google/design.md lint DESIGN.md after any token additions or component changes. Fix broken-ref (undefined token paths), contrast-ratio (accessibility failures — particularly {colors.accent}/#000000 text at 14.4px should meet AA), and orphaned-tokens (defined but unused tokens).

  10. Respect the aesthetic constraints. When adding new pages or sections not shown in the screenshot, make decisions based on the system's voice: prefer photography over illustration, prefer {colors.accent} on CTAs only, prefer Cormorant Garamond for any text larger than 24px, prefer {rounded.DEFAULT} or {rounded.lg} over sharp corners, and prefer the 48px section rhythm. If a new component doesn't fit neatly into the existing sub-systems (hero immersion vs. editorial content), create a new sub-system name and document it consistently.

Font Setup: Load Cormorant Garamond from Google Fonts: <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600;700&display=swap" rel="stylesheet">. Define CSS custom properties:

:root {
  --font-display: 'Cormorant Garamond', 'Playfair Display', Georgia, serif;
  --font-body: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

Apply font-family: var(--font-display) to h1, h2, h3, .display-text and font-family: var(--font-body) to everything else.

Known Gaps

  • Hover state styling: Per the no-hover policy, exact hover background-color shifts, border appearances, and transform translates for buttons, links, and cards are not documented. The {component.button-accent-hover} entry provides the active/pressed background color; other hover treatments (slight lift, border reveal, opacity shift) must be designed at implementation time following the cubic-bezier(0.165, 0.84, 0.44, 1) 200ms convention.
  • Loading and pending states: No skeletons, spinners (beyond the detected spin keyframe), or disabled-button treatments were visible in the static screenshot. The {component.button-accent} likely needs a disabled variant with reduced opacity — suggest 50% opacity on {colors.accent} with pointer-events: none.
  • Form validation states: No input fields were detected in the extracted DOM (the inputs array is empty). Error borders, helper text colors ({colors.error} is not defined and should be added if forms exist), and focus-ring treatments for text inputs, email fields, or date pickers are absent.
  • Below-fold content: The screenshot captures only the hero section and navigation. All content sections (villa description, amenities, gallery, testimonials, booking form, footer) are inferred from the CSS architecture but not visually verified. Spacing rhythms, card layouts, and typography application in these sections are extrapolated.
  • Dark mode: The entire system is documented for light mode ({extensions.mode}: light). A dark mode variant would require inverting {colors.canvas} and {colors.ink}, adjusting {colors.accent-subtle} for dark backgrounds, and potentially swapping the hero overlay approach. No dark-mode CSS variables or media queries were detected.
  • Dropdown panel styling: The navigation dropdown menus (w-dropdown-list children of w-dropdown-toggle) were not rendered visibly. Their background color, shadow, border-radius, item hover states, and animation entrance/exit are unknown.
  • Mobile menu panel: The full-screen mobile navigation overlay (triggered by uui-navbar02_menu-button) was not visible. Its background color (likely {colors.canvas} or {colors.surface-dark}), typography scaling, close button treatment, and link stacking are undocumented.
  • Exact hero overlay opacity: The darkness of the hero photograph overlay (which ensures white text readability) is inferred from {colors.surface-dark} being the contextual text color, but the specific opacity percentage (40%? 50%? 60%?) of the black overlay div is not in the extracted CSS.
  • Footer content and structure: The {component.footer} entry is architecturally inferred. Its actual column layout (links, legal, social, newsletter signup), background treatment (white vs. dark), and internal spacing are not visible.
  • Accessibility focus indicators: Beyond the inferred inset-shadow focus ring, no explicit :focus-visible styles were extracted. WCAG 2.2.1 requires visible focus indicators on all interactive elements — implement a 2px offset outline in {colors.primary} or {colors.accent} at implementation time.