Design system · Mobile · v1.0 · based on skill in Claude: kc-ds-mobile v1.0

KCDS Mobile — Design system documentation

Companion to KCDS web. Native mobile shell patterns, touch & gesture rules, and product canonicals for the 3 KC products that ship mobile apps: Learner Portal, LMS Admin, and Safety. Use the platform switcher in the sidebar to jump to web.

Foundations · Scope

Scope & products

KC has 17 products. Three have mobile apps in production or development. Mobile is a smaller surface than web — most products are web-only.

ProductMobileStatusTech
Learner Portal Production v14.6 Native iOS (Swift + SwiftUI), Android (Kotlin + Compose). ~91 white-label builds.
LMS Admin In development v1 Native iOS + Android. Admin-first: portal config, assignments, grading.
Safety Production React Native 0.84.1, TypeScript. Field incident reporting.
Performance ManagementPlannedPhase 2React Native (placeholder)
USMPPlannedPhase 7Mobile SDKs
All other ~14 KC productsWeb-only

The skill's primary focus is Learner Portal mobile (~17 audited production screens, 91 tenant builds). LMS Admin inherits Learner Portal patterns with admin overrides. Safety only inherits foundations (color tokens, typography, spacing, dark theme) — shell patterns differ because it's React Native.

Foundations · Rules

The six rules that override everything

Every audit, every new screen, every code review — these six checks come first. If a screen fails one of these, fix it before anything else.

  1. Sentence case for interactive elements. Buttons, tabs, badges, form labels, nav items, status labels. Start course not Start Course.
  2. Title Case for headings and content names. Empty state titles, modal titles, section headers, page titles, training material names. No Courses Yet not No courses yet. Applies to H1–H4 and any text that serves as a heading. (kc-design-foundations → capitalization.md)
  3. Safe-area insets on every fixed/sticky element. env(safe-area-inset-bottom) on bottom tab bar, sticky CTAs, sheet actions. env(safe-area-inset-top) on top app bar. Missing this overlaps the iOS home indicator on every modern phone.
  4. Accent resolves from product, never hardcoded. Reference --kcds-color-primary or platform-specific token. Hardcoding #F97316 outside Learner Portal breaks every other product.
  5. Hit areas ≥ 44pt × 44pt. Visible target can be smaller (24pt icon) but the tappable zone must be 44pt. Use padding to expand.
  6. Dates are relative when fresh, absolute when old. 2h ago / Yesterday / Mar 12. Never 2026-03-12T14:36:42Z in UI.
  7. Bottom tab bar is fixed: 5 tabs, never reorder. Home · My learning · Library · Events · Account. Bottom tabs are app-level navigation, not contextual.
Foundations · Typography

Typography — mobile ramp

Same typeface (Lato) and same token names as web, but headings shrink. Reading distance on mobile is ~25-35cm vs 50-70cm desktop, so 32px H1 already feels large in hand.

TokenWebMobileNotes
--kcds-text-display80px34pxRare on phone; hero/splash only
--kcds-text-h132px24pxPage titles, screen headers
--kcds-text-h224px20pxSection headers, card headers
--kcds-text-h320px17pxSub-headers, list group headers
--kcds-text-body16px16pxNo change — mobile body floor (iOS auto-zoom prevention)
--kcds-text-caption12px12px (13px when primary content)Bump to 13 when caption IS the row content (audit timestamps)

The 16px body floor is absolute. iOS Safari auto-zooms any input with font-size < 16px on focus, breaking layout. Body and form inputs always 16px.

Dynamic Type (iOS) and Font Scale (Android)

KC apps respect user font-size accessibility settings. Use minHeight not height on text rows. Test at AX5/AX6 — fixed-height rows clip at largest sizes.

Foundations · Touch

Hit areas

The visible target and the tappable zone can differ. Apple HIG and Material both call for 44pt × 44pt minimum tappable area, regardless of how small the visible icon is.

visible 24pt · tap 44pt

Icon is 24pt for visual proportion, but the tappable area is 44pt — expanded with transparent padding around the icon. Users with low fine-motor control can still hit it; everyone else just doesn't notice.

Common patterns:

  • Top app bar icons: 24pt icon centered in 44pt × 44pt button
  • Tab bar items: full column tap area (~75pt × 56pt), icon centered
  • List row trail icons: stretch the tap zone to row height (≥56pt)
  • Card chevrons: tap zone extends across the entire row, not just the icon
Foundations · Spacing

Spacing

Mobile uses the same spacing scale as web — 4 / 8 / 12 / 16 / 20 / 24 / 32 / 40 / 48. But mobile uses smaller values more often, because viewport is narrow.

Standard screen padding

Screen edge16px horizontal (never less)
Section gap24px between major sections
Card gap12px between cards in a list
Row vertical padding12px top/bottom on list rows
Touch gap8px minimum between adjacent tap targets
Foundations · Density

Density

Mobile has only comfortable density — there is no compact mode. Compact density on web exists for data-heavy admin surfaces (LMS rosters, etc.) where users sit at desks and scan dense tables. On mobile, the smaller viewport and touch targets make compact untenable.

The LMS Admin mobile app is the closest case to "admin density", but even there touch targets stay at 44pt and rows stay at ≥56pt height. The compactness comes from showing fewer items per screen, not from shrinking each item.

Foundations · Dark theme

Dark theme

