Portal design systemOwner portal & agency dashboard
← Back to owner portal
Design system reference

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
Background--pf-bgPage background
#ffffff
Surface--pf-surfaceCards, panels, modals
#fbfbfd
Surface 2--pf-surface-2Nested panels inside a card (feature cards, message bubbles)
#e8e8ed
Border soft--pf-border-softHover fills, disabled fields — never an actual border

Text

#1d1d1f
Text--pf-textHeadings, body text
#6e6e73
Text secondary--pf-text-secondaryLabels, captions, meta
#86868b
Text tertiary--pf-text-tertiaryOff-state badge text/dots, placeholders

Accent

#0071e3
Accent--pf-accentPrimary buttons, active nav/tabs, links, focus glow
#0077ed
Accent hover--pf-accent-hoverHover state for accent-filled buttons
#eef6fe
Accent soft--pf-accent-softFocus glow, unread-lead tint, "mine" message bubbles
DoUse accent blue for the one primary action per view, active nav/tab states, and links.
Don'tReach for a second saturated color to "add interest" — this system deliberately has exactly one. Everything else is neutral gray or a status color.

Status meaning

#167a33
Success--pf-successActive/confirmed/paid states
#e6f6ea
Success soft--pf-success-softTint behind success text
#96590a
Warning--pf-warningPending/paused/restricted states
#fdf1de
Warning soft--pf-warning-softTint behind warning text
#d70015
Danger--pf-dangerCancelled/offboarded/destructive states
#fce8e9
Danger soft--pf-danger-softTint behind danger text

Add-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
Reviews dot--pf-dot-reviews
#0071e3
Direct Booking dot--pf-dot-booking
#2ba9bc
Stats dot--pf-dot-stats

Typography

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.

600, -0.015em tracking
1.9rem · page title (Clients, client detail)
Clients
600 · 1.3rem
card/modal heading
Contact & account
600 · 1.05rem
section/chart heading
Revenue trend
400 · 1rem
color --pf-text
Body copy — guest names, message text, table values.
400 · 0.85–0.95rem
color --pf-text-secondary
Secondary text for captions, meta lines and supporting detail.
600 · 0.78rem
color --pf-text-secondary
Form label

Difference 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.

4px
Icon-to-text gaps
8px
Chip/badge gaps, tight stacks
12px
Related items in a group, card grid gaps
16px
Default gap between elements
20px
Section gaps (pf-grid), card padding (booking cards)
24px
Card padding (pf-card), page gutter
32px
Between 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 only
980–999px · pillBadges, status pills, toggles

Buttons 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.

✕ Wrong
✓ Right

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).

✕ Wrong
✓ Right

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

Badges, status & toggles

Direct Booking Stats & Analytics Legacy site Pending Confirmed Cancelled

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

Off
On — accent blue
On, but means something negative — amber

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

Client authorized — add-ons are now live.
This account is pending authorization — every paid add-on stays suspended.
That username is already taken.

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.

You · Jul 7, 9:32 AM
Your room's ready for early check-in.
Guest · Jul 7, 9:40 AM
That's wonderful, thank you!

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.

PairingUsed forRatioAA
Aatext / bgBody text on the page background15.5:1Pass
Aatext / whiteBody text on cards16.9:1Pass
Aatext-secondary / bgLabels, captions4.65:1Pass
Aawhite / accentPrimary button text4.70:1Pass
Aasuccess / success-softActive/confirmed pills4.85:1Pass
Aawarning / warning-softPending/paused pills5.05:1Pass
Aadanger / danger-softCancelled/offboarded pills4.58:1Pass

Rules