VESTA Design System v4
Vesta Design System · v4 · October 2026

One system for every Vesta screen, from the ops desk to the washroom kiosk.

Built from what the four apps actually ship today: the task log, checklist rounds, guest feedback kiosks and the founders' console. Same tokens, same components, same words, same navigation model. It changes how things look, read and are found. It does not change what they do.

Vesta
One brand colourVesta flame replaces Admin blue, navy and purple gradients, and seven Checklist blues. Kiosks keep an org brand slot.
One typefacePublic Sans everywhere (Admin already loads it). Karla, Roboto fallbacks and Open Sans go.
One status languagePending was four different yellows. Every state now has one tone, one label and one icon.
One way to find thingsGrouped navigation, one Settings area, the same page anatomy in every app.
Rules a screen passes or fails

Principles

Six checks for every screen. If a design breaks one, change the design or change this page first.

Same thing, same name, same look

A task is a task in the nav, the page title, the dialog and the export. Pending is the same amber chip on the dashboard, the log and the PDF.

Check: search the screen for synonyms (Request, Snag, Job) and second colours for one state.

Exceptions stand out, normal stays quiet

Only raised priority, escalation, stale and missed work get colour. A normal task has no priority badge. A healthy org has no banner.

Check: squint. Only the things that need someone should glow.

One primary action per view

The flame fill marks the single most likely next step: Close task, Submit, Create. Everything else is secondary, ghost or in a menu.

Check: count flame buttons in the viewport. More than one is a bug.

Summary before detail

Numbers, then trends, then lists, then records. Tiles filter the list below them. Details open beside the list, so context is never lost.

Check: the top 300px answer "is anything wrong right now?"

Colour never works alone

Every state carries a label or icon, every chart series a legend or direct label, every rating face a word under it.

Check: view in greyscale. Nothing becomes ambiguous.

Built for where it is used

The desk is dense and keyboard friendly. The floor is one-handed with 44px targets and poor signal. The kiosk is read from a metre away by a stranger in a hurry.

Check: the screen uses its context's type size and target size (see Three contexts).
tokens.css

Foundations

Every value below is a CSS custom property in assets/tokens.css, defined for light and dark. Apps map these into MUI, Tailwind or Onsen; components never use raw hex.

Colour

The flame is the brand: the logo, focus rings, the one primary action and the active nav item. White text on the pure flame (500) is only 3.4:1, so filled buttons use 600 (4.6:1, AA) and text links use 700. Neutrals are a warm stone ramp, the same family Superadmin already ships.

Open the full colour palette, light and dark side by side
Kiosks are white-labelled
A guest kiosk may set --kiosk-brand, --kiosk-bg and its logo for the org (Adani violet, BIAL teal, Encalm navy and gold). The brand carries white text, so it must reach 4.5:1 on white; BIAL's logo teal does not, so its brand is a deeper teal. Nothing else changes per org. Vesta appears as "Powered by Vesta". The kiosk is always light (.v-light).

Status language

Seven tones with fixed meanings, mapped straight from the platform's enums. A record's state is a soft pill with a dot (or an icon on mobile). Priority uses a different shape, a flag with text, so the two never blur.

ToneMeansTasksChecklistsOrgs and alerts
Priority, escalation and freshness
Critical Highly critical Non-critical shows nothing.
L2 · next in 18 min No update in 47 min Just now Escalation is red, stale (30 min untouched) is amber, a realtime update gets a green edge for 60 s.

Charts and ratings

Categorical series take slots in this fixed order, never cycled; a ninth series folds into Other. The order was validated for colour-blind separation in both themes. Status charts use the status tones instead. Guest ratings use a diverging scale with a neutral middle.

Categorical order
Guest rating, 1 to 5
12345

1 to 2 negative, 3 neutral, 4 to 5 positive, as the platform's analytics count them. Faces on the kiosk use the same five colours with a word under each.

Trend, two series
CreatedClosed
Stacked by status
ClosedPending
Part of whole
Closed 1,021Pending 142In progress 61Parked 38Rejected 22

Type

Public Sans for everything, with tabular figures wherever numbers line up. The scale has nine steps; each context uses a different base (14 desk, 16 floor, 18 to 48 kiosk). Labels are sentence case. Uppercase is only for small eyebrows with a little letter spacing.

Space, shape, depth

A 4px grid. Two radii do most of the work: 8 for controls, 12 for cards. Borders separate; shadows only lift what floats (menus, drawers, dialogs). The v1 2px corners and Superadmin's 16px cards meet in the middle.

