Portfolio Case Study · 2026

Dial

Rolex Resale Tracker

Independent watch dealers track inventory in spreadsheets. Dial is a purpose-built tool for managing Rolex holdings - P&L, comparable sales, and market value in one place.

Role
Product Designer · Design Engineer
Deliverables
Figma System · React App · Case Study
Stack
Figma · React · Local State
Timeline
2 Weekends
Dial - Portfolio Dashboard
S6 Portfolio Dashboard
Dial - Watch Detail Panel
S2 Watch Detail Panel

The gap spreadsheets can't close

Small independent watch dealers and serious collectors - managing 5 to 20 watches at a time - rely on spreadsheets and memory to track what they own, what they paid, and what the market is doing. The result is fragmented data, slow decision-making, and missed timing on sales.

Primary User

An independent Rolex dealer or serious collector managing 5–20 watches. Technically comfortable but not a developer. Checks Chrono24 daily. Tracks comps manually in a Notes app or spreadsheet. Makes buy/sell decisions based on gut feel rather than organized data.

Inventory with status tags

Every watch marked Holding, Listed, or Sold - at a glance, across the full portfolio.

Per-watch P&L

Purchase price vs. current market value vs. final sale price - tracked per watch, not per spreadsheet tab.

Comparable sales log

Attach 2–3 comps to each watch with source, date, condition, and price. Pulled from Chrono24.

Market value tracking

Manual market value entries over time, visualized as a sparkline on each watch card.

Portfolio summary bar

Total invested, total market value, realized and unrealized gain - always visible at the top.

Built bottom-up,
not screen-first

Before designing a single screen, I built the full design system using Atomic Design - establishing visual language at the token level, composing it into atoms, then molecules and organisms, before any layout decisions were made.

Tier 1
Tokens
Color, type, spacing, shadow values. The raw vocabulary of the system.
Gold/500 · Text/Primary · Space/4
Tier 2
Atoms
Smallest reusable elements. Badge, Button, Input, Icon, Divider.
Badge/Holding · Button/Primary
Tier 3
Molecules
Atoms combined for a specific job. Watch Card, Stat Card, Comp Row.
WatchCard/Compact · CompRow
Tier 4
Organisms
Full UI sections. Inventory List, Portfolio Bar, Detail Panel.
InventoryList · PortfolioBar
Tier 5
Templates
Layout skeletons. Zones defined, no real content yet.
Dashboard · DetailView
Tier 6
Screens
Templates with real data. Watch photos, actual prices, real comps.
Submariner 126610 · $14,200
Why systems-first?

Building screen-first creates local decisions that compound into inconsistency. A design system forces every visual decision to be made once, at the source - then referenced everywhere. Change a token, update the entire system. No hunting, no drift.

Single source of truth

Every color, spacing value, and typography decision lives in a token. No raw hex values in component fills. No hardcoded sizes on frames. If something needs to change, it changes in one place and propagates automatically.

Free plan constraint → architecture decision

Figma's free plan allows only one variable collection. Solved with slash-group naming: Primitives/Gold/500 and Semantic/Text/Primary live in the same collection, organized by prefix. A constraint that forced a cleaner naming system.

Visual language from the ground up

Three decisions that define how Dial feels - and the reasoning behind each one.

Color - Primitive Ramps
900
700
600
500
400
300
Semantic Mapping - Chain
Primitives/Gold/500
Semantic/Brand/Primary
Primitives/Zinc/400
Semantic/Text/Secondary
Primitives/Zinc/900
Semantic/Surface/Base
Type Scale
Display · 30px
Submariner
Body/lg · 18px
Watch inventory
Body/md · 14px
Purchased Nov 2023
Mono/data · 13px
$14,200.00
Label/sm · 11px
MARKET VALUE
Key Decisions
01
Why dark-first?

Data-dense UIs are more legible on dark backgrounds - high contrast for numbers, reduced eye strain during extended sessions. Dark also reads as premium, which aligns with the Rolex category and the serious-collector user.

02
Why gold as the brand accent?

Gold is the most legible warm accent on a dark background and has direct category resonance with Rolex. It's used sparingly - semantic token names (Brand/Primary) rather than color-literal names - so a future rebrand swaps one value, not dozens of component fills.

03
Why DM Mono for data values?

Monospace fonts are tabular by nature - digits align vertically in lists, making comparison scanning fast. Geist handles all UI copy. Fraunces handles display headings. Three fonts, three distinct roles, no overlap.

10 atoms · 6 molecules · every decision intentional

Every component in the system exported directly from Figma - the same token values, the same constraints, the real thing.

Atoms - 10 components
Atom · Badge
Holding Listed Sold

Slash-named standalone components (Badge/Holding etc.) rather than variant sets.

Atom · Button

Three variants covering the full action hierarchy. Primary uses Brand/Primary token - swap one value to rebrand.

Atom · Input
126610LN
Submariner Date
Required field

Default · Focus · Error states. Focus ring uses Brand/Primary at 12% opacity.

Atom · Tag / Filter Pill
All
Holding
Listed
Sold

Inventory filter pills. Active state uses Brand/Primary border + tinted fill. Separate from Badge atom - different job, different component.

Atom · Divider
Comps

Three variants: subtle, labeled, and gold-accent for section breaks in the Detail Panel.

