Money input component
components specs/components/money-input.kmd
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.
When this spec applies
Primary triggers
- Implement a money/price input field
All triggers
- Capture a price / amount / money value
- Add a currency selector to an amount field
- Format and validate money input
Specification body
Component — Money input
Status: v0.1.0 — Draft. Promoted from the Shopify Polaris parity scan (meta/docs/stack #091). Source: https://polaris.shopify.com/components/selection-and-input/text-field (price/currency usage). Display mask:
specs/data/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: BRL/USD/EUR = 2, JPY/CLP = 0, BHD/KWD = 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.kmdmoney 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"; nevernumeric(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).
References
specs/data/masks.kmdspecs/data/field-types.kmdspecs/errors/user-facing-messages.kmdspecs/i18n/contract.kmd