Spacing (px)
Radius
Elevation

Icons

Tabler, outline, 1.75 stroke, on a 24 grid; 18px in UI, 16 dense, 20 to 24 on mobile, 56 on kiosk tiles. Superadmin already uses Tabler. Admin and Checklists already ship Iconify, which serves the same set as tabler:name, so no new dependency. Feedback category icons stay data-driven from the template.

Three contexts

DeskAdmin, Superadmin. Mouse and keyboard, long sessions, many records.
Base text
14px
Controls
36px (32 compact)
Table row
44px, two-line 56px
Layout
Sidebar 248px, content max 1440px
Overlays
Drawer for records, dialog for create and confirm
FloorChecklists app. One hand, gloves, glare, weak signal.
Base text
16px
Targets
44px minimum, 56px primary
Navigation
Bottom tabs, sticky action bar
Offline
Drafts saved on device, always visible
Overlays
Bottom sheets
KioskGuest feedback. A stranger, a metre away, ten seconds.
Question
48px bold, one per screen
Targets
96px minimum, faces 180px
Theme
Always light, org brand slot
Reset
Idle 30 s, thank-you 3 s, visible
Staff
Labelled Staff button, no hidden taps
vesta.css

Components

Reference implementations, live on this page. Class names carry a v- prefix so they never collide with Onsen, MUI or Tailwind. Each app rebuilds these in its own stack with the same tokens and states.

Buttons

Labels are verbs in sentence case ("Close task", not "CLOSE"). Danger is an outline in place; the solid red button exists only inside a confirmation.

Fields

Sets the department and escalation.
Enter minutes as a number, for example 30.

Labels sit above fields. Required gets a red asterisk; optional says "Optional". Errors appear under the field, say what to do, and never use a browser alert.

Choosing

Segmented control for ranges and views (one pattern, replacing four). Tabs for saved views with counts. Option buttons for answers on touch. Filter chips: dashed when empty, tinted when set.

Chips, tags, counts

Feedback Checklist Housekeeping Guest complaint 423 RKARMP

Status pill: a record's state. Tag: quiet metadata (source, department). Chip with tone: a flag worth noticing. Count: how many.

Cards and numbers

Most reported tasks
Last 7 days
Open log
Task #48213
Department
Engineering, Electrical
Assigned to
Rohit Kumar
Source
Feedback
Open for
1 h 08 min

Table

ChecklistLocationStatusSubmittedItems
Showing 1 to 3 of 2,140

One table pattern replaces MUI DataGrid, the custom request table, raw Tailwind tables and card grids: sticky header, 44px rows, primary column with a meta line, numbers right aligned, the whole row opens the record, pagination at the bottom.

Feedback and states

Escalated to level 2
Next level in 18 min if the task stays open.
Checklist has 2 unanswered items
Answer them in Washroom hygiene and Fixtures, then submit.
Synced
5 feedback responses sent.
Task #48213 closed
No tasks match these filtersTry a wider date range or clear the status filter.

Toast for success after an action. Inline alert for something that needs reading. Empty states say why and offer the way out. Loading uses skeletons in place of the content, never a card that says "Loading data".

Overlays

Dialog: create and confirm

Delete 3 drafts?

They are only on this phone. Photos in them are deleted too.

Menu
Task #48213

Records open in a right drawer beside the list (see Task log, detail). Create and confirm use a centered dialog (New task). Mobile uses bottom sheets. Escape closes all of them.

Mobile and kiosk

Washroom hourly check
T3 · Gate 14 washroom (Men) · 6 of 14
Floor dry and clean?
How was this washroom?
T3 · Gate 14 washroom
Powered by Vesta

Floor: app bar, progress, big answer buttons, evidence slot, sticky Save and Submit, four bottom tabs. Kiosk: one question, five labelled faces, org brand slot. Full flows in Screens.

How pieces combine

Patterns

The same page anatomy in Admin and Superadmin, the same list to detail flow everywhere, the same words.

Page anatomy (desk)

SidebarOrg switcher
Grouped nav
Settings
Account
BreadcrumbJump to · Live · Theme · New task
1 Page title + one-line description, actions on the right
2 Views as tabs, with counts
3 Filter row: search, filter chips, More filters, refresh
4 Content: tiles (optional), then table or cards. Rows open a drawer.
List to detail

Click a row: a right drawer opens with facts, activity and the actions for that state. The list stays put, so triage is click, act, next. On mobile the record is a full screen with a back button.

