Skip to content

TokenMeter

How much of the context window a conversation is using.

Use when you are building a chat UI against a model with a context limit. Every client that omits this works for a week and then stops working: each turn carries the whole history, the request grows monotonically, and one day the provider refuses it. The refusal arrives as a wall — this is the instrument that makes the growth visible before then.

Quick Start

Basic Usage

html
<r-token-meter limit="65536" used="12800"></r-token-meter>
js
const meter = document.querySelector('r-token-meter');
meter.limit = 65536;
meter.used = 41200; // context the next request will carry
meter.spent = 128431; // tokens billed across the conversation, optional

The bar fills to used / limit and escalates through three levels: ok, warn (from 80% of the limit) and over. level is reflected onto the host, so the page can react to the same escalation the bar shows:

css
r-token-meter[level='warn'] ~ .composer-hint {
  display: block;
}

used and spent are different numbers

  • used — what the next request will carry: the history, not the whole conversation. This is the number the limit applies to, and the one the bar draws.
  • spent — what has been billed across the conversation so far. It only ever grows, and it is not bounded by the window.

Truncating a transcript lowers used and leaves spent alone. Showing only one of them answers only one of the two questions a user has ("will the next message fit?" and "what has this cost me?").

Without a limit

With limit unset or zero the bar disappears and only the counts remain — useful while the window size is unknown.

Relabelling

html
<r-token-meter label="上下文" limit="65536" used="41200"></r-token-meter>
<!-- label="" leaves only the counts -->

API Reference

Properties

PropertyAttributeTypeDefaultDescription
limitlimitnumber0Context window size in tokens. Zero or absent hides the bar.
usedusednumber0Tokens the next request will carry.
spentspentnumber0Tokens billed across the conversation so far.
labellabelstring'Context'Prefix for the readout; '' leaves only the counts.
levellevel'ok' | 'warn' | 'over'derivedHow full the window is. Set by the element — writing it is overwritten on the next update.
sheetsheetstring''CSS injected into the shadow root.

Counts are formatted the way a reader scans them: exact below a thousand (847 is a number someone can hold), abbreviated above (41.2k, 128k) — the third digit of 128,431 tells a reader nothing they act on.

Parts

PartElement
meterThe whole element
trackThe bar's background
fillThe filled portion
textThe label and counts

Accessibility

The element always carries a title stating the numbers, so colour is never the only carrier of the warning — the bar going amber is a second signal, not the only one. Keep it that way if you restyle the levels.

Styling

<r-token-meter> exposes 9 CSS custom properties of its own, plus the semantic tokens it reads from the theme. Set one anywhere it inherits from — :root, a wrapper, or the element:

css
r-token-meter {
  --ran-token-meter-fill-background: var(--ran-color-bg-subtle);
}

Parts: fill · meter · text · track

The full list is in style tokens; which token to reach for is the design system.

Best Practices

  • Update used from the same place you build the request, not from a rendering pass — the number people trust is the one the next request will actually send.
  • Escalate around the meter, not inside it. At level="over" the useful UI is a suggestion (summarise, start a new thread), and that belongs to the app.
  • Don't animate the fill on a theme change — see design guidelines.

Released under the MIT License.