Portal
The shared design system for the owner portal (/admin) and the agency dashboard
(/platform) — two different audiences (a business owner vs. the agency running the
platform) that intentionally look and feel like one product. Quiet, neutral, Apple-inspired:
near-black text, soft gray surfaces, a single blue accent, color reserved for status meaning.
All tokens live in public/css/platform.css under :root; owner-portal-only
components (booking table, calendar, charts) live in public/css/admin.css, built on
the same tokens. This is a completely separate system from the hotel guest site's own
/style-guide — never mix the two.
Color
Neutral surfaces and text do almost all the work. Accent blue is the only saturated brand color, used for primary actions and links. Feedback colors (success/warning/danger) are reserved strictly for status meaning, never decoration.
Surfaces
#f5f5f7#ffffff#fbfbfd#e8e8edText
#1d1d1f#6e6e73#86868bAccent
#0071e3#0077ed#eef6feStatus meaning
#167a33#e6f6ea#96590a#fdf1de#d70015#fce8e9Add-on category dots
Each paid add-on (Reviews, Direct Booking, Stats & Analytics) gets its own small accent dot on the agency dashboard's client-list badges — the badge pill itself stays neutral gray; only the dot carries the hue.
#af52de#0071e3#2ba9bcTypography
A single sans-serif family — no serif anywhere in this system, unlike the hotel guest site's Fraunces headings. System font stack (SF Pro on Apple devices) falling back to Inter.
1.9rem · page title (Clients, client detail)
card/modal heading
section/chart heading
color
--pf-textcolor
--pf-text-secondarycolor
--pf-text-secondaryDifference from the hotel guest site: field labels here are plain sentence case, not the hotel's uppercase-tracked labels — one more deliberate signal that this is a different product with a different voice (software, not a lodge).
Spacing
A 4px base scale, same idea as the hotel guest site's — the numbers just get applied to a denser, data-heavy UI (dashboards, tables, charts) instead of a marketing page.
4pxIcon-to-text gaps8pxChip/badge gaps, tight stacks12pxRelated items in a group, card grid gaps16pxDefault gap between elements20pxSection gaps (pf-grid), card padding (booking cards)24pxCard padding (pf-card), page gutter32pxBetween component groups (room-type groups, dashboard sections)Shape & elevation
No borders anywhere — depth comes from a subtle shadow on cards/panels (--pf-shadow-sm) sitting on the page's own --pf-bg, not from an outline. State reads from fill color alone, same rule as the hotel guest site.
--pf-radius-sm · 8pxForm field inputs, alerts, small tags--pf-radius · 14pxCards, panels, chart tiles--pf-radius-lg · 20pxModals, the login card--pf-radius-btn · 16pxButtons only980–999px · pillBadges, status pills, togglesButtons are rounded-square, badges/pills stay pill-shaped — badges, status pills and toggles are still the full 980–999px pill, but buttons use a softer 16px rounded-square so they read as a distinct, tappable shape from informational chips. The hotel guest site squares its buttons/fields even further (--radius-btn, 10px, see /style-guide) — three separate products, three separate style guides on purpose.
A closed calendar day, an offline room, a cancelled booking: color says everything, so an outline on top of it is redundant clutter. Nothing in this system uses a border as its identity — not even outline buttons (.btn-outline is a soft neutral fill, not a bordered/transparent button).
Don't use a colored left-border accent to signal a card's status (tried on the agency dashboard's Add-on usage cards — read as a stray line, not a status). Tint a fill instead: an icon badge, a pill, or a background tint.
Buttons
Rounded-square (16px), 600 weight, 11px×22px padding (14×28 for large, 7×15 for small). One primary action per view.
.btn-primary/.btn-forest accent blue → accent-hover · .btn-outline neutral-soft fill, no border · .btn-gold solid near-black (used for the one "authorize this pending account" CTA, not a second brand color) · .btn-danger red → darker red on hover
.btn-sm/.btn-lg resize padding & font-size only — shape and color stay identical to the default. .btn-block stretches to 100% width (used for the login form's Sign in button).
Form fields
Borderless, filled fields (--pf-radius-sm, 8px — not pill, unlike buttons). Two interaction tiers, not four — this system has no separate keyboard-only outline ring the way the hotel guest site does; the same focus glow applies for pointer and keyboard alike. Try the fields below.
Interaction tiers
- Rest: filled with
--pf-bg(the page's own background color, one step off the white card it sits on), no border, no inset ring. - Hover (select/date only): steps one surface darker to
--pf-border-soft. - Focus (any modality): the surface wakes to white (
--pf-surface) and a soft 3.5px halo in--pf-accent-softappears — no border-color change, no separate keyboard tier. Transition 150ms ease on background-color and box-shadow only. - Details: caret is
--pf-accent; font-size ≥16px (prevents iOS focus zoom); disabled =--pf-border-softfill +--pf-text-tertiary.
Badges, status & toggles
Status pills pair a --pf-* text tone with its own -soft tint — never a border. Add-on badges stay neutral gray at rest; only their leading dot picks up color once the add-on is on (see the category dots above).
Toggles
Checked almost always means blue/"on" (add-ons, free subscription, portal access). The one exception is the owner portal's "take this room offline" toggle: checked there means removing availability, so it stays amber instead of blue — the color follows what checked means, not a blind rule.
Alerts
Message thread
Reused from the owner portal's guest-conversation modal. The current user's own messages sit right in accent-soft; the other party sits left in neutral-soft.
Accessibility
Every status color pairing below meets WCAG 2.1 AA (4.5:1 for normal text, 3:1 for large text/UI). Two tokens (--pf-success, --pf-warning) were darkened slightly from their first draft after checking — the originals landed at 3.75:1 and 4.10:1, just short of AA.
| Pairing | Used for | Ratio | AA |
|---|---|---|---|
| Aatext / bg | Body text on the page background | 15.5:1 | Pass |
| Aatext / white | Body text on cards | 16.9:1 | Pass |
| Aatext-secondary / bg | Labels, captions | 4.65:1 | Pass |
| Aawhite / accent | Primary button text | 4.70:1 | Pass |
| Aasuccess / success-soft | Active/confirmed pills | 4.85:1 | Pass |
| Aawarning / warning-soft | Pending/paused pills | 5.05:1 | Pass |
| Aadanger / danger-soft | Cancelled/offboarded pills | 4.58:1 | Pass |
Rules
- Focus: every interactive element shows a visible focus state — the soft accent-blue glow on fields, a 3px solid accent outline on toggles/calendar cells/raw inputs. Never removed.
- Color alone: status is always color + a text label, never color alone.
- Touch targets: buttons and nav items are ≥44px tall.
- Type floor: nothing below 0.68rem, and only for bold uppercase micro-labels (room status pills).