Dark theme is opt-in via [data-kcds-theme="dark"] on root. The dark palette is near-black neutral, not the warm #111827 gray-blue used in Tailwind defaults. Try the Theme toggle in the sidebar — every component on this page will switch.

Rule — light renders on first paint

The first frame of any generated KC page is always light, regardless of OS preference. Designers reviewing previews on dark-themed machines must not get dark-on-dark flash. Dark theme is explicit attribute, never auto-applied via prefers-color-scheme.

Surface stack

SurfaceLightDark
Page background#F3F7F9#0A0A0B
Card / sheet#FFFFFF#131316
Elevated section#FFFFFF#1A1A1D
Track / progress#EAEFF3#2A2A2D
Foundations · Brand

Product accents

Mobile inherits the same per-category accent system as web. Switch categories in the sidebar to see chrome change. Each product picks its category and that's the accent used everywhere — top bar, primary buttons, FAB, active tab.

ProductCategoryPrimary
Learner PortalLegacyOrange #FF881A
LMS AdminLegacy (TBD)Orange #FF881A
SafetyKC ComplySlate #475569
Performance (planned)KC PerformIndigo #4F46E5
USMP (planned)PlatformCyan #0891B2
Touch & gestures

Gestures overview

KC mobile uses platform-standard gestures. Don't invent new ones. Users come from other apps and expect familiar interactions.

GestureWhereAction
TapAnywhere tappablePrimary action
Long pressList items, cardsReveal context menu (rarely used in KC)
Swipe right→leftList rowsReveal trailing actions (delete, archive)
Swipe down at topScroll containersPull to refresh
Edge swipe rightiOS onlyBack navigation (system handles)
Swipe downOn a sheetDismiss sheet
Pinch zoomImages, videoAllowed; not used in app chrome
Touch & gestures

Pull to refresh

Available on any scroll container that shows lists fetched from the network — My learning, Library, Events, Inbox. Triggers a fresh fetch and replaces stale data.

  • Spinner appears when user pulls down ~40px from scroll position 0
  • Release with sufficient pull triggers fetch
  • Use orange spinner (Learner Portal) or product accent
  • Show for minimum 600ms even if fetch returns instantly — prevents flash
  • On error, show a toast/snackbar, keep existing data visible
Touch & gestures

Swipe actions

Available on list rows when the row has a destructive or quick action that's faster than tapping into detail and finding a button.

  • Swipe right→left reveals trailing actions (1-2 max)
  • Destructive action (delete): red background, white icon
  • Neutral action (archive, mark read): gray/neutral background
  • Full swipe (past 60% width) commits the leading action
  • Always provide an alternate tap path — swipe is a power-user shortcut, not the only way
  • Add haptic feedback on action commit (success) and on cancel (warning)
Touch & gestures

Haptics

Use sparingly. Haptics are seasoning, not the main course. Trigger them on meaningful state changes only — never on every tap.

EventiOS patternAndroid pattern
Success (course completed)UINotificationFeedbackGenerator successHapticFeedbackConstants.CONFIRM
Error / warningUINotificationFeedbackGenerator errorHapticFeedbackConstants.REJECT
Selection change (picker, segmented)UISelectionFeedbackGeneratorHapticFeedbackConstants.CLOCK_TICK
Light tap (toggle on)UIImpactFeedbackGenerator lightHapticFeedbackConstants.VIRTUAL_KEY

On Android API < 30, fall back gracefully — older devices have a smaller haptic vocabulary.

Touch & gestures

Keyboard avoidance

When the soft keyboard appears, content must not be obscured. iOS and Android handle this differently — but KC mobile guarantees the active input is always visible.

  • iOS: Use KeyboardAvoidingView with behavior="padding" in React Native, or constrain layouts to the keyboard's safe area in native
  • Android: Default adjustResize windowSoftInputMode handles most cases. Test in landscape
  • Sticky CTA buttons rise above the keyboard when input is focused
  • Auto-scroll the active input into view with 24px buffer above the keyboard top
  • Tap outside dismisses keyboard except when typing into another input
Shells

M1 — Bottom tab shell

The primary shell. Every screen directly reachable from the bottom tab bar uses M1. Top app bar (56pt) + scrollable content + bottom tab bar (56pt + safe area).

9:41
Welcome back, Kseniia
My progress
Q2 Compliance training In progress · 42%
Leadership essentials Started yesterday
Data analysis fundamentals Due Mar 30

Spec

Top app bar height56pt + env(safe-area-inset-top)
Top bar backgroundWhite (default) or accent (Home only)
Content areaflex: 1; overflow-y: auto — scroll lives only here
Bottom tab bar height56pt + env(safe-area-inset-bottom)
Tabs5 fixed (Home, My learning, Library, Events, Account) — never reorder
Tab icon styleThin outline, stroke-width: 2, never filled

Critical CSS — phone container + scroll body

.phone {
  width: 390px;
  height: 100dvh;          /* ← height, NOT min-height */
  overflow: hidden;        /* prevents content overflow */
  display: flex; flex-direction: column;
}
.top-bar {
  flex-shrink: 0;          /* never compress */
  position: sticky; top: 0; z-index: 50;
}
.scroll-body {
  flex: 1;                 /* take all remaining space */
  overflow-y: auto;
  padding: 16px 16px calc(var(--tab-height) + var(--safe-bottom) + 16px);
}

Common mistake: min-height: 100dvh on .phone → parent stretches to content → flex: 1 on scroll-body has no ceiling → no scrolling.

Shells

M2 — Single flow shell

