Skip to content

Lion POS design system (vendor UI)

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.


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.

Layer in mockVisualRole
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 neutralMatte charcoal / slate panel (~#121212#1F2937), not pure #000000Premium “than đen” — warm, soft black
Secondary copyLight silver-grey on dark (~#9CA3AF#D1D5DB)Taglines, metadata
UI shell (tablet)Dark mode: charcoal panels, white line items and totals, amber row highlightOperational 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#C2410C lion-orange-burn)
  • Mid: amber (**#D97706** lion-amber-gold)
  • Highlight: gold (~#D4AF37#FFD700; hero may use #F59E0B at the top of .lion-auth-hero-panel)
Mock (dark)Light theme (code)Token
Charcoal backgroundPorcelain canvas#F4F2EE bg-lion-bg-base
Dark UI cardsWhite / ivory elevated cards#FFFFFF .lion-stat-card, .lion-surface-card
White UI textCharcoal default text#1C1917 text-lion-text-primary
Silver subtextSlate secondary#57534E text-lion-text-muted
Gradient buttonsButton 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.


ItemValue
Product nameLion POS (LION_POS_BRAND_NAME)
TaglineSmart management, seamless growth
System logo@indochina/shared Logoauth / marketing / cashier login only
In-app header (store context)StoreBrandMark — selected store logo + store name, not Lion POS

RoleHexTailwindUse
Canvas (60%)#F4F2EEbg-lion-bg-basePage background
Surface#FFFFFFbg-lion-bg-elevated, .lion-surface-card, .lion-stat-cardCards, stats KPI tiles, modals
Structure (30%)#EDEAE4 / #D8D3C8bg-lion-bg-muted, border-lion-borderSidebars, dividers
Text default#1C1917text-lion-text-primaryHeadings, table body, amounts, stats values
Text secondary#57534Etext-lion-text-mutedLabels, subtitles, table secondary
Text tertiary#78716Ctext-lion-text-subtleHints, period lines under stats
Error / destructive#B91C1Ctext-lion-text-criticalValidation errors, negative stock, failed states — not normal data
Accent (10%)#C2410C#D97706bg-lion-brand-gradient, text-lion-accent-goldPrimary 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.


  • 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-critical for prices, customer names, order totals, or stats — it reads as an error state.
  • Money / tabular numbers: text-lion-text-primary + tabular-nums; use font-semibold for 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.

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)”
SurfaceColorToken / class
Page canvasPorcelain#F4F2EE (--background, bg-lion-bg-base)
Field fillWhite#FFFFFFformControlSurfaceClasses, 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).


  1. Page shell: bg-lion-bg-base, default text text-lion-text-primary.
  2. Panels/tables: DashboardPanel on elevated/muted surfaces as today; stats row above tables uses DashboardStatCard.
  3. List tables: listLayout on DashboardDataTable unless the table is a small inline/form embed.
  4. Tables: primary cell text → text-lion-text-primary; links may use hover:text-lion-accent-gold.
  5. Errors: text-lion-text-critical or text-destructive only for failures.
  6. CTAs: Button variant="default" (primary actions) or variant="brand" on auth — both render the burnt-orange → amber gradient under .lion-theme-light via .lion-primary-cta / .lion-btn-primary.
  7. Toggles: use FormSwitch (or ToggleField → alias with layout="inline"). Track uses .lion-switch-track — off: #EDEAE4, on: orange → amber gradient; knob white.
  8. Form fields: white control background (formControlSurfaceClasses); page stays porcelain — see Form controls above.

Vendor split-auth layout uses AuthSplitShell (variant="vendor"):

PanelTreatment
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 formbg-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.


Vendor home uses SaasMarketingLanding with variant="lion" (apps/vendor-web/src/app/page.tsx). Admin marketing keeps the default blue SaaS variant.

AreaLion variant behavior
Canvasbg-lion-bg-base, charcoal headings (text-lion-text-primary)
Header / footerShared 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
Copyi18n marketingLanding.lion.* overrides (EN/VI); API heroTitle / heroSubtitle still win when set
PAX galleryStays 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").