Atom · Skeleton

Loading state placeholder. Corner radius matches the element it replaces - applied to inner rectangle, not the outer component frame.

Atom · Thumbnail

Three sizes: sm/32px for compact cards, md/48px for expanded cards, lg/72px for the detail panel hero.

Atom · Icon

Lucide icons at 16px. Colored via semantic tokens - Text/Secondary for neutral, Brand/Primary for positive delta, Status/Error for negative. Dropped directly at organism level rather than wrapped as instances.

Atom · Delta Indicator
+$1,200 +9.2%
−$800 −5.8%
$0 0.0%

Shows absolute and percentage change. "Delta" - the mathematical term for difference - is used throughout Dial's naming for this component.

Atom · Nav Item
Inventory
Portfolio
Market
Settings

Active state: gold tint + Brand/Primary icon color. Default state: tertiary text, no background.

Molecules - 6 components
Molecule · Watch Card / Compact
Watch Card / Compact

The primary list item. Thumbnail atom + ref/name/price text + Badge atom + Delta Indicator atom, composed in Auto Layout. Compact keeps the list scannable - expanded shows more detail on hover or selection.

Molecule · Watch Card / Expanded
Watch Card / Expanded

Same atoms as Compact, restructured for a wider layout. Used in the detail panel context where more horizontal space is available.

Molecule · Stat Card
Stat Card/ Positive

Used in the Portfolio Summary Bar. Label in Label/sm, value in Mono/data at 20px.

Molecule · Sparkline Cell
Stat Card/ Positive

Market value trend over time. labels in DM Mono at 6px - small enough to annotate without competing with the line.

Molecule · Comp Entry Row
Stat Card/ Positive Stat Card/ Positive

Source chip uses a fixed-width wrapper to maintain column alignment regardless of chip label length - a non-obvious Figma constraint solved with a wrapper frame.

Molecule · P&L Row
Stat Card/ Positive Stat Card/ Positive

Three-column layout using DM Mono throughout. Delta cell pulls color from the Delta Indicator atom - green for gain, red for loss, tertiary for flat.

Want to see the full Figma file?
All 10 atoms · 6 molecules · token variables · build annotations
Request Access

Six screens, one coherent product

Each screen is a template filled with real watch data - reference numbers, actual prices, real comps pulled from Chrono24.

Dial - Dashboard Inventory List
Dashboard - Inventory List

The primary view. Portfolio bar across the top, filter pills for status, watch cards in a scrollable list. The sidebar holds navigation and quick stats.

Primary View
Dial - Watch Detail Panel
Watch Detail Panel

Click a watch in the list to open the detail panel. P&L breakdown, sparkline chart, comps log, and photos - all without leaving the inventory view.

Core Flow
Dial - Add Watch Drawer
Add Watch Drawer

Slides in as an overlay on the right. Reference, model, colorway, condition, purchase price, date. The background remains visible - context is preserved.

Entry Flow
Dial - Mark as Sold Modal
Mark as Sold Flow

Confirm the final sale price to close out the P&L. Status updates to Sold, realized gain is calculated and surfaced in the portfolio summary bar.

Exit Flow
Dial - Empty State
Empty State

The first-run experience. Clear value proposition, single call-to-action. Empty states are often an afterthought - designed here as a deliberate onboarding moment.

Onboarding
New Screen 06 · Portfolio
Dial - Portfolio Dashboard
Portfolio Dashboard

The analytical view. Portfolio Bar anchors current state at the top - total invested, market value, unrealized and realized gain. The FilterBar below scopes everything beneath it by time range and status. Stat cards, a cumulative P&L chart, inventory breakdown, and a ranked Top Performers table give the full business picture at a glance.

Analytics View

Constraints & what comes next

Constraints that shaped the system
01
One variable collection (Figma free plan)

Solved with slash-group naming - Primitives/Gold/500 and Semantic/Text/Primary in the same collection, organized by prefix. Forced a cleaner naming convention than a multi-collection setup would have produced.

02
No variant sets → slash-named components

Badge/Holding, Badge/Listed, Badge/Sold as standalone components instead of a true variant set. Slightly more components to manage, but each is independently documented and swappable.

03
Icon instances impractical inside components

Lucide icons dropped directly at organism level rather than wrapped in an Icon atom instance. The Icon atom exists as documentation and engineering reference - not as a Figma instance dependency chain.

04
Local state only (no backend)

The React app uses local state for portfolio scope. Real data persistence (cloud sync, multi-device) is a next-step feature, not a launch blocker for a portfolio demonstration.

What I'd build next
WatchCharts API integration

Pull live market values automatically rather than relying on manual comp entry. Removes the biggest friction point in the current workflow.

Cloud sync + multi-device

Supabase or Firebase backend to persist inventory across devices. The data model is already designed for this - Watch, MarketEntry, Comp, Portfolio as separate tables.

Broader reference coverage

Expand beyond Rolex to Patek Philippe, AP, and other blue-chip brands. The category taxonomy and data model generalize with minimal changes.

Export / reporting

PDF P&L summary per watch or per period. Useful for tax documentation and for dealers presenting portfolio performance to partners or investors.

Light mode + brand themes

The semantic token system is already designed for this - adding a light mode is a variable mode switch, not a redesign. The primitive→semantic chain makes it trivial.