busy-office-ui
Menu

Amount

A money or quantity value, laid out to scan down a column. This is CSS-first: your app formats the number (locale grouping, decimal precision, sign) and supplies the parts as markup — the component styles and aligns them, with tabular figures so every row lines up digit-for-digit. One component, driven by settings. Not for capturing a value — this is display only; entry is Money for currency or Quantity for a count.

The rule for this family: displaying a value is Amount's job; capturing one is Money's (currency) or Quantity's (count). One display component for every numeric value; two entry components, split by what the value is — and when it is neither a selectable currency nor a steppable count (a rate, a fixed-currency price, a percentage), a plain .bo-input--numeric is the whole answer: no fourth component, just the input modifier.

Money — symbol, grouping, minor units

$ 18,940.50
View code
<span class="bo-amount">
  <span class="bo-amount__currency">$</span>
  <span class="bo-amount__value">18,940<span class="bo-amount__fraction">.50</span></span>
</span>

__currency and __fraction are quieted so the whole-number part reads first.

Currency as an ISO code (either side)

1,234.00 EUR
View code
<span class="bo-amount">
  <span class="bo-amount__value">1,234.00</span>
  <span class="bo-amount__currency">EUR</span>
</span>

Source order decides prefix vs suffix — $ before, EUR/kr after. No modifier needed.

Custom / non-ISO currency

2,400PTS ₡18.50
View code
<!-- an app-defined currency (points, credits, store scrip) works exactly
     like ISO codes and symbols — __currency accepts any text -->
<span class="bo-amount"><span class="bo-amount__value">2,400</span><span class="bo-amount__currency">PTS</span></span>
<span class="bo-amount"><span class="bo-amount__currency">₡</span><span class="bo-amount__value">18.50</span></span> <!-- an internal "credit" symbol -->

Loyalty points, internal credits, any app-defined unit of value — __currency is plain text with no ISO allowlist. Same component, same alignment, no special-casing needed.

Editable money — not .bo-amount

View code
<!-- Amount (.bo-amount) is a DISPLAY value — a <span>, not a form control;
     there is no ".bo-amount--input" variant, on purpose (an <input> can't host
     __currency/__fraction as nested markup and stay a single accessible value).
     Editable money has two shapes:
       currency SELECTABLE -> .bo-money (currency select + amount input as one field)
       currency FIXED      -> .bo-form-field + .bo-input--numeric, currency in the label -->
<div class="bo-form-field">
  <label class="bo-form-field__label" for="am-unit-price">Unit price (USD)</label>
  <input class="bo-input bo-input--numeric" type="number" id="am-unit-price" value="12.50" min="0" step="0.01" inputmode="decimal" />
</div>

.bo-amount is read-only display; it's a <span>, not a form control, and there's deliberately no input variant of it (the nested __currency/__fraction parts can't live inside a native <input> and still be one accessible value). Editable money has two shapes: when the user picks the currency, use the Money field — a currency select and amount input working as one control, precision following the currency. When the currency is fixed (this demo's unit price), a plain .bo-input--numeric with the currency in the label is all it takes — the same way Quantity puts the unit in __unit text next to an editable control.

Precision — a setting, not a variant

¥56,000 $4,208.00 $0.0725
View code
<!-- 0 decimals (whole units) -->
<span class="bo-amount"><span class="bo-amount__currency">¥</span><span class="bo-amount__value">56,000</span></span>

<!-- 2 decimals (money) -->
<span class="bo-amount"><span class="bo-amount__currency">$</span><span class="bo-amount__value">4,208.00</span></span>

<!-- 4 decimals (unit price / FX rate) -->
<span class="bo-amount"><span class="bo-amount__currency">$</span><span class="bo-amount__value">0.0725</span></span>

Whole units, money, or 4-dp unit prices — same component; the digits come from your formatter.