Detail screens and deep flows. User navigated here from M1 (tapped a course, event, assignment). Back arrow returns to parent. Tab bar is hidden.

9:41
Course detail
Compliance · 18 lessons · 4h 12m

Q2 Compliance training

Required pathway for all employees. Covers GDPR, data handling protocols, and Q2 policy updates from the legal team.

Spec

Top app barBack arrow (leading) + centered title + max 2 trailing icons
Back arrow hit area44pt × 44pt; icon-only, no text label
Title17pt medium, sentence case, ellipsis if long
Hero image (optional)Full viewport width, ~200pt tall, 16:9 ratio
Sticky bottom CTAPrimary action (e.g. Continue, Submit), full width, 56pt + safe area
Bottom tab barHidden in M2
Shells

M3 — Anchored sheet

Bottom sheet that slides up from below. Used for filters, action choices, contextual editing. Backdrop dims the underlying page; sheet is dismissible via swipe down, backdrop tap, or X button.

9:41
Filters

Course type

Spec

Sheet widthFull viewport (max 430px on desktop preview)
Top corner radius24px (top only, bottom is screen edge)
Max height92dvh — leaves ~8% backdrop visible above
Drag handle36×4px, border-radius: 9999px, centered, 12px from top
Backdroprgba(0,0,0,0.45)
DismissSwipe down · backdrop tap · X button
Apply buttonTop-rounded only (24px 24px 0 0), 64pt tall, sticky bottom
Shells

M4 — Conversational

AI chat / Q&A surfaces. Used by the in-app AI assistant. Distinct from M1/M2 because the input bar is sticky at bottom (above keyboard) and the message stream is the primary content. Used in Learner Portal AI tutor and LMS Admin AI assistant.

Spec

  • Top app bar: assistant name + clear button (trash icon)
  • Message stream: alternating user (right, accent bg) and assistant (left, surface) bubbles
  • Auto-scroll to latest message on send
  • Input bar: 56pt height, sticky bottom + safe area, mic + send buttons
  • Citations: inline numbered footnote links → tap opens M3 sheet with source
  • Typing indicator: three-dot animation while assistant generates
Shells

Top app bar

56pt height + safe-area-inset-top. Two variants: M1 (no back arrow, optional logo) and M2 (back arrow + centered title + max 2 trailing icons).

9:41
Welcome back
My learning
Course detail
M1 (accent) · M1 (white) · M2 (back arrow)

When to use each variant

M1 accentHome tab only. Welcome message, brand presence.
M1 whiteMy learning, Events, Library tabs. Title + filter/search icons.
M2 centeredDetail screens. Back arrow leading, title centered, 1-2 trailing actions.
Components · Actions

Buttons

56pt height (full width or pill), 28pt corner radius, 16pt body text weight 400. Larger and thicker than web buttons because they're thumb-driven.

VariantUse case
Primary (filled)The main action of the screen. One per screen.
Secondary (outline)Alternative action equally available alongside primary.
Tertiary (text)Low-stakes actions: cancel, dismiss, more info.
DestructiveDeletion or unrecoverable action. Always confirm via modal.
Components · Navigation

Bottom tab bar

Fixed at the bottom of every M1 screen. Five tabs, never reorder. Active state = color change only (icon + label both turn accent). Icon outline style never fills.

Height56pt + env(safe-area-inset-bottom)
Tab count5 fixed
Icon24pt outline, stroke-width 2 (never change between states)
Label10-11pt, sentence case, weight 400 in both states
ActiveIcon + label both turn accent color
InactiveBoth gray (text-muted)
Border1px top border, subtle
Components · Inputs

Search bar

56pt height — same as CTA buttons. Never 44pt and never pill-shaped. Sits at top of Library and Account tabs (no top app bar above search).

9:41
Tap to focus → soft keyboard appears
Height56pt (same as CTA buttons, never 44pt)
Corner radius12pt (moderate, NOT pill)
Border1px subtle
BackgroundSurface default (white)
Leading icon18pt search, text-tertiary color
Font size16pt (iOS zoom prevention floor)
Components · Data display

List rows

Workhorse of mobile UI. Used for every list — courses, assignments, notifications, settings. Min 56pt height to maintain 44pt hit area. Leading icon, title, optional subtitle, trailing chevron.

Q2 Compliance training In progress · 42%
Leadership essentials Completed Mar 8
New compliance pathway 2 hours ago

Spec

Min height56pt
Horizontal padding16pt
Leading element36×36 circle (avatar, icon, status) — 14pt gap to body
Title16pt, weight 600
Subtitle13pt, text-tertiary
TrailingChevron right or value text + chevron
Divider1px subtle, between rows (not after last)
Components · Actions

Floating Action Button (FAB)

Anchored bottom-right above the tab bar. Used for the screen's primary creation action when it deserves persistent prominence — e.g. "Report incident" in Safety, "Ask AI" in Learner Portal.

9:41
Incidents
Slip near loading dock Reported 2h ago

One FAB per screen max. Position 16pt from screen edges, above the tab bar. Size: 56×56 circle. Background: accent. Icon: 24pt white.

Components · Overlays

Action sheet

A smaller variant of the M3 sheet — used for short lists of contextual actions (e.g. "Share", "Edit", "Delete"). Slides from bottom, doesn't take full height. See M3 shell for the filter-style sheet pattern.

  • Used for ad-hoc actions on a specific item (tap-and-hold or ⋯ menu)
  • Title at top, list of actions below, Cancel at bottom (iOS pattern)
  • Destructive actions: red text
  • Dismiss via swipe down or Cancel