Filters

Show the five filters people use most as chips. Put the rest in "More filters", grouped by When, Who, What. Show what is applied and "Clear all". Search runs on Enter and as you type.

Forms

One column, in the order people think: what, where, how urgent, details, evidence. Defaults filled in. Validate on submit and on blur, never with alerts. The primary button names the result: "Create task".

Destructive actions

Outline danger in place, solid red only in the confirm dialog, which names the thing and the consequence. Prefer undo in a toast when the action can be undone.

Loading, empty, error

Skeletons where content will be. Empty states explain and offer the next step. Errors say what failed and offer Retry. No "WIP" placeholders above working tools.

Realtime and offline

A Live dot in the top bar when updates stream. New rows get a green edge, not a toast for every event. Offline work shows a count waiting to sync, in words.

Words and formats

One name per thing, from the user's side of the screen. The left column is what the apps say today.

TodayUseNotes
Request, Snag, Task/Snag, Job, Work orderTask"Snag Details" becomes the task drawer. Exports and emails follow.
Item (request item), Task ReportedTask typeThe catalogue entry that sets department and escalation.
Desk, DeptDepartmentNever abbreviate in UI.
Blueprint, TemplateChecklist template"Template" alone when the context is clear.
Instance, Checklist Log, Checklist LogsChecklist (one), Checklist log (the list)
Unsubmitted Checklists, My Saved DraftsDraftsSaved on this device until submitted.
FBS, Feedback System, Guest Feedback SystemFeedback (product), Response (one entry)FBS stays an internal code name.
Issue / Sub-Issue, DelightersIssue, ComplimentShown as a path: Washroom hygiene › Hand dryer.
Non Critical / Critical / Highly CriticalNon-critical, Critical, Highly criticalValues stay normal, urgent, extra urgent. Same labels in every app.
Organisation, Org, HotelOrganization (org in tight spaces)One spelling everywhere.
Super telemetry, Org explorerOverview, OrganizationsPage title always matches its nav label.
FormatExample
Date and time8 Oct 2026, 09:12
Under 24 hours12 min ago · 2 h ago
Durations1 h 08 min
Money (India)₹1,20,000 · ₹4.2 L
Big counts1,284 · 12.4k in charts
IDs#48213, tabular, muted
Voice

Plain, specific, calm. Say what happens: "Close task", then "Task #48213 closed". Errors explain and fix: "Upload timed out. Your answers are saved as a draft; submit again when you have signal." No "Info:" prefixes, no developer terms (indexeddb, role >= 10), no emoji in buttons. Times show in the org's timezone.

Information architecture

Navigation

Every page that exists today keeps a home. Daily work sits in the sidebar; setup moves into one Settings area. Gates (Admin, Mgr, Org, Super) are unchanged from today's role and org checks.

Admin

Today 14 top-level groups

  • Task Management
    • Dashboard · Task/Snag Log · Analytics · Configuration
  • Airside Operations
    • Document Hub · Follow-Me Vehicles · Apron Control · AM · Configuration
  • Vendor SLA
    • Dashboard · Deductions · Reports · Help · Settings
  • EM Shift Brief
    • Entries · Reports · Settings
  • GIS Map
    • Map View · List View · Categories
  • Feedback
    • Dashboard · Analytics · Feedback · Heatmap · Staff Feedback · Settings (Templates, Locations, Location <> Staff Map, User Template Mapping, Heatmap Config, Settings (Super-admin))
  • Staff Deployment (twice, per role)
    • Deployments · History · Vendors · Shift in Charge · Templates
  • Checklist
    • Dashboard · Checklist Logs · Configuration (hub of 6 cards)
  • Reports · Shifts · Notifications Center · QR Code Generator · Scheduled Reports
  • Admin
    • Manage Org · Manage Departments · Data Manager · Org Config · Bulk Importer · Bulk Exporter · Mobile App Version · Manage Users (Users, User - Org. Mapping, Roles, User - Role Mapping, User - Dept Mapping, My Settings) · Users Activity
  • Floating settings button (theme, layout, direction)

