@minmaps-dev/kiosk-design-system
v0.1.0
Published
Visitor-facing kiosk design tokens for the MinuteMaps SDK. VADS-based.
Readme
MinuteMaps Kiosk Design System
User-facing touchscreen wayfinding kiosks for hospitals, built on the MinuteMaps SDK (MapLibre GL JS). This system covers everything around the rendered map — header, floor selector, destination search, on-screen keyboard, category browse, directions panel, accessibility controls, and the idle/attract state.
This is not the operator-facing CMS design system. The two products share system shape (semantic tokens, ShadCN-style architecture) but no visual values. Operators get info-density; visitors get calm and clarity.
The person standing at the kiosk
A 68-year-old visiting her husband in cardiac recovery. A dad with two kids trying to find radiology before an appointment. A first-time visitor whose English is their second language, holding a paper referral. They are stressed, unfamiliar with the building, often in a hurry, sometimes scared. They have never used this kiosk before and will not read instructions.
Every design decision serves one of these outcomes:
- They find where they're going quickly.
- They feel less stressed than when they walked up, not more.
- Nothing about the experience feels clinical or cold.
Product context
- Deployment: hospital lobbies, wing entrances, elevator banks. Wall-mounted portrait, occasionally landscape freestanding. ADA-mounted — top of screen is out of reach for wheelchair users.
- Sessions: 30s–2min, frequently abandoned. Full reset on inactivity. Zero PII persists between users (HIPAA-adjacent — no search caching across sessions).
- Input: touch only. No mouse, no keyboard, no hover states. All text input via on-screen keyboard.
- Audiences: patients, visitors, family. Wide age and ability range. EN/ES at minimum.
- Stack (fixed): React + TypeScript, Next.js App Router, ShadCN/ui "new-york" with neutral base, CSS variables, Tailwind v4, lucide-react, MapLibre GL JS via
minutemaps-sdk.
Index
colors_and_type.css— design tokens, in one file. VADS primitives → shadcn semantic → Tailwind@theme, plus kiosk-only additions (radii, shadows, motion, tap targets) and variant scopes ([data-density="kiosk"],[data-text="lg|xl"],[data-contrast="high"],.dark,prefers-reduced-motion). No legacy aliases — components consume the shadcn layer directly.globals.css— shadcn-convention entry point. Just@importscolors_and_type.css.assets/— logo marks, attract-screen placeholder imagery.preview/— design-system card previews (colors, type, spacing, components, brand). Single token vocabulary throughout.ui_kits/kiosk/— React JSX recreation of the kiosk UI; openindex.htmlfor an interactive click-through.SKILL.md— agent skill manifest for reuse in Claude Code.
Content fundamentals
Tone: a warm, knowledgeable receptionist who has all the time in the world for you. Not clinical, not cute, not clever. Confident and brief.
Voice rules
- Second person, always. "Where are you going?" not "Where would the user like to go?" Never "please" (it's servile); never "sorry" unless something is actually broken.
- Sentence case everywhere. No Title Case On Buttons. Buttons read like spoken phrases: "Start walking", "Show me the way", "Take the elevator".
- Contractions are fine and preferred. "You're here" beats "You are here".
- Short sentences. One idea per line. If a sentence needs a comma, try splitting it first.
- No jargon. "Imaging" not "Radiology Dept. 3B". "Elevator" not "vertical circulation". Medical terms only when they're on the user's paperwork (Radiology, Oncology, Cardiology — those stay).
- Numbers, not words, for wayfinding. "Floor 3", "Room 412", "5 min walk".
Casing
| Context | Rule | Example | |---|---|---| | Buttons, tiles, menu items | Sentence case | "Find a destination" | | Destination names | As labeled in-building | "Emergency Department", "MRI Suite 2" | | Headings | Sentence case | "Where are you going?" | | Status / alert banners | Sentence case, period optional | "Elevator 3 is out of service today" | | Language names | In-language, native script | "English", "Español", "中文", "Tiếng Việt" |
No emoji. No exclamation points.
Hospitals are emotionally heavy environments. Enthusiasm reads as tone-deaf. No 🎉, no 👋, no "Welcome!". The warmth comes from typography, color, and spacing — never from punctuation or decoration.
Specific copy examples
- Home screen headline: "Where are you going?" (not "Welcome to Mercy General" — already on the wall above)
- Search placeholder: "Search for a room, service, or doctor"
- Empty state: "Nothing matches yet. Try a shorter word."
- Error: "We can't find that right now. A front-desk attendant can help."
- Route found: "Imaging is a 4 minute walk." (singular "minute" when it's 1, no "mins" abbreviation)
- Floor change: "Take the elevator up to Floor 3."
- Arrival: "You're here."
- Idle screen: "Tap anywhere to find your way."
- Accessibility toggle on: "Showing wheelchair-accessible routes." (a plain statement, not an alert)
What to avoid
- "Please", "Sorry", "Oops", "Uh-oh"
- "Welcome!", "Hi there!", "Let's go!"
- Title Case on buttons
- Emoji of any kind
- Exclamation points
- "Click" (it's a touchscreen — use "tap")
- Instructional micro-copy that presumes the user is confused ("First, tap the search bar…")
Visual foundations
The system follows the VA.gov Design System (VADS, public-domain federal work, built on USWDS). Government-faithful, calm, accessibility-forward.
Color
Defined in colors_and_type.css as VADS primitives with a shadcn semantic layer wired on top. Components consume the semantic layer (--background, --primary, --card, --accent, …); reach for VADS primitives (--vads-color-*) only when a specific scale step is needed.
- Background is white (
--vads-color-white→--background). - Body text is
#1b1b1b(--vads-color-base→--foreground). Secondary text uses--muted-foreground(--vads-color-gray-medium,#757575); subtle/hint text uses--vads-color-base-light. - Primary action: VA blue
#005ea2(--vads-color-primary→--primary). Press:--primary-press(#1a4480). Soft / selected fill:--primary-soft(#d9e8f6). - Accent (cool secondary): VA primary-alt cyan (
--vads-color-primary-alt-lightest→--accent,--vads-color-primary-alt-darkest→--accent-foreground). Used for amenity tiles, the secondary tint that isn't blue. - Destructive: VA red
#d83933(--vads-color-secondary→--destructive). - Semantic feedback uses VADS verbatim:
--success#00a91c,--warning#ffbe2e,--info#00bde3. Each has a paired--*-soft(the lighter tint) and--*-foreground. - Focus ring is the USWDS yellow
--vads-color-gold-light(#face00) on a0.25remoutline, applied to:focus-visible. - No decorative gradients. Surfaces are flat. Elevation is conveyed through the three-step shadow scale (
--shadow-sm/md/lg).
Typography
- Body / sans: Source Sans 3 (
--vads-font-family-sans) — Adobe's open-source release of Source Sans Pro, exactly the stack VADS uses. Pulled from Google Fonts. - Display: Manrope as the open-source stand-in for the licensed Neulis Sans (
--vads-font-family-display). Used forh1andh2. When licensed Neulis Sans files arrive, drop them into a newfonts/folder and uncomment the@font-faceblock at the top ofcolors_and_type.css— the cascade promotes them above Manrope automatically. - Serif: Bitter (VADS default, available in
--vads-font-family-serif), reserved for editorial body type when needed. - Mono: Roboto Mono.
- VADS scale:
body-small 15·body-medium 17(default) ·body-large 20·h6/h5 15·h4 17·h3 20·h2 32·h1 40. - Kiosk density override:
[data-density="kiosk"]on root bumps body / headings to 22 / 26 / 28 / 44 / 60 px for 4–6 ft viewing. Set inui_kits/kiosk/index.html. - Text-size accessibility:
[data-text="lg"]and[data-text="xl"]multiply body sizes again (in-UI toggle). Layered on top ofdata-density. - Weights: Source Sans 3 400 / 600 / 700. Manrope 500 / 700 for display.
- Line lengths: paragraphs cap at
var(--vads-size-line-length-5)(72ex).
Spacing
USWDS units, --vads-spacing-05 (4px) through --vads-spacing-15 (120px). Component padding starts at 24px on kiosk, never less than 16px. Whitespace around primary actions is deliberate.
Radii
Modest at VADS default (--radius = 8px), generous on kiosk ([data-density="kiosk"] bumps to 16px+). Token names: --radius-sm/md/lg/xl/2xl/pill.
Shadows
Diffused, multi-layer. Never hard drop shadows.
--shadow-smfor resting cards--shadow-mdfor floating panels and the keyboard--shadow-lgfor modals and the language switcher--shadow-inneronly on depressed keys
Borders
Used sparingly. Most separation is via color and shadow. When used:
- 1px
var(--border)for hairline dividers (inside search results, keyboard rows) - 2px
var(--primary)for focused/selected states
Backgrounds & imagery
- No photography in the chrome. The attract screen is the only place real imagery appears, and even then it should be soft, slightly blurred — a hospital lobby at golden hour, or a soft abstract form. Never stock photos of people or medical equipment.
- No patterns, no textures, no noise overlays.
- No illustration of people or characters. If an illustration is needed (empty states), use a simple single-weight line drawing of a neutral object.
Motion
Calm and quick. Never bouncy.
- Duration:
--dur-fast(120ms) for press feedback,--dur-base(200ms) for state changes,--dur-panel(300ms) for panel transitions,--dur-screen(350ms) for full-screen changes. - Easing:
--ease-out(gentle decelerate). No bouncy springs, no shake, no wobble, no elastic. prefers-reduced-motion: all motion durations collapse to 0; transitions and animations are flattened.
Interaction states
No hover (touch device). Focus is replaced by press:
- Press: surface shifts to
var(--press-surface)and scales to0.98over 120ms ease-out. Returns over 200ms. - Selected: 2px
var(--primary)border +var(--primary-soft)fill. - Disabled: 40% opacity, no press feedback.
- Loading: a subtle pulse on the soft surface, never a spinner.
Transparency & blur
- Used only for overlay scrims behind modals:
var(--scrim)withbackdrop-filter: blur(12px). - The on-screen keyboard floats over the map with an opaque surface — not glass — so text is legible.
- Never on primary surfaces.
Layout rules
- Portrait is canonical (1080×1920 target). Landscape (1920×1080) is a supported variant.
- Thumb-reach zone: the bottom 60% of a portrait screen is sacred for primary actions. Header is informational only.
- Footer action bar (Home, Back, Language, Accessibility, Help) is always visible, always 96px tall, bottom-edge pinned.
- Single column for most surfaces. Two-column only for directions + map.
Elevation system
| Level | Use | Surface | Shadow |
|---|---|---|---|
| 0 | canvas | --background | — |
| 1 | cards, tiles | --card | --shadow-sm |
| 2 | floating panels, keyboard | --card | --shadow-md |
| 3 | modals, language switcher | --card | --shadow-lg |
Iconography
Lucide (via lucide-react in code, https://cdn.jsdelivr.net/npm/lucide-static@latest/icons/ in HTML previews; the kiosk UI kit inlines a curated subset in ui_kits/kiosk/Icon.jsx).
- Stroke width:
2pxeverywhere. Never1px(too thin at kiosk distance), never2.5px+(chunky). - Size: three sizes —
28px(inline),40px(tile icon),56px(category hero). Nothing smaller than 28px. - Color: icons inherit text color. Tile icons take their tint from the Tile's accent prop (
primary/accent/success/warning/destructive/neutral). - No filled variants. No emoji. No unicode symbols (×, →, ←, ✓). Always Lucide equivalents.
Required icons (recurring)
search, map-pin, navigation, arrow-right, arrow-left, arrow-up, home, x, check, accessibility, languages, help, clock, qr, heart-pulse, clipboard, coffee, hash, alert, elevator, stairs, plus, minus, type, contrast.
Lucide has no dedicated elevator or stairs glyph. The kit substitutes a custom elevator pictogram and
stretch-verticalfor stairs. If MinuteMaps has custom wayfinding glyphs, drop them intoassets/icons/and we'll swap.
Logo
A simple wordmark + mark are in assets/. Placeholder; replace with the real brand mark when available.
Accessibility commitments
- WCAG AA minimum, AAA for large reading text where feasible.
- In-UI text size toggle: Normal (default) / Large / Extra large. Maps to
[data-text="lg|xl"]on the root; scales type only, layout reflows. Not browser zoom. - High-contrast mode:
[data-contrast="high"]swaps to a darker palette with stronger borders. Toggle in the accessibility panel. - Wheelchair-accessible route toggle: a visible control in the route preview, also in the accessibility panel — not buried.
- Language switcher is always one tap from the footer action bar. Languages are shown as large tiles with native-script labels.
- Focus ring is the USWDS yellow
0.25remoutline.
Sibling system
Not represented here. The operator CMS lives in a separate design system; they share the shape of tokens (semantic naming) but not the values. Do not import from it.
