Ballena Design System
Overview
Ballena presents itself as a digital extension of a fine-dining establishment — restrained, gallery-like, and deliberately sparse. The visual language trades the expected restaurant warmth for an almost brutalist austerity: vast fields of absolute black ({colors.surface-dark}) serve as the dominant canvas, against which warm cream-colored type ({colors.on-dark} / {colors.body}) floats with generous letter-spacing. This is not a system of decoration but of subtraction — every element earns its place through scale hierarchy rather than ornament.
The brand voltage lives almost entirely in typographic drama. At {typography.display-xl}, headlines reach 68.3333px with a mere 400 weight and 1.70833px tracking, creating a architectural, monumental presence that commands attention without aggression. Against this, body copy collapses to an intimate {typography.body-md} at 11.6667px — one-sixth the display size — establishing a hierarchy so extreme it becomes the defining characteristic of the brand. Color plays a supporting role: the near-black {colors.primary} (#03090d) reads as the deepest void, while {colors.canvas} (#f8f2e6) provides a warm parchment-like surface for inverted sections.
Shape language favors the subtle over the pronounced. Border radii cluster tightly between {rounded.xs} (2.5px) and {rounded.md} (8.5px), with only rare excursions to {rounded.lg} (100px) for pill-like treatments. Buttons eschew fills entirely in favor of transparent backgrounds with delicate 1px borders at 25% opacity ({colors.hairline}). The overall effect is Japanese-wabi-sabi-meets-Swiss-design: precise, quiet, and confident in its emptiness.
Two sub-systems emerge from the structure: the hero/dark dialect where black surfaces carry cream typography (the dominant voice), and a light-surface dialect where {colors.canvas} backgrounds host {colors.ink} text for form inputs, modals, and potentially reservation interfaces.
Key Characteristics:
- Extreme typographic scale ratio (~5.86x between
{typography.display-xl}at 68.3333px and{typography.body-md}at 11.6667px) - Dual-canvas architecture:
{colors.surface-dark}(#000000) for editorial/hero content,{colors.canvas}(#f8f2e6) for interactive/surface elements - Proprietary
Sweet Sans Proas the sole brand typeface across all weights (300–800) - Transparent button surfaces with
{colors.hairline}borders (rgba(255,255,255,0.25)) and uppercase{typography.button}treatment - Near-zero shadow system — depth is achieved through color inversion, not elevation
- Wide letter-spacing on all display text (1.70833px at 68.3333px = ~2.5% of em)
- Input fields use
{colors.surface}backgrounds with{rounded.md}corners and no visible borders - Motion is restrained to 300ms
ease-in-outtransitions on color, transform, and opacity properties
Colors
Brand & Accent
- Primary (
{colors.primary}— #03090d): The foundational brand color — a near-black blue-black used for the dominant hero backgrounds, primary ink on light surfaces, and the deepest void in the visual field. - Primary Active (
{colors.primary-active}— #000000): Pure black, used for section block backgrounds and the most saturated dark surfaces. - Accent (
{colors.accent}— #c2644f): A warm terracotta/coral appearing sparingly as a semantic highlight — likely for CTAs or decorative accents on interior pages. - Accent Secondary (
{colors.accent-secondary}— #779580): A muted sage green, probably used for complementary branding moments or secondary status indicators.
Surface
- Canvas (
{colors.canvas}— #f8f2e6): The primary warm-cream surface — used for input backgrounds, modal containers, and any light-section backgrounds. - Canvas Alt (
{colors.canvas-alt}— #f8f2e6): Identical to Canvas; maintained for potential variant divergence in extended surfaces. - Surface (
{colors.surface}— #f8f2e6): The standard light surface token, functionally equivalent to Canvas in current usage. - Surface Soft (
{colors.surface-soft}— #f9fbf3): A slightly cooler, green-tinted cream for subtle surface variation. - Surface Dark (
{colors.surface-dark}— #000000): Pure black — the dominant hero and section background color, creating the signature void-like presence. - Surface Elevated (
{colors.surface-elevated}— #03090d): Near-black, used for header overlays and elevated dark surfaces needing slight warmth differentiation from pure black.
Text & Ink
- Ink (
{colors.ink}— #03090d): The darkest readable text color on light surfaces — near-black with imperceptible warmth. - Body (
{colors.body}— #f8f2e5): Warm cream text color for body copy and labels rendered on dark surfaces — the primary reading color against{colors.surface-dark}. - On Primary (
{colors.on-primary}— #ffffff): Pure white, reserved for the highest-contrast text needs: navigation elements, buttons on dark backgrounds, and the wordmark. - On Dark (
{colors.on-dark}— #f8f2e5): Cream text for large display typography on black backgrounds — softer than pure white, creating refined legibility. - On Surface (
{colors.on-surface}— #03090d): Ink-equivalent for text within light-surface cards and containers. - Muted (
{colors.muted}— #7d7d79): Mid-tone gray for secondary text, captions, and de-emphasized information.
Hairlines & Borders
- Hairline (
{colors.hairline}— rgba(255, 255, 255, 0.25)): The signature border treatment — 25% opacity white, used for the Book Now button outline and any light-on-dark border needs. - Hairline Dark (
{colors.hairline-dark}— rgba(3, 9, 13, 0.19)): 19% opacity near-black for subtle dark-on-light dividers, observed on the header container. - Border Subtle (
{colors.border-subtle}— rgba(3, 9, 13, 0.2)): Slightly stronger dark overlay at 20% opacity for input focus states or container boundaries. - Overlay (
{colors.overlay}— rgba(0, 0, 0, 0)): Fully transparent — the baseline for transparent button and toggle backgrounds.
Typography
Font Family
The system is built exclusively on Sweet Sans Pro, a proprietary geometric-humanist sans-serif available in six weights (300 Light, 400 Regular, 400 Italic, 500 Medium, 700 Bold, 800 Heavy). It serves every role from monumental 68px headlines to 10px micro-copy, creating total typographic unity. A secondary face, TT Commons Pro, appears sparingly (17 instances vs. 325 for Sweet Sans Pro) in label and UI chrome contexts — likely for navigation toggles, close buttons, and interface controls where a slightly more neutral geometric character is preferred. The fallback stack degrades gracefully through system fonts: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif.
Hierarchy
| Token | Size | Weight | Line Height | Letter Spacing | Use |
|---|---|---|---|---|---|
| {typography.display-xl} | 68.3333px | 400 | 61.5px | 1.70833px | Hero headlines, major sectional titles |
| {typography.display-lg} | 68.3333px | 400 | 61.5px | 1.70833px | Equivalent to display-xl in current implementation |
| {typography.title-md} | 68.3333px | 400 | 61.5px | 1.70833px | Title-level display text |
| {typography.title-sm} | 20px | 400 | 18px | 0.5px | Subheadings, section labels |
| {typography.body-md} | 11.6667px | 500 | 11.6667px | 0.510417px | Primary body copy, button labels, navigation |
| {typography.body-sm} | 10px | 500 | ~12px | 0.5px | Micro-copy, input placeholder text |
| {typography.button} | 10px | 500 | 1.2 | 0.08em | Uppercase button text (Book Now, CTA links) |
| {typography.label} | 13.3333px | 400 | 1.4 | 0px | UI labels, close buttons, toggle text (TT Commons Pro) |
| {typography.nav-logo} | 20px | 400 | 1 | 0.1em | BALLENA wordmark in navigation |
Principles
The typographic system's defining move is its refusal to use weight for emphasis. Headlines sit at weight 400 (Regular) despite their massive 68.3333px scale — they command attention through size and generous letter-spacing alone, never through boldness. Body copy at 11.6667px paradoxically uses weight 500 (Medium), making it visually denser than the headlines in terms of stroke-to-size ratio. This inversion — lighter weights at larger sizes, medium weights at micro-sizes — creates a sophisticated tension that feels editorial rather than corporate.
Letter-spacing is consistently positive across all roles, never tightened. The display text carries 1.70833px of tracking (approximately +0.025em), giving the monumental headlines an airy, architectural quality reminiscent of luxury fashion editorial. Even the tiny 10px button text receives 0.08em (0.8px) tracking when rendered in uppercase, ensuring the narrow characters don't collapse visually. All text-transform for buttons and labels follows uppercase conventions, while headlines and body remain in sentence case.
The line-height on display text is intentionally tight at 61.5px for a 68.3333px font size (~0.9 ratio), causing lines of the multi-line hero headline ("SHAPED BY SEA. / GROUNDED / IN LAND") to stack with minimal leading — another signal of confidence and restraint.
Note on Font Substitutes
Sweet Sans Pro is a commercial typeface from Commercial Type with no Google Fonts equivalent. For prototyping or open-source implementations, substitute with DM Sans (weights 300–500) for its similar geometric-humanist proportions and clean apertures, or Outfit for a slightly wider alternative. Define CSS variables:
:root {
--font-display: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-ui: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
TT Commons Pro can be substituted with Inter or IBM Plex Sans at weight 400 for UI chrome elements.
Layout
Spacing System
-
Base unit: Approximately 5px (the most frequent spacing value at 42 occurrences).
-
Tokens:
{spacing.xxs}(3px) — Micro-adjustments, tight internal padding{spacing.xs}(5px) — Base unit, input padding, minimal gaps{spacing.sm}(9px) — Small element spacing{spacing.md}(10px) — Standard compact padding, section margins{spacing.base}(15px) — Medium component padding{spacing.lg}(17px) — Card internal padding, content gutters{spacing.xl}(20px) — Button padding ranges, larger gaps{spacing.xxl}(21px) — Section internal spacing{spacing.section}(33px) — Major vertical rhythm between blocks{spacing.wide}(37px) — Extended section breaks{spacing.hero}(100px) — Hero vertical padding, major breathing room{spacing.section-xl}(105px) — Maximum vertical section padding
-
Section padding (vertical):
{spacing.hero}(100px) for the hero band;{spacing.section}(33px)–{spacing.section-xl}(105px) for content sections. -
Card internal padding:
{spacing.lg}(17px) for standard card containers. -
Gutters: Content is constrained to approximately 1425px max-width based on extracted container data, centered within the viewport.
Grid & Container
The layout operates on a single-column editorial paradigm at desktop width. The main container spans 1425px with no explicit column grid detected — content flows vertically in full-width bands. The hero section uses a centered text column (likely 800–1000px max for readability at 68px) within the black field. No multi-column marketplace grids, sidebars, or sticky rails are present in the above-fold structure.
Density choices favor extreme spaciousness: the 100px hero vertical padding and 68px type create a poster-like composition where a single statement occupies the majority of the viewport. This is not an information-dense layout — it's a sequential, immersive experience designed for scrolling through atmospheric bands.
Whitespace Philosophy
Whitespace is the primary design material. The system uses negative space not as padding between elements but as the elements themselves — the black void between lines of the headline is as intentional as the letters. This is anti-marketplace, pro-gallery: each section is given enough room to resolve as an independent image before the user scrolls to the next. The 11.6667px body text floats in fields of dark nothingness, making every word feel placed rather than typeset.
Header Architecture
|--------------------------------------------------------------------------------|
| [BOOK NOW] BALLENA [≡] |
| (btn-primary) (nav-logo) (menu-toggle) |
|--------------------------------------------------------------------------------|
| <- 600px constrained -> <- 44px tap -> |
|---------------------------------------------------------------------------------
The header uses a three-zone layout within a 600px-constrained inner container: flush-left CTA button, center-aligned wordmark, flush-right hamburger toggle. The header background is fully transparent ({colors.overlay}), allowing the hero image or video to show beneath. A subtle {colors.hairline-dark} overlay (19% opacity black) provides a 2.5px backing container for structural definition without visual weight.
Hero Section
|================================================================================|
| |
| |
| |
| SHAPED BY SEA. |
| GROUNDED |
| IN LAND |
| |
| |
| (potential scroll indicator) |
| |
|================================================================================|
| background: {colors.surface-dark} (#000000) |
| text: {typography.display-xl} (68.3333px, weight 400, ls: 1.70833px) |
| color: {colors.on-dark} (#f8f2e5) |
| padding: {spacing.hero} (100px) vertical |
|================================================================================|
The hero is a full-viewport black band with left-aligned (or center-left) display text. No supporting imagery is visible in the screenshot — the type IS the image. The three-line headline stacks with tight leading (61.5px line-height for 68.3333px type), creating a solid rectangular block of text that reads as a graphic object.
Elevation & Depth
| Level | Treatment | Use |
|---|---|---|
| 0 (Flat) | No shadow, flat color fill | Base surfaces: hero bands, section blocks |
| 1 (Hairline) | 1px border at reduced opacity | Button outlines ({colors.hairline}), input fields |
| 2 (Overlay) | Semi-transparent color layer | Modal backdrop (60% black), header glass effect |
| 3 (Floating) | No detected shadows — modal containers appear as flat cards on overlay | Popup/modal containers |
The depth philosophy is resolutely flat-layered: no box-shadows exist in the extracted CSS. Elevation is communicated through color juxtaposition (black on cream, cream on black) and through semi-transparent overlay screens for modals. When a popup appears, it sits atop a 60% opacity black scrim — the only "shadow-like" treatment in the system. This rejection of drop-shadow depth reinforces the gallery/print aesthetic: elements exist on planes, not in space.
Shapes
Border Radius Scale
| Token | Value | Use |
|---|---|---|
| {rounded.xs} | 2.5px | Book Now button, sharpest corners in the system |
| {rounded.sm} | 5px | Card containers, general content boxes |
| {rounded.DEFAULT} | 6.66667px | Close buttons, modal containers, UI chrome |
| {rounded.md} | 8.5px | Text input fields, rounded input treatment |
| {rounded.lg} | 100px | Pill-shaped elements (rare, for special treatments) |
The radius vocabulary is tightly constrained — five values spanning 2.5px to 100px, with four of them clustered below 10px. This creates a subtly softened rectilinear language: nothing is truly sharp (the minimum 2.5px takes the edge off), but nothing is overtly rounded either. The 100px pill radius exists as an outlier for exceptional cases (possibly avatar circles or tag pills). Input fields receive the most generosity at 8.5px, softening the interactive touch points.
Components
No hover states documented. Hover behavior is unreliable to extract from a single screenshot. State variants live as separate entries in the
components:frontmatter (e.g.{component.button-primary-active},{component.text-input-focus}); add hover styling at implementation time per your stack's conventions.
Buttons & CTAs
{component.button-primary} — The "BOOK NOW" call-to-action in the top-left navigation. Fully transparent background ({colors.overlay}) with a delicate 1px {colors.hairline} border (white at 25% opacity). Text renders in {colors.on-primary} (pure white) using {typography.button} (10px, weight 500, uppercase, 0.08em letter-spacing). Rounded to {rounded.xs} (2.5px) — the sharpest corner treatment in the system, giving it a crisp, architectural feel. Padding is 7.5px horizontal and 15px vertical. Its active state ({component.button-primary-active}) likely shifts to a subtle white background tint while maintaining the border. This button embodies the system's restraint: visible but whisper-quiet.
{component.button-secondary} — Text-only link buttons such as "explore more" and "View Full Menu" observed in content sections. Zero background, zero border, zero border-radius. Text in {colors.body} (cream) using {typography.body-md} (11.6667px, weight 500, uppercase). These are essentially styled anchor tags that rely on context (positioning within content) to read as interactive. Active state ({component.button-secondary-active}) likely inverts to {colors.on-primary} white.
{component.menu-toggle} — The hamburger menu icon (three horizontal lines) in the top-right corner. Transparent background, 44×44px touch target area ({component.menu-toggle}.size), padded to 20px internally. Uses {typography.label} (TT Commons Pro, 13.3333px, weight 400) for the icon rendering or associated label. Color is {colors.ink} (near-black), suggesting it may invert or sit on a light scroll-triggered header state.
{component.menu-close} — Close button (X) for the mobile menu or modal dismissals. 26.67px square ({component.menu-close}.size) with {rounded.DEFAULT} (6.66667px) corners and 6.66667px internal padding. Renders in {colors.ink} via {typography.label}.
Navigation & Header
{component.top-nav} — The persistent header bar spanning the viewport width. Transparent background allows hero content to show through. Contains three children: {component.button-primary} (left), {component.nav-logo} (center), {component.menu-toggle} (right). Height is auto-determined by content (approximately 44–50px). The header container itself has a 2.5px padding and sits within a 600px-constrained width, centering its contents.
{component.nav-logo} — The "BALLENA" wordmark centered in the header. Renders in {colors.on-primary} (white) at {typography.nav-logo} (20px, weight 400, 0.1em letter-spacing) using Sweet Sans Pro. Generous 10px padding ensures click/tap area. The wide letter-spacing gives the logotype an expansive, luxury-brand character.
Hero & Display
{component.hero-band} — The full-viewport hero section. Solid {colors.surface-dark} (pure black) background filling edge-to-edge. Contains {component.hero-headline} as its primary child. Vertical padding of {spacing.hero} (100px) creates massive top and bottom breathing room. This is the signature surface of the brand — the "void" against which all identity projects.
{component.hero-headline} — The three-line display text: "SHAPED BY SEA. / GROUNDED / IN LAND". Transparent background, {colors.on-dark} (cream) text color. Uses {typography.display-xl} (68.3333px, weight 400, 61.5px line-height, 1.70833px letter-spacing). The tight line-height (90% of font size) causes the lines to stack densely, forming a solid rectangular text block. Left-aligned within the content column.
Forms & Inputs
{component.text-input} — Form input fields for reservations or contact forms. {colors.surface} (warm cream #f8f2e6) background with {colors.ink} (near-black) text. Uses {typography.body-sm} (10px, weight 500) for surprisingly small input text. Rounded to {rounded.md} (8.5px) — the most generous radius in everyday use. Internal padding is a tight 5px. Total height approximately 28px. Notably, these inputs have zero border — the background-color contrast against the parent surface defines the edge. Focus state ({component.text-input-focus}) maintains all properties, likely adding a subtle {colors.border-subtle} outline or background shift.
Cards & Containers
{component.card-surface-light} — Content card on a light background. {colors.surface} fill with {colors.on-surface} text. {typography.body-md} for content, {rounded.sm} (5px) corners, {spacing.lg} (17px) internal padding. Used for informational blocks, feature callouts, or content modules on cream-colored sections.
{component.card-surface-dark} — Content card on a dark/background-matched surface. {colors.surface-dark} (black) background with {colors.on-dark} (cream) text. Same padding and radius as its light counterpart. Creates seamless "card-less" appearance when placed on the hero or dark sections.
{component.modal-container} — Popup/modal dialog box. {colors.canvas} (cream) background providing strong contrast against the dark overlay. {colors.ink} text color. {typography.body-md} for content. {rounded.DEFAULT} (6.66667px) corners. {spacing.xl} (20px) internal padding. Contains a {component.modal-close} button in the upper area. Width is responsive-medium per the popup class.
{component.modal-overlay} — Full-viewport scrim behind modals. Approximately 60% opaque black background. Contains {component.modal-container} centered within. Zero padding — it's a pure coverage layer.
{component.preloader} — Initial page load overlay. {colors.surface-dark} (black) background covering the entire viewport. Displays animated branding content (logo, phrases) during asset loading. Disappears via fade-out animation once the page is ready.
Section Containers
{component.section-block} — Reusable content section wrapper. {colors.surface-dark} background, {colors.on-dark} text color. {spacing.md} (10px) internal padding. Used for each major scrolling section of the page.
{component.footer-band} — Page footer area. {colors.surface-dark} background extending full-width. {colors.muted} (gray #7d7d79) text for de-emphasized footer content. {typography.body-md} scale. {spacing.section} (33px) vertical padding with {spacing.lg} (17px) horizontal.
Do's and Don'ts
Do
- Do maintain the 5.86x scale ratio between
{typography.display-xl}(68.3333px) and{typography.body-md}(11.6667px) — this extremity is the brand's signature typographic gesture. - Do use
{colors.on-dark}(#f8f2e5 warm cream) for large display text on black backgrounds rather than pure white — the warmth prevents harshness at scale. - Do keep button backgrounds transparent (
{colors.overlay}) with{colors.hairline}borders for primary actions — filled buttons do not exist in this vocabulary. - Do apply
{typography.button}(10px, uppercase, 0.08em letter-spacing) to all CTA text regardless of button style — consistency in micro-treatment signals intentionality. - Do reserve
{rounded.md}(8.5px) for input fields specifically — they're the softest interactive elements and should feel tactilely distinct. - Do use
{colors.surface}(#f8f2e6) for all input backgrounds — the warm cream creates gentle contrast without the sterility of pure white. - Do set display text to weight 400 even at 68px — the system explicitly avoids bold headlines, relying on scale for dominance.
- Do give hero sections
{spacing.hero}(100px) of vertical padding minimum — the air around the type is as important as the type itself. - Do use positive letter-spacing throughout (never negative or zero for display text) — the airy character is non-negotiable.
- Do treat
{typography.label}(TT Commons Pro, 13.3333px) as the exclusive font for UI chrome: toggles, close buttons, and interface controls.
Don't
- Don't introduce drop-shadows or elevation layers — the system achieves depth through color-plane juxtaposition and overlay scrims only.
- Don't use bold weight (700+) for any headline or display text — the maximum weight in the display role is 400.
- Don't round corners beyond
{rounded.md}(8.5px) for any standard component — the 100px pill radius is reserved for exceptional cases only. - Don't place
{colors.body}(#f8f2e5) text directly on{colors.canvas}(#f8f2e6) — the contrast ratio fails; ensure text on light surfaces uses{colors.ink}instead. - Don't add gradients, textures, or patterns to the
{colors.surface-dark}hero background — the purity of the black void is central to the aesthetic. - Don't expand the typeface beyond Sweet Sans Pro and TT Commons Pro — the two-face discipline is deliberate and maintains the system's reductive character.
- Don't reduce the letter-spacing on
{typography.display-xl}below 1.70833px — the wide tracking is what makes the monumental type feel luxurious rather than imposing. - Don't create "filled" button variants with solid backgrounds — the outlined-transparent button is the only primary action style in this design language.
- Don't add visible borders to text inputs — the
{colors.surface}background against the parent surface provides sufficient edge definition. - Don't crowd content into the full 1425px width for text-heavy sections — the editorial column should be narrower (800–1000px) to maintain the gallery proportion.
Motion & Animation
Transition Tokens
--transition-color: background-color 0.3s ease-in-out, color 0.3s ease-in-out;
--transition-all: all 0.3s ease-in-out;
--transition-transform: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
--transition-opacity: opacity 0.6s ease-in-out;
--transition-fill: fill 0.3s ease-in-out;
--transition-base: 0.3s ease-in-out;
The motion system is unified around a single duration (300ms) and a single easing function (ease-in-out). Every interactive state change — color shifts on hover/focus, background transitions, transform animations for reveal effects, opacity fades for enter/exit — shares this timing. The consistency creates a measured, unhurried pace appropriate to the fine-dining context.
Keyframe Animations
components-animate__appear-animation— Fade-in entrance for content blocks as they scroll into view.components-animate__slide-in-animation— Slide-and-fade entrance, likely for text columns or images entering from left/right.components-animate__loading— Preloader pulse or sequence during initial page load.components-button__busy-animation— Loading state animation for buttons after click submission.slide-in-left/slide-in-right— Directional slide entrances for panel/content reveals.slide-out-left/slide-out-right— Exit animations for dismissed panels.fade-in/fade-out— Simple opacity-based entrance and exit.__wp-base-styles-fade-in/__wp-base-styles-fade-out— Framework-level (WordPress/Elementor) fade utilities.components-modal__appear-animation/components-modal__disappear-animation— Modal enter/exit with scale or fade.components-resizable-box__*animation— Resize handle drag animations for editable containers (admin-facing).components-validated-control__indicator-jump— Form validation feedback micro-animation.loadingpulse— Pulsing loading indicator.
Interaction Patterns
- Scroll-triggered reveals: Content blocks animate in via
appear-animationorslide-in-animationas they enter the viewport, creating a sequential, cinematic scroll experience. - Modal transitions: Popups (like the auto-open promotional modal detected in the DOM) fade in with
modal__appear-animationatop a 60% opacity black overlay, and reverse on close/dismiss. - Button state changes: All color and background transitions on buttons complete over 300ms, providing smooth feedback without jarring instant switches.
- Preloader sequence: On initial page load, a full-screen black preloader displays branded content (logo, flanking phrases) before fading out via
fade-outto reveal the hero. - Form validation feedback: Input fields trigger
indicator-jumpanimation on validation state change, providing subtle acknowledgment. - Menu toggle interaction: The hamburger icon triggers panel reveal (likely slide-in from right) using
slide-in-rightanimation. - Opacity crossfades: Image or background changes use 600ms opacity transitions (
opacity 0.6s ease-in-out) — double the standard duration for smoother visual continuity.
Imagery Style
- Photography style: While the hero screenshot shows pure typographic composition (no visible photograph), the restaurant context suggests food photography will appear on interior pages — likely shot in moody, low-key style consistent with the black-dominated palette.
- Color treatment: Any photography integrated into the design will likely undergo desaturation or duotone treatment to harmonize with the near-black/cream palette — full-color imagery would disrupt the austere atmosphere.
- Role of imagery: In the observed hero, typography is the imagery. Where photos appear, they likely serve as atmospheric full-bleed backgrounds behind overlay text, or as contained gallery images within dark frames.
- Illustration style: None detected; the brand relies on type and photography rather than illustration.
- Abstract/graphic elements: Minimal to none — the system rejects decorative graphics in favor of spatial composition and typographic scale.
- Image-as-depth: Given the flat shadowless approach, any hero imagery would function as a planar layer (background photo → dark overlay → text) rather than a floating card with cast shadow.
Icon System
- Library: Custom SVG icons — no standard library (Lucide, Heroicons, Material) detected in the CSS classes or font references.
- Specific icons observed: Hamburger menu (three horizontal lines) in
{component.menu-toggle}, close/X icon in{component.menu-close}and{component.modal-close}. - Treatment: Icons render in
{colors.ink}(near-black) or{colors.on-primary}(white) depending on context, usingcurrentColorfor theme alignment. The hamburger icon appears to be 20–24px within its 44px touch target. Stroke-based construction is assumed given the minimalist aesthetic. - Alignment: Icons center vertically within their container padding, maintaining optical balance with adjacent text elements.
- Additional expected icons: Based on the restaurant context, the system likely includes location pin, phone, clock, arrow-right/down (for CTAs), social media marks, and possibly a custom wordmark SVG for the BALLENA lockup.
Recommended Frontend Stack
- Framework: Next.js (App Router) or Astro for static/marketing optimization
- Styling: CSS Modules or Vanilla Extract (token-driven, no Tailwind detected)
- Fonts: Self-hosted Sweet Sans Pro OTF files + TT Commons Pro TTF; define @font-face with font-display: swap
- Animation: Framer Motion (for scroll-triggered reveals matching the keyframe library) or vanilla CSS animations
- Icons: Custom SVG components (inline React/Astro components for color inheritance)
- Component lib: None (custom components; Radix Primitives for accessible modal/dialog if needed)
Responsive Behavior
Breakpoints
| Name | Width | Key Changes | |---|---|---| | Mobile | < 768px | Single column; hero text scales down; nav collapses to hamburger-only; modal goes full-screen | | Tablet | 768px – 1024px | Two-column potential for text-image blocks; nav may show partial items | | Desktop | 1025px – 1425px | Full layout as observed: 1425px max container, three-zone header | | Wide | > 1425px | Container centers with auto margins; hero padding may increase beyond 100px |
Touch Targets
{component.button-primary}: ~120px × 35px (7.5px × 15px padding + text) — exceeds 44×44px minimum comfortably.{component.menu-toggle}: 44px × 44px (20px padding + icon) — meets WCAG AAA minimum precisely.{component.text-input}: Height ~28px (below 44px recommendation) — may need height increase or padding boost for mobile touch usability.{component.menu-close}: 26.67px × 26.67px (below 44px) — should be wrapped in a larger invisible hit area for mobile.{component.button-secondary}: Height determined by text only (~12px) — requires padding increase or container expansion for adequate touch target.
Collapsing Strategy
- Navigation: The header already shows hamburger-only pattern (no visible menu items), suggesting mobile-first or always-hamburger approach. On wider screens, the menu may expand to show inline links while retaining the three-zone structure.
- Hero columns: The single-column text layout scales fluidly — font-size may reduce via clamp() or media query to maintain viewport proportion (e.g., 68px → 40px → 32px).
- Modal: Transitions from
pum-responsive-medium(constrained centered box) to full-viewport overlay on mobile, with{component.modal-close}positioned for easy thumb reach. - Input fields: Maintain
{rounded.md}corners but increase vertical padding from 5px to 12–14px on mobile to achieve 44px minimum touch height. - Typography scale: The 68.3333px display text should use responsive sizing (e.g.,
clamp(32px, 8vw, 68.3333px)) to prevent overflow on narrow viewports while preserving the scale intent.
Image Behavior
- Hero imagery: If background images exist beyond the typographic hero, they should use
background-size: coverwithbackground-position: centerand a dark overlay gradient to ensure text legibility. - Food photography: Likely contained within aspect-ratio boxes (4:3 or 16:9) that shrink proportionally on mobile, maintaining
{rounded.sm}corners. - Logo/wordmark: The BALLENA text mark may reduce in size or letter-spacing on very narrow screens to prevent line-breaking.
Iteration Guide
-
Build a landing page that closely mimics the design system in this DESIGN.md. Use Next.js or Astro with CSS Modules for styling, ensuring every value originates from the YAML frontmatter tokens via the
{section.token}paths — never hard-code a color, size, or radius. -
Establish the dual-canvas architecture first: create global CSS variables for
{colors.surface-dark}(black) and{colors.canvas}(cream), then build your section components to accept atheme="dark"|"light"prop that swaps background/text colors. This mirrors the observed dark-hero + light-modal pattern. -
Implement the typographic scale as CSS custom properties mapping to
{typography.*}tokens. Pay particular attention to the 1.70833px letter-spacing on display text — this is easily overlooked but critical to the brand feel. Usefont-variant-ligatures: noneif Sweet Sans Pro's ligature features interfere with the wide tracking. -
Add state variants (
-hover,-disabled,-focus) per your framework's conventions. The spec documents Default and Active/Pressed for buttons, and Focus for inputs. Hover states should follow the 300msease-in-outtransition timing defined in the Motion section. -
When adding a new component, decide first which sub-system it belongs to: the dark dialect (
{colors.surface-dark}background,{colors.on-dark}text) for editorial/hero/content sections, or the light dialect ({colors.canvas}background,{colors.ink}text) for interactive/forms/modals. Do not mix tokens across dialects arbitrarily. -
Variants of an existing component must live as separate frontmatter entries (e.g.,
button-primary-active, never nested insidebutton-primary). This flat structure enables systematic state management and documentation. -
Run
npx @google/design.md lint DESIGN.mdafter edits —broken-ref,contrast-ratio, andorphaned-tokenswarnings flag issues automatically. Pay special attention to the{colors.body}-on-{colors.canvas}contrast warning (these are too similar). -
When in doubt about emphasis: choose bigger type before bolder type (the system uses weight 400 at 68px), choose transparency before fills for buttons, and choose color inversion over shadows for depth. Restraint is the governing principle.
-
Font setup: Self-host the Sweet Sans Pro OTF files (Light, Regular, Italic, Medium, Bold, Heavy) and TT Commons Pro TTF files (Light, Regular, Italic, Medium). Define
@font-facerules withfont-display: swapfor each. Create CSS variables--font-display: "Sweet Sans Pro", ...and--font-ui: "TT Commons Pro", ...for systematic reference.
Known Gaps
- Hover state styling: Per the no-hover policy, hover styles for buttons, links, and interactive elements are not documented in the component entries. Implementation teams must derive these from the Active/Pressed states and the 300ms transition timing.
- Loading skeletons / pending states: While a
loadingpulsekeyframe andcomponents-animate__loadinganimation were detected in the CSS, the visual appearance of skeleton loaders or spinner components was not observable from the static screenshot. - Form validation states beyond focus: Error, success, and disabled states for text inputs are inferred but not visually confirmed. The
components-validated-control__indicator-jumpkeyframe suggests validation feedback exists. - Authenticated/reservation flow surfaces: The booking/reservation UI (triggered by "BOOK NOW") is not visible in the above-fold screenshot. Input field styling is partially known, but the full form layout, date pickers, and confirmation states are undocumented.
- Sub-brand palettes: If Ballena offers private events, catering, or a separate bar/lounge concept, those sub-brands' color or typographic variations are not represented here.
- Footer complete structure: The footer band component is inferred from common patterns; the actual footer content (links, address, social icons, legal text) was not in the viewport.
- Scroll-triggered animation specifics: While keyframe names are extracted, the exact intersection-observer thresholds (e.g., "animate when 20% visible") and animation parameters (translate distance, opacity endpoints) require implementation decisions.
- Dark mode considerations: The site already uses a dark-primary aesthetic; whether a true "light mode" inversion exists (white backgrounds with black text as default) is unknown from this single viewport capture.
- CSS variable naming mismatches: The source contains WordPress/Elementor-specific class names (
block-header__book-btn,pum-theme-default-theme,elementor) that may misleadingly suggest a page-builder dependency; the design tokens themselves are framework-agnostic.