Design proposal · Sample data

Usage limits

A new row in the session list menu opens one focused screen that answers a single question: how much of each account’s quota is gone, and when does it come back. Every number on this page is invented. Nothing here is a live reading, and no real account or credential is quoted.

The recommendation

One sheet, one card per account, one refresh. It borrows the retry-later sheet’s shape and the S2 list’s colours, so it adds no new visual system.

Grouped by account, not by host

  • A provider heading, then one card per account underneath it.
  • Hosts are a line inside the card. Two hosts never become two quotas when they share one sign-in.
  • They are only merged on proof. An unidentified host gets its own card and says so.

Windows named by their duration

  • “Current window · 5 h” and “Weekly · 7 days”, taken from the duration the provider reported.
  • Never from the API’s own key name. primary is not a promise of five hours.
  • A window that was not reported says so, and draws no bar.

A reading, with its provenance

  • Every card footer says when the reading was taken and which host took it.
  • One Refresh in the header. Opening the sheet refreshes.
  • A refresh never blanks the numbers already on screen.

The sheet

Pick a scenario to inspect each state. The same six scenarios drive the canvas boards.

Phone · 390 pt. The sheet hugs its content: about 700 pt in the normal state, with .large as a second detent.

Anatomy, top to bottom

  • Header, 48 pt. Close on the left, “Usage limits” centred at 17 semibold, Refresh on the right. No other controls.
  • Provider heading, 13 pt semibold uppercase, 0.04em tracking, at tertiaryLabel.
  • Account card, radius 20, tertiarySystemGroupedBackground. Plan at 17 semibold, identity and hosts at 13 grey.
  • Quota row, ~83 pt. Label and percentage on one baseline, a 6 pt capacity bar, then the reset line at 13 pt.
  • Footer, 34 pt. When the reading was taken, and from where.

Honest states

The states that matter most are the ones where there is no good number. Each gets its own sentence rather than a plausible-looking zero.

A missing window is never 0%

  • The live Codex login returns a weekly window and nothing else. That shape must survive the design.
  • Such a row reads Not reported in grey and draws no bar at all. An empty track reads as “0% used”, which would be a lie in the most expensive direction.
  • Each window is classified by its own reported duration, so a provider that adds a third bucket appears without a code change.

A failed read is not a signed-out account

  • Copy names the host in plain words: “LFG couldn’t reach Air for an update. This does not mean the account is signed out.”
  • No status codes in the UI. The last good reading stays visible underneath, with its age.
  • Sign-in copy appears only when the provider itself says the credential was rejected.

Stale data stays readable

  • The footer turns amber and says how old the reading is. The numbers are never dimmed — a stale reading is still the best information there is.
  • An offline host keeps its card at 58% opacity rather than disappearing, so the account does not silently lose a window.

Pay-per-token has nothing to draw

  • An API-key account shows a short explanation and no bars. There is no subscription window, so none is invented.
  • Claude with no observation yet says “No reading yet”, not 0% and not an error.

Thresholds

UsedBar and valueCopyWhy
0–74%Accent blue“Resets 4:10 PM · in 1 h 12 m”Information, not a warning.
75–89%AmberUnchangedAttention without alarm — the same amber the list uses for “needs input”.
90–99%Amber“Little left · resets …”A word, so colour is never the only carrier.
100%Red, full“Used up · back at 4:10 PM”The reset time becomes the headline, because it is the only thing left to act on.
Not reportedNo bar“Not reported”A zero-length bar would read as 0% used.

iPad

A fitted form sheet about 460 pt wide, centred over the split view, with the same two-column content. It does not stretch to fill a regular-width sheet — half an empty sheet was the retry-later mistake.

iPad · landscape. .presentationSizing(.fitted). The sidebar and detail stay visible behind the scrim, so the sheet reads as a scoped detour rather than a new screen.

Mac

A 320 pt popover anchored to the ••• button in the host strip — not a sheet. This is a glance, and a popover dismisses on an outside click with nothing to confirm.

Mac · the narrow 400 pt window the desktop app is actually used in. The popover fits with 10 pt to spare on each side.

Mac specifics

  • Width 320 pt, content height about 430 pt, capped at 520 pt with a scroll. Metrics drop to the desktop scale: 13 / 12 / 11 / 10 pt.
  • Refresh is a 24 pt circular button in the popover header; ⌘R also refreshes.
  • Esc closes. Tab reaches Refresh, then any Try again button.
  • A sheet would have been consistent with Retry later, but Retry later is a task with a commit step. This has no commit step, so a modal is a cost with no payoff.

Rules, tokens and accessibility

RolePhone dark / lightMac dark / lightCode
Sheet ground#1C1C1E / #F2F2F7#232327 / #FFFFFFgrouped background · TaskPalette
Account card#2C2C2E / #FFFFFF#26262B / #EFEFEFtertiarySystemGroupedBackground
Bar fill, normal#0A6EE0 / #0088FF#3B8EFF / #007AFFTaskColors.accentFill
Bar fill, 75%+#FF9F0A / #B35100#FFB340 / #B35100S2 attention
Bar fill, 100%#FF453A / #D70015#FF6B61 / #D70015TaskColors.danger
Bar tracklabel ink 16%label ink 16%derived, no semantic twin
Reset line#A1A1A6 / #6C6C70#8A8A93 / #737377S2 secondaryInk · S2Desk.secondary
Footer / provider head#8E8E93 both#8E8E93 bothS2 metaInk

Accessibility

  • Each quota row is one combined element: “Claude Code, Max 20×, current window, 5 hours, 62 percent used, resets in 1 hour 12 minutes.”
  • The bar is hidden from VoiceOver. A gauge that repeats the row’s own value just double-speaks it; Apple’s gauge guidance is satisfied by the row text, which names the value and the range.
  • Every warning state carries a word as well as a colour, and blue / amber / red differ in lightness, not only hue.
  • Dynamic Type scales the text; the bar stays 6 pt and the row wraps to two lines at accessibility sizes.
  • Targets are at least 44 pt on the phone, including Try again.

Keyboard

  • Esc closes. ⌘R refreshes on iPad and Mac.
  • Tab order: Close → Refresh → each Try again in card order.
  • The sheet traps focus while open and restores it to the menu button on close.

Refresh behaviour

  • Opening the sheet refreshes every host. A short cache stops a reopen from hammering them.
  • While refreshing: the header reads “Refreshing”, card footers read “Refreshing…”, and the numbers stay.
  • A per-card Try again appears only in the error state. There is no per-card refresh otherwise — with at most four cards, two taps for one job is noise.

What this is not

  • Not context-window fullness, and not token spend for one conversation. These quotas belong to the account and span every conversation on it.
  • Not an analytics dashboard. No history, no burn-rate chart, no projections — those need data nobody is collecting yet.