v4 3 sections + Settings

  • Top of sidebar
  • Org switcher with org logo (was below the account, with a blue glow)
  • Operate
  • Tasks
    • Overview · Task log · Analytics
  • Checklists
    • Overview · Checklist log
  • Feedback
    • Overview · Responses · AnalyticsMgr · HeatmapMgr · Staff ratingsMgr
  • Staff deployment
    • Deployment sheets · History
  • Airport modules Org
  • Airside operations · Vendor SLA · EM shift brief · GIS map
    • Daily pages only; their settings move to Settings
  • Insights
  • Reports
    • Downloads · Scheduled reportsSuper
  • Bottom of sidebar
  • SettingsAdmin
    • Organization · People · Tasks · Checklists · Feedback · Staff deployment · Notifications · Airport modules · Tools
  • Help and support · Account (profile, appearance, sign out)
  • Top bar
  • Breadcrumb · Jump to page or task ID · Live · New task

Checklists app

Today Hub and spoke

  • Dashboard: ticking clock, cog menu (Settings, Reset storage, Logout), org row, QR card, Quick Access list (My Checklists, Upcoming, Completed, Unsubmitted)
  • Every inner page: "Back" always returns to the dashboard
  • Settings: timezone only, old visual style

v4 Four tabs

  • Home: Scan checklist QR (thumb zone), Up next today, org scope chip
  • Checklists: start by location, search
  • Drafts: saved on this device, count badge on the tab
  • History: submitted checklists, status, PDF
  • Avatar opens Account: timezone, reset storage, sign out

Guest feedback kiosk

Today Hidden wizard

  • Steps change in place with no progress shown; 36px back icon
  • Logo tap reloads the page; on one org the brand text opens logout
  • Staff, language and status scattered to corners at 50% opacity

v4 Visible steps

  • Rate → What to improve → Details → Thank you, with step dots and a labelled Back
  • Footer holds location, staff on duty, connection in words, and a labelled Staff button
  • Same steps on the guest's phone via QR

Superadmin

Today 13 flat items

  • Home (card grid) · OpEx · Telemetry · Org explorer · Action center · Config explorer · FBS audits · Compare · Billing · Knowledge base · Design · Superadmins · Cold Archive

v4 5 groups

  • Monitor
  • Overview (landing) · Action center
  • Customers
  • Organizations · Compare · Billing
  • Audits
  • Config explorer · Feedback audits
  • Company
  • Operating costs · Knowledge base
  • Bottom
  • Superadmins · Cold archive · Brand and design
Review set

Screens

The main flows of each app, rebuilt with this system using realistic sample data. Each screen has a "Design notes" button listing what changed and why. Nothing here adds functionality.

Adopting v4

Port notes

Each app keeps its stack. Map the tokens once, rebuild a handful of shared pieces, then move screen by screen. No behaviour changes at any step.

Order of work

StepWhatRisk
1Load tokens.css and Public Sans; map tokens into each app's theme (snippets below). Swap icons to tabler: names.Low: colours, type and radius only.
2One shared status module per app (labels, tones, icons from the platform enums) and the chip, tag, priority and rating components.Low: replaces duplicated maps.
3Page header, filter row, table, drawer, dialog, empty and skeleton states.Medium: touches every list page.
4Navigation config and the Settings area (routes stay; only menus and breadcrumbs change).Medium: links and role gates.
5Screen by screen, following the review set.Per screen.

Admin (MUI v5)

Replace the Minimal template's blue preset and the green base palette in theme/palette.ts; drop gradients.button and the hard-coded navy and purple. Keep MUI; only the theme and a few overrides change.

// theme/palette.ts (excerpt): values from tokens.css
const PRIMARY = { lighter: '#ffe6d8', light: '#ff7340', main: '#d93a00', dark: '#b33000', darker: '#8a2500', contrastText: '#fff' };
const GREY = { 100: '#f3f1ec', 200: '#ebe8e1', 300: '#e0dcd2', 400: '#c9c3b6', 500: '#8a8579', 600: '#6b665c', 700: '#57534a', 800: '#35322c', 900: '#1c1a17' };
// semantic: success #1f9d55, warning #e09a00, error #d92d20, info #2a78d6 (text: the *-fg tokens)
background: { default: '#faf9f7', paper: '#ffffff', neutral: '#f3f1ec' },
text: { primary: '#1c1a17', secondary: '#57534a', disabled: '#8a8579' },
divider: '#e4e0d7'

// theme/index.tsx
shape: { borderRadius: 8 },
typography: { fontFamily: '"Public Sans", "Segoe UI", system-ui, sans-serif', button: { textTransform: 'none', fontWeight: 600 } },
components: {
  MuiButton: { defaultProps: { disableElevation: true }, styleOverrides: { root: { height: 36, borderRadius: 8 } } },
  MuiCard:   { styleOverrides: { root: { borderRadius: 12, border: '1px solid #e4e0d7', boxShadow: '0 1px 2px rgba(28,26,23,.05)' } } },
  MuiTableCell: { styleOverrides: { root: { height: 44, fontSize: 13 } } }
}
// config.ts: themeColorPresets 'blue' is no longer used; remove the preset switch.