Decimal precision is app/domain data, not a CSS rule — there's no --decimals modifier, because "how many decimals does this currency get" varies by currency, not by design choice. The complete per-currency table (ISO 4217 minor units) lives on the Money field's currency decimals reference — most apps already get this from an Intl.NumberFormat call or a currency-master table, which is exactly where it belongs (not this CSS layer).

Negative & credit — two-channel

$(2,110.00) $−730.25 $+9,320.75
View code
<!-- debit: parentheses OR a minus sign carry the meaning; red is the 2nd channel -->
<span class="bo-amount bo-amount--negative"><span class="bo-amount__currency">$</span><span class="bo-amount__value">(2,110.00)</span></span>
<span class="bo-amount bo-amount--negative"><span class="bo-amount__currency">$</span><span class="bo-amount__value">&minus;730.25</span></span>

<!-- credit: a + sign carries the meaning; green is the 2nd channel -->
<span class="bo-amount bo-amount--positive"><span class="bo-amount__currency">$</span><span class="bo-amount__value">+9,320.75</span></span>

The sign or parentheses live in the text, so colourblind and screen-reader users get the meaning without the colour. --negative/--positive only add the hue (both AA on surface and canvas, CI-gated).

The same contract for dates

Aug 10, 2026 Overdue by 4 days
View code
<!-- two-channel: the badge carries the word, colour only adds hue -->
<span class="bo-cluster">
  <span class="bo-u-tabular">Aug 10, 2026</span>
  <span class="bo-badge bo-badge--danger">Overdue by 4 days</span>
</span>

An overdue date is the same decision as a negative amount: the meaning must survive without colour, so the word Overdue belongs in the text. This guidance moved here when .bo-date was deprecated — it was the only real decision that component carried, and it is a rule about values, not about a date-specific widget.

Block — right-aligned totals on cards

PO-88213 total
$ 12,400.00 $ 1,240.00 tax

--block right-aligns a standalone amount — card totals, summary panels. Inside a table you don't need it: the cell's __col--numeric already aligns the column.

Unit of measure

4× EA $12.50/ kg $3.10/ pc
View code
<!-- quantity × unit -->
<span class="bo-amount"><span class="bo-amount__value">4</span><span class="bo-amount__unit">&times; EA</span></span>

<!-- rate per unit -->
<span class="bo-amount"><span class="bo-amount__currency">$</span><span class="bo-amount__value">12.50</span><span class="bo-amount__unit">/ kg</span></span>

<!-- unit price beside a quantity -->
<span class="bo-amount bo-amount--muted"><span class="bo-amount__currency">$</span><span class="bo-amount__value">3.10</span><span class="bo-amount__unit">/ pc</span></span>

__unit carries × EA, / kg, pcs, hrs — a quantity’s unit or a rate’s denominator. This is a read-only display value; for an editable count with its own unit, see Quantity — same __unit convention, plus increment/decrement and native validation.

Mixed currencies in one table

InvoiceVendorAmount
INV-3391Acme Supply Co. (US)$4,208.00
INV-3392Ling Freight Pte Ltd (SG)SGD1,052.00
INV-3393Nakamura Parts KK (JP)¥92,500
INV-3394Al-Rashid Trading (BH)BHD120.500
View code
<table class="bo-data-table">
  <thead><tr><th>Invoice</th><th>Vendor</th><th class="bo-data-table__col--numeric">Amount</th></tr></thead>
  <tbody>
    <tr><td>INV-3391</td><td>Acme Supply Co. (US)</td><td class="bo-data-table__col--numeric"><span class="bo-amount"><span class="bo-amount__currency">$</span><span class="bo-amount__value">4,208.00</span></span></td></tr>
    <tr><td>INV-3392</td><td>Ling Freight Pte Ltd (SG)</td><td class="bo-data-table__col--numeric"><span class="bo-amount"><span class="bo-amount__currency">SGD</span><span class="bo-amount__value">1,052.00</span></span></td></tr>
    <tr><td>INV-3393</td><td>Nakamura Parts KK (JP)</td><td class="bo-data-table__col--numeric"><span class="bo-amount"><span class="bo-amount__currency">¥</span><span class="bo-amount__value">92,500</span></span></td></tr>
    <tr><td>INV-3394</td><td>Al-Rashid Trading (BH)</td><td class="bo-data-table__col--numeric"><span class="bo-amount"><span class="bo-amount__currency">BHD</span><span class="bo-amount__value">120.500</span></span></td></tr>
  </tbody>
