Split bar

A segmented progress bar that visualises how an expense is divided across people. Each person gets a color-coded segment — ink, coral, teal, butter — that springs in sequentially. At a glance you see who owes what, before you read a single number.

Variants

Four-way split — Goa Airbnb ₹12,000
Aarav ₹3,840
Priya ₹3,360
Rohan ₹2,880
Kabir ₹1,920
Even split — Farzi Cafe ₹4,200
You ₹1,400
Priya ₹1,400
Rohan ₹1,400
Settled — all clear

Segments are mapped to person slots in order: ink (you, always first), coral, teal, butter, then back to muted fills for groups larger than four. Each segment width is the person's share as a percentage of the total.

Spring animation staggers 80ms between segments so they read left-to-right — not as a simultaneous burst.

Sizes

Prominent — 12px
Subtle — 6px
SizeClassHeightUse case
Prominent.split-bar-prominent12pxExpense summary card, settle card header
Subtle.split-bar-subtle6pxCompact list items, inline breakdown

States

Default — split active
Loading / skeleton
Settled — fully resolved
StateVisualWhen
DefaultColor segments, spring animation on mountExpense has outstanding balances
LoadingSingle mute fill at 50% opacity, no animationData is fetching
SettledFull-width teal, no animationAll balances are zero — celebrated state

On dark surfaces

Prominent on dark — Swiggy order ₹840
Subtle on dark

Segment colors are unchanged on dark surfaces — they carry enough contrast. The muted fill for the loading state uses --dark-border instead of --ink-line.

Anatomy

Wrapper (overflow:hidden, border-radius pill)
Segment (flex-shrink:0, width = share%)
Color slot (sb-ink / sb-coral / sb-teal / sb-butter)
PartElementNotes
Wrapper.split-barFlex container, pill radius, overflow hidden. Width is always 100% of its parent.
Segment.split-bar-segmentWidth set inline as a percentage matching the person's share. Never use fractions — round to 1 decimal.
Color slot.sb-ink .sb-coral .sb-teal .sb-butterSlot order is fixed — ink is always "you", coral is person 2, and so on.
Label row.split-bar-labelsOptional. Mirrors the segment widths exactly. Each label is truncated with ellipsis if it overflows.

Usage

Do Always put "you" in the first ink slot. Keep label widths in sync with segment widths. Use prominent size in expense summary cards and subtle size for inline breakdowns.
Don't Don't add more than 5 segments — past 4 the bar becomes illegible. Don't skip the animation on first mount — it's how users read the proportions. Don't use the split bar for progress (use cap meter instead).
Do Show the settled state (full teal) when all balances are zero. It's a moment worth celebrating — don't leave the bar empty or hidden.
Don't Don't use custom colors outside the four defined slots. The palette is intentional — it maps to avatar colors for consistency across the app.

Accessibility

Screen readers The split bar is decorative — the information it conveys must also be present in text (e.g. "Aarav ₹3,840 · Priya ₹3,360"). Wrap the bar in aria-hidden="true" and ensure the parent context has a readable breakdown elsewhere on the same screen.
Motion The spring entrance animation runs once on mount. Users with prefers-reduced-motion: reduce should receive static segments — add a media query that sets animation: none on .split-bar-segment.
Contrast All four segment colors (ink, coral, teal, butter) pass WCAG AA against the warm paper and warm canvas backgrounds at 12px and above. The muted fill at 50% opacity is decorative-only; it is never the sole indicator of status.

Code

HTML

<!-- Prominent split bar, 4-person Goa Airbnb -->
<div class="split-bar-wrap" aria-hidden="true">
  <div class="split-bar split-bar-prominent">
    <div class="split-bar-segment sb-ink"    style="width:32%"></div>
    <div class="split-bar-segment sb-coral"  style="width:28%"></div>
    <div class="split-bar-segment sb-teal"   style="width:24%"></div>
    <div class="split-bar-segment sb-butter" style="width:16%"></div>
  </div>
  <div class="split-bar-labels">
    <div class="split-bar-label" style="width:32%">Aarav ₹3,840</div>
    <div class="split-bar-label" style="width:28%">Priya ₹3,360</div>
    <div class="split-bar-label" style="width:24%">Rohan ₹2,880</div>
    <div class="split-bar-label" style="width:16%">Kabir ₹1,920</div>
  </div>
</div>

<!-- Settled state -->
<div class="split-bar split-bar-prominent" aria-hidden="true">
  <div class="split-bar-segment sb-teal" style="width:100%"></div>
</div>

<!-- Reduced motion override -->
<style>
  @media (prefers-reduced-motion: reduce) {
    .split-bar-segment { animation: none; }
  }
</style>

CSS classes

ClassPurpose
.split-barBase wrapper — flex, pill radius, overflow hidden
.split-bar-prominent12px height — expense summary, settle card
.split-bar-subtle6px height — compact list items
.split-bar-segmentSingle person's colored portion, with spring animation
.sb-inkSlot 1 — always "you"
.sb-coralSlot 2 — person 2
.sb-tealSlot 3 — person 3, also used for settled state
.sb-butterSlot 4 — person 4
.sb-muteLoading placeholder fill
.split-bar-labelsOptional label row below the bar
.split-bar-labelIndividual label — width must match corresponding segment width

Design tokens used

TokenValueRole
--ink#171513Slot 1 fill (you)
--coral#E76F51Slot 2 fill
--teal#0E7C66Slot 3 fill, settled state
--butter#F4C94ESlot 4 fill
--ink-line#DDD2B8Muted loading fill
--r-pill999pxWrapper border radius
--ease-springcubic-bezier(0.34,1.56,0.64,1)Segment entrance animation
--d-slow420msAnimation duration