Tailwind sub-apps in Admin (Airside, Vendor SLA, EM shift brief) are on Tailwind 3: extend tailwind.config.js colours with the same values and replace purple-600, indigo-600, teal-600 accents with primary. Iconify stays; use icon="tabler:clipboard-list".

Superadmin (Tailwind v4)

Already closest to v4. Import tokens, point the theme at them, and switch dark mode to the data-theme attribute so the login page follows it too.

/* src/index.css */
@import "tailwindcss";
@import "./tokens.css";
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
@theme inline {
  --color-primary: var(--primary);     --color-primary-hover: var(--primary-hover);
  --color-accent: var(--accent);       --color-surface: var(--surface);
  --color-border: var(--border);       --color-fg: var(--text);
  --color-fg-2: var(--text-2);         --color-fg-3: var(--text-3);
  --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px;
  --font-sans: var(--font-sans);
}
/* primary button: bg-primary hover:bg-primary-hover (600/700, white text passes AA) */

Checklists and Feedback (Onsen + MUI, React 17)

Add a single MUI ThemeProvider (today MUI runs on defaults, which is where the extra blues come from) with the same palette as Admin. Override Onsen through its CSS variables and stop forcing Karla. Replace cl-styles.js constants with token references.

/* src/theme.css, imported after onsen-css-components.css */
@import "./tokens.css";
:root {
  --background-color: var(--bg);              --text-color: var(--text);
  --button-background-color: var(--primary);   --button-cta-background-color: var(--primary);
  --toolbar-background-color: var(--surface);  --toolbar-border-color: var(--border);
  --list-background-color: var(--surface);     --card-background-color: var(--surface);
}
html, body, .page { font-family: var(--font-sans); }   /* drop the Karla !important block in public/index.html */
/* Onsen variable names: confirm against node_modules/onsenui/css/onsen-css-components.css */

/* Kiosk org themes shrink to two variables each (FbsOrgCustomThemes.css); root gets class v-light */
.theme-custom-bial   { --kiosk-brand: #05807a; --kiosk-bg: #fdfdfd; }  /* logo teal #0bb3ab is 2.6:1 with white text */
.theme-custom-violet { --kiosk-brand: #8d278f; --kiosk-bg: #fff5fe; }
.theme-custom-encalm-light { --kiosk-brand: #152347; --kiosk-bg: #e7e2d9; }

Status module (all apps)

// status.ts: one source for labels, tones and icons (values from AppConstants.php)
export const TASK_STATUS = {
  0:  { label: 'Pending',     tone: 'amber',  icon: 'tabler:clock' },
  2:  { label: 'In progress', tone: 'blue',   icon: 'tabler:progress' },
  5:  { label: 'Completed',   tone: 'blue',   icon: 'tabler:checks' },
  3:  { label: 'Parked',      tone: 'slate',  icon: 'tabler:player-pause' },
  6:  { label: 'Resolved',    tone: 'violet', icon: 'tabler:circle-check' },
  1:  { label: 'Closed',      tone: 'green',  icon: 'tabler:circle-check' },
  [-1]: { label: 'Rejected',  tone: 'red',    icon: 'tabler:circle-x' },
};
export const CHECKLIST_STATUS = {
  0: { label: 'Pending', tone: 'amber' }, 2: { label: 'Jobs in progress', tone: 'blue' },
  3: { label: 'Awaiting approval', tone: 'violet' }, 1: { label: 'Completed', tone: 'green' },
  [-1]: { label: 'Missed', tone: 'red' },
};
// tone -> CSS vars: --{tone}-fg (text), --{tone}-bg (fill), --{tone}-line (border), --{tone} (dot)

Files in this folder

FilePurpose
assets/tokens.cssAll tokens, light and dark. The only file apps must import.
assets/vesta.cssReference components. Read it to rebuild a component in MUI or Onsen.
assets/icons.jsTabler sprite for these pages (generated by tools/build-icons.mjs).
assets/shell.jsDemo shell: the v4 navigation config and status maps used by the screens.
assets/charts.jsReference chart marks (line, bars, donut) with the spec applied.
screens/The review set, one folder per app.
tools/Contrast check (npm test), icon builder, gallery thumbnails.