</table>

Right-alignment (__col--numeric) plus tabular figures keep every row's digits stacked even though the __currency affix text differs per row ($, SGD, ¥, BHD) — verified live, not assumed: the affix sits outside the tabular-figure run, so a 3-letter code next to a 1-character symbol doesn't shift where the digits land. No dedicated fixed-width affix column needed for this case.

In a column — totals align, subtotal is strong

Line itemQtyUnit priceAmount
Standing desk 4× EA $1,052.00 $4,208.00
Monitor arm 8× EA $92.50 $740.00
Restocking fee — — $(120.00)
Total $4,828.00
View code
<div class="bo-data-table-container" tabindex="0">
  <table class="bo-data-table">
    <thead>
      <tr><th>Line item</th><th class="bo-data-table__col--numeric">Qty</th><th class="bo-data-table__col--numeric">Unit price</th><th class="bo-data-table__col--numeric">Amount</th></tr>
    </thead>
    <tbody>
      <tr>
        <td>Standing desk</td>
        <td class="bo-data-table__col--numeric"><span class="bo-amount"><span class="bo-amount__value">4</span><span class="bo-amount__unit">&times; EA</span></span></td>
        <td class="bo-data-table__col--numeric"><span class="bo-amount bo-amount--muted"><span class="bo-amount__currency">$</span><span class="bo-amount__value">1,052.00</span></span></td>
        <td class="bo-data-table__col--numeric"><span class="bo-amount"><span class="bo-amount__currency">$</span><span class="bo-amount__value">4,208.00</span></span></td>
      </tr>
      <tr>
        <td>Monitor arm</td>
        <td class="bo-data-table__col--numeric"><span class="bo-amount"><span class="bo-amount__value">8</span><span class="bo-amount__unit">&times; EA</span></span></td>
        <td class="bo-data-table__col--numeric"><span class="bo-amount bo-amount--muted"><span class="bo-amount__currency">$</span><span class="bo-amount__value">92.50</span></span></td>
        <td class="bo-data-table__col--numeric"><span class="bo-amount"><span class="bo-amount__currency">$</span><span class="bo-amount__value">740.00</span></span></td>
      </tr>
      <tr>
        <td>Restocking fee</td>
        <td class="bo-data-table__col--numeric">&mdash;</td>
        <td class="bo-data-table__col--numeric">&mdash;</td>
        <td class="bo-data-table__col--numeric"><span class="bo-amount bo-amount--negative"><span class="bo-amount__currency">$</span><span class="bo-amount__value">(120.00)</span></span></td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <td>Total</td>
        <td class="bo-data-table__col--numeric"></td>
        <td class="bo-data-table__col--numeric"></td>
        <td class="bo-data-table__col--numeric"><span class="bo-amount bo-amount--strong"><span class="bo-amount__currency">$</span><span class="bo-amount__value">4,828.00</span></span></td>
      </tr>
    </tfoot>
  </table>
</div>

Amounts inherit the cell’s right-align (__col--numeric); tabular figures keep the decimal points stacked. --strong weights the total, --muted quiets the unit price.

Markup

<span class="bo-amount">
  <span class="bo-amount__currency">$</span>              <!-- symbol or ISO code, either side -->
  <span class="bo-amount__value">18,940                 <!-- your app formats grouping + precision -->
    <span class="bo-amount__fraction">.50</span>         <!-- optional: quiet the minor units -->
  </span>
  <span class="bo-amount__unit">/ kg</span>              <!-- optional unit of measure -->
