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.
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).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--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.
| Tone | Means | Tasks | Checklists | Orgs and alerts |
|---|
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.
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.
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.
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
- 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
- Base text
- 16px
- Targets
- 44px minimum, 56px primary
- Navigation
- Bottom tabs, sticky action bar
- Offline
- Drafts saved on device, always visible
- Overlays
- Bottom sheets
- 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
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
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
Status pill: a record's state. Tag: quiet metadata (source, department). Chip with tone: a flag worth noticing. Count: how many.
Cards and numbers
- Department
- Engineering, Electrical
- Assigned to
- Rohit Kumar
- Source
- Feedback
- Open for
- 1 h 08 min
Table
| Checklist | Location | Status | Submitted | Items |
|---|
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
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
Delete 3 drafts?
They are only on this phone. Photos in them are deleted too.
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
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.
Patterns
The same page anatomy in Admin and Superadmin, the same list to detail flow everywhere, the same words.
Page anatomy (desk)
Grouped nav
Settings
Account
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.
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.
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".
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.
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.
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.
| Today | Use | Notes |
|---|---|---|
| Request, Snag, Task/Snag, Job, Work order | Task | "Snag Details" becomes the task drawer. Exports and emails follow. |
| Item (request item), Task Reported | Task type | The catalogue entry that sets department and escalation. |
| Desk, Dept | Department | Never abbreviate in UI. |
| Blueprint, Template | Checklist template | "Template" alone when the context is clear. |
| Instance, Checklist Log, Checklist Logs | Checklist (one), Checklist log (the list) | |
| Unsubmitted Checklists, My Saved Drafts | Drafts | Saved on this device until submitted. |
| FBS, Feedback System, Guest Feedback System | Feedback (product), Response (one entry) | FBS stays an internal code name. |
| Issue / Sub-Issue, Delighters | Issue, Compliment | Shown as a path: Washroom hygiene › Hand dryer. |
| Non Critical / Critical / Highly Critical | Non-critical, Critical, Highly critical | Values stay normal, urgent, extra urgent. Same labels in every app. |
| Organisation, Org, Hotel | Organization (org in tight spaces) | One spelling everywhere. |
| Super telemetry, Org explorer | Overview, Organizations | Page title always matches its nav label. |
| Format | Example |
|---|---|
| Date and time | 8 Oct 2026, 09:12 |
| Under 24 hours | 12 min ago · 2 h ago |
| Durations | 1 h 08 min |
| Money (India) | ₹1,20,000 · ₹4.2 L |
| Big counts | 1,284 · 12.4k in charts |
| IDs | #48213, tabular, muted |
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.
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
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.
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
| Step | What | Risk |
|---|---|---|
| 1 | Load 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. |
| 2 | One shared status module per app (labels, tones, icons from the platform enums) and the chip, tag, priority and rating components. | Low: replaces duplicated maps. |
| 3 | Page header, filter row, table, drawer, dialog, empty and skeleton states. | Medium: touches every list page. |
| 4 | Navigation config and the Settings area (routes stay; only menus and breadcrumbs change). | Medium: links and role gates. |
| 5 | Screen 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
| File | Purpose |
|---|---|
assets/tokens.css | All tokens, light and dark. The only file apps must import. |
assets/vesta.css | Reference components. Read it to rebuild a component in MUI or Onsen. |
assets/icons.js | Tabler sprite for these pages (generated by tools/build-icons.mjs). |
assets/shell.js | Demo shell: the v4 navigation config and status maps used by the screens. |
assets/charts.js | Reference 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. |