Components · Feedback

Snackbar / Toast

Brief message at the bottom of the screen. Used for confirmations and non-critical errors that don't block the user. Auto-dismisses after 4 seconds.

9:41
My learning
Course bookmarked

Position: 76pt from bottom (above tab bar). Auto-dismiss 4s. Single optional action. Dark background for contrast against any page surface.

Components · Feedback

Banners — offline, access-locked

Persistent system-state messages that sit below the top app bar. Two main variants: Offline (network down) and Access locked (subscription / tenant restriction).

9:41
My learning
You're offline — showing cached content
This course requires Premium access
Components · Feedback

Network status banner

Full-width persistent banner anchored directly above the tab bar. Appears automatically when connectivity changes. Two states: Not connected (red) and Connected (green). "Connected" auto-dismisses after 3s.

Not connected

9:41
Hello [User], access your assignments here
Go to assignments
Course: On-De...
Challenges N...
In progress
Not connected

Connected (auto-dismisses 3s)

9:41
Hello [User], access your assignments here
Go to assignments
Course: On-De...
Challenges N...
In progress
Connected
Not connectedRed #EF4444 · wifi-off icon · persists until restored
ConnectedGreen #22C55E · wifi icon · auto-dismisses after 3s
PositionFixed, directly above tab bar · no border-radius · full width
Height48pt · icon + label centered
Components · Feedback

Updating state

Indicates background data refresh on screens like My Progress. Non-blocking — content stays visible and interactive. Peach strip below top bar. Two variants: pill label or spinner.

Variant A — pill

9:41
My progress
Updating
My points Recently viewed Courses
150 Total pts
Daily activity: 50 pts
Course completion: 100 pts

Variant B — spinner

9:41
My progress
My points Recently viewed Courses
150 Total pts
Daily activity: 50 pts
Course completion: 100 pts
Strip backgroundPeach --kcds-color-primary-tint
Variant AWhite pill, "Updating", 14pt regular — default
Variant BOrange spinner — when update takes >2s
ContentFully visible and interactive — not dimmed, not blocked
Components · Learner Portal

Downloads screen

M2 screen. Accessible from My learning → Downloads → "Show all". Lists downloaded courses with real thumbnails, storage info, per-item delete, and "Delete all" sticky CTA (peach fill).

9:41
Downloads
Available free space: 200 MB
Planning, Scheduling Series
Course: On-Demand
Planning, Scheduling, and Contracts Series
Beginner | 1h 25m | Author: Jeremy Scott
En 5/12 · 60MB
Become a Data Analyst
Course: On-Demand
Become a Data Analyst
Beginner | 1h 25m | Author: Jeremy Scott
En 5/12 · 60MB
ShellM2 — back arrow, no tab bar
ThumbnailReal photo (not gradient placeholder) — object-fit: cover
Delete icontrash-2, 18pt, accent color · shows confirmation modal
DownloadingOrange spinner replaces trash icon · same card layout
Delete all CTAPeach fill, dark text, sticky bottom — same as "Cancel request"
Components · Actions

Active filters

When filters are applied: filter icon gets an orange dot badge, a removable chips row appears below the toolbar. Search replaces the topbar. Empty state text adapts to the cause.

Filters active — no search

9:41
Assignments
Available Passed Access locked
Total 0
Learning path Not started In progress
No Assignments Found
No assignments match your current filter selection. Try adjusting your filters to see more results.

Search + filters — no results

9:41
product3333333
Total 0
Learning path Not started
No Assignments Found
We couldn't find any assignments matching your search and filters. Try adjusting your criteria.

Search only — no results

9:41
product3333333
Total 0
No Assignments Found
We couldn't find any assignments matching your search. Try different search terms.
Filter icon dotOrange dot 8pt, top-right of icon box, white 2px gap — shown only when ≥1 filter active
Chips rowAppears when ≥1 filter active · each chip: label + × · horizontal scroll · never wraps
× removesOne filter only — NOT all filters at once
Search = topbar replacementEntire topbar replaced by search input + "Cancel" button (orange text)
"Clear search and filters" CTAShown ONLY when BOTH search query AND filters are active simultaneously
Empty state copyAdapts to cause: filters only / search+filters / search only — three distinct messages
Components · States

Empty states

Used when a list, page, or section has no data to display yet. Always include an icon, a title (Title Case), short explanation, and an action when one is possible.

9:41
My learning

No Courses Yet

Browse the Library and bookmark courses to start learning.

Components · Data display

Course card

Full-width vertical card used in Library catalog. Image cover on top, body with title, metadata, status. Vertical not horizontal — Library was redesigned to vertical cards for better thumb-scrolling and visual breathing room.

Business courses
Q2 Compliance training
Beginner · 4h 12m · Sarah Chen
In progress 42%
Business courses
Leadership essentials
Intermediate · 2h 30m · James Morris
Passed
Cover imageFull width × 140-200pt height
Card1px border-default, 16pt corner radius, no shadow
Body padding12pt vertical, 16pt horizontal
Type label13pt text-tertiary
Title17pt weight 700, line-height 1.3
Meta row13pt text-tertiary: level · runtime · author
Status row14pt weight 600: icon + label, color per state
Canonical · Learner Portal

Learner home

Home tab (M1). Orange hero banner with greeting and bell. Sections: Continue learning (horizontal scroll cards), My progress (donut + details), Recommended (list rows), Upcoming events. 5-tab bar.