</span>

<!-- modifiers: --negative  --positive  --strong  --muted  --block -->

Quick reference generated

ClassRole
.bo-amount block (root)
.bo-amount--block variant: block
.bo-amount--muted variant: muted
.bo-amount--negative variant: negative
.bo-amount--positive variant: positive
.bo-amount--strong variant: strong
.bo-amount__currency part: currency
.bo-amount__fraction part: fraction
.bo-amount__unit part: unit
.bo-amount__value part: value

API generated from CSS

Blocks bo-amount
Parts bo-amount__currencybo-amount__fractionbo-amount__unitbo-amount__value
Variants bo-amount--blockbo-amount--mutedbo-amount--negativebo-amount--positivebo-amount--strong
Component tokens --bo-amount-affix-fg--bo-amount-fg
JS required None — CSS-only.
  • CSS-first: your app formats the number (locale, grouping, precision, sign); the component styles the parts and aligns them with tabular figures.
  • Negative/credit is two-channel — the sign or parentheses MUST be in the markup; --negative/--positive only add colour.
  • Currency and unit are muted affixes; put the currency before or after the value by source order.
  • __currency accepts a symbol, an ISO 4217 code, or an app-defined currency (points, credits) — no allowlist or validation in CSS.
  • Decimal precision is per-currency domain data, not a CSS setting — there is no --decimals modifier; your app (or an Intl.NumberFormat call) decides and formats the digits before they reach this component.
Contrast — 17 AA pairs this component uses (generated, CI-gated)
TextonLightDark
text-primary bg-surface 17.74 16.15
text-primary bg-canvas 16.98 18.08
text-muted bg-surface 7.56 6.65
text-muted bg-muted 6.87 5.66
bg-surface text-primary 17.74 16.15
danger-text danger-subtle 5.91 8.06
success-text success-subtle 4.79 10.17
danger-text bg-surface 6.47 8.89
danger-text bg-canvas 6.19 9.96
success-text bg-surface 5.02 12.02
success-text bg-canvas 4.8 13.46
text-primary bg-hover 16.12 13.75
text-primary bg-selected 17.01 11.86
text-primary bg-surface-raised 17.74 14.51
text-primary bg-muted 16.12 13.75
success-text bg-muted 4.56 10.23
danger-text bg-muted 5.88 7.57

Maturity generated

Introduced
v0.1.0
CSS floor
Chrome/Edge 99 · Firefox 97 · Safari 15.4
Alignment scored
2026-08-21
Assistive-tech evidence
None recorded

Pin this component's first release with npm i @busy-office/ui@0.1.0. The floor is derived from this component's stylesheet alone — set by @layer — so it sits at or below the whole framework's Chrome/Edge 119 · Firefox 129 · Safari 17.5. No assistive-technology runtime result is recorded for any component: it needs a human listening to VoiceOver and NVDA on owner hardware; no gate in this repo can simulate announcement behaviour. What automation covers instead is test:axe across every page at 1440 and 390, and check:forced-colors — see accessibility.

Design-system alignment generated

100% (15/15) — how closely this component follows the design language. Each rule is scored 0-3 against a checkable citation; rules that don't apply leave the denominator. Scored 2026-08-21.

Rule Score Evidence
Typography 3 / 3 both affixes are 0.875em of the VALUE they sit beside, and the comment says why a scale step would be wrong in one density and right in the other (94.13)
Colour 3 / 3 zero raw hex; negative/positive is the two-channel signature contract, AA-gated
Spacing 3 / 3 gap and margins all from spacing tokens
Interaction n/a a display span — no interaction surface; the input variant was deliberately refused
Content 3 / 3 opener carries the wrong-choice clause: "Not for capturing a value" — this is display only, and the clause names and links the two entry components instead (Money for currency, Quantity for a count)
Fit 3 / 3 display-only in every matrix context; adopted across the under-using screens (92.4)
↑↓ move Enter open Esc close