Money input component

A currency-aware amount field — optional currency selector, per-currency decimal precision, locale-grouped formatting on blur, robust parsing of what the user types, and a minor-unit storage contract. Pairs the money display mask (data/masks.kmd) with input behavior. Modeled after Polaris (price fields). Used by Koder Exchange, Invest, Billing, marketplace, checkout.

Status: v0.1.0 — Draft. Promoted from the Shopify Polaris parity scan (metadocsstack #091). Source: https://polaris.shopify.com/components/selection-and-input/text-field (pricecurrency usage). Display mask: `specsdata/masks.kmd`.

R1 — Currency

  • The field has an associated currency. Either fixed (shown as a

    prefix/suffix affix, e.g. R$) or selectable via an adjacent currency selector when the surface supports multiple currencies.

  • Changing the selected currency re-applies that currency's precision (R2)

    and symbol; it never silently rescales the entered numeric amount.

R2 — Precision per currency

  • Decimal places follow the currency, not the locale: BRLUSDEUR = 2,

    JPYCLP = 0, BHDKWD = 3. Use the ISO-4217 minor-unit exponent, not a hardcoded 2.

  • On blur, the value is rounded to that precision (half-up); the input

    shows the formatted amount.

R3 — Formatting & parsing

  • Display (on blur / read): grouped per the UI locale

    (specs/data/masks.kmd money mask) with the currency affix.

  • Parsing (what the user types): accept the locale's decimal +

    grouping separators, an optional leading sign, and a pasted formatted string; reject letters/extra symbols. Be liberal in, strict out.

  • inputmode="decimal"; never numeric (decimals needed).

R4 — Storage contract

  • Persist as an integer in the currency's minor units (cents) +

    the currency code — never a float (no binary-float money), aligned with specs/data/field-types.kmd. Display is derived from this pair.

R5 — Range & validation

  • Optional min/max + non-negative constraints; violations show an inline

    message (specs/errors/user-facing-messages.kmd), never silent clamp.

  • Empty vs zero are distinct (empty = unset).

R6 — Accessibility & i18n

  • Real <label>; the currency affix/selector is part of the accessible

    name ("Amount in Brazilian Real").

  • Right-alignment is presentational only; the value reads correctly to AT.
  • All formatting/parsing is locale + currency aware

    (specs/i18n/contract.kmd); the stored minor-unit value is locale-invariant.

Não-escopo

  • FX conversion between currencies (a service concern).
  • The money display mask mechanics (specs/data/masks.kmd).
  • Accounting/rounding policy beyond per-currency precision (domain rules).