9:41
KnowledgeCity
Welcome back, Kseniia
3 assignments due this week
Continue learning Show all
Compliance
Q2 Compliance training
Lesson 8 of 18 · 42%
Leadership
Leadership essentials
Started yesterday
Recommended for you Show all
Data analysis fundamentalsSuggested by your manager
Excel for business analystsPopular this week
Upcoming events Show all
Mar 28
Safety leadership workshop
10:00 AM · Zoom · 45 seats left
Canonical · Learner Portal

Library

Library tab (M1). No top app bar — search is the header. Tint gap + big white block pattern: orange-tint page, white rounded block with category chips, filter row, and vertical course cards.

9:41
Search courses...
Filters
1,339 courses
Course: On-demand
Strategic decision-making
Beginner · 3h · Sarah Chen
Course: On-demand
GDPR compliance essentials
Intermediate · 2h 15m · James Morris
Canonical · Learner Portal

My learning

My learning tab (M1). Orange hero with greeting + horizontal scroll assignment cards. Below hero: rounded white sections for Continue learning, My progress (donut), Recently viewed hscroll. No tint page — white background, orange hero.

9:41
My learning
3 due this week · 1 overdue
Browse new courses
Course: On-demand
Q2 Compliance training
42% · Due Mar 30
Course: On-demand
Leadership essentials
Started yesterday
Continue learning Show all
Course: On-demand · Compliance
Q2 Compliance training
Lesson 8 of 18 · 42%
My progress Details
60%
12 completed · 3 in progress · 1 overdue
12
Done
3
Active
1
Overdue
Components · Content

Page banner

Full-width promotional or motivational banner. Edge-to-edge, no side margins, rounded corners. Left: icon + title + subtitle. Right: decorative illustration. Background can be any brand color — not just orange.

9:41
My learning
No Activity for Today!
Kickstart your learning to gain valuable knowledge and collect more points.
Impressive work today!
Keep that energy up!
Available offline
5 courses downloaded for offline use.
WidthEdge-to-edge — no horizontal margin. border-radius: 16px all corners.
Left zoneIcon (32×32, soft bg square, 8px radius) + title (700) + subtitle (400)
Right zoneDecorative illustration or icon placeholder, 70×70, border-radius: 12px
BackgroundsPrimary color (solid), white/neutral (with border), dark. Avoid primary-tint as banner bg — it looks wrong with non-orange accents.
CTASeparate full-width button below banner — not inside it
Canonical · Learner Portal

Course detail — Instructor-led / Webinar

Course detail (M2). Hero thumbnail, content type label + inline type badge, status badge, sticky CTA. Sub-tabs: Events (active) / Overview / Files. Events tab shows live event card with meta table and "Join the class" CTA.

12:30
Conflict management
BRAND
Course: Instructor-led Webinar
Conflict management
Passed
WEBINAR · ZOOM
Live now
2024-03-03 · 11:11
Comprehensive Marketing and Research Analysis: Exploring...
This is very interesting lecture with many interesting events this is very interesting lecture...
Course nameIntegrated Marketing and Research... InstructorOliver Johnson Runtime1h 30m Remaining seats50 of 100
Content type label"Course: Instructor-led" text + inline Webinar badge (orange fill, white text, UPPERCASE, border-radius: 4px)
Sub-tabsEvents / Overview / Files — horizontal scroll if needed. Events tab shows the event card.
Event card badgeNeutral surface-canvas background, dark text, UPPERCASE — NOT orange fill
Live nowPulsing orange dot + "Live now" text, orange, font-weight: 700
Meta table2-column grid: label (tertiary, 90px) + value (primary). font-weight: 400 on values.
CTA"Join the class" — primary orange. Context-aware: "Show certificate" when passed, "Join the class" when live.
Tab barHidden — M2 shell, user navigated into course from a tab
Canonical · Learner Portal

Account

Account tab (M1). No top app bar — avatar + name acts as the header. Mode B: orange-tint page background. White edge-to-edge cards float on tint. Icons bare accent-color (no container box).

9:41
K
Kseniia Zaitseva
kseniia.z@example.com
Profile
Notifications
Appearance System
Help & support
Sign out
Canonical · Learner Portal

Event information

Event detail (M2). Page-bg background. ONE white edge-to-edge card with all content: event badge (UPPERCASE), title, meta table, Cancel request (neutral surface fill, dark text), approval chain. Back arrow is dark — not orange. Tab bar hidden.

9:41
Event information
WEBINAR · ZOOM
Pending
Mar 3, 2025 · 11:11
Comprehensive Marketing and Research Analysis
An in-depth session covering brand positioning, market trends, and data-driven insights for product growth.
InstructorSarah Chen Runtime1h 30m Remaining seats12 PlatformJoin Zoom
Approval status
Ivanov I.I. Approved (Mar 1, 2025)
Petrov P.P. Pending · Current approver
Sidorov S.S. Next
Components

Sub-tabs / Segmented control

Horizontal row of equal-width tabs used to switch between views inside a screen. Not the shell tab bar — sub-tabs live inside a page section. Used in My learning (Current / Upcoming / Past), Events detail, course detail (Overview / Content / Reviews), LMS Admin lists.

