Lion POS design system (vendor UI)
Executive summary
Section titled “Executive summary”Vendor-web and the POS workspace use the Lion POS light theme: porcelain page background, white elevated cards, charcoal default text, and burnt-orange → amber accents for CTAs only. Tokens live in shared CSS/Tailwind; this page is the reference for designers and frontend work.
Brand reference mock (marketing image)
Section titled “Brand reference mock (marketing image)”The heritage mock (shield + lion, serif wordmark, tablet in a restaurant) defines two layers of color. Vendor-web ships light mode by inverting surfaces while keeping the same accent gradient and charcoal hue family.
What the image establishes
Section titled “What the image establishes”| Layer in mock | Visual | Role |
|---|---|---|
| Accent (10%) | Metallic bronze → amber → gold gradient on logo, wordmark, and primary buttons (“Confirm Royal Order”) | Brand energy, CTAs, active states — not body copy |
| Deep neutral | Matte charcoal / slate panel (~#121212–#1F2937), not pure #000000 | Premium “than đen” — warm, soft black |
| Secondary copy | Light silver-grey on dark (~#9CA3AF–#D1D5DB) | Taglines, metadata |
| UI shell (tablet) | Dark mode: charcoal panels, white line items and totals, amber row highlight | Operational POS; every block sits on a defined surface (relevant for stats/KPI tiles) |
Gradient stops in the mock align with implementation:
- Bottom / shadow: copper–burnt orange (
~#8B4513→#C2410Clion-orange-burn) - Mid: amber (
**#D97706**lion-amber-gold) - Highlight: gold (
~#D4AF37–#FFD700; hero may use#F59E0Bat the top of.lion-auth-hero-panel)
Light mode mapping (vendor-web today)
Section titled “Light mode mapping (vendor-web today)”| Mock (dark) | Light theme (code) | Token |
|---|---|---|
| Charcoal background | Porcelain canvas | #F4F2EE bg-lion-bg-base |
| Dark UI cards | White / ivory elevated cards | #FFFFFF .lion-stat-card, .lion-surface-card |
| White UI text | Charcoal default text | #1C1917 text-lion-text-primary |
| Silver subtext | Slate secondary | #57534E text-lion-text-muted |
| Gradient buttons | Button variant="brand", .lion-btn-primary | #C2410C → #D97706 |
Than đen (charcoal text) in product UI = #1C1917 (Tailwind stone-900 family: RGB 28, 25, 23). It is the same “deep neutral” family as the mock’s charcoal background, applied as foreground on light surfaces instead of background on dark surfaces. Do not use pure #000000 (too flat on porcelain) or #B91C1C (text-lion-text-critical) for normal labels, prices, or stats — that reads as an error.
Contrast check on white (#FFFFFF): charcoal text ≈ 15.8:1 (WCAG AAA). On porcelain (#F4F2EE): still AAA for body and table data.
| Item | Value |
|---|---|
| Product name | Lion POS (LION_POS_BRAND_NAME) |
| Tagline | Smart management, seamless growth |
| System logo | @indochina/shared Logo — auth / marketing / cashier login only |
| In-app header (store context) | StoreBrandMark — selected store logo + store name, not Lion POS |
Color palette (60 / 30 / 10)
Section titled “Color palette (60 / 30 / 10)”| Role | Hex | Tailwind | Use |
|---|---|---|---|
| Canvas (60%) | #F4F2EE | bg-lion-bg-base | Page background |
| Surface | #FFFFFF | bg-lion-bg-elevated, .lion-surface-card, .lion-stat-card | Cards, stats KPI tiles, modals |
| Structure (30%) | #EDEAE4 / #D8D3C8 | bg-lion-bg-muted, border-lion-border | Sidebars, dividers |
| Text default | #1C1917 | text-lion-text-primary | Headings, table body, amounts, stats values |
| Text secondary | #57534E | text-lion-text-muted | Labels, subtitles, table secondary |
| Text tertiary | #78716C | text-lion-text-subtle | Hints, period lines under stats |
| Error / destructive | #B91C1C | text-lion-text-critical | Validation errors, negative stock, failed states — not normal data |
| Accent (10%) | #C2410C → #D97706 | bg-lion-brand-gradient, text-lion-accent-gold | Primary buttons, active tab, small icons — not default paragraph or money columns |
Constants (for docs/tests): packages/shared/src/lib/lion-pos-design-tokens.ts.
CSS variables under .lion-theme-light on <html>: packages/shared/src/styles/lion-pos-light-theme.css (imported from apps/vendor-web/src/index.css). Boot: main.tsx adds lion-theme-light.
Typography
Section titled “Typography”- Default: charcoal
text-lion-text-primary— all screens should read black/charcoal, not red or amber, for normal content. - Do not use
text-lion-text-criticalfor prices, customer names, order totals, or stats — it reads as an error state. - Money / tabular numbers:
text-lion-text-primary+tabular-nums; usefont-semiboldfor emphasis instead of accent color unless highlighting a true KPI in a chart. - Trend deltas on
DashboardStatCard: green/red only on the small change chip, not the main value.
Stats / KPI cards
Section titled “Stats / KPI cards”Shared components:
DashboardStatCard(packages/shared/src/components/dashboard/dashboard-stat-card.tsx) — orders/customers lists, POS orders overview.StatsCard(packages/shared/src/components/ui/stats-card.tsx) — legacy KPI layout.
Both use .lion-stat-card: white background, border, and layered elevation shadow (deeper on hover) on the porcelain page. Without this surface, stats blend into the canvas (poor contrast).
When ignorePeriodLabel is set (POS orders stats), the subline still uses muted/subtle text — not critical red.
Dashboard list tables (sticky header + scroll)
Section titled “Dashboard list tables (sticky header + scroll)”Main list screens (orders, products, customers, finance lists, inventory, admin catalog, etc.) use listLayout on shared DashboardDataTable:
stickyHeader: column headers stay visible while the tbody scrolls inside a bounded panel.- Scroll area:
DASHBOARD_LIST_TABLE_SCROLL_CLASS(max-h~ viewport minus stats/toolbar; horizontal overflow for wide grids). - Pagination: stays below the scroll wrapper (fixed footer bar), not inside the table
<tfoot>.
<DashboardDataTable listLayout columns={columns} rows={rows} pagination={{ ... }} />Finance report sections keep their own max-h (FINANCE_REPORT_STICKY_TABLE_BODY_SCROLL_CLASS) and may use stickyTableFooter for grand-total rows. Inline form line tables and tiny embeds should not use listLayout.
Optional column pinning (sticky: "left" | "right" on DataTableColumn) is per screen — e.g. vendor orders pin order id + date and actions.
Form controls (inputs, selects, textareas)
Section titled “Form controls (inputs, selects, textareas)”| Surface | Color | Token / class |
|---|---|---|
| Page canvas | Porcelain | #F4F2EE (--background, bg-lion-bg-base) |
| Field fill | White | #FFFFFF — formControlSurfaceClasses, dashboardFormFieldClasses.control |
Do not use bg-background on text fields: under .lion-theme-light, --background is porcelain, so inputs look beige/grey.
Use FormField / FormSelectField / FormRadioField from @indochina/shared with variant="dashboard" on dashboard modals and dense forms. Standalone primitives (Input, Textarea, SelectNative) already apply formControlSurfaceClasses (bg-white + primary focus ring).
Implementation checklist (frontend)
Section titled “Implementation checklist (frontend)”- Page shell:
bg-lion-bg-base, default texttext-lion-text-primary. - Panels/tables:
DashboardPanelon elevated/muted surfaces as today; stats row above tables usesDashboardStatCard. - List tables:
listLayoutonDashboardDataTableunless the table is a small inline/form embed. - Tables: primary cell text →
text-lion-text-primary; links may usehover:text-lion-accent-gold. - Errors:
text-lion-text-criticalortext-destructiveonly for failures. - CTAs:
Buttonvariant="default"(primary actions) orvariant="brand"on auth — both render the burnt-orange → amber gradient under.lion-theme-lightvia.lion-primary-cta/.lion-btn-primary. - Toggles: use
FormSwitch(orToggleField→ alias withlayout="inline"). Track uses.lion-switch-track— off:#EDEAE4, on: orange → amber gradient; knob white. - Form fields: white control background (
formControlSurfaceClasses); page stays porcelain — see Form controls above.
Auth screens (login, cashier login)
Section titled “Auth screens (login, cashier login)”Vendor split-auth layout uses AuthSplitShell (variant="vendor"):
| Panel | Treatment |
|---|---|
| Left hero | .lion-auth-hero-panel gradient; brand via AuthHeroBrand (white plate behind logo mark + white wordmark/tagline — avoids logo blending into orange and dark tagline on gradient) |
| Right form | bg-lion-bg-base, text-lion-text-primary / text-lion-text-muted, links text-lion-accent-gold, submit Button variant="brand" |
Do not use Logo with brightness-0 invert on the hero panel — use AuthHeroBrand instead.
Public marketing landing (/)
Section titled “Public marketing landing (/)”Vendor home uses SaasMarketingLanding with variant="lion" (apps/vendor-web/src/app/page.tsx). Admin marketing keeps the default blue SaaS variant.
| Area | Lion variant behavior |
|---|---|
| Canvas | bg-lion-bg-base, charcoal headings (text-lion-text-primary) |
| Header / footer | Shared Logo (wordmark + tagline in header) |
| CTAs | .lion-primary-cta gradient (Get started, pricing highlight, dashboard when signed in) |
| Stats KPI row | .lion-stat-card white tiles on porcelain |
| Copy | i18n marketingLanding.lion.* overrides (EN/VI); API heroTitle / heroSubtitle still win when set |
| PAX gallery | Stays dark (terminal screenshots); eyebrow uses amber accent instead of sky blue |
Theme classes: packages/shared/src/lib/marketing-landing-theme.ts. Copy helper: useMarketingLandingT("lion").
Related docs
Section titled “Related docs”- POS — workspace routes and checkout
- Frontend apps — monorepo web layout