Kisum Design System
Related documentation: Frontend Implementation · Frontend Applications · Frontend Nextkt (palette exception)
Published source of truth. This page is the full, committed design system for the Kisum platform (also live at docs.kisum.dev). Local workspace DESIGN.md (if present) is optional tooling sidecar for Impeccable/design panels — do not treat it as the GitHub canonical copy.
Cursor enforcement: .cursor/rules/00-design.mdc — applies when editing Frontend-Kisum*/**/*.{tsx,css} or System-Kisum-Checkout/**/*.{tsx,css}.
Design System: Kisum
Section titled “Design System: Kisum”Design tokens (YAML)
Section titled “Design tokens (YAML)”Machine-readable token block for design tools, Tailwind mapping, and Impeccable sidecars:
name: Kisumdescription: Platform-wide analyst-confident design system for all Kisum product surfaces (Admin, Promoters, Artists, Venues, Finance, Checkout, Website)colors: kisum-primary: "#8466AC" kisum-accent: "#9A7AFF" kisum-tint: "#F3EEF8" sidebar-bg: "lab(98.26% 0 0)" main-bg: "#FFFFFF" ink: "#18181B" surface: "#FFFFFF" shell: "#F4F4F5" body-bg: "#F9FAFB" border-subtle: "#F3F4F6" muted-text: "#71717A" danger: "#D34848" md-surface: '#faf8fd' md-surface-dim: '#dbd9de' md-surface-bright: '#faf8fd' md-surface-container-lowest: '#ffffff' md-surface-container-low: '#f5f3f7' md-surface-container: '#efedf2' md-surface-container-high: '#e9e7ec' md-surface-container-highest: '#e3e2e6' md-on-surface: '#1b1b1f' md-on-surface-variant: '#4a454f' md-inverse-surface: '#303034' md-inverse-on-surface: '#f2f0f4' md-outline: '#7b7580' md-outline-variant: '#ccc3d0' md-surface-tint: '#6e5095' md-primary: '#6a4d91' md-on-primary: '#ffffff' md-primary-container: '#8466ac' md-on-primary-container: '#fffafa' md-inverse-primary: '#d8b9ff' md-secondary: '#5e5e63' md-on-secondary: '#ffffff' md-secondary-container: '#e4e1e8' md-on-secondary-container: '#65646a' md-tertiary: '#5f5f00' md-on-tertiary: '#ffffff' md-tertiary-container: '#78781d' md-on-tertiary-container: '#fffaff' md-error: '#ba1a1a' md-on-error: '#ffffff' md-error-container: '#ffdad6' md-on-error-container: '#93000a' md-primary-fixed: '#eddcff' md-primary-fixed-dim: '#d8b9ff' md-on-primary-fixed: '#28074d' md-on-primary-fixed-variant: '#55397b' md-secondary-fixed: '#e4e1e8' md-secondary-fixed-dim: '#c8c5cc' md-on-secondary-fixed: '#1b1b20' md-on-secondary-fixed-variant: '#47464c' md-tertiary-fixed: '#e8e881' md-tertiary-fixed-dim: '#cccb68' md-on-tertiary-fixed: '#1d1d00' md-on-tertiary-fixed-variant: '#494900' md-background: '#faf8fd' md-on-background: '#1b1b1f' md-surface-variant: '#e3e2e6'typography: display: fontFamily: "var(--font-manrope), ui-sans-serif, system-ui, sans-serif" fontSize: "1.125rem" fontWeight: 600 lineHeight: 1.25 letterSpacing: "-0.025em" body: fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif" fontSize: "0.875rem" fontWeight: 400 lineHeight: 1.5 letterSpacing: "normal" label: fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif" fontSize: "0.6875rem" fontWeight: 600 lineHeight: 1.2 letterSpacing: "normal"rounded: sm: 0.25rem DEFAULT: 0.5rem md: 0.75rem lg: 1rem xl: 1.5rem full: 9999pxspacing: xs: "4px" sm: "8px" md: "16px" lg: "24px" xl: "32px" container-max: 1280px gutter: 1.5rem margin-mobile: 1rem margin-desktop: 2.5rem stack-sm: 0.5rem stack-md: 1rem stack-lg: 2remcomponents: button-kisum: backgroundColor: "{colors.kisum-primary}" textColor: "#FFFFFF" rounded: "{rounded.full}" padding: "8px 16px" button-kisum-hover: backgroundColor: "#74579A" textColor: "#FFFFFF" rounded: "{rounded.full}" padding: "8px 16px" button-outline: backgroundColor: "{colors.surface}" textColor: "{colors.kisum-primary}" rounded: "{rounded.full}" padding: "8px 16px" card-surface: backgroundColor: "{colors.surface}" textColor: "{colors.ink}" rounded: "{rounded.xl}" padding: "24px" overview-badge: backgroundColor: "{colors.kisum-tint}" textColor: "{colors.kisum-primary}" rounded: "{rounded.full}" padding: "2px 8px"backgrounds: bg-sidebar: css: "background-color: var(--sidebar)" token: "{colors.sidebar-bg}" bg-main: css: "background-color: var(--main)" token: "{colors.main-bg}"css-vars: sidebar: "{colors.sidebar-bg}" main: "{colors.main-bg}"0. Scope
Section titled “0. Scope”This is the single Kisum platform design system. Every Kisum product UI must follow it:
Frontend-Kisum-Promoters,Frontend-Kisum-Artists,Frontend-Kisum-VenuesFrontend-Kisum-Admin,Frontend-Kisum-Finance,Frontend-Kisum,Frontend-Kisum-WebsiteSystem-Kisum-Checkoutand other Kisum-branded surfaces in this workspace
Do not fork aesthetics per app, module, or persona. Reuse the same tokens, typography, elevation rules, and component patterns.
Exception: standalone Nextkt ticketing (consumer storefront + operator admin) may retain its logo-derived teal/navy palette until an explicit migration — it is not a Kisum Core module app. See Frontend Nextkt.
Promoters product voice (persona, anti-references): Frontend-Kisum-Promoters/PRODUCT.md — complements but does not override platform tokens.
1. Overview
Section titled “1. Overview”Creative North Star: “The Booking Brief”
Kisum reads like a corporate-modern research desk with an editorial twist, built for live-music industry decisions. Surfaces are calm, information-dense, and typographically precise: operators scan signals, compare metrics, and move to action without wading through decorative UI. The system serves workflow, not spectacle.
This is explicitly not generic SaaS cream UI: no warm-tinted near-white wallpaper, no endless gray card stacks, no purple CTAs on every row. Kisum purple is an accent for emphasis and brand continuity, not the dominant surface treatment. Detail tabs, list views, admin tables, and workflow surfaces across all Kisum apps share one visual language so users do not relearn layout per module.
Key Characteristics:
- Analyst-grade density with scannable hierarchy (section headers, rank numerals, metric blocks)
- Refined management-dashboard polish: generous whitespace, disciplined grids, and listings treated as decision-quality content cards
- Flat-by-default surfaces; depth from borders, tint, and spacing rather than heavy shadow stacks
- Inter for UI body copy; Manrope for semibold section titles and display-weight emphasis
- Kisum purple (
#8466AC) reserved for ranks, badges, primary actions, and focus moments - ShadCN/Radix primitives with Tailwind 4 tokens; extend patterns, do not fork aesthetics per screen
2. Colors
Section titled “2. Colors”A restrained product palette: cool neutrals carry the shell, white cards hold content, Kisum purple marks decision points.
Primary
Section titled “Primary”- Kisum Purple (
#8466AC/ kisum-600): Primary brand accent. Section count badges, rank numerals,kisumbutton fills, top-loader accent (#9A7AFF). Use for emphasis, not backgrounds. - Kisum Accent (
#9A7AFF/ kisum-500): Lighter highlight for theme-color, hover states, and chart emphasis. Pair with purple-600 for legibility on white. - Kisum Tint (
#F3EEF8): Soft purple wash for pills and chips (e.g. Overview section count badges). Never as full-page background. - Material Accent Tokens (
md-*in token YAML above): Imported low-level tonal tokens kept for compatibility/reference only. Do not let them override the canonical Kisum semantic tokens (kisum-primary,surface,body-bg,ink).
Neutral
Section titled “Neutral”- Ink (
#18181B/ zinc-950): Primary text on light surfaces. Body and titles on cards. - Muted Ink (
#71717A/ zinc-500): Secondary labels, subtitles, metadata. Must still meet 4.5:1 on white; bump toward ink if contrast fails on tinted surfaces. - Surface (
#FFFFFF): Card and row backgrounds on detail sections, list rows, and admin panels. - Shell (
#F4F4F5/ zinc-100): App chrome atlg+(lg:bg-zinc-100on<html>). - Body Background (
#F9FAFB/ gray-50): Page canvas inside the shell — see §6 App shell (mandatory); never replace with white or cream. - Sidebar Background (
lab(98.26% 0 0)/--sidebar): Cool near-white sidebar panel viabg-sidebar(tokensidebar-bg). - Main Background (
#FFFFFF/--main): White main content panel viabg-mainorbg-backgroundonMainColumn/<main>(tokenmain-bg). - Border Subtle (
#F3F4F6/ gray-100): Row and tile borders at rest (border-gray-100). - Neutral Slate (
#5E5E62): Legacy management-surface text reference; preferInkfor primary text and use this only where existing components already depend on it.
Tertiary
Section titled “Tertiary”- Danger (
#D34848): Destructive actions and error emphasis. Hover#B53030.
Named Rules
Section titled “Named Rules”The Purple Budget Rule. Kisum purple appears on ≤10% of any given screen. If purple is everywhere, hierarchy collapses and the UI reads as generic SaaS.
The No-Cream Rule. Do not tint the page background warm (cream, sand, parchment). Neutrals stay cool (zinc/gray). Warmth comes from artist imagery and data, not body bg.
3. Typography
Section titled “3. Typography”Display Font: Manrope (local, --font-manrope) with ui-sans-serif fallback
Body Font: Inter (Google, display: swap) with system-ui fallback
Label Font: Inter (same stack as body)
Character: Inter keeps dense tables and metadata readable; Manrope adds confident weight to section titles without switching to a display serif. Pairing is utilitarian-analyst, not editorial-magazine.
Hierarchy
Section titled “Hierarchy”- Display (600,
text-lg/ 1.125rem, tight tracking): Overview section headers (OverviewSectionHeader), tab-level titles.text-wrap: balanceon multi-line headings. - Headline (600,
text-sm–text-base): Row titles, artist names in lists, KPI values. - Title (500–600,
text-sm): Card titles, semibold metadata labels. - Body (400,
text-sm/ 0.875rem, line-height 1.5): Descriptions, bio preview. Cap prose at ~65–75ch (max-w-3xlon biography blocks). - Label (600,
text-[11px]–text-xs): Count badges, platform pills, freshness badges. Sentence case preferred; avoid all-caps body copy. - Data fields (400–600,
text-sm, tabular where numeric): Use Inter for scan accuracy across rankings, listings, metrics, and management rows.
Named Rules
Section titled “Named Rules”The Density Rule. Prefer text-sm body on analyst surfaces. Step up to text-base only for hero artist name or primary KPI, not every paragraph.
4. Elevation
Section titled “4. Elevation”Mostly flat surfaces. Depth is conveyed through tonal layering (shell → white card → bordered row) and 1px borders, not stacked shadows. Shadows appear as a response to interaction (hover:shadow-sm, hover:-translate-y-0.5 on TrackRow), not at rest on every container.
ShadCN Card uses shadow-sm at rest; on dense list/overview surfaces prefer the flatter row pattern (border-only at rest, shadow on hover). Do not nest card-in-card-in-card stacks.
Shadow Vocabulary
Section titled “Shadow Vocabulary”- Hover lift (
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05)): Track rows and interactive tiles on hover only. - Focus ring (
ring-[3px] ring-ring/50): Buttons and inputs via ShadCN focus-visible treatment. - Modal / Popover depth: Standard dialog shadow with backdrop blur only where focus isolation is needed for a management task.
Named Rules
Section titled “Named Rules”The Flat-By-Default Rule. Surfaces are flat at rest. Shadows signal interactivity, not decoration.
5. Components
Section titled “5. Components”Buttons
Section titled “Buttons”- Shape:
rounded-md(6px) for default ShadCN variants;rounded-fullfor brandedkisum/kisum_outlineCTAs. - Primary (kisum):
bg-kisum-600(#8466AC), white text,shadow-xs, hoverbg-kisum-600/90. - Outline (kisum_outline): White background,
border-kisum-600, purple text, hoverbg-kisum-600/10. - Icon buttons: Prefer icon-only or icon-leading management actions with subtle hover backgrounds, especially edit/delete/overflow actions.
- Hover / Focus:
transition-all; focus-visible ring 3px at 50% ring color. Destructive useskisum_destructivefull-round variant.
- Overview badge:
rounded-full bg-[#F3EEF8] px-2 py-0.5 text-[11px] font-semibold text-[#8466AC]. - Genre / status pills: Small rounded pills on biography; positive/negative accent chips for boolean states.
Cards / Containers
Section titled “Cards / Containers”- Corner Style:
rounded-xl(12px) for overview rows and ShadCN cards. - Background: White (
bg-white) on gray shell; avoidbg-gray-100cards unless grouping personnel under agencies. - Border:
border-gray-100at rest,border-gray-200on hover. - Internal Padding:
p-2.5compact rows;p-4–p-6for section containers.
Inputs / Fields
Section titled “Inputs / Fields”- Style: ShadCN input with
border-input,rounded-md, backgroundbg-background. - Filter bars: Treat filters as one cohesive control group. Use segmented controls for binary/mode toggles and Select dropdowns with refined chevrons for option sets.
- Filtering is instant-apply, in the toolbar (canonical). Every facet is a toolbar dropdown that writes straight to the live filter state and refetches; applied filters are shown as removable chips below the toolbar. Do not add a filter drawer/sheet with
tempFilter+ an Apply button — that pattern is retired. Drawers/sheets remain correct for forms (create/edit/detail), not for filtering. As of 2026-07-22 Promoters has no filter drawer left — Venues (Country / Type / Capacity) and City rankings (City / Region) were the last two and are now dropdowns. - Range facets (capacity, fee, size) are preset bands, not sliders. A range facet is a dropdown of selectable bands; each band maps to the exact
min/maxpair the query already accepts. Never invent a band the API cannot express, and never stage a slider behind an Apply button. - Labels: Prefer labels above inputs or compact
label-smtreatment; avoid floating labels unless already present in the component family. - Focus: Ring-based focus-visible per ShadCN defaults.
- Error:
aria-invalidring destructive/20.
Navigation
Section titled “Navigation”- App sidebar structure: Mandatory — Promoters, Venues, and Artists MUST use the canonical app-shell tree (see §6 App shell); only
SideNavBodymenu may differ. - Detail tabs: Horizontal tab strip on entity detail pages; active state uses brand emphasis without full purple fill of the tab bar.
6. App shell (mandatory — all Kisum frontends)
Section titled “6. App shell (mandatory — all Kisum frontends)”Every Kisum product frontend MUST use this shell layering. Do not fork per app, module, or persona. Nextkt is the only documented exception.
<body> (mandatory)
Section titled “<body> (mandatory)”The root layout <body> MUST always use exactly these classes (no substitutions, no omissions):
className="min-h-screen bg-gray-50 antialiased dark:bg-gray-900"This is the page canvas. It must stay cool gray — not white, not cream, not zinc-100 alone.
Reference: Frontend-Kisum-Promoters/src/app/layout.tsx.
Sidebar desktop root (mandatory)
Section titled “Sidebar desktop root (mandatory)”The ShadCN Sidebar desktop wrapper (data-slot="sidebar") MUST always include this exact base class string:
'group peer hidden text-sidebar-foreground xl:block'State, variant, and collapsible classes may be appended via cn(); do not replace or drop the mandatory base. Required on all Kisum frontends that use the shared sidebar pattern.
Reference: Frontend-Kisum-Promoters/src/components/ui/app-shell.tsx (SideNav desktop block).
Kisum app-shell primitive naming (Promoters canonical)
Section titled “Kisum app-shell primitive naming (Promoters canonical)”Promoters renames the forked shadCN sidebar block for readability. Other Kisum frontends should adopt the same export names when they sync primitives from Promoters.
| shadCN name | Kisum name | Role |
|---|---|---|
SidebarProvider | AppShellProvider | Shell state (open/collapse/mobile) |
useSidebar | useAppShell | Hook for shell state |
Sidebar | SideNav | Left navigation panel |
SidebarContent | SideNavBody | Scrollable nav items |
SidebarHeader / SidebarFooter | SideNavHeader / SideNavFooter | Top/bottom of side nav |
SidebarInset | MainColumn | Main work column (<main>) |
SidebarTrigger | SideNavToggle | Open/collapse control |
File: Frontend-Kisum-Promoters/src/components/ui/app-shell.tsx. Application composition: Frontend-Kisum-Promoters/src/components/app-shell/.
In-scope persona frontends (mandatory): Promoters (canonical), Venues, Artists. Copy this tree per repo — no shared package. Public Form (Fullstack-Kisum-Public /form/*): same app-shell primitives and §6 mechanical rules; documented substitutions — Register header row + Log in footer instead of company switcher + NavUser; empty SideNavBody. Out of scope: Admin, Checkout, Website, legacy base, Nextkt. Finance (2026-07-13, full redesign pass): visual alignment complete on existing Sidebar + MainLayoutClient + vendor layout — transparent desktop sidebar on the gray body canvas, white bg-main panel, zero gradient CTAs/text, flat-at-rest surfaces, shared Button kisum variants, Manrope h1–h3, charcoal dark tokens (see Frontend Finance — Design system alignment); full AppShellProvider + @main tree still optional future work.
Top header (mandatory — Promoters canonical, 2026-07-21)
Section titled “Top header (mandatory — Promoters canonical, 2026-07-21)”The shell header (components/app-shell/main-chrome/page-header.tsx) is a single flex row:
height: var(--head-h) /* 68px */; flex-shrink: 0; display: flex; align-items: center;justify-content: space-between; gap: 20px; padding: 0 var(--pg-pad);border-bottom: 1px solid var(--border); background: var(--surface-shell)Contents, left → right: breadcrumbs · search · theme · notifications · section CTA.
- Breadcrumbs:
font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase, separator/in--border-hover(.k-crumb-sep), last crumb--text-strong, earlier crumbs--mutedand linked (.k-crumb-link). Overflow truncates with an ellipsis — it does not scroll. The sidebar toggle sits to their left on mobile / collapsed desktop. - Search:
height: 40px; padding: 0 12px; background: var(--surface-card); border: 1px solid var(--border); border-radius: var(--radius-btn); flex: 1 1 auto; min-width: 120px; max-width: 420px— a 16px lucidesearchin--muted, the placeholder in 13px--muted, and a right-aligned⌘Kchip (11px/700 --muted,1px solid var(--border),radius 4px,padding 1px 5px). Opens aCommandDialog;⌘K/Ctrl+Kworks anywhere in the app. - Theme toggle and notification bell: both 40×40
.k-ibbuttons (--surface-cardon a--border,--radius-btn,--text-body). Theme shows lucidemoonin light andsunin dark. The bell’s unread state is a 7px--accentdot attop: 8px; right: 9pxwith a 1.5px--surface-cardring. - Primary CTA:
height: 40px; padding: 0 16px; background: var(--accent); color: var(--text-on-accent); border-radius: var(--radius-btn); font-size: 13px; font-weight: 700; box-shadow: var(--shadow-accent)with a leading 16px lucide icon (plusby default).
Narrow viewports (mandatory). Five controls do not fit at 375px, so the header degrades rather than overflowing: page padding drops to 16px below 768px; the search field collapses to a 40px icon button below 1024px (losing its label and ⌘K chip); the CTA drops its label to a 40px accent icon button below 640px; and the breadcrumb trail shows only the current crumb below 768px. These rules live in .k-appbar* / .k-crumb-item in kisum-ds.css — the header must not be styled inline, or the media queries cannot win.
The CTA is per-route and page-owned. Pages publish their primary action to the shell through a small client registry (src/stores/use-header-action.ts → useHeaderAction({ label, onClick | href, icon?, disabled? })); the header renders whatever is registered and renders no button at all when a route registers nothing. A page must publish the handler it already owns (drawer / dialog / sheet / link) rather than gain new behaviour, and must not keep a duplicate copy of the same button in its own PageHeader action slot. Never register a label with no real action behind it.
The search palette must query real endpoints. In Promoters it is wired to /api/artists/search and /api/venues/search (the only entity search endpoints the promoter BFF exposes), grouped as Artists and Venues, debounced ~300ms, with server-side results (shouldFilter={false} on the cmdk root) that navigate to the real detail route. A third Go to group lists in-app destinations derived from the sidebar menu. Do not add a result group for an entity with no search endpoint.
Dark theme (mandatory mechanism)
Section titled “Dark theme (mandatory mechanism)”Dark mode is a pure token swap, never a per-component dark: sweep. .dark on <html> is the only switch; src/styles/kisum-ds.css re-points the raw Kisum tokens and the Tailwind --color-* tokens the shadCN components read, so both styling systems follow one class.
- State lives in
src/stores/use-theme.ts(useTheme()/applyTheme()), persisted tolocalStorageunderkisum-theme, default light. - A tiny inline script in the root layout applies the stored class before first paint;
<html>carriessuppressHydrationWarningbecause the server and client class lists intentionally differ. - Tailwind’s own
dark:variant is enabled in Promoters since 2026-07-22 (@custom-variant dark (&:where(.dark, .dark *))), bound to the same.darkclass. The token layer stays the source of truth. An element that already uses a design token (bg-surface,bg-shell,text-foreground,text-muted-foreground,border-border,var(--surface-card), …) is already theme-correct and MUST NOT also carry adark:class — that is how double-darkened surfaces and invisible text appear. Usedark:only for a fixed colour that has no token (Tailwind palette values such asbg-zinc-950/10,bg-emerald-100,bg-amber-50,border-white/10, fixed gradients, and native<option>colours). - Consequence for the brand scale:
--kisum-400/500/900/950have no dark override, sodark:text-kisum-400,dark:text-kisum-200/300anddark:bg-kisum-950/*render wrong or invisible on dark surfaces. Never hand-pick a brand step behinddark:— usetext-kisum-600/bg-kisum-tint, which follow the theme.
Stylesheet split (Promoters — share kisum-ds.css)
Section titled “Stylesheet split (Promoters — share kisum-ds.css)”| File | Role |
|---|---|
src/styles/kisum-ds.css | Canonical Kisum tokens (--surface-*, --text-*, --kisum-*), .dark overrides, shadcn bridge vars, .k-* helpers, scrollbars, app-bar/crumb rules, and shared page patterns (.page-container, .stat-card, .data-table, .badge-*, .btn-*, shell data-* hooks). Copy or import this file in other Kisum persona frontends before app Tailwind. |
src/styles/tailwind.css | Tailwind v4 + shadcn @theme inline wiring only. Must load after kisum-ds.css (layout.tsx import order). |
src/styles/globals.css | Legacy widgets (calendar-era buttons, TipTap, masonry) — not the color source of truth. |
6.3 Canonical folder tree (mandatory)
Section titled “6.3 Canonical folder tree (mandatory)”Every in-scope persona frontend MUST use this exact layout. Domain views (calendar, dashboard, feature pages) live outside components/app-shell/.
src/ components/ ui/ app-shell.tsx # Kisum primitives (AppShellProvider, SideNav, MainColumn, …) app-shell/ index.ts # export AppShellLayout app-shell-layout.tsx # client shell composer footer.tsx side-nav/ app-sidebar.tsx nav-main.tsx nav-user.tsx companies-switcher.tsx sidebar-package-plan.tsx create-company-modal.tsx create-company-form.tsx subscribe-package-modal.tsx modal-tour-guest.tsx combobox-add/ # 3 combobox forms skeleton/ # skeleton-menu, -page, -switcher, -user, -event main-chrome/ page-header.tsx custom-breadcrumb.tsx notification-dropdown.tsx overlays/ company-select-dialog.tsx subscribe-company-overlay.tsx middle-agent/ # addon only — Promoters, Artists, Venues middle-agent-app-shell-layout.tsx side-nav/middle-agent-sidebar.tsx main-chrome/page-header.tsx app/ layout.tsx # main parallel slot + RootSlotSwitcher _components/RootSlotSwitcher.tsx default.tsx @main/ layout.tsx # thin server → AppShellLayout default.tsx …routes… (middle-agent)/ # addon route group (not parallel slot) layout.tsx …routes…Promoters-only (do not copy to other modules): side-nav/nav-events.tsx, app/public/layout.tsx + public sidebar subtree.
6.4 Route wiring (mandatory)
Section titled “6.4 Route wiring (mandatory)”- Root:
app/layout.tsxacceptschildren(auth,(middle-agent)/*,/intake/*) andmain(@mainslot).RootSlotSwitcherrendersmainwhen authenticated except on/middle-agent/*and/intake/*, which must use thechildrenslot.@main/default.tsxand rootdefault.tsxreturnnull(never redirect) so parallel-slot fallbacks do not fight(middle-agent)URLs. Venues/Artists have no/publicbranch; Promoters also switches tochildrenfor/public/*. - Persona app:
app/@main/layout.tsx(server) reads cookies/bootstrap → wrapsAppShellLayout(client) →{children}are route pages. - Middle-agent addon:
app/(middle-agent)/layout.tsx→MiddleAgentAppShellLayoutfromcomponents/app-shell/middle-agent/. URLs under/middle-agent/*. Finance has no middle-agent shell. - Middle-agent sidebar: Same shell contract as main (
SideNav collapsible="icon",SideNavToggle,SideNavRail,SideNavGroup+SideNavMenuButtonnav, company switcher, footer back-link +NavUser). Violet accent classes only — structure matches main. - Main ↔ middle-agent transition:
ShellTransitionLinksetssessionStoragedirection;ShellViewTransitionwraps bothAppShellLayoutandMiddleAgentAppShellLayoutfor a 300ms fade/slide on cross-shell navigation.
flowchart TB root[app/layout.tsx] rsw[RootSlotSwitcher] auth[children slot signin] mainSlot["@main/layout.tsx"] asl[AppShellLayout] sideNav[side-nav/AppSidebar] mainCol[MainColumn] ph[main-chrome/PageHeader] content[route children] root --> rsw rsw --> auth rsw --> mainSlot mainSlot --> asl asl --> sideNav asl --> mainCol mainCol --> ph mainCol --> content
6.5 Exceptions table
Section titled “6.5 Exceptions table”| Item | Promoters | Venues | Artists | Finance | Admin / Checkout / Website / legacy |
|---|---|---|---|---|---|
| Canonical app-shell tree | canonical | mandatory | mandatory | visual alignment complete (full redesign 2026-07-13); structural tree TODO | out of scope |
nav-events.tsx | yes | no | no | no | no |
app/public/ shell | yes | no | no | no | no |
| Middle-agent shell | when addon | when addon | when addon | no | no |
| Finance vendor portal | n/a | n/a | n/a | fully aligned (same tokens; no gradients, kisum Button) | n/a |
6.6 Migration checklist
Section titled “6.6 Migration checklist”- Promoters — canonical (done).
- Venues — copy tree +
@main+ Kisum primitive names; relocate domain files out ofapp-shell/. - Artists — same + normalize
middle-agent/underapp-shell/middle-agent/. - Finance — full visual redesign done (2026-07-13): transparent desktop sidebar, white
bg-mainpanel, gradient-free components, kisumButtonvariants, Manrope headings, charcoal dark tokens; optional later: port full PromotersAppShellProvider+@maintree. - New persona apps — MUST adopt this tree from day one; no
ui/sidebar.tsxorlayouts/sidebar/.
Plan reference: workspace docs/superpowers/plans/2026-06-27-kisum-app-shell-normalization.md.
Surface layering (mandatory)
Section titled “Surface layering (mandatory)”| Layer | Utility / token | Value | Use |
|---|---|---|---|
| Page canvas | bg-gray-50 on <body> | #F9FAFB | Outermost shell behind side nav + main |
| Side nav | bg-sidebar → var(--sidebar) | lab(98.26% 0 0) | Side nav inner surfaces (data-sidebar="sidebar") |
| Main content | bg-main or bg-background on MainColumn / <main> | #FFFFFF | Primary work area — always white |
| Cards / boxes / rows | bg-white / bg-surface + border-gray-100 | #FFFFFF | Content objects on the white main panel |
Wire --main: #FFFFFF (or alias --main to --background) and expose bg-main in Tailwind @theme where not already present. The token YAML maps sidebar-bg → --sidebar and main-bg → --main. Cards and panels on the main area use white/surface — not bg-gray-50 on top of white main unless an existing grouped-subsection pattern already requires it.
App sidebar structure (mandatory — all Kisum frontends)
Section titled “App sidebar structure (mandatory — all Kisum frontends)”Every Kisum persona frontend in scope (Promoters, Venues, Artists) MUST use the same app-sidebar shell. Admin, Checkout, Website, legacy base, and Nextkt are out of scope for this contract. Finance migration is Phase 2.
Only SideNavBody navigation items may differ per module (menu keys, optional secondary blocks such as events). Header, collapse behavior, company switcher row, footer user block, guest upsell slot, spacing, icons, and colors MUST match the canonical Promoters implementation.
Canonical reference (do not reinvent):
Frontend-Kisum-Promoters/src/components/app-shell/app-shell-layout.tsxFrontend-Kisum-Promoters/src/components/app-shell/side-nav/app-sidebar.tsxFrontend-Kisum-Promoters/src/components/app-shell/side-nav/nav-user.tsxFrontend-Kisum-Promoters/src/components/app-shell/side-nav/companies-switcher.tsxFrontend-Kisum-Promoters/src/components/app-shell/side-nav/sidebar-package-plan.tsxFrontend-Kisum-Promoters/src/components/ui/app-shell.tsx— primitives:SideNavToggle,SideNavRail, collapse group
Mandatory component order
Section titled “Mandatory component order”Render inside <SideNav collapsible="icon" className="z-40"> in exactly this order:
SideNavHeader— logo row + company switcher rowSideNavBody— module menu only (variable)- Guest upsell slot (conditional — see below)
SideNavFooter—NavUserSideNavRail— last child; edge collapse toggle
Side nav root (mandatory)
Section titled “Side nav root (mandatory)”<SideNav collapsible="icon" className="z-40"> {/* …header, body, guest?, footer… */} <SideNavRail /></SideNav>collapsible="icon"is mandatory — desktop collapses to an icon rail, not offcanvas-only hide.className="z-40"on the side nav root (stacking above main chrome where needed).SideNavRailMUST be present as the final child.
Logo row (mandatory — inside SideNavHeader)
Section titled “Logo row (mandatory — inside SideNavHeader)”Wrapper classes (exact):
className="mb-6 flex w-full items-center pt-2.5 group-data-[collapsible=icon]:flex-col-reverse group-data-[collapsible=icon]:pt-2 group-data-[collapsible=icon]:pl-0"| Asset | Expanded | Icon-collapsed (group-data-[collapsible=icon]) |
|---|---|---|
| Full wordmark | /logo.svg, className="flex h-auto w-32 group-data-[collapsible=icon]:hidden" | hidden |
| Icon mark | /logo-icon.svg, className="hidden h-auto w-6 group-data-[collapsible=icon]:flex" | visible (w-6) |
Both use alt="KISUM". Do not substitute different logos or sizes per module.
SideNavToggle (mandatory — expanded header only)
Section titled “SideNavToggle (mandatory — expanded header only)”<SideNavToggle className="ml-auto group-data-[collapsible=icon]:hidden" />- ShadCN ghost icon button with
PanelLeftIcon(fromapp-shell.tsxprimitive). ml-autoin the logo row when expanded.- Hidden when icon-collapsed — expand/collapse in icon mode via
SideNavRail(andSideNavToggleinPageHeaderon mobile). - Action:
toggleSideNav()(provided by primitive).
SideNavRail (mandatory)
Section titled “SideNavRail (mandatory)”<SideNavRail />Thin edge hit area on the side nav border for collapse/expand when the header trigger is hidden. Do not omit or replace with a custom control.
Company switcher row (mandatory — below logo row, inside SideNavHeader)
Section titled “Company switcher row (mandatory — below logo row, inside SideNavHeader)”Same visual treatment in every module; only eligibility/filter logic may differ internally.
| State | Component | Key classes / behavior |
|---|---|---|
| Loading | Skeleton placeholder | Match Promoters SkeletonSwitcher pattern |
| No active company | NoCompanySelectedPlaceholder | Dashed border, Building2 icon, muted “No company selected” copy; icon-only when collapsed |
| Active company | CompaniesSwitcher | Row: bg-gray-200/80 p-2 rounded-md; size-10 square avatar; bold name + subtitle; dropdown with ChevronsUpDown |
Icon-collapsed: compact avatar (group-data-[collapsible=icon]:p-0 on row; labels group-data-[collapsible=icon]:hidden).
SideNavBody (module-specific — menu only)
Section titled “SideNavBody (module-specific — menu only)”Navigation items, grouping, and optional module-only blocks (e.g. Promoters upcoming events in nav-events.tsx) live here. Do not move header, footer, guest upsell, or logo into the body.
Guest upsell slot (mandatory pattern when guest role applies)
Section titled “Guest upsell slot (mandatory pattern when guest role applies)”Between SideNavBody and SideNavFooter, when the user has guest access and profile is loaded:
Expanded (icon rail hidden content):
- Wrapper:
className="group-data-[collapsible=icon]:hidden" SidebarPackagePlancard:rounded-none border-x-0 py-4 shadow-none; title “You’re currently using free plan”; full-widthvariant="kisum"button,rounded-full py-5, label Subscribe Plan
Icon-collapsed:
- Hide the card wrapper above
- Show
BellRingIconbutton:className="m-2.5 hidden size-8 rounded-md p-0 text-kisum group-data-[collapsible=icon]:flex hover:bg-kisum-100/75",variant="secondary" Tooltip: content “Subscribe to a plan”,side="right",align="center"- Same
onClickas the expanded subscribe CTA (opens subscription flow)
Omit the entire slot when not guest or while profile is loading.
SideNavFooter — NavUser (mandatory)
Section titled “SideNavFooter — NavUser (mandatory)”<SideNavFooter id="sidebar-footer-user" data-onborda="sidebar-footer-user"> {/* SkeletonUser while loading; NavUser when ready */}</SideNavFooter>Footer trigger row (SideNavMenuButton size="lg"):
- Container:
min-h-16,hover:bg-kisum-50 - Avatar:
size-12 rounded-full border border-gray-200→size-8when icon-collapsed - Company pill (when company selected):
rounded-md border border-gray-200 px-2 py-1 text-xs font-medium text-kisum-600 - User name:
font-bold capitalize, truncated - Chevron:
ChevronsUpDownIconwithml-auto size-4
Dropdown (desktop: side="right", align="end", min-w-80): user summary, switch company, profile, companies, plan & billing, get started, sign out — mirror Promoters nav-user.tsx structure and icon colors (text-kisum-600 for nav icons, text-red-500 for sign out).
Sidebar icons, spacing, and colors (mandatory)
Section titled “Sidebar icons, spacing, and colors (mandatory)”- Surface: inner sidebar
bg-sidebar, texttext-sidebar-foreground— not main-panel white. - Spacing: header/footer primitives use ShadCN
p-2; logo rowmb-6 pt-2.5(collapsed:pt-2 pl-0). - Purple accent (sidebar): company pills, guest CTA, footer hover
hover:bg-kisum-50, guest belltext-kisum/hover:bg-kisum-100/75, dropdown iconstext-kisum-600— respect Purple Budget Rule. - Neutrals: company row
bg-gray-200/80, bordersborder-gray-200, secondary copytext-muted-foreground. - Collapse visibility: use
group-data-[collapsible=icon]:*selectors on children — do not invent alternate collapse widths, breakpoints, or offcanvas-only sidebars for Kisum module apps.
Overview / dense list section (signature pattern)
Section titled “Overview / dense list section (signature pattern)”Reference implementations today live in Promoters (OverviewSectionHeader, TrackRow); other apps should match this pattern or extract shared components — do not invent alternate row/card aesthetics.
- Section header:
text-lg font-semibold tracking-tight text-gray-900, optional 20px source icon, purple count badge, right-aligned action (ml-auto). - Dense row: Rank or index in purple tabular nums, 44px thumb when media applies, truncated title/subtitle, metric block right-aligned; hover lift
-translate-y-0.5+shadow-sm.
Listing / Marketplace Cards
Section titled “Listing / Marketplace Cards”- Editorial Insight Card: High-quality artist or venue imagery may anchor the card, but the card must remain a management object: clear metadata, status, owner/action context, and a predictable action footer.
- Status badges: Float over imagery only when contrast is guaranteed; otherwise place in a dedicated header row.
- Action footer: Keep primary view/action and edit/unpublish/manage controls aligned and consistently padded.
7. Layout & Spacing
Section titled “7. Layout & Spacing”The layout follows a fixed-grid management philosophy for readability and comparison. Keep app content centered in the 1280px container token, use a 12-column grid for dense listing/filter surfaces, and collapse cleanly to 2 columns on tablet and 1 column on mobile with 16px margins.
Page main section (mandatory — Promoters, Artists, Venues)
Section titled “Page main section (mandatory — Promoters, Artists, Venues)”Every @main page (below the sticky app header / breadcrumb bar, above page content) MUST wrap its primary content in the canonical page container. Do not add extra horizontal padding on the app-shell {children} wrapper — padding lives on this container only.
Container (mandatory class string):
mx-auto flex w-full max-w-7xl flex-1 flex-col gap-12 p-6 sm:p-8 lg:p-10Prefer the shared helper in each persona app:
PageMainSection/PAGE_MAIN_SECTION_CLASSPageSectionHeaderwithPAGE_SECTION_TITLE_CLASS+PAGE_SECTION_DESCRIPTION_CLASS- Path:
src/components/app-shell/main-chrome/page-main-section.tsx(Promoters canonical)
Page section title (primary h1 at top of main content):
className="text-4xl font-bold"Page section description (subtitle directly under that h1):
className="mt-2 text-base leading-relaxed text-slate-500"Do not use text-2xl font-semibold, text-muted-foreground, or ad-hoc max-w-[1160px] / max-w-6xl / max-w-5xl wrappers for standard list and workflow pages. Detail sub-heroes, cards, modals, and tab bodies are excluded.
- Desktop: Listing cards may span 4 columns (3 per row) or 3 columns (4 per row) depending on information density.
- Tablet: 2-column grid unless data comparison requires a table or horizontal scroll pattern already used elsewhere.
- Mobile: 1-column grid, no horizontal overflow.
- Vertical rhythm:
stack-sm(8px) for label/control pairs,stack-md(16px) for card internals,stack-lg(32px) for major section breaks.
7b. Drawers (Sheet) — form drawers
Section titled “7b. Drawers (Sheet) — form drawers”Right-anchored form drawers (Create task, entity editors) follow one shape:
- Panel: fixed width (Create task uses
460px) capped at94vw,background: var(--surface-card),border-left: 1px solid var(--border),box-shadow: var(--shadow-modal), flex column, full height. - Backdrop:
rgba(24,24,27,.45)withbackdrop-filter: blur(3px). - Rhythm: header and body
24px 28px, footer18px 28px, both separated by1px solid var(--border-subtle). Body sections sit20pxapart; label/control rows sit14pxapart. - Label/control rows:
display: grid; grid-template-columns: 96px 1fr; gap: 14px. Labels are12.5px / 700 / var(--text-body). Controls are42pxtall,0 12pxpadding,var(--surface-page)on1px solid var(--border)atvar(--radius-btn), with a trailingchevron-downatvar(--muted)/opacity: .6. - Accent-tinted surfaces (the title panel, a secondary Upload button) use
border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent)— not the plain--bordertoken. - Drop zones (attachments) use a dashed
1px var(--border)border atvar(--radius-card). - Write these with inline
styleusing the CSS variables. Design tokens expressed as arbitrary Tailwind values lose to shadcn base classes throughtailwind-merge; inline styles always win.
Nested layers inside a drawer. While a Radix DropdownMenu / Popover / Select is open it becomes the top-most dismissable layer and sets pointer-events: none on the drawer content, while the drawer’s overlay stays interactive. A click that visually lands inside the drawer is therefore delivered to the overlay and read as a backdrop click, which closed the drawer and discarded the form. SheetContent must guard onPointerDownOutside / onInteractOutside / onFocusOutside and dismiss only when no nested popper is mounted and the interaction really hit the overlay. Escape needs no handling — Radix gives it to the top-most layer.
8. Do’s and Don’ts
Section titled “8. Do’s and Don’ts”- Do set
<body className="min-h-screen bg-gray-50 antialiased dark:bg-gray-900">on every Kisum frontend root layout. - Do keep the sidebar desktop root at
group peer hidden text-sidebar-foreground xl:blockand main content white (bg-main/bg-background). - Do implement
AppSidebarwithcollapsible="icon", logo/logo-icon swap,SideNavToggle,SideNavRail, company switcher row,NavUserfooter, and guest upsell slot exactly as in Promoters — mandatory on Promoters, Venues, Artists. - Do wrap every
@mainpage body inPageMainSection(orPAGE_MAIN_SECTION_CLASS) withtext-4xl font-boldpage titles andmt-2 text-base leading-relaxed text-slate-500subtitles — mandatory on Promoters, Artists, Venues. - Do keep Kisum purple for ranks, badges, and primary CTAs only (The Purple Budget Rule).
- Do verify muted text contrast on tinted surfaces; bump
text-muted-foregroundtoward ink when close to 4.5:1. - Do respect
prefers-reduced-motion: replace hover translate lifts with border/color shifts only. - Do pair platform brand colors in charts with labels and numeric context, not color alone.
- Do style drawer panels, rows, and controls with inline
style+ CSS variables, and guardSheetContentagainst nested-popper dismissals (see 7b).
Don’t:
Section titled “Don’t:”- Don’t change the mandatory
<body>or sidebar desktop root classes per app or route. - Don’t fork side nav header, footer, collapse, logo swap, company switcher layout, or guest upsell pattern per module — only menu items in
SideNavBodymay differ. - Don’t omit
SideNavRail, usecollapsible="offcanvas"instead ofcollapsible="icon", or hide the logo-icon swap on collapse. - Don’t set the main content area or default page panel to
bg-gray-50— main is white; gray-50 is body-only. - Don’t ship generic SaaS cream UI: warm-tinted near-white backgrounds, muted gray card stacks, purple CTAs everywhere, identical icon-heading-text grids.
- Don’t clone consumer social profiles (Instagram/Spotify artist page layout with engagement-first hierarchy).
- Don’t add dashboard chaos: competing chart widgets with no clear primary signal on one screen.
- Don’t use
border-leftorborder-rightgreater than 1px as a colored accent stripe on cards or list items. - Don’t use gradient text (
background-clip: text) for headings or metrics. - Don’t nest identical card grids (card inside card inside card).
- Don’t add shell-level
px-4 py-6padding around{children}when pages already usePageMainSection— avoids double padding. - Don’t let an outside-interaction on a drawer close it while a nested dropdown, popover, calendar, or select is open — that click is only dismissing the popper.
When changing the design system
Section titled “When changing the design system”- Edit this page (
System-Kisum-Docs/src/content/docs/frontend/3-0-kisum-design-system.md) — tokens YAML block + sections 0–8. - Update
.cursor/rules/00-design.mdcif agent-facing constraints change. - Optionally sync local
DESIGN.mdfor Impeccable/design-tool sidecars (local only; not required on GitHub). - Do not maintain duplicate full copies under individual frontend repos — use a pointer file (see
Frontend-Kisum-Promoters/DESIGN.md).