PropertyValue
Layoutdisplay: flex — each tab flex: 1 (equal width). Never wrap to second line.
Height56px — same as CTA buttons. Never 40px or 48px.
Border radius12px — NOT pill (9999px)
Font size / weight16px / 400 (Regular) — never bold
Active tabbackground: var(--kcds-color-primary-tint) + border: 1px solid transparent (NO visible border on active)
Inactive tabbackground: var(--kcds-color-surface-default) (white) + border: 1px solid var(--kcds-color-border-default)
Overflow ruleIf tabs don't fit: overflow-x: auto + flex-shrink: 0 on each tab. Use flex: 1 ONLY when count is fixed and labels are short.
.tab-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 20px;
  height: 56px;              /* same as CTA buttons — always */
  border-radius: 12px;       /* NOT pill, NOT 8px, NOT 16px */
  font-size: 16px;
  font-weight: 400;          /* Regular — never bold */
  white-space: nowrap; cursor: pointer; flex-shrink: 0;
}
.tab-btn--active {
  background: var(--kcds-color-primary-tint);
  border: 1px solid transparent;  /* NO visible border on active */
  color: var(--kcds-color-text-primary);
}
.tab-btn--inactive {
  background: var(--kcds-color-surface-default);
  border: 1px solid var(--kcds-color-border-default);
  color: var(--kcds-color-text-primary);
}
Sub-tabs — 56px, flex:1
Don't: pill-shaped chips for sub-tabs · height below 56px · flex-wrap: wrap (tabs never wrap) · font-weight: 700 on tab labels · visible border on active tab.
UX patterns

Page composition

Rules for structuring a mobile screen: scroll container, section cards, horizontal rows, and drag-to-scroll. Based on approved canonical screens (My Learning, USMP Skills catalog).

Scroll pattern

Scroll works through body, not an inner container. min-height: 100vh on .phone lets content grow beyond the screen; body scrolls naturally.

.phone {
  width: 390px;
  min-height: 100vh;          /* NOT height — body scrolls */
  background: var(--bg-canvas);
}

.scroll {
  padding: 12px 0;
  padding-bottom: calc(var(--tab-height) + env(safe-area-inset-bottom) + 12px);
  display: flex; flex-direction: column; gap: 0;
  /* NO overflow-y — body scrolls */
}

Section cards

Each logical section is a separate white card. Cards are edge-to-edge — no horizontal margins. An 8px gap between cards reveals the canvas background as a visual separator.

.card {
  background: var(--bg);
  border-radius: 16px;
  border: 1px solid var(--border);
  overflow: hidden;
  margin-bottom: 8px;
  /* NO margin-left/right — edge-to-edge */
}

.card-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 16px 16px 12px;
}
.card-title { font-size: 17px; font-weight: 700; }
.card-link  { font-size: 13px; color: var(--kc-accent); font-weight: 400; }

Horizontal scroll rows

Horizontal content rows (skill cards, thumbnails, category pills) use drag-to-scroll JS — required for mouse/trackpad users.

.hrow {
  display: flex; gap: 12px; overflow-x: auto;
  padding: 0 16px 14px;
  cursor: grab;
}
.hrow::-webkit-scrollbar { display: none; }

/* Drag-to-scroll JS (required) */
function drag(el) {
  let down=false, sx, sl;
  el.addEventListener('mousedown', e=>{
    down=true; el.classList.add('drag');
    sx=e.pageX-el.offsetLeft; sl=el.scrollLeft; e.preventDefault();
  });
  el.addEventListener('mouseleave',()=>{ down=false; el.classList.remove('drag'); });
  el.addEventListener('mouseup',  ()=>{ down=false; el.classList.remove('drag'); });
  el.addEventListener('mousemove', e=>{
    if(!down)return;
    el.scrollLeft=sl-(e.pageX-el.offsetLeft-sx)*1.2;
  });
}
document.querySelectorAll('.hrow').forEach(drag);
Don't: margin: 0 16px on cards · overflow-y: auto + height: 100vh on .phone · all sections in one big card · font-weight: 700 on buttons (only on headings) · sections without a card wrapper.
UX patterns

Page backgrounds

KC mobile uses two background modes. The product and screen role determine which to use.

Mode A — White

Plain white page. Cards have border + optional box-shadow.

Use for: content screens, dense lists, library category, course detail, admin tables.

Card with border
Card with border
Mode B — Tint

Accent-tint background. White cards edge-to-edge, NO border, NO shadow — tint gap separates.

Use for: Account, settings, profile, utility hubs.

Card — no border, no shadow
Card — no border, no shadow

Mode B — border-radius rules

Tint above card?Tint below card?border-radius
Yes Yes20px (all corners)
No Yes0 0 20px 20px
Yes No20px 20px 0 0

Dark theme

Both modes flip in dark theme. Mode A: --bg → near-black #0A0A0B. Mode B: --kc-accent-light#0F0F11. Pattern logic identical — only colors change. Always activated by [data-theme="dark"], never by prefers-color-scheme media query.

Don't: Mode B card with horizontal margin · Mode B card with border or shadow · tab-bar clearance padding on .scroll for a Mode B panel (creates white strip) · auto-applying prefers-color-scheme: dark.
UX patterns

Iconography

KC mobile uses Lucide icons exclusively. Library pinned at @0.460.0 — same as web. All icons SVG outline style.

No emoji. All icons must be Lucide SVG outline. Emoji render inconsistently across iOS / Android / Windows, don't adapt to dark theme, and break RTL layouts. Exception: user-generated content (a learner's own text that contains emoji). Applied retroactively — replace any emoji with the equivalent Lucide icon.

