Design Direction · UX/UI

Guest experience platform for hotels.

The UX/UI layer of the Guesteria system: the components, layout and screen anatomy behind three tonally distinct surfaces - the white, dense Front-Desk console, the warm, airy guest app and the cool, compact staff app.

Inspect components Read the spec
surface A · Console
White canvas, dense grid.
Sortable tables, KPI tiles, a white rail. Cool blue-grey is a panel fill only.
surface B · Guest
Warm canvas, photo-led.
Two-column catalogue grids, a floating 5-tab bar, a navy primary.
surface C · Staff
Cool canvas, verb CTAs.
Count-tile filters, a flat 4-tab bar, Get blue / Start orange / Finish green.
the line
palette rhythm
1 Primary
2 Sub-prime
3 Structure
4 Subtone
lockup
Guesteria full lockup
type
Aa
Display / Body
context A
Skyline · staff/ops
context B
Earthy · guest
app iconGuesteria app icon
the markGuesteria circular mark
grid
4px
spacing base step
console grid
12
columns on the console
container
1440
console cap, tables scannable
hit target
44px
touch minimum, guest app

11 Components

The union observed across 75 twins

Mirrors the ShadCN vocabulary plus the Guesteria-specific variants the product actually ships (status split-buttons, the five-star + optional-comment rating, verb-coloured staff CTAs, the .guest-chip room-row, the operator's merge-field composer). Each family below is tagged by the surface it belongs to — Front-Desk, Guest, or Both; the staff app is a third surface and is called out where it diverges.

Buttons — Both

CTA colour routes by SURFACE and by VERB, not by actor. The guest primary is dark navy (--brand-800 #2D2F44) on every guest screen sampled - "Next", "Pay now", "Back to Dashboard", "Confirm", "Decline", "Upgrade room type" (Guest/Guest - My requests1.png, Guest/Guest - Pre-check-in-Step 8.png, Guest/Guest - Chats-Request by menu.png). The staff primary is full-width and coloured by the VERB, three hues in one card component on one screen: Get blue, Start orange, Finish green (Staff/staff - Dashboard.png). The console's manager action is the status split-button (11.2), and its create actions are violet (Front Desk/FD - Requests list - Guest.png, "+ Guest service" / "+ Internal service"). Violet marks the AMOUNT, the unread count, the active-nav fill and the translation chip - never the pay action.

The actor-routed variants .btn-staff and .btn-guest are NOT shipped. No screen carries a Skyline-blue "ordinary staff CTA" or an Earthy-brown "ordinary guest CTA"; they survive in the stylesheet as legacy role aliases only. Do not reach for them when building a screen.

Staff verb buttons - one hue per verb, same component, same screen (Staff/staff - Dashboard.png):

Legacy aliases, kept in the stylesheet and shipped on no screen:

VariantBackgroundBorderTextUse
Guest primary--brand-800 #2D2F44 navynone#FFFFFFEVERY guest-app primary: "Next", "Pay now" (with a card glyph), "Back to Dashboard", "Confirm", "Decline", "Upgrade room type"
Staff verbone hue per verb - Get blue, Start orange, Finish greennone#FFFFFFThe full-width staff task action. Two cards with the SAME status carry different buttons, because assignment - not status alone - picks the verb
Primary (.btn-primary)--brand-600 violetnone#FFFFFFConsole CREATE actions ("+ Guest service", "+ Internal service"). Violet's other jobs are marks, not buttons: the price, the unread count, the active-nav fill, the translation chip
Secondary--surface-canvas1px --line-strong--text-primary"Filters", "Skip", "Add feedback", and the "Cancel" that abandons a dialog or a form - the card demotes its own action to an outlined secondary once the object is terminal
Split-button--ctx-1-700 slate, or --status-* at a terminal statenone#FFFFFFLabel + caret manager actions: "Set", "Approve", "To Do" all slate; only "Done" green and "Canceled" red
.btn-staff / .btn-guest--ctx-1-800 / --ctx-2-800none#FFFFFFLEGACY ALIASES. Actor-routed CTAs are not shipped on any screen; do not use
Tertiarytransparentnone--text-primaryToolbar, dense console surfaces
Linktransparentnone--accent-linkTrue hyperlink, "All" / "See original"
Destructive--status-errornone#FFFFFF"Cancel request", "Mark Canceled" - an action that destroys the object
SizeHeightStateSpec
sm28pxrestthe resting fill
md (default)36pxhover-10% luminance
lg44pxpressed-20% luminance
xl (marketing only)52pxfocus2px --focus-ring, 2px offset
--disabled40% opacity
--loadingspinner replaces label, width preserved

Split-button (manager actions) — Front-Desk

A label + caret manager action whose primary segment is the NEXT state and whose dropdown holds the others. The fill follows the status only at the TERMINAL states. Set, Approve and To Do all render in the same desaturated slate (--ctx-1-700 #6D82A3); only Done is green and only Canceled is red (Front Desk/FD - Requests list - Guest.png, Front Desk/FD - Requests list - Internal.png). At a terminal state the button greys out entirely. The caret half opens the dropdown of the other statuses.

Primary — sizes

Primary — states

rest
hover
pressed
focus-visible
disabled
loading

Secondary — sizes

Secondary — states

rest
hover
pressed
focus-visible
disabled
loading

Tertiary — sizes

Tertiary — states

rest
hover
pressed
focus-visible
disabled
loading

Ghost — sizes & states

rest
hover
pressed
focus-visible
disabled
loading

Destructive — sizes & states

rest
hover
pressed
focus-visible
disabled
loading

Icon treatment

Label-only, icon-only (square), leading icon, trailing icon. Icon is 14-16px; padding tightens for icon-only.

Form fields — Both

Used for the booking confirmation and any messages from the property.
Use at least 12 characters.
Shown for the control's states only. Translation carries NO global control - no confidence slider, no thread switch, no conversation language setting; it is a per-message toggle, documented at dmd 14.
New chat messages Assigned requests Passport ID card Service active
+34
Country-code phone: flag + code prefix + number in one field.
2
Quantity stepper: -/value/+ control.
Tap to put signature here
Signature capture: off-white card, prompt + pencil tile.
ElementRestingFocusErrorDisabled
Text / textarea1px --line-strong, ~8px radius, 40px height; char counter ("0/750")--focus-ring border + --focus-ring-soft glow2px --status-error, error helper--surface-sunken, 40% text
Select / dropdownsame plus caretsamesamesame
Date / date-range / timefield plus calendar icon; range chipsamesamesame
Checkbox~18px, ~4px radius; checked fills --control-active2px focus ringerror stroke40% opacity
Radiocircle, 2px stroke; document-type choice2px focus ringerror stroke40% opacity
Togglepill, --control-active ON, gray OFF; encodes service active/inactive2px focus ringn/a40% opacity
Quantity stepper-/value/+ controln/an/a40% opacity
Signature captureoff-white card, "Tap to put signature here" + pencil tilen/arequired asteriskn/a
Country-code phoneflag + code prefix + number--focus-ring bordererror stroke40% opacity

Label and helper both sit on the Caption step - 13/18, --text-secondary for the label at 500, --text-tertiary for the helper. There is no 12px step on the master scale (dmd 4); the step below Caption is Micro at 11/16. Required fields carry an asterisk. Idle focus is desaturated - a --focus-ring border plus a --focus-ring-soft glow on a white background, never the saturated --brand-600 border or a --brand-50 fill. Committed controls (checked checkbox/radio, toggle ON, slider fill/thumb) fill --control-active (#4E4670), not the CTA violet.

Brand mark in the shell — Both

The header .nav-brand carries the REAL Guesteria full lockup - the shipped assets/logos/guesteria--logo.svg (navy knockout), or assets/logos/guesteria--logo-light.svg (off-white knockout) on a dark ground - set at ~24px tall. Never an icon-plus-wordmark composition fabricated from the bare circular mark - that is struck at logo 4. Which file goes on which surface is the logo artifact's own table at logo 3. In the shell the standalone assets/logos/guesteria--icon.svg badge is the app / favicon slot; off product it may also stand in as the SHORT logo at small or square scale.

shell header, light ground
Guesteria full lockup, navy knockout
shell header, dark ground
Guesteria full lockup, off-white knockout
app / favicon / strict slot only
Guesteria circular badge

Tabs — Front-Desk

Two controls, not one strip. The centered QUEUE TABS are a three-way segmented control over the object kind; the All / Priority pair is a separate segmented control in the table TOOLBAR below them, beside the Filters chip, the list / kanban / calendar view switcher and the two create buttons (Front Desk/FD - Requests list - Guest.png). Merging them into one four-tab strip loses the fact that a queue and a filter are different axes.

All Priority Filters

Breadcrumb

INFERRED, not observed. Neither console screen carries a breadcrumb - both identify location with the top-bar page title plus the active rail item (Front Desk/FD - Requests list - Guest.png, Front Desk/FD - Chats 3.png). The pattern is drawn here because the console has a Management > Staff > person depth that needs one; treat it as a proposal.

Pagination

Sidebar nav — Both

The console left nav rail stays white, never a colored block. Top to bottom: the "Guesteria" wordmark + chevron; a property / workspace switcher card (property name "Verde Canario" + thumbnail + chevrons); then grouped nav. Group eyebrows are NOT tinted by meaning. Guests, Inbox and Management all render their eyebrow in one neutral grey; the only coloured item in the rail is the ACTIVE one, and it takes the brand violet regardless of which group it sits in (Front Desk/FD - Requests list - Guest.png, Front Desk/FD - Chats 3.png, FD_Settings/FD_Settings - Pre Check-In-Step 7.png).

The page ground stays white. A guest in the content is never a hand-rolled avatar + room badge - it is the .guest-chip component, so the room tag and the room-type code travel with it:

AS312 Alex SmithFamily - FML

Many items carry dual count badges: a gray total plus a violet unread ("Chats 23 | 4", "Requests 28 | 2" - the shipped frames disagree on the Requests badge, which also renders 12 | 1). The active item takes the SAME violet in every group - a --brand-50 fill with --brand-700 text and a 3px --brand-600 left bar. There is no per-group active colour:

Arrivals Services Dashboard

Guest-app navigation — Guest

The guest app carries its own navigation anatomy: a bottom tab bar, a per-screen top app bar, and a slide-in drawer. The drawer DUPLICATES the tab bar rather than extending it, and the overlap is partial. Room, My Stay and Activities sit in both. Concierge and Requests - two of the five tabs - are in the tab bar ONLY; there is no Requests item in the drawer. Hotel and Hotel Services are in the drawer only. A guest who learns the app from the drawer never finds the chat (Guest/Guest-Dashboard+Sidebar1.png). Preserve the asymmetry.

bottom tab bar

5 tabs - Room, Activities, Concierge, Requests, My Stay - with a center emphasized circular "Concierge" tab linking to reception chat. A floating rounded surface. Concierge and Requests live here and nowhere else.

Room Activities Concierge Requests My Stay
top app bar (mobile)

Circular back button, centered screen title ("Concierge", "Pre Check-In", "Scan"), language pill (flag + "EN") at right.

Concierge EN
Pre Check-In EN
Scan EN
slide-in drawer

Brand header (circular hotel logo + name) + collapse toggle, a search field, a user block, a nav list with leading line icons and an expandable "Hotel" item, and a footer with a language switcher, "Download App", and Terms / Privacy.

Verde Canario Hotel
AS Alex Smithalex.smith@example.com
Room My Stay Hotel
Wi-Fi Rules Info FAQ Map Amenities
Hotel Services Activities
ENG Terms / Privacy

Dropdown menu — Front-Desk

Anchored popover, used for assignee pickers ("Robert Williams"), the date-range "Last 7 days" selector, row overflow (3-dot) menus, and the caret half of split-buttons. 32px row, --line-default separators.

Global search — Both

Guesteria does not ship a command palette. Its console equivalent is a centered global search field in the top bar, present on every console screen, plus a per-list search inside panels; the guest drawer carries its own full-width "Search" input. Advanced search behavior below is illustrative - low-confidence, only the field itself is observed.

Esc
Guests
Alex Smith - Room 312Family - FML
Verde Canario HotelProperty
Reservation 2947594234Booking
Recent
Chats 4 - conversation list
Requests 2 / Completed 12
navigateEnter select · Esc close

Dialog (modal) — Both

Cancel this request?

This will mark the request Canceled and notify the guest. This action can't be undone from the request list.

Centered, ~480px max width, 16px radius, --shadow-xl, scrim --surface-overlay. Primary CTA right of the footer, cancel at left.

Right-side panel — Front-Desk

The console's signature container: a collapsible right rail holding the Guest Experience journey panel or the Reservation Profile, anchored to the viewport edge and collapsible from the top bar.

Bottom sheet (mobile) — Guest

Use for guest action menus and currency/option pickers where a dropdown would clip. Placement is inferred from the mobile-native pattern requirement, not directly observed in a twin.

Choose currency

Popover, tooltip, hover-card — Front-Desk

Help ?

Tooltip: ~4px radius, --surface-inverse background, --text-inverse, on collapsed nav icons and utility glyphs. 120ms open delay.

Pre-Check-Outs / Departures

Monday, 16 June 2025 - Occupancy 420 80% / Guest Satisfaction 8.5/10.

New Approved Canceled

Toast / notification — Front-Desk

+
Pre Check-in Confirmed
Alex Smith completed pre-check-in for room 312.
×
i
Notifications
You have 8 unread updates.
×
!
Warning
Request #201 (Room Service) has been unassigned for over 15 minutes.
×
!
Error
WhatsApp token expired for Front-Desk.
×

~12px radius, --shadow-lg. Auto-dismiss info/success; keep error until dismissed.

Cards — Both

Resting card
--surface-canvas, 1px --line-default, ~12px radius (console) / ~16px (guest), --shadow-sm. Hover lifts to --shadow-md with a 1px --brand-200 border tint.
Updated 2hDone
Selected card
2px violet (--brand-600) border, padding shrinks by 1px to preserve outer dimensions.
Active
Disabled card
60% opacity, no shadow, no hover.
Canceled

Data table — Front-Desk

The shipped queue runs THIRTEEN columns in this order: row checkbox, priority marker, Name, Rooms, Requests, Created, Due date, Status, Created by, Responsible, Payment, the manager's action split button, and an overflow ellipsis (Front Desk/FD - Requests list - Guest.png). There is no money on the row. The guest's own card carries the price and "Pay now"; the operator gets a Payment PILL - "Unpaid" red, "Paid" green - riding beside a separate status pill, so a request can be Approved and Unpaid at once. The abridgement below drops Created / Due date / Created by / Responsible for width; the priority marker demonstrates both of its states (an orange filled circle for high, a grey outline for normal). Sortable columns with up-arrow sort glyphs, select-all + row checkboxes, hairline row dividers, sticky header, and a per-row action cluster. Body 14/20; column headers muted gray. The guest cell is the .guest-chip component, and it is a THREE-COLOUR cluster, not one tint: a warm sand ROUNDED-SQUARE avatar tile carrying the initials, a NEUTRAL grey room-number pill overlapping its lower edge, the guest name in near-black, and the room-type code in the warm olive --ctx-2-700 (Front Desk/FD - Chats 3.png, enlarged). The manager action is the split-button, slate at the intermediate states and status-coloured only at Done and Canceled (11.2).

! Guest Request Status Room Payment
AS312Alex SmithFamily - FMLLate check-outApproved312Unpaid
AC112Alex CarterLux - ST-CBreakfast in the roomDone112Paid
HS214Hiroshi StounaPre-Check - PCPRoom Service #201New201Unpaid
JD312John DoeFamily - FMLBaby Crib x2Canceled----
RWRobert WilliamsFront DeskInternal ChatOn Hold----

The two avatar shapes are the opposite way round from what a reader expects. A STAFF or operator row takes a CIRCULAR avatar - a photograph where one exists - with a grey role caption under the name and no room tag (Front Desk/FD - Requests list - Guest.png, the Created by column; Staff/staff - Chats.png). The GUEST-chip takes the rounded SQUARE tile with initials and the room pill. Code / IDs: reservation IDs render in mono with tabular numerals - 2947594234 - and so does every amount where an amount belongs, which on the console is the guest's request detail and never the queue row.

Guest-chip inventory — Front-Desk

One component, three room types. The rounded-square avatar tile carries the initials in warm sand, the neutral grey tag near its bottom edge carries the room number, and the small uppercase code carries the room type in warm olive. Observed codes: FML, PCP, STC, ST-C, LSV, XT11, SGL, PLX (Front Desk/FD - Chats 3.png, Front Desk/FD - Requests list - Guest.png). The COMPONENT is console-only - the avatar tile plus the overlapping room pill plus the code line is an operator's addressing device. The room NUMBER and the room-type CODE are not console-only: the guest stay card reads "Family (FML) Room #312" and a staff task card reads "Room: #112 FML" (Guest/Guest-Dashboard+Sidebar1.png, Staff/staff - Dashboard - Listed Task.png).

JD312John DoeFamily - FML HS214Hiroshi StounaPre-Check - PCP AC112Alex CarterLux - ST-C

Data table — ranked and scheduling variants

A ranked variant shows Top-5 with progress bars; a scheduling variant is a weekly grid.

ranked · Top-5
Breakfast in the room
Room Service
Concierge chat
Laundry
Baby Crib x2
scheduling · weekly grid
MoTuWeThFrSaSu

Avatar & avatar group — Both

Shape encodes WHO, and it is inverted from the intuitive reading. Round = staff, operator or team - a photograph wherever one exists, with a grey role caption. Rounded square = a GUEST, initials on warm sand, carrying the room pill. Never draw a guest round or a colleague square.

JD AC RW AS JD AC RW +4

Progress & skeleton — Both

Pre-Check-In progress
Indeterminate (loading)
Skeleton loader

Accordion — Guest

What's included under Hotel?
Six children - Wi-Fi, Rules, Info, FAQ, Map and Amenities - live in the guest drawer's expandable "Hotel" item, with a vertical guide line connecting the nested list.
How do I change my check-out time?
Request it from the Concierge tab; Front-Desk approves or declines and the status updates in your Requests list.
Which languages does chat support?
Translation is per MESSAGE, in both directions, and there is no thread mode: an inbound bubble arrives translated with "See original" beneath it, an outbound one shows "Translating" while it sends. No screen carries a "translate this thread" switch or a conversation language setting.

Badges, pills & chips — Both

FML 312 Service Arrivals 45 Family (FML) Paid On Hold Not sent
All × Priority × Room Restaurant Laundry
VariantBackgroundTextUse
Neutral--surface-sunken--text-secondaryCategory pills (Restaurant, Room, Laundry), transport chips, assignee chips
Room pill--ctx-3-600 neutral grey#FFFFFFThe room NUMBER on the console guest-chip - solid neutral. The number itself also appears as plain text on guest and staff cards; only this pill is console-only
Brand (.badge-brand)--brand-50--brand-700Unread count badges, "Service" chip, brand-tied tags, the active state
Skyline (staff/ops)--ctx-1-200--ctx-1-800Staff/ops data: Arrivals 45, Occupancy 420 80%, bell 8
Earthy (guest)--ctx-2-200 / --ctx-2-50--ctx-2-800 / --ctx-2-700Guest data: the avatar tile, stay dates, offer name, and the ROOM-TYPE CODE (FML, PCP, ST-C) where the surface renders it warm - the code reads warm olive on Front Desk/FD - Chats 3.png (R-B about 24) and neutral dark grey on Front Desk/FD - Requests list - Guest.png, so warm olive is a SURFACE-dependent rendering, not a global token. The room number beside it is neutral on both
Success--status-success-soft#065F46Approved, Done, Paid
Warning--status-warning-soft#92400ENew, On Hold
Error--status-error-soft#9F1239Canceled, Not sent

Additional chip types from the product: removable preference tags; "TOP" / promo badges (orange) on service cards; language pills (flag + "EN"); date-range chips; the gray-total + violet-unread dual count badge in the nav. Filter chips ("All" / "Priority") are pills with an optional trailing "x"; the selected chip uses the violet variant.

Inline highlight .hl — Both

The inline highlight tag (.hl) is a separate primitive - NOT a badge. Use it sparingly to focus a PHRASE inside running copy with a contrast tint behind the text: .hl (violet) for a brand rule, .hl-staff for a staff rule, .hl-guest for a guest rule. Never a status signal.

Brand rule: violet marks the amount, the unread count and the active item.

Console rule: the rail stays white and its eyebrows stay grey.

Guest rule: the guest-app primary action stays navy on every screen, "Pay now" included.

System banners — Both

Distinctive: KPI stat tiles — Front-Desk

ArrivalsPre-Check-In 30%
45
today
In-HouseUpsells 5
70
guests
DeparturesPre-Check-Out 30%
45
today

Distinctive: Activity Log — Front-Desk

RW
Robert Williams
Room Cleaning 7:30 PM - marked Done.
2m
JD
Front-Desk
Assigned Robert Williams to Room Service #201.
14m
AS
Alex Smith
Completed pre-check-in for room 312; document scan verified.
1h

Distinctive: media overlay card — Both

Guide
Find the hotel map and local activities.
Best practice
Tint the header strip, keep the data on white.

Image & interaction handling — Both

object-fit: cover A hotel interior, filling its frame

Photos use cover: the frame is filled, the crop is accepted.

object-fit: contain

The in-product phone-preview mockup uses contain: nothing of the screen is cropped away. Decorative images take alt="" and role="presentation"; everything else always carries an alt. Images use srcset with 1x / 2x / 3x.

hit targets & non-colour affordance
36 44 Approved New Canceled

Hit targets: 44px is the floor on touch (the guest app is mobile-first). The desktop console is deliberately denser and its controls measure below that - .btn-sm 28px, a dropdown row 32px, the split button ~30px, a rail item 36px - because there the whole ROW is the target, not the label. Do not read 36px as a desktop minimum. Every interactive element has a non-colour affordance (icon, underline, border) in addition to colour - status pills carry a glyph and a label, not colour alone.

Distinctive: content over imagery

Lift copy off a photographic or coloured surface three ways, same primitives as the creatives but web-tuned: a RHYTHM tint over the image (a colour-coded overlay), a LIQUID-glass panel (frosted - over a dark image use .glass--ink so the light text reads), or a SOLID .island callout (a dashboard announcement). The island radius tracks its type; glass sits over a ground only.

A photo under a context wash
Rhythm tint
A real photo under a sub-prime wash, colour-coded by actor.
A bright office interior
Liquid-glass panel
Frosted over a real photo: it reads through the blur, the light text stays legible, the edge catches the light.
What's new
Solid island callout
A dashboard announcement - the SaaS-adapted cousin of the creative island, drawn from the rhythm.
Liquid over a colour field
A frosted panel over a colour ramp - the dark frost keeps the light text crisp.
Rhythm tint
A colour field, no photo - the sub-prime wash carries the copy.
Guest room
Liquid CTA over a photo
The new .btn-liquid rides the frost - the same material as the panel.

Distinctive: liquid + rhythmic buttons

.btn-liquid wears the .glass material on a button: a light frost takes a DARK label (over a light / photo-light ground); --ink is a dark frost with a LIGHT label (over a dark ground / photo); --ctx1 / --ctx2 mix a rhythm tone into the frost. A library glyph rides INSIDE the button (stroke=currentColor, so the icon recolours WITH the label as the rhythm tone changes). Like all glass, it sits over a GROUND, never the plain canvas.

Behaviour within the color rhythm: the SAME action (icon + label) recoloured by each rhythm tone - only the frost tint + the inherited text/icon colour shift, never the geometry.

brand . cool . warm - one button, three rhythm tones

Distinctive: liquid blocks - colour & image

The liquid approach beyond the buttons: a frosted .island.glass wrap-block + a liquid .tagwrap focus-wrap, over a COLOUR rhythm field and over a darker field. Light frost -> dark text over a light field; .glass--ink -> light text over a dark field / photo.

Glass over a light field
A near-clear frost over a sub-prime ground - the DARK text reads, the luminous edge gives depth without a slab.
Glass over a colour field
Over a dark field the ink frost keeps the LIGHT text crisp - the same material, picked by the ground.
Glass over a deep field
The frost refracts the ground into a calm field; the block stays distinct, the copy legible.

Run the whole flow from one calm console.

A message that reads over any ground.

Distinctive: empty state — Both

+
No requests yet
Guest and internal requests will appear here as they come in from chat, the app, and the front desk.
Request a service

Distinctive: offer / service cards — Guest

A guest offer card is a PHOTO card, not a pricing-plan column. The photo is the card; over it sit an orange TOP badge and a grey translucent category chip; under it the name, then the price in brand violet. They lay out TWO-UP, in the catalogue grid, under a horizontal Featured / Recommended carousel band (Guest/Guest - Activities_grid.png, Guest/Guest - Dashboard - Check-in State.png). There is no feature list and no plan column on any guest screen - the earlier .feature-list / pricing-column reading was template residue, and it is withdrawn. The one card that carries a spec row is the upgrade offer, which sets two pills side by side instead of a bulleted list (Guest/Guest - Pre-check-in-Step 3.png).

TOP Room Service
Breakfast in the room
45 EUR
Family In-house
Baby Crib x2
40 EUR

Photo, badge, chip, name, violet price - in that order, two-up. The commit action is NOT on the card: it lives on the request detail the card opens, and it is the navy guest primary ("Pay now"), never a per-card button row. A catalogue screen is a Featured / Recommended carousel band above this grid.


12 Layout

Spacing & rhythm

Spacing and grid are inferred (the source defines no numeric spacing tokens; only "rounded / pill" plus approximate twin px). Base unit 4px - every spacing token is a multiple. Section rhythm 64px; subsections 32px; in-card stacking 16px. The console runs dense but gridded with generous card padding; the guest app runs roomy with one content card high on the screen.

--space-1 / 4
--space-2 / 8
--space-3 / 12
--space-4 / 16
--space-5 / 24
--space-6 / 32
--space-7 / 48
--space-8 / 64
--space-9 / 96
--space-10 / 128

Containers, gutters & grid

DimensionValue
Spacing scale4, 8, 12, 16, 24, 32, 48, 64, 96, 128
Container widthsthe console is a full-bleed dashboard (cap ~1440 to keep tables scannable); the guest app is a 446px mobile artboard; marketing centers ~1200; reading content ~640
Page gutter24px at <768, 32px at >=768, 48px at >=1280
Grid12 columns on the console. The guest app runs TWO grids, chosen by screen kind: the dashboard and the wizard are a single column with horizontal card carousels (Guest/Guest - Dashboard - In stay State.png), and every CATALOGUE screen is a two-column grid of photo cards - Activities, Services, Amenities (Guest/Guest - Activities_grid.png, Guest/Guest - Amenities_grid view.png). The grid does not replace the carousel: Activities and Services each keep a horizontal Featured / Recommended carousel BAND above the grid, so a catalogue screen is a band plus a grid. A browse screen is a grid; a task screen is a column
Section rhythm64px between sections, 32px between subsections, 16px inside a card

Horizontal wave divider

A horizontal rule built from the brand line (assets/curves/divider-1--wave.svg, traced from the divider-line asset): an asymmetric monoline that runs full 100% width and BLEEDS off both side edges, while the whole wave stays visible top-to-bottom. Height is a viewport tier (.dw-sm / .dw-md / .dw-lg = 10 / 20 / 30vh). The stroke is a near-tone of the ground. Three variants keep it from reading as a repeat, and all three are rendered below, in file order: asymmetric divider-1, calm divider-2, busy divider-3. It is a DERIVED full-width rule, so preserveAspectRatio="none" is allowed - not a cropped-corner brand curve. It is the ONLY sanctioned exception to the slice / named-edge law.

1 of 3 - divider-1--wave.svg, the ASYMMETRIC variant and the default: two shallow undulations that DRIFT upward across the width, leaving the left edge low and arriving high at the right. It is the DEEPEST of the three - the largest vertical travel across the 121-unit box. Use it where the sections above and below carry different weight, since the drift gives the rule a direction. All three are drawn here at .dw-md = 20vh, so the only thing that changes between them is the path.

2 of 3 - divider-2--wave-calm.svg, the CALM variant: one shallow S, near-symmetric about the centre and returning to within a hair of the height it started at. It is the QUIETEST of the three - the least vertical travel. Use it where the divider must not compete - between two dense sections, or under a heading that already carries a curve.

3 of 3 - divider-3--wave-busy.svg, the BUSY variant: the same one-S shape as the calm, but deeper, and flattened at crest and trough - both control pairs sit at one height - so it holds at the extremes and turns fast through the middle. Use it where the divider is the only ornament in view. Never run the same variant twice in a row - three variants exist so that consecutive dividers do not repeat.

13 Surface directions

Three surfaces, one system

Guesteria's surfaces are not one responsive layout: they are THREE tonally independent design directions sharing one brand — a white, dense Front-Desk / Tenant / Admin console (desktop web, 1296x848 artboards), a warm, airy Guest app (iOS, mobile web, WhatsApp, 446x948) and a cool, compact Staff app (mobile, WhatsApp, same 446x948). A viewer can name the surface from the page ground alone, with the type and photography cropped out. The surface sets only the CANVAS; component colour is carried by the OBJECT KIND it represents, not by the block, and not by the actor holding the phone.

The staff app is not the console on a phone and not the guest app for staff. Its ground is the arithmetic inverse of the guest ground, it re-implements the request card, the status pill and the bottom bar rather than reusing the guest ones, and its status vocabulary has already drifted from the other two - Done renders violet there while the green pill reads "Finished" (Staff/staff - Dashboard.png, Staff/staff - Dashboard - Listed Task.png, Staff/staff - Chats.png). Every shared component is tested three times.

Front-Desk console
Canvas: white Dense
The page ground is WHITE; cool blue-grey appears only as a grouped-panel fill inside the page, never as the canvas (Front Desk/FD - Requests list - Guest.png, Front Desk/FD - Requests - View request -Guest.png). Multi-column sortable tables, KPI tiles, a white left nav rail with a property switcher and dual count badges, and a top bar with global search (no command palette). Photography appears only as a 32-40px row thumbnail. One shell serves three roles - the same rail and top bar carry "Front Desk / Verde Canario" and "Guesteria Admin / Hotel Brand #1".

Observed info-chips, read off the master Front-Desk screens and kept verbatim. Staff/ops chips carry the Skyline role; unread inbox counts and the active state stay violet.

Arrivals 45 In-House 70 Departures 45 Chats 4 Requests 2 / Completed 12 Occupancy 420 80% Guest Satisfaction 8.5/10 bell 8 FML PCP ST-C 312 New Approved Done
Guest app
Canvas: warm Spacious
Airy and photograph-led, large radii; a floating translucent 5-tab bar with a raised circular Concierge tab that content scrolls under, one navy primary CTA per screen. Task screens are one column, catalogue screens are two-column photo grids. Guest data tints to --ctx-2-* (Earthy Comfort); the PRICE is violet and the pay action beside it is navy.

Observed info-chips, read off the master Guest screens. Guest data carries the Earthy role; the price renders in the brand violet on every guest screen sampled.

Verde Canario Hotel Alex Smith 2 adults - 2 childs 16 June - 22 June Family (FML) Breakfast in the room 45 EUR 10%
Room Activities Concierge Requests My Stay
Baby Crib x2 40 EUR

The price 40 EUR is the brand violet; "Pay now" beside it is dark navy with a card glyph. Violet and navy have swapped the roles a reader expects: violet marks the AMOUNT, navy COMMITS (Guest/Guest - My requests1.png, Guest/Guest - Chats.png).

Staff app
Canvas: cool Compact
A cool off-white ground (--ctx-1-50) carrying white task cards, each with a fixed-width photo strip down its left edge and a category tab on the photo. A flat opaque 4-tab bar - Dashboard, Schedule, Chats, Account - with no raised tab and no translucency. The filter row IS the count row: horizontally scrolling tiles where each tile is both the number and the filter. The CTA is full-width, ~40px, and coloured by the verb (Staff/staff - Dashboard.png).

Observed on the master Staff screens. Two cards with the SAME status, Approved, carry different buttons - assignment picks the verb, not status alone:

All Tasks 21 Unassigned 2 Priority 5 Active 4 On Hold 1
Approved Done Finished

"Done" is VIOLET here and "Finished" is the green one - a word the status table does not otherwise carry. Do not assume the guest app's status colours transfer.

Context is bound to the smallest element that carries the distinction, not to the block, so one row can hold several at once. A content-heavy row stays WHITE: tint only the low-detail header strip, then put the dense content in a white inner card. Below, a Front-Desk Requests work-row carries a guest avatar tile (warm) beside a neutral room pill and an olive room-type code, a violet-outlined per-message translate pill, and a SLATE Approve split-button — four colour jobs in a single row, none of them tinting the row itself:

Front-Desk Requests
AS312 Alex SmithFamily - FML New See original

Independence is real: a component may exist on one surface and not the others - the staff app has presence tiles and a shift grid that no other surface carries, and the guest app shows no staff identity at all, attributing every reply to "Cocierge". Tag every component by surface (Front-Desk / Guest / Staff / Both); tint it by the OBJECT KIND it carries (a guest request is cool, an internal request warm yellow, a note warm peach, a guest identity warm sand and olive), and keep the tint off every container.

14 User-story blocks

Top journeys, rebuilt as blocks

No one-off styling; every block is traceable to a real screen.

1. Front-Desk agent clears the morning queue
Signature block: a KPI tile row (Arrivals 45 Skyline, Requests 2 / Completed 12 violet) + a work-list row with a .guest-chip and a SLATE Approve split-button. Rebuilt from components.data-table + components.badges-chips. Screens: Front Desk/FD - Dashboard.png, Front Desk/FD - Requests list - Guest.png.
2. Operator answers + translates a chat
Front-Desk agent replying across a language gap. Signature block: a 3-pane chat (list / thread / composer) - a .guest-chip in the list, and a hanging translation pill under each bubble. Two things are OPERATOR-only. Merge fields render LITERALLY in the composer and in the operator's own outbound bubble ({{guestFirstName}}, {{guestProductPage}}) and resolve on send - the conversation-list preview of that same message already reads "Hey, Maria"; no guest screen carries a token (Front Desk/FD - Chats 3.png against Guest/Guest - Chats.png). And the transport chip ("WhatsApp") rides beside every timestamp on the console while the guest thread carries no transport mark at all. Translation is a per-message pill labelled by ACTION - Translate, Translating, See original - attached outside the bubble at its trailing edge. There is no thread mode, no language picker, and the word "AI" appears nowhere on any screen; the target language is a reservation attribute ("Preferred language: Spanish"), resolved before the first message. The in-flight "Translating" state sits on the OUTBOUND bubble, which is the tell that translation is a send-path step and not a display filter.
AS312Alex SmithFamily - FML
Hola, llego tarde esta noche.
WhatsApp See original
now
RW
Reception
Hey, {{guestFirstName}}! No problem, your room is ready.
WhatsApp Translating
3:10 PM
3. Guest completes pre-check-in
Signature block: a multi-step wizard row with an olive/taupe progress bar, a live phone preview in the console, a document-scan reticle (red to green), Earthy doc chips, and a NAVY "Next". The step counter is not a constant. Pre-check-in runs Step N/8, but the pre-check-out wizard prints two denominators in one flow and draws two of its five frames against the wrong one: Step 1 fills ~12% under a printed 1/5, and Step 4 fills ~75%, matching neither 4/5 nor 4/8. Steps 2, 2.2 and 5 agree with their labels - Step 2 fills ~25%, exactly 2/8 - so the defect is two frames drawn against a five-step model, not a bar that is generally wrong. The fills are read off the olive against the track and carry about half a percentage point of antialiasing noise; the measurement, its method and its tolerance are owned by sources/app/ui-approaches.md (Guest/Guest - Pre-check-out - Step 1.png, Guest/Guest - Pre-check-out - Step 2.png, Guest/Guest - Pre-check-out - Step 4.png). The denominator must be derived from the enabled step set - a hard-coded "N/M" per screen cannot survive a wizard whose length is a console setting. Screens: Guest/Guest - Pre-check-in-Step 1.png, FD_Settings/FD_Settings - Pre Check-In-Step 7.png.
4. Guest orders a concierge service and pays
Signature block: a concierge offer card ("Baby Crib x2 / 40 EUR") with the price in brand VIOLET and a dark NAVY "Pay now" carrying a card glyph. The card is not chat-specific - it is the same request object, sitting inside a tinted outgoing bubble with the bubble's own padding, timestamp and read receipt around it. Rebuilt from components.cards + components.buttons. Screens: Guest/Guest - Chats.png, Guest/Guest - My requests1.png.
5. Guest reviews the stay + bill
Guest rates the stay and sees the bill. Signature block: a SINGLE five-star row with an optional comment above the itemized line items, which render in mono. There is no total row and no pay action: the Bill Summary screen is excluded from the corpus because the product must not depict a checkout. There is no dual rating. No emoji-face row appears on any screen - the second control beside the stars is a plain "Add comment (optional)" pill in the thread, or a Comments/Preferences textarea in the modal (Guest/Guest - Chat - Rate the communication.png, Guest/Guest - Request_feedback.png). The same five gold stars carry the feedback back into the console's request detail. Warm canvas, neutral bill text, gold stars. Rebuilt from components.cards + components.badges-chips + components.form-fields.
★★★★★ Add comment (optional)
Breakfast in the room45 EUR
Baby Crib x240 EUR
No total, and no pay action. The Bill Summary screen is deliberately outside this corpus - payments are handled by the partner, so a checkout or a card form must not be depicted inside the product (selection.md L155-160). Line items are the shipped part; summing them here would be an unsourced number.

Rule: a story block ships only when it cites the source screen it reconstructs. No screen, no block.

15 Depth

Radius & elevation

Seven radius tokens, four shadow tiers. Cards step from sm to md on hover; modals and drawers sit on xl. Radius values are inferred from approximate twin pixels (cards ~12px, inputs ~8px, guest cards ~16px) - the source mandates "rounded / pill", not exact numbers.

Radius scale

xs / 4
sm / 8
md / 12
lg / 16
photo / 32
island / 0.9em
pill

Elevation tiers

shadow-sm / resting card, KPI tile
shadow-md / hovered card, dropdown
shadow-lg / toast, popover, legend
shadow-xl / dialog, drawer, phone preview

16 Responsive

Breakpoints & touch

Six breakpoints. The guest app is mobile-first (xs-sm); the operator console is desktop-first (lg-2xl). Breakpoints are inferred; the PRD requires platform-native mobile patterns. Touch targets are 44px minimum on md and below.

TokenRangeStrategy
xs< 480Guest and staff app default: one column for task screens, TWO columns for catalogue screens, bottom tab bar, bottom-sheet menus, sticky primary CTA
sm480-767Same two grids, sticky bottom CTA
md768-1023Console rail collapses; right panel stacks under content
lg1024-1279Console full left rail + content + optional right panel
xl1280-1535Default console dashboard layout
2xl>= 1536Console caps ~1440 to keep tables scannable, never wider

17 Dashboard preview

How the primitives compose

The real Guesteria Front-Desk dashboard: a WHITE left nav rail with NEUTRAL group eyebrows and one violet active item + top bar + the central dashboard grid + a collapsible right "Guest Experience" panel, inside a rounded dark device frame. The page ground is white; the cool tint appears only on the low-detail panel headers. Each component is coloured by the object it carries - staff/ops counts Skyline, the guest in each work-list row a .guest-chip (warm tile, neutral room pill, olive code), the manager action a SLATE split-button, the dense table on white (Front Desk/FD - Dashboard.png).

? bell 8 JDJohn DoeFront-Desk

Hey, John! How are you today?

Monday, 16 June 2025 10:00
ArrivalsPre-Check-In 30%
45
today
In-HouseUpsells 5
70
guests
DeparturesPre-Check-Out 30%
45
today
Chats 4 Answered automatically 30%
AS312Alex SmithFamily - FML WhatsApp
AC112Alex CarterLux - ST-C Guest App
Requests 2 Completed 12
Room Service #201 New Robert Williams
Baby Crib x2 New Unassigned
All Priority Filters Property: Verde Canario × Unassigned
Request Guest Status Room Started
Late check-outAS312Alex SmithFamily - FMLApproved3129:00 AM
Breakfast in the roomAC112Alex CarterLux - ST-CDone1128:30 AM
Room Service #201HS214Hiroshi StounaPre-Check - PCPNew20110:00 AM
Baby Crib x2JD312John DoeFamily - FMLCanceled----
Showing 4 of 12
Activity Log
1
Room Cleaning 7:30 PM
Robert Williams
Done
2
Room Service #201
Robert Williams
New
3
Late check-out
Front-Desk
Approved
Analytics Last 7 days
Monday, 16 June 2025

Occupancy 420 80% / Guest Satisfaction 8.5/10 / Upsells 17

Key Metrics
Occupancy420 80%
Guest Satisfaction8.5/10
Upsells17

18 Agent prompt guide

Quick token reference

brand violet   #826AFF
brand deeper   #532DF5
brand darkest  #2D2F44
violet soft    #C7BCFF
violet tint    #F2F0FF
focus ring     #A59CD3   (idle field focus border - desaturated, never the CTA)
control active #4E4670   (checked/toggle/slider fills - desaturated, never the CTA)
guest CTA navy #2D2F44   (--brand-800: EVERY guest primary - Next, Pay now, Confirm)
split slate    #6D82A3   (--ctx-1-700: Set / Approve / To Do; only Done green, Canceled red)
staff verbs    blue Get, orange Start, green Finish - one hue per verb, staff app only
.btn-staff     #2E4258   (LEGACY alias, not shipped on any screen)
.btn-guest     #443F2C   (LEGACY alias, not shipped on any screen)
Skyline blue   #A2B8D6   (ctx-1: staff/ops data)
Earthy beige   #CAC19F   (ctx-2: guest data + the guest-chip avatar tile)
olive / taupe  #878065   (--ctx-2-700: wizard progress; ROOM-TYPE CODES where the surface renders them warm)
room pill grey #6E6B79   (--ctx-3-600: the room NUMBER, console only)
guest price    #826AFF   (--brand-600, in product #826BFF - violet, never neutral)
guest canvas   #EAE7E1   (--surface-guest: the third surface token)
staff canvas   #F3F7FC   (--ctx-1-50: the inverse of the guest ground)
console canvas #FFFFFF   (#F4F4F5 is a PANEL fill inside the page, not the ground)
card           #FFFFFF
text           #0A0A0B
muted          #52525B
line           #E4E4E7
success        #13B981
warning        #F59E0B
error          #E11D48
info           #2563EB
star gold      #F5B100
radius card    12 (console) / 16 (guest)
radius btn     8
shadow card    0 1px 2px rgba(10,10,11,0.04)
shadow modal   0 24px 64px rgba(10,10,11,0.18)
font display   Codec Pro
font body      Source Sans 3

Generation rules & fast cautions

What an agent generating a Guesteria surface applies, and what it must never do.

Generation rules of thumb

  • Pick the CANVAS first, from THREE: white console (--surface-canvas), warm guest (--surface-guest #EAE7E1), cool staff (--ctx-1-50). The surface only sets the ground, and only as a low-detail frame - it does not tint the components inside, and it never floods a data-dense block. Cool blue-grey on a console page is a grouped-PANEL fill, never the page ground.
  • Tint by the OBJECT KIND, not by the block and not by the actor: staff/ops data → Skyline --ctx-1-*; guest data → Earthy --ctx-2-*; a guest request cool, an internal request warm yellow, a note warm peach. Apply it to the smallest element that carries the distinction - a button fill, a chip outline, a two-letter avatar tile - and keep every container neutral.
  • Sidebar: keep the rail WHITE and the group eyebrows one NEUTRAL grey. Only the ACTIVE item takes colour, and it is the brand violet in every group. Do not tint a group by meaning.
  • A guest in any row is the .guest-chip component - a warm rounded-SQUARE avatar tile, a NEUTRAL grey room-number pill on its lower edge, and the room-type code in warm olive (--ctx-2-700). A staff or operator row is a CIRCULAR avatar, a photo where one exists, with a grey role caption and no room tag. Never swap the two shapes.
  • Route the primary CTA by SURFACE and by VERB. Guest primary = navy --brand-800, always, including "Pay now". Staff primary = the verb's own hue (Get blue, Start orange, Finish green). Console manager action = the split-button, slate at the intermediate states. Console create action = violet. Violet is otherwise a MARK, not a button: the price, the unread count, the active-nav fill, the translation pill.
  • A guest offer card is a PHOTO card, two-up in a catalogue grid under a Featured / Recommended carousel band: photo, then an orange TOP badge and a translucent category chip over it, then the name, then the price in brand violet. No feature list, no plan column, no per-card button row - the commit action lives on the detail the card opens. The .feature-list class stays in the stylesheet for non-product surfaces; no guest screen uses it.
  • Header current-item indicator: every .nav-links link's href="#id" pairs with a <section id="id"> in the body, ids unique; an inline scroll-spy lights the matching .nav-link (a desaturated --control-active underline via .nav-link.is-active) for the section in view. Wire ids before relying on it.
  • Guest price / commerce emphasis renders in the brand VIOLET (--brand-600), on every guest screen - card grids, dashboard carousels, request cards and chat cards alike. The commit action beside it is navy. The system has no warm-red commerce hue.
  • Idle field focus = --focus-ring border + --focus-ring-soft glow on white; committed controls fill --control-active. Never set focus or an idle/committed control to the saturated --brand-600.
  • Status: use color-coded pills with a glyph (Approved green, In Progress blue, New amber, Canceled red). Manager split-buttons take slate at Set / Approve / To Do and the status colour only at Done (green) and Canceled (red). On the STAFF app the vocabulary differs - Done is violet there and the green pill reads "Finished"; map status to label per client rather than assuming one table.
  • Tables: hairline rows, sticky header; the guest cell is a .guest-chip, with the room-type code in mono olive (FML / PCP / ST-C); color balances (green/red). Overdue is a RED DATE, not a pill - no "Overdue" badge exists anywhere.
  • Empty states always carry a primary action. Derive a wizard's step counter from the enabled step set - "N/8" is the pre-check-in case, not a constant; the pre-check-out flow ships /5 and /8 in the same wizard. The progress bar is olive/taupe.
  • Product photography runs RAW inside a rounded rectangle - untinted and unmasked. It is not bare: the Hotel Info header is the one plain case, while EVERY catalogue thumbnail carries furniture over the image - the orange TOP badge on a Featured card, two chips on a grid card. Generate no bare catalogue photo. The tint / disc-badge / curve-mask treatments belong to the off-product register.
  • Translation is a per-message pill labelled by ACTION (Translate / Translating / See original), hung outside the bubble at its trailing edge. No thread mode, no language picker, and never the word "AI" - the target language is a reservation attribute. Merge-field tokens are an OPERATOR affordance: they render literally in the composer and the operator's outbound bubble and resolve on send; no guest screen carries one.

Fast cautions

  • Never set the focus ring or an idle/committed control to the brand fill. Idle focus uses --focus-ring (#A59CD3) + --focus-ring-soft; committed controls use --control-active (#4E4670).
  • Never put a background fill on the sidebar rail, and never tint a group eyebrow. Keep the rail white, the eyebrows one neutral grey, and the violet for the active item alone.
  • Never paint a guest CTA violet, brown or blue - EVERY guest primary is navy --brand-800, "Pay now" included. Violet on a guest surface marks the price, not the button.
  • Never draw a guest avatar round or a colleague's avatar square, and never make the room-number pill warm - the pill is neutral grey; the warmth in that cluster is the avatar tile and the room-type code.
  • Never tint a whole block by its surface, and never flood a data-dense block (table, form, multi-row card) with a --ctx-*-50/--brand fill. Tint a low-detail header strip and white-wrap the content; tint each component by the actor/data it carries.
  • Never render the guest price as neutral text. It is the brand VIOLET (--brand-600) on every guest screen; a warm-red price hue is not a brand colour and stays excluded.
  • Never animate width or height. Animate transform and opacity.
  • Never use pure black on pure white; cards are #FFFFFF but text bottoms at #0A0A0B.
  • Never signal a status by color alone; pair it with a glyph and a label.