Maison Design System
Covers both the 18-screen Maison mobile prototype and the IBM × LVMH pitch microsite as one cohesive visual system. Self-initiated work — speculative, never deployed, not commissioned by Louis Vuitton. Tokens, easing, typography, all 18 screens' components, and microsite-specific patterns documented below. Color gaps flagged in the Tokens section are untokenized values hardcoded in the prototype source. (Screen count updated 2026-07-12: two cold-start client screens added — see the Content section.)
All declared tokens in the app :root. The cream is the single cross-surface token shared by the app, the microsite, and the case study.
Cream is the single token shared identically across all three surfaces — app, microsite, and case study — while nearly everything else only lives in one or two of them. It anchors interactive elements, brand marks, and active states throughout the app; --cream-soft is reserved for hover states and italic emphasis. --cream-dim isn't declared as a token in the app source at all — it exists only as a repeated inline rgba() value.
--cream-dim is not declared as a token — it appears inline as rgba(212,184,150,.55). The case study names it --lv-cream-soft. Declare once and replace both.The four swatches below are the app's actual screen-level background layers, from the darkest (labeled --stage here, sitting behind the phone frame itself) up to --navy-2 (tabbar and gradient stops). Card surfaces --surface and --surface-2, documented in the next block, add a fifth and sixth step above these — together the stack creates depth entirely through tonal shift within one navy family, with no separate accent color doing the work.
--stage is not a declared token in app/index.html — #050818 is real (used as the real body background and the first stop of .stage's gradient) but only ever appears as a literal hardcoded hex value, never tokenized. Shown here under a descriptive label for this reference, not as a claim the app declares a --stage custom property.Cards never use a flat fill — every card gradient runs top (--surface) to bottom (--surface-2), a linear-gradient(180deg, …) that reads as a subtle physical lift rather than a color change. This is the pattern behind appointment cards, rec cards, and every other elevated panel in the app.
linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%) — never a flat fill. The gradient reads as a subtle lift, not a color change.Borders throughout the app use cream-alpha rather than white-alpha, keeping every rule and outline warm instead of cool-gray. --line is the default card and input border; --line-2 strengthens it for hover states and featured cards. --line-3 is referenced in the push banner's hover state but was never actually declared in :root — the swatch shown here is an approximation, flagged as a gap rather than papered over.
All three text tokens draw from the same warm white base (#F5F0E6) at different opacities rather than three independently chosen grays — --text for names and primary labels, --muted for metadata, --dim for inactive tab labels and section headings. On cream backgrounds (button labels, badges), text reverses to --navy-deep instead of white, since white would break the token family's warm cast.
#F5F0E6. On-cream text (e.g., button labels) uses --navy-deep for reversal.Three status tones — each desaturated enough to feel like they belong in the navy field, not like system alerts pasted in.
Three semantic colors carry status meaning site-wide: --ok marks the live infra dot and authentication success, --warn covers general caution states, --alert drives the brand-defense alert strip and counterfeit signals. All three stay desaturated enough to sit inside the navy field rather than reading as a system dialog dropped on top of the UI — fills stay at 6% opacity and borders at 20%, never full saturation.
rgba(token, 0.06) for tinted backgrounds and rgba(token, 0.20) for borders — never full saturation fills. This keeps alerts readable without reading as consumer UI error styling.Values used throughout the prototype that are hardcoded rather than tokenized. All four appear multiple times and should be declared before the system is extended.
app/index.html and work/lv.html but are not tokenized in either.Three sepia browns run every avatar gradient and product-adjacent background in the app, but none of the three is declared as a CSS custom property in app/index.html — they appear only as repeated inline hex values. The same three values, slightly off, are independently redefined in work/lv.html's own --lv-sepia-* tokens, which is the reconciliation problem the block-note documents.
--lv-sepia-deep: #2c2317, --lv-sepia-mid: #5a4830, --lv-sepia-warm: #6e5a3e. The app and case study values are close but not identical — pick one set and align.The cream-to-ivory gradient sits behind every product image in the app — piece hero, passport hero, wardrobe cards, and rec cards all use the identical two-stop gradient plus a radial highlight at 50% 30% to simulate ambient light falling on the piece. Like the sepia palette above, neither stop is a declared token; both are inline hex values repeated at each usage site.
linear-gradient(180deg, #F2EBDF 0%, #DCCFB7 100%) with a radial light bloom at 50% 30% and a hatching overlay for atelier paper texture.The push banner's hover state references var(--line-3) in the app's CSS, but --line-3 is never declared anywhere in :root — the property silently falls through to its initial value, so the hover border currently renders as if no border-color were set at all. This panel documents the exact failure and proposes a value one step brighter than --line-2.
var(--line-3) but this token is never declared in :root. The push hover border currently renders with no value (falls through to initial). Proposed value: rgba(212,184,150,0.34) — one step above --line-2.
The app and the case study each solved “a slightly lighter cream for emphasis” independently and arrived at two different answers: the app's --cream-soft is a solid, opaque lighter cream (#E8D5B7), while the case study's --lv-cream-soft is a semi-transparent overlay (rgba(212,184,150,.55)). Both get used for emphasis text, but a solid fill and a transparent overlay behave differently depending on what sits behind them.
--cream-soft as the solid hover value. Add --cream-overlay: rgba(212,184,150,.55) explicitly for transparency use cases.--cream-soft: #E8D5B7 (a solid lighter cream). The case study independently defines --lv-cream-soft: rgba(212,184,150,.55) (a semi-transparent cream). Both are used for emphasis text, but they produce visually different results against dark backgrounds. Pick one semantics: solid lighter cream for hover/emphasis, transparent cream for overlay text.
How the app and microsite token systems relate. The microsite uses a 3rd-party template with its own naming convention; the app uses a custom system. Only cream bridges them natively.
The microsite runs on a third-party marketing template with its own token-naming convention, so of the ten visual roles in this table only cream and the Radikal typeface are genuinely shared between app and microsite — everything else either diverges or exists on only one side. Rows marked “App only” (the ok semantic, the sepia palette) are real product tokens the template has no equivalent for; rows marked “Microsite only” (loader orange, case-study amber) are presentation choices native to the editorial surfaces.
| Visual role | App token | Microsite token | Status |
|---|---|---|---|
| Brand accent | --cream (#D4B896) | --accent--dark: #D4B896 (override) | ✓ Shared |
| Typeface | Radikal (300–700) | Radikal (100–900) + italics | ✓ Shared |
| Navy deep | --navy-deep (#001A49) | loader fill #001a49 | ✓ Shared (inline only) |
| Background | --navy-1 #0E1430 | --base--dark #161616 (template default) | ≠ Diverges |
| Body text | --text #F5F0E6 | --t-bright--dark #FFFFFF | ≠ Diverges |
| Secondary text | --muted rgba(245,240,230,.62) | --t-medium--dark #ACACAC | ≠ Diverges |
| Semantic ok | --ok #9CC8A0 | None | App only |
| Sepia palette | #3a2e1f / #5a4830 (inline) | None | App only |
| Orange loader | None | #ff9933 (loader bg only) | Microsite only · intentional |
| Amber eyebrow | None | --amber #C4892A (case study) | Case study only · not in app |
Radikal is the only typeface in the system — one face carries everything from ultra-thin display to heavy eyebrow labels. Letter-spacing is the primary lever for expressing hierarchy.
Radikal alone carries every register in the system, from a 300-weight 36px welcome headline down to a 600-weight 9px badge label, with letter-spacing doing the work of distinguishing hierarchy rather than switching typefaces. All ten rows trace directly to real screen usage — the Welcome h1, ceremony h2, screen titles, card labels, and body copy sizes are the actual values pulled from app/index.html, not a generic type scale.
for the floor.
has arrived.
One primary curve runs the entire system — sine-in-out. Click any row to see the ball travel the track at that curve. All demos use identical duration and distance so only the shape varies.
Sine in-out (cubic-bezier(0.37, 0, 0.63, 1)) is scoped to a single interaction — the bag-react glow — because it eliminates abrupt starts and stops so that pulse reads as weight rather than a UI snapping into place. The scan sweep and loader fill use plain ease-in-out/ease, not this curve; each curve documented here is scoped to one specific interaction rather than being an interchangeable alternate. Click any row to see the actual curve shape animate at equal duration and distance.
0.37, 0, 0.63, 1) is real but single-use: it's bag-react's glow pulse only. The scan sweep (scan-sweep/scan-shine) and loader fill use the generic ease-in-out and 0.6s ease respectively — not this curve. Correction: "Spring" and "Veil" are both real, but sourced from the case study (work/lv.html), not app/index.html — the app itself doesn't use either curve. "Spring" is .lv-motion-trigger's hover transform (the case study's own motion-lab play button); "Veil" is the case study's page-transition veil.var(--ease-sine) = cubic-bezier(0.37, 0, 0.63, 1)App: bag-react glow only. Also drives the case study's own motion suite (scan/loader/tile, work/lv.html).
var(--ease-out-quint) = cubic-bezier(0.22, 0.61, 0.36, 1)Screen slide (translateY) — forward/back navigation
var(--ease-out-cubic) = cubic-bezier(0.33, 1, 0.68, 1)Screen opacity fade — slightly snappier than slide
var(--ease-weighted) = cubic-bezier(0.32, 0.72, 0, 1)Push banner entry · Face ID progress ring
Case study only — motion-lab play button hover
Case study page veil / hover cursor — deliberate pause
Micro-interactions: hover fills, border colors, opacity
The app has no explicit duration token set — values are inline. The pattern below documents the de-facto scale from source. Click any row to preview the fill timing.
The app declares no duration tokens — every transition-duration value is written inline at its call site, so this scale is reconstructed from source rather than read off a token list. Durations span two full orders of magnitude, from 120ms hover fills up to 2800ms for the bee-mark ring's ambient pulse; click any row to preview the actual fill timing.
--dur-micro: 120ms, --dur-fast: 180ms, --dur-base: 420ms, --dur-nav: 540ms, --dur-ambient: 2400ms.The app uses a de-facto scale from source — no spacing tokens declared. Values below document the recurring patterns.
Like duration, spacing has no declared scale — every value below is a de-facto pattern pulled directly from the app's inline styles. Screen edge padding is the most consistent rule in the system: 22px on every standard screen, stepping up to 32px only on the four full-bleed ceremony screens where content needs more room to breathe; bottom padding is always 96px so content never sits under the tabbar.
No radius tokens declared. Values follow a deliberate logic — rounder for interactive targets, squarer for data displays and luxury product contexts.
Corner rounding follows a deliberate logic rather than one flat default: objects that hold or display the physical product — the viewfinder frame, piece hero, passport hero — stay square or use the standard 16px card radius, while interactive affordances (buttons, chips, badges, the segmented control) are always full pill at 999px. Rounding is reserved for things you tap, never used decoratively on things you're meant to look at.
The defining UX convention of the Maison system. Rather than the app mediating between the SA and the client, the physical object — the bag — becomes the interface. The three-stage pattern spans seven screens across two modes (SA view and client view).
The system's central design logic in three steps: the SA reads context around the piece before the client arrives (Presentation), the camera and scan pipeline confirm the object's identity in real time (Authentication), then an NFC tap transfers that verified story to the client's own phone as a persistent passport (Handoff). Every component in the Object and Passport sections exists to serve one of these three stages.
The same underlying object record renders completely differently depending on who's looking at it: the SA sees the full provenance graph, confidence scores, and client history; the client sees only the plain-language origin story, care instructions, and resale value, with every score and pipeline detail stripped out. The Handoff segmented control is the one UI element that bridges both modes.
- Full provenance graph visible
- Confidence scores + raw pipeline output
- Handoff controls (consent, NFC pair)
- Brand defense + alert context
- Client purchase history + preferences
- Origin story in plain language
- 45-year chronology of the piece
- Care instructions + resale valuation
- No scores, no pipelines, no SA-side context
- The piece speaks; the data disappears
Persistent chrome at the top of every non-ceremony screen. Carries brand identity, contextual icon buttons, and the SA avatar. Collapses entirely in onboarding and ceremony modes.
Every non-ceremony screen carries the same topbar: the LV bee-mark, the current Maison wordmark, a notification bell, and the SA's avatar. The bell's notification dot only appears when a brand-defense alert is active, so an empty bell is a real signal — nothing to review — not a placeholder state.
.topbar is padding: 60px 22px 10px (the 60px accounts for the real device's status-bar safe area) with no own background (it sits on the app's navy body) — this demo uses padding:14px 22px and an explicit var(--navy-1) background instead, since the real safe-area padding would just be empty space in a contained card rather than a status bar.The topbar isn't universal — it deliberately disappears on the onboarding and ceremony screens (Welcome, Face ID, Client Arrives, Tap to Pair) where a full-bleed, single-focus layout matters more than persistent navigation chrome. It returns on Floor mode, every child screen reached from a client or piece record, and both utility tabs.
- Floor mode (home tab)
- Child screens (client card, authenticate, handoff, passport)
- History + Settings tabs
- Welcome + Face ID (onboarding mode)
- Client Arrives (ceremony mode)
- Tap to Pair (ceremony mode)
Three root destinations — Floor, History, Settings — using the absolute-darkest stage color to float above screen content. Tab changes use a fade-only transition (no slide) to signal lateral movement vs. hierarchy navigation.
Floor, History, and Settings are the only three root destinations in the app, and switching between them uses a fade-only transition rather than a slide — a deliberate signal that these are lateral, equal-weight destinations rather than a hierarchy the way pushing into a client card is. The bar sits on --stage, the single darkest value in the system, so it keeps visual mass even against the darkest screen content behind it.
--stage (#050818) — darker than the app background so the bar has mass even on the darkest screens. Hidden in onboarding and ceremony modes. Real .tabbar is position:absolute; bottom:0; left:0; right:0; height:76px; padding-bottom:18px (pinned to the viewport bottom, with padding to clear a device's home-indicator area) — this demo keeps only the padding/layout that renders sensibly inside a contained card.A live data strip below the topbar. Communicates that IBM's provenance graph is actively connected — not a static label but a real-time indicator. Disappears in onboarding and ceremony modes.
The pill's job is to make an invisible backend connection feel present — the pulsing dot and the live latency readout (14ms in this demo) turn “IBM's provenance graph is connected” from a static claim into something the SA can visually verify at a glance. The latency figure uses tabular-nums specifically so the digits don't shift horizontally as the value updates.
rgba(212,184,150, 0.07) — warm but barely visible. The pulsing green dot uses a box-shadow keyframe to breathe. Latency value uses font-variant-numeric: tabular-nums so the number doesn't shift as it updates.The back-button + title pair that opens every child screen. Back button uses a 10px square radius — the only square-ish interactive element in the system, consistent with its role as a navigation affordance rather than an action.
The eyebrow (10px, uppercase, muted) exists for screens whose identity is ambiguous without it — “Client” above Mme. Laurent's name clarifies this is a client record, not a piece record. Section headings elsewhere reuse the identical type spec, but for grouping list items rather than establishing screen identity.
Mme. Élise Laurent
Two semantic types. Primary is always a cream fill — reserved for the single most important action on screen. Ghost is for secondary actions that must be visible but not competing. Both are full-pill (999px).
Cream fill is reserved for exactly one action per screen — the single most important next step, never a secondary or destructive one. Hover lightens to --cream-soft and the active state scales down to 97%, giving the button a slight physical compression on tap rather than a color-only feedback.
--cream. Label: --navy-deep. Hover: --cream-soft. Active: scale(.97). Font: 13px · weight 500 · 0.04em tracking. Never use primary for destructive actions.Ghost buttons pair with a primary action to offer a dismissal or deferral path — “Greet later” beside a presumed “Begin” primary, for instance — and are always positioned as the secondary button in a pair, never standing alone as the main call to action. The border-only treatment keeps it visible without competing against whatever primary fill sits next to it.
--line-2. Label: --cream. Hover: rgba(212,184,150,0.10) fill. Used alongside primary to give a dismissal or deferral option — always the left or secondary button in a pair.Full-width buttons appear only on ceremony screens — Welcome, Pair, Handoff — where there's exactly one action on screen and it needs the largest possible touch target for a moment meant to feel deliberate, not incidental. Pair is the one exception to true full-bleed: it subtracts 64px to leave breathing room from the screen edges during the NFC pairing moment.
Chips are navigable filter elements (Maison affiliation). Badges are non-interactive status labels. Both use the pill shape, distinguishing them from data tiles which use square-ish radii.
Chips show every Maison a client has purchased from across the LVMH group, not just Louis Vuitton — Élise Laurent's real spread here is 22 LV pieces, 5 Dior, 3 Tiffany & Co., 2 Loro Piana. The client's primary Maison gets a stronger fill and border than the others; the piece count is styled as muted metadata rather than bolded, so it reads as a count, not part of the Maison's name.
rgba(212,184,150,0.14) fill + rgba(212,184,150,0.36) border. Default: rgba(212,184,150,0.06) fill + --line border. The count uses muted text and normal weight to feel like metadata, not part of the Maison name.Badges share the same pill shape as chips but are strictly non-interactive labels for card-level state — an arrival status, an alert count, an authentication result — and unlike chips they never carry a count suffix or click behavior. The visual similarity to chips is intentional; the behavioral distinction is what keeps a viewer from expecting a badge to filter something.
Used exclusively on the Handoff screen to manage data consent permissions. The client controls which attributes the SA can see during the session.
Toggles appear in exactly one place in the app: the Handoff screen's consent block, where the client controls which of their own data attributes the SA can continue to see mid-session. Purchase history and preference notes default to on; contact details defaults to off, reflecting that the SA needs the first two to do the job well but shouldn't assume access to direct contact info without it being explicitly granted.
rgba(212,184,150,0.15) track, white thumb. On state: --cream track, --navy-deep thumb. Click any row to toggle the demo. Consent rows use no outer card wrapper — they live inside the Handoff screen's consent block.The only two-panel segmented control in the system — exclusively on the Handoff screen to toggle between SA view and the client's preview of what they'll receive after NFC pairing.
This is the only two-panel segmented control in the entire system, and it exists for one purpose: letting the SA preview exactly what the client's phone will show once the NFC pair fires, before it actually transfers. The active segment renders as a cream badge sitting inside the pill container rather than as a separate button.
--cream fill + --navy-deep text. Container: rgba(212,184,150,0.05) background + --line border + 999px radius + 4px padding. The active segment's fill reads as a badge living inside the pill, not a separate element.The primary list item on the Floor screen. Featured variant marks the next arriving client with a radial cream halo and a stronger border — drawing the eye without using color.
The featured variant — reserved for whichever client is arriving next — adds a radial cream halo and a stronger border, so the SA's eye lands on the most time-sensitive card without any color signaling it. Metadata differs by appointment type: an arriving client shows Maison tenure, a repair shows the service type, a first visit flags itself as referred.
rgba(212,184,150,0.32). Avatar background: sepia gradient (#3a2e1f → #5a4830) — the untokenized sepia palette. Time value uses --cream to separate it from metadata text.The opening element of the Client Card screen — a large avatar and summary line that immediately establishes who this client is across all Maisons.
The client hero opens every Client Card screen with a 64px avatar — the largest in the system, and the only one that gets a full 2px cream border rather than a thin ring — followed by the client's cross-Maison chip strip. Mme. Laurent's version shown here is the fully-populated case; the second instance below is the app's real first-visit client, Sara Hadid — her card omits the chip strip, since there's no cross-Maison history to show, but the "Across the group" heading stays and is followed by a real referral note instead, not left empty.
--cream as a subtle rhythm device.clientCardHadid): Sara Hadid, first visit, referred — zero purchase history. Correction 2026-07-17: an earlier version of this block claimed her card "renders with no chip strip at all... the component correctly omits the section rather than rendering an empty row" — true for the chip strip itself, but incomplete: the real screen keeps the "Across the group" heading and substitutes a real referral quote (.quote, same component documented in Quote Block below) in place of the chips, rather than showing nothing. Quote text and source line are the exact real strings from app/index.html.Horizontal scroll strip showing the client's owned pieces. Cards use the ivory product background — the same palette as piece hero and passport hero. Scroll snaps to card-start. Scrollbar hidden.
The wardrobe strip is the one place in the client card where multiple owned pieces show at once — five cards plus a “26 more” count that keeps Élise Laurent's real 32-piece total honest without cramming every item into the scroll. Two of the six cards (Diane and Iconic Bracelet) open a real Piece Detail using the same PIECES-driven mechanism as the Recommend cards; the other four still show a placeholder toast, a genuine scope boundary rather than a bug.
#F2EBDF → #DCCFB7) with a radial highlight at 50% 30% simulating ambient light on the piece. Overflow is margin: 0 -22px; padding: 0 22px in the app to bleed edge-to-edge while items start at screen padding.PIECES-driven mechanism as the Recommend screen's cards) — Diane specifically demonstrates a retired-line piece, whose certificate stays active after the model itself is discontinued. The other four still show a one-line toast; no PIECES entry has been written for them, a known scope boundary rather than a bug.2-column grid of key metrics on the Client Card. Numeric values use tabular-nums. Cream emphasis on the most significant number in each tile.
Four key metrics render as a 2×2 grid rather than a list, so an SA can read a client's lifetime spend, visit recency, piece count, and preferred SA in one glance. The two “key” figures — lifetime value and piece count — get cream emphasis, while the other two stay plain, so the eye lands on the numbers that matter most.
<em> styled to --cream to make the number immediately scannable. Tile background is the same rgba(212,184,150,0.04) as data rows — a very warm near-invisible tint.The same four-cell grid renders completely differently for a client with no history: an em dash instead of a zero or blank cell, deliberately, since a zero would misread as a measured value rather than “nothing recorded yet.” This is Sara Hadid's real cold-start card — referred by Place Vendôme, zero visits, no lifetime figure to show.
Horizontal key-value pair used in the Piece Detail screen to show object metadata. Each row is a standalone card at 10px radius.
Data rows present a piece's core metadata as a stacked list of key-value cards rather than a table, each its own 10px-radius card. Reference and material stay in plain text; year and atelier — anything a client could use to trace the piece's provenance — get cream emphasis, the same treatment fact-grid gives its most important numbers.
--cream emphasis. 6px gap between rows so they read as a list, not a table.SA observation captured from a prior visit. The cream left border is the single strongest decorative use of the brand color in the system — a thin vertical stripe that carries the SA's voice without overshadowing the text.
The quote block is the strongest use of the cream accent as pure decoration in the system — a 2px left border carrying a captured SA observation without competing with the italic text it frames. It exists to surface institutional memory: what one SA learned about a client on a prior visit, available to whichever SA sees them next.
--cream, left side only. Text is italic weight 300 with rgba(212,184,150,0.55) — the unresolved cream-dim token. The source line uses 0.10em tracking and muted color to read as provenance, not byline.The exact same component does double duty for clients with nothing on record — rather than hiding the block or inventing a plausible-sounding note, it states the absence directly, in a non-italic register that reads as fact rather than overheard color. Having nothing to say gets the same visual care as having something to say.
Used in both the Floor screen's "Recently" strip and as full list items in the History tab. A compressed appointment card — smaller avatar, tighter type, value label right-aligned.
History items are a compressed version of the appointment card — smaller avatar, tighter type, a right-aligned value label (Service, Auth) in place of a time. They appear both as a “Recently” strip on the Floor screen and as full list items in the History tab, so the same compact format has to work as both a glance and a browsable list.
IBM Granite-generated product suggestions on the Recommend screen. The "why" field is the key AI signal — always italic, cream-dimmed, and framed with quotation marks to distinguish machine reasoning from factual data.
Three real recommendations for Mme. Laurent — Alma BB, Side Trunk MM, LV Bloom Bracelet — each with a “why” line citing something specific from her actual purchase history (structured silhouettes since 2022, the Diane she retired, her 2024 Iconic bracelet) rather than a generic pitch. The product image area renders the ivory gradient in this demo; in production each card carries a real product photograph.
app/index.html. The real recommend screen shows exactly three cards for Mme. Laurent (Alma BB €1,790, Side Trunk MM €3,500, LV Bloom Bracelet €395), matching this block's "Three suggestions" title for the first time. The "why" italic uses rgba(212,184,150,0.55) — the unresolved cream-dim token. Opening/closing guillemet-style quotes are added via ::before/::after pseudo-elements in --cream.A compact disclosure bar that appears below AI-generated recommendation cards. Its presence signals that the AI's output passes IBM's responsible AI governance — the dot turns this from a label into a live indicator.
The governance tag is the disclosure that follows every AI-generated recommendation — a compact strip naming IBM watsonx and confirming the output passed consent and regional-compliance filtering. It shares the ok-green semantic and the pulsing dot language of the infra pill deliberately: this is a live check being run, not a static legal footer.
rgba(156,200,160,0.06) background + rgba(156,200,160,0.20) border. The green dot matches the infra pill's live-status dot — same visual language: this too is a live check, not a static label.The same disclosure component reports an absence instead of a pass for Sara Hadid's cold-start recommendations — no purchase history exists, so none is claimed, rather than fabricating a “quiet preference.” It deliberately keeps neutral tag styling rather than switching to a warning color, since declining to fabricate familiarity is the system working correctly.
A step-list showing the authentication checks running in real time while the SA holds the camera over the piece. Three states: pending (default), active (cream ring + cream label), done (ok-green with timestamp).
The pipeline is a live step list — material signature, stitching pattern, hardware serial, provenance graph match — that the SA watches complete in real time while holding the camera over the piece. Each step morphs from pending to an active cream ring to a done ok-green checkmark; the result card only renders once all four steps finish, confirmed here for a genuine Alma BB with no anomalies across four checkpoints.
display:none → display:block via JS class toggle.Added 2026-07-12 behind a demo-only toggle: before this existed, the scanner in the interactive prototype could only ever resolve positive, so “brand defense” was a claim the prototype never actually demonstrated. The failing step swaps its timestamp for a literal “MISMATCH” label, and the result routes to brand-defense review without touching the client's own passport.
rgba(232,159,138,…) (the --alert token), and the failing step's timestamp is replaced with a literal "MISMATCH" label rather than a duration — the point isn't that it was slower, it's that it didn't match. Before this existed, the scanner could only ever resolve positive, so "brand defense" was a claim the interactive prototype never actually demonstrated.The full-width product showcase used in Piece Detail and Authenticate. A 4:5 panel with the ivory gradient background, radial highlight, and hatching texture. In production, a product image is positioned at 44% from top with a drop shadow.
The piece hero is the full-width product showcase that opens both Piece Detail and Authenticate — a 4:5 ivory-gradient panel with a radial highlight and a barely-visible hatching texture meant to read as atelier paper. The confidence badge (98.4% match, shown here for a real Alma BB) sits in the same panel rather than as a separate element.
linear-gradient(180deg, #F2EBDF 0%, #DCCFB7 100%) (untokenized gap). Hatching: repeating-linear-gradient(45deg, rgba(0,0,0,0.025) 0 1px, transparent 1px 6px) — barely visible, reads as atelier paper. Confidence badge uses ok-green on a navy-deep semi-transparent background.The Authenticate screen's camera frame. The physical bag occupies the center; a scanning line sweeps from 18% to 82% using a sine-in-out bounce animation. Two blend-mode overlays (plus-lighter + soft-light) create a warm glow that appears to emanate from the scan itself passing across the product.
The viewfinder places the physical piece inside a reticle on the Authenticate screen — the scanline sweeps from 18% to 82% on a 2.4s sine-in-out bounce, with luminance peaking twice per cycle, both times at the center where the bag sits. The blend-mode overlays that create the warm glow crossing the surface are omitted from this static demo but drive the effect in the real prototype.
scan-sweep (2.4s, moves top position) and scan-shine (1.2s, pulses opacity + box-shadow). Luminance peaks twice per sweep cycle — both times at the center where the bag sits. The scan-glow and scan-tint blend-mode overlays track the scanline; they're omitted from this demo but critical in the prototype for the "warm light crossing a surface" read.A vertical timeline on the Passport screen documenting the piece's journey since manufacture — atelier, purchase, ownership transfers, service events. The cream gradient line fades out at the bottom to imply the story continues.
The provenance timeline documents a piece's full lifecycle on the Passport screen — crafted at Asnières in 2019, sold at Faubourg the same year, a handle replacement in 2022, transferred to Passport in 2024 — using a single gradient line that fades to transparent at the bottom to imply the story is still being written. This is the real chronology for the Alma BB shown throughout Piece Hero and Score Card.
linear-gradient(180deg, var(--cream) 0%, transparent 100%) — 1px wide, runs from the first dot to off the bottom. Dots are 11px circles: background:var(--cream); border:2px solid var(--navy-deep); box-shadow:0 0 0 1px var(--cream). Year labels are uppercase cream at 0.16em tracking.Provenance confidence score on the Passport screen. Uses the ok-green semantic throughout — label, number accent, and border — to read as a positive verification, not a metric.
The score card gives the pipeline's numeric output (98.4%) its own dedicated moment on the Passport screen, styled entirely in the ok-green semantic — label, number, and border all reinforcing “this is verified,” not presenting the figure as a neutral metric.
2-column grid of care instructions on the Passport screen. Uses icon tiles rather than text-only rows so the client can scan the four most important care actions without reading.
Care instructions render as four icon tiles rather than a text block, so a client can scan the most important actions — avoid rain, condition annually, store upright, where to get repairs done — without reading paragraphs. Each tile pairs a symbol with a one-line title and a slightly longer instruction.
The Aftercare screen's post-purchase channel. Bubbles use two visual lanes: inbound (them) is warm-tinted dark, outbound (me) is solid cream with navy text. The sticky thread input uses the navy-2 background to lift it off the scroll content.
The message thread is the Aftercare screen's post-purchase channel between SA and client, using two visual lanes — inbound bubbles in warm-tinted dark, outbound in solid cream with navy text — with a sticky composer pinned as the thread scrolls. Fixed 2026-07-12: the send button previously had no click handler at all; it now appends the typed reply as a new outbound bubble and, after a pause, a canned inbound response.
rgba(212,184,150,0.08) background + --line border + bottom-left-radius flattened to 4px. Outbound: --cream background + --navy-deep text + bottom-right flattened. Thread input uses position:sticky; bottom:0 so it stays visible as the thread grows.Opening card on the Resale Path screen. Shows current estimated value with the same card gradient as appointment cards — provenance data has the same visual weight as client data.
The resale summary opens the Resale Path screen with the same card gradient used for appointment cards — a deliberate signal that provenance-backed valuation data carries the same visual weight as client-facing information. The €1,840 figure is grounded explicitly in current secondary-market data for this exact model and condition, not a flat estimate.
Three-column metric strip at the top of the Brand Defense screen. Large cream numerals on transparent tiles — data without chrome.
Three large cream numerals open the Brand Defense screen — total takedowns, the SA's own region, and the resolution rate — rendered on transparent tiles with no card chrome at all, so the numbers carry the weight rather than a bordered container.
Brand defense alert entries. The geo prefix (2-letter or abbreviated location code) provides immediate spatial context — an SA scanning the list reads the geography before the detail.
Each feed item leads with a 2-letter geo prefix so an SA scanning the list reads the geography before the detail — a marketplace counterfeit in France, a supply-chain diversion in Italy, a matched serial number in the US. System references (provenance graph, IBM supply intelligence) render inline in cream to credit which underlying platform surfaced each alert.
--cream inline to credit the underlying platform. Background: rgba(212,184,150,0.04) — same as fact tiles, establishing these as data items.Inline notification strip on the Floor screen. Uses the alert semantic palette to draw attention without alarming — a tinted background rather than a bold red.
The alert strip is a tappable, inline notification on the Floor screen — clicking it navigates straight to Brand Defense — using a tinted background rather than a bold red to draw attention without alarming an SA about to greet a client. Hover strengthens both the background and border.
rgba(232,159,138,0.06) background + rgba(232,159,138,0.22) border. Icon tile: rgba(232,159,138,0.16). The strip is tappable — clicking navigates to the Brand Defense screen. Hover darkens background to rgba(232,159,138,0.12) and strengthens border to 0.40.Confirmation feedback for non-navigating taps — appointment info, copied reference numbers, locked appointments. Cream fill reversed with navy text: the same color pairing as the primary button, which reinforces that something the user initiated has confirmed.
Toast confirms non-navigating taps — appointment info, a copied reference number, a locked appointment — using the same cream-on-navy pairing as the primary button, reinforcing that whatever the user initiated has completed. It sits above the tabbar so it's never clipped, and auto-dismisses after 2.4 seconds.
rgba(212,184,150,0.96) — near-opaque cream. Text: --navy-deep. Appears at bottom:110px (above tabbar), centered. Enters via translateY(20px) → translateY(0) + opacity 0→1 in 240ms. Auto-dismisses after 2.4s.In-app arrival notification that appears above the status bar area when a client the SA is expecting has arrived. Slides down from off-screen using the weighted-finish easing. Tappable to open the Client Arrives screen.
The push banner slides down from off-screen using the same weighted-finish curve as the Face ID progress ring, appearing whenever a client the SA is expecting arrives at the entrance. It's tappable — opening the Client Arrives ceremony screen directly — and its hover state references the undeclared --line-3 token documented in Color Gaps.
#14193F (navy-2). Border: --line-2. Hover state references undeclared --line-3 — this is the color gap that needs resolution. Entry easing: var(--ease-weighted) (cubic-bezier(0.32, 0.72, 0, 1)) at 420ms (same as Face ID progress ring — the "weighted finish" curve throughout the system for elements landing into view).Contextual panel on the Handoff screen explaining what data transfers during the NFC pairing. The cream-tinted border distinguishes it from standard data rows — this panel is explanatory, not navigable.
The handoff bridge is explanatory, not navigable — a single panel on the Handoff screen spelling out exactly what transfers during NFC pairing (the piece's Passport) and what stays private (the SA's own session notes). Its cream-tinted border visually distinguishes it from a standard data row, signaling “read this” rather than “tap this.”
Settings rows collapse their borders into a single grouped list — only the first and last rows keep their corner radii, and there's no gap between rows, so the group reads as one continuous list. Each row ends in either a chevron (navigable) or a toggle (boolean), and the value cell's cream text signals which kind of row it is before the user taps.
border-top:0 on each subsequent row collapses borders. Only the first and last rows carry their corner radii. Value cells use cream text with a right-pointing chevron for navigable items, or a toggle for boolean settings.Components unique to ibm-louisvuitton/index.html and work/lv.html. These surfaces share Radikal and the cream color with the app but use different layout systems, additional typefaces, and a distinct brown-black rather than navy-1 as their base background.
The microsite's loading screen. An orange field (#ff9933) unique to this surface — not in the app palette. The LV bee SVG fills with navy-deep as a clip-path animation driven by the page's load progress. Hover the demo to see it fill.
The microsite's loading screen uses an orange field (#ff9933) that appears nowhere else in the whole system — app, microsite, or case study — making it the loader's own dedicated identity color. The LV bee fills with solid navy-deep via an animated clip-path, driven by the actual page load progress rather than a fixed duration.
#ff9933 is used nowhere else in the system — it is loading-screen only. The ghost layer is fill:rgba(0,26,73,0.15) (navy-deep at 15% opacity on orange = warm bronze silhouette). Fill layer is solid #001a49 revealed by animating clip-path:inset() from 100% → 0%. Progress is driven by JS reading the page load counter.The microsite hero uses Radikal Black (wt 900) at viewport-scaled size — calc((100vw − gutters) / 6.2) — so the headline always fills the usable width regardless of viewport. Background is warm dark brown #1C1008, distinct from the app's navy stack.
The microsite hero headline scales its font-size directly off viewport width — calc((100vw − gutters) / 6.2) — so “IBM×LVMH” always fills the usable width regardless of screen size, rather than jumping between fixed breakpoint sizes. The divisor 6.2 is tuned to the character count of “IBM×LV,” the longer of the headline's two lines, so both lines fill equally.
calc((100vw - 6rem) / 6.2) at base, scaling to calc((100vw - 12rem) / 6.2) at ≥768px and calc((100vw - 18rem) / 6.2) at ≥1600px. The divisor 6.2 matches the number of characters in "IBM×LV" — tuned so both lines fill equally. Caption uses Radikal 500 with 0.26em tracking at 9px.LVMH
Five branded color schemes applied to the microsite's service pitch cards. Each scheme pairs a background with a complementary foreground — card hover reveals a button that swaps foreground/background. These schemes do not appear in the app.
Five branded color schemes exist across the microsite's service pitch cards, of which three are shown here — orange/navy for AI, espresso/gold for Data, cobalt/white for Supply. None of the five appears in the app; they're presentation choices specific to the editorial pitch context, each pairing a background with a complementary foreground that swaps on hover.
#ff9933 / navy-deep · espresso #362518 / gold #bda476 · cobalt #0047ab / white · sage #bda476 / espresso · forest #2d4a36 / eucalyptus #d8e4c8. Cards use the drift and float animations (60s rotation + 6s vertical breathe) on product images.Three animated demo cards in work/lv.html — each shows a specific motion applied in the prototype (Scan, Loader, Tile entrance). A motion readout strip below each stage shows the easing curve graph and an animated dot that travels the path.
This is one of three animated demo cards in work/lv.html, each pairing a live motion demo with a readout strip that plots the actual easing curve as an SVG path beneath it. The scan card runs the same 2.4s duration as the app's Viewfinder scan line, but on sine-in-out — the case study's own custom curve, replacing the app's generic ease-in-out as a deliberate design decision. The readout strip itself is unique to the case study page and doesn't exist in the app.
offset-path when the card is active. Three demos: Scan (mirrored U), Loader (sigmoid bottom→top), Tile entrance (overshoot). Primary easing cubic-bezier(0.37, 0, 0.63, 1) used for all three demo dots.The specs catalog is a linked grid of all six named motion demos in the case study — Scan sweep, Bee water-fill, Tile entrance, Screen slide, Bag glow, Face ID ring — each cell linking to its corresponding demo further down the page. The grid uses intersecting hairline borders rather than a gap between cells, specific to this catalog.
border-top + border-left on the container, box-shadow: 1px 0 0 0 … 0 1px 0 0 … on each cell.Large editorial statements in case study text blocks. Uses a hypothetical serif typeface (Hatton in the source, fallback to Radikal Light here) at italic wt 200. The split variant places a shorter opening claim at full size and a longer elaboration at ~60% size.
Pull quotes carry the case study's largest editorial claims — “The object becomes the interface” — in a dimmed white rather than pure white, a deliberate restraint that keeps them from competing with body text in adjacent columns. The split variant pairs a short primary claim at full size with a longer elaboration at roughly 60% size directly beneath it.
clamp(40px,5.4vw,80px) · wt 200 · italic · rgba(255,255,255,.5). Sub-line: clamp(24px,3.2vw,44px) · same weight and color. The dimmed white (not pure white) reads as editorial restraint — a deliberate choice that keeps pull quotes from competing with body text in adjacent columns.