Size table

ContextSize
Tab bar icons24px
Top app bar actions20px
List row leading icon18px
Badge / count12–14px
Category icon (in cards)22px
Card thumbnail icon28–32px
Inline / body text icon16px

Top app bar padding rule

Top bar icon alignment requires explicit margin — don't rely on parent padding alone.

.topbar__title {
  margin-left: 16px;   /* explicit — don't rely on parent padding */
}
.topbar__actions {
  margin-right: 8px;   /* explicit — keeps action icons in position */
  display: flex; gap: 4px; align-items: center;
}

Account tab icons — no container box

Icons in the Account tab list rows are plain accent-color SVG — no background container box. Differs from other contexts where icons have a tinted square/circle container.

Other tabs
Account tab
Account tab
UX patterns · LMS Admin

Available on web

When a section exists on web but is too complex for mobile — don't hide it from navigation. Show a dedicated screen that tells the user where to go. Positive framing: available on web, not not available on mobile.

Reports builder
This section is available on the web version
Reports builder — available on web screen
.web-only-screen {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 40px 32px; gap: 16px; text-align: center;
}
.web-only-icon {
  width: 64px; height: 64px; border-radius: 16px;
  background: var(--kc-accent-light);
  display: grid; place-items: center;
  color: var(--kc-accent);
}
.web-only-title  { font-size: 20px; font-weight: 700; }
.web-only-desc   { font-size: 15px; color: var(--text-secondary);
                   line-height: 1.5; max-width: 260px; }

LMS Admin uses this pattern for: Reports builder, Certificate templates, Account settings, Category manager, Communication center, TNA, Question bank, Import data.

Rule: the section stays in navigation (More / sidebar) — it's never hidden. The user knows the feature exists and where to find it.

KC-unique components

Activity feed

Chronological log of learning actions with points earned. Used on My Progress → My points tab. Groups events by date with group headers.

Today · August 5
Course passed
Creating a Business Continuity Plan
+ 130 pts
Event attended
Safety leadership workshop
+ 20 pts
Friday · August 2
Course viewed
Exceeding Customer Expectations
+ 42 pts
Activity feed — date groups + event rows
ElementSpec
Date header format"Today · August 5" / "Friday · August 2"
Date header14pt regular, text-tertiary color
Event icon20px, type-specific color (accent or green for events)
Event type label14pt 500, same color as icon, sentence case
Content name14pt regular, black, underlined (tappable)
Points14pt 500, accent color, trailing, "+ N pts"
Row min-height56pt
Row separator1px rgba(0,0,0,0.06)
KC-unique components

Approval chain

Visualizes a multi-step approval workflow for event registration. Appears on Event Information screen (M2) when an event requires manager/HR approval. KC-unique — no web equivalent.

Approval status
Ivanov I.I.
Approved (Mar 1, 2025)
Petrov P.P.
Pending
Sidorov S.S.
Next
Approval chain — Approved → Pending → Next
StateIconColor
Approvedcircle-checkGreen #22C55E
Pending — currentclockGray #9CA3AF
Next — lockedlockLight gray #D1D5DB
Rejectedcircle-xRed #EF4444

Cancel request button (when status is Pending): peach tint fill — background: var(--kc-accent-light), dark text, NO red, NO outlined destructive. Height 56pt, full width, border-radius: 24px, font-weight: 400. Scrolls with content, requires confirmation dialog.

KC-unique components

Course accordion

Shows course content structure: modules with collapsible lesson lists. Used on Course detail → Content tab.

Working in After Effects CC
Lessons: 4 · 9m 58s
Introduction
2m 29s
Reading material
3m 15s
Animating complex interfaces
Lessons: 3 · 7m 12s
Course accordion — expanded + collapsed modules
ElementSpec
Module title15pt / 500 / primary
Module meta"Lessons: N · Xm Xs" · 12pt regular · tertiary
Module row height56pt
Chevron∧ expanded / ∨ collapsed · 20pt · tertiary
Lesson title14pt regular · primary
Lesson duration12pt regular · tertiary
Lesson row height56pt
Expand animation200ms smooth
Status — donecircle-check · 18px · green #22C55E
Status — not donecircle · 18px · light gray #D1D5DB
KC-unique components

Lesson strip

Horizontal strip of lesson thumbnails at the top of the video player screen. Shows all lessons in the current module with completion status. Allows quick navigation between lessons.

PropertyValue
Thumbnail size48×48pt
Corner radius8pt
Completion badgeGreen circle-check overlay, bottom-right
Current lesson indicatorAccent-color underline, 4pt height, below thumb
Strip height72pt (48pt thumb + 12pt padding each side)
ScrollHorizontal, no visible scrollbar
KC-unique components

Offline banner

Indicates a course is available offline. Appears on Course detail → Content tab above the course accordion. Tappable — navigates to Account → Downloading settings.

Course is available offline
PropertyValue
BackgroundAccent at 5% opacity
Iconcloud-download · 20px · accent color
Text"Course is available offline" · 14pt regular · accent color · sentence case
Trailingchevron-right · 16px · accent color
Height48pt
Corner radius12pt
Tap destinationAccount → Downloading settings

When to show: course marked as downloadable by admin AND at least one lesson is available offline. Show "Download for offline" CTA instead if no content is downloaded yet.

UX patterns

Typography — link rules

Underline = tappable link. Always. No exceptions. Never use text-decoration: underline for visual decoration — if text is underlined, it must have a tap handler and destination.

Link typeStyle
Inline tappable (course name, event name)Underlined · color: var(--text-primary) · NOT blue · NOT orange
"Show all" / navigation promptsNo underline · color: var(--kc-accent) · font-weight: 400
Destructive link (e.g. remove item)color: var(--kcds-color-status-error) · no underline
/* Tappable link — body text color, underline only */
.link {
  text-decoration: underline;
  text-underline-offset: 2px;
  color: var(--text-primary);   /* NOT blue */
  cursor: pointer;
}

/* "Show all" / section action */
.link--action {
  color: var(--kc-accent);
  text-decoration: none;
  font-weight: 400;
}
UX patterns

Horizontal scroll

overflow: hidden on any ancestor of a horizontal scroll container will silently clip the content and prevent swiping. This is the single most common cause of broken carousels on mobile previews.

Rule: overflow: hidden is only allowed on leaf nodes that contain images/media. Never on layout wrappers, page containers, sections, or any ancestor of a scroll row.
/* WRONG — any of these breaks horizontal scroll */
.phone   { overflow-x: hidden; }
.hero    { overflow: hidden; }
.section { overflow: hidden; }

/* CORRECT — overflow:hidden only on leaf image containers */
.card  { overflow: hidden; }  /* clips thumbnail to rounded corners */
.thumb { overflow: hidden; }

/* Correct horizontal scroll */
.hscroll {
  display: flex; gap: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  margin-right: -16px;   /* bleeds to right edge */
  padding-bottom: 4px;
  cursor: grab;
}
.hscroll.dragging { cursor: grabbing; user-select: none; }
.hscroll::-webkit-scrollbar { display: none; }
.hscroll > *:last-child { margin-right: 16px; }
.hscroll > * { flex-shrink: 0; }  /* items never compress */

Drag-to-scroll is required on ALL overflow-x: auto rows — not just course cards, but also category chips, filter chips, and any other horizontal row. Without it, mouse/trackpad users can't scroll.

function enableDragScroll(el) {
  let isDown = false, startX, scrollLeft;
  el.addEventListener('mousedown', e => {
    isDown = true; el.classList.add('dragging');
    startX = e.pageX - el.offsetLeft; scrollLeft = el.scrollLeft;
    e.preventDefault();
  });
  el.addEventListener('mouseleave', () => { isDown = false; el.classList.remove('dragging'); });
  el.addEventListener('mouseup',    () => { isDown = false; el.classList.remove('dragging'); });
  el.addEventListener('mousemove',  e => {
    if (!isDown) return;
    el.scrollLeft = scrollLeft - (e.pageX - el.offsetLeft - startX) * 1.2;
  });
}
// Apply to ALL horizontal scroll rows
document.querySelectorAll('.hrow, .hscroll, .cats, .chips').forEach(enableDragScroll);
UX patterns

Typography weight rule

KC mobile uses only two weights: 400 (Regular) and 700 (Bold). 600 (SemiBold) is not used.

700 Bold — allowed only on:
  • Course titles, event titles (main heading of a card)
  • Page / screen section headers (h1, h2 structural headings)
400 Regular — everything else:
  • Badges (event type, status badges)
  • Status text ("Pending", "Approved", "Live now")
  • Meta values (Instructor, Runtime, Remaining seats)
  • Tab labels, button labels, nav labels
  • Settings row labels, filter chips
  • "Today" button, "Show all" links
  • All secondary / helper text
Event type badge: text-transform: uppercase · letter-spacing: .04em · font-weight: 400 · always uppercase regardless of source data. Background: var(--kcds-color-primary-tint) (NOT gray surface-canvas) · border-radius: 8px (NOT pill) · border: none · font-size: 11px.
UX patterns

Content type labels

All content must show a type prefix so users know what they're looking at before tapping in. Never drop the prefix to save space.

TypeLabel format
On-demand courseCourse: On-demand
Instructor-led courseCourse: Instructor-led
Instructor-led + WebinarCourse: Instructor-led · Webinar
Instructor-led + ZoomCourse: Instructor-led · Zoom
Learning pathLearning path
Skill pathSkill path
Rules: always show the prefix, never drop "Course:" · sentence case after colon: "Course: On-demand" not "Course: On-Demand" · when course type + delivery platform both apply, join with " · " · never a separate badge for the delivery platform — include in label.
Reference

Mobile vs web — delta table

Where mobile diverges from kc-design-foundations (web). All other rules are identical — cross-link to web skill rather than re-documenting.

TopicWeb (kc-design-foundations)Mobile (kc-ds-mobile)
DensityComfortable / CompactComfortable only — no compact
Hit area floor44×44 CSS px, pointer:coarse44pt always — no media query
NavigationPattern A/B + hamburgerM1 bottom tab + M2 back stack
Hamburger drawerDefault mobile transformationWeb-only, not in native apps
Motion duration200ms default150ms default
Motion styleCSS cubic-bezierSpring (translate) + timing (opacity)
HapticsN/ADefined: light / medium / heavy / success / warning / error
SheetsOptional patternPrimary pattern for overlays
Typography headingsFull scale (H1 up to 80px)Reduced scale (H1 max 28–32pt)
Bottom tab barOptional for some productsAlways 5 tabs, fixed order
Dark theme activationdata-kcds-theme="dark"Same — data-theme="dark". Never auto prefers-color-scheme.
Scrollbar stylingThemed via CSS scrollbar-colorHide all scrollbars (display:none)