Skip to content
Brok UI

Loading…

No results

Money Value

Open source

A read-only money value that formats integer minor units or decimal strings by currency and locale without floating-point calculation, with accounting and compact modes.

Version
v1.1.3
Stability
stable
License
MIT
Related
Currency Input

Preview

€ 1.299,00 $42.50 ¥4,800 1 299,00 €
Mode
Size
previews.components.money-value.default.blade.php Blade
{{-- Symbol side and spacing follow the locale's own currency pattern. --}}
<div class="flex flex-wrap items-baseline gap-6">
    <x-ui.money-value minor="129900" currency="EUR" locale="nl-NL" />
    <x-ui.money-value amount="42.50" currency="USD" locale="en-US" />
    <x-ui.money-value minor="4800" currency="JPY" locale="ja-JP" />
    <x-ui.money-value amount="1299" currency="EUR" locale="fr-FR" />
</div>

Mode options

Standard Current
Accounting Current
Compact Current

Size options

Sm Current
Md Current
Lg Current
Xl Current

Installation

terminal
php artisan ui:add money-value

Registry contract

php artisan ui:add money-value writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.

  • blade resources/views/components/ui/money-value.blade.php
Registry dependencies
None — installs on its own.
Packages
composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.

money-value.md
# Brok UI: Money Value (`money-value`)

A read-only money value that formats integer minor units or decimal strings by currency and locale without floating-point calculation, with accounting and compact modes.

Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.

## Install

```bash
php artisan ui:add money-value
```

## Usage

```blade
{{-- Symbol side and spacing follow the locale's own currency pattern. --}}
<div class="flex flex-wrap items-baseline gap-6">
    <x-ui.money-value minor="129900" currency="EUR" locale="nl-NL" />
    <x-ui.money-value amount="42.50" currency="USD" locale="en-US" />
    <x-ui.money-value minor="4800" currency="JPY" locale="ja-JP" />
    <x-ui.money-value amount="1299" currency="EUR" locale="fr-FR" />
</div>
```

## Props

- `amount` (decimal-string|null, default `null`) — A decimal string; it is never converted to a float.
- `minor` (integer-string|null, default `null`) — An integer in currency minor units; it takes precedence over amount.
- `currency` (ISO 4217 code, default `USD`) — Currency code and minor-unit scale.
- `locale` (locale, default `en-US`) — Locale for symbols and separators.
- `mode` (standard|accounting|compact, default `standard`) — Display mode.
- `emptyLabel` (string, default `—`) — Localized empty value label.
- `invalidLabel` (string, default `Invalid amount`) — Localized invalid value label.
- `size` (sm|md|lg|xl, default `md`) — Display size; lg/xl for a total or a price hero.
- `signed` (boolean, default `false`) — Colour a negative amount in the destructive tone.

## Use when

- Use to summarize, sequence, or present data so users can scan it quickly.
- Display a server-provided monetary value with an ISO 4217 currency.

## Avoid when

- Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
- Use currency-input when a person must edit an amount.
- Do not use this component for billing, tax, conversion, or arithmetic.

## Anti-patterns

- Adding display ornament without informational value

## Rules

- Use the `<brok:money-value>` tag (or `<x-ui.money-value>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.

## Links

- Docs: https://brokui.dev/docs/components/money-value
- Registry JSON (files, props, contract): https://brokui.dev/r/open/money-value.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Examples

long-content.blade.php Blade
{{-- A number is one unbreakable run; a tight cell truncates it instead of folding
     the symbol onto its own line. Sizes: sm to xl. --}}
<div class="flex w-full max-w-md flex-col items-start gap-4">
    <x-ui.money-value amount="123456789012345678901234567890.12" currency="EUR" locale="nl-NL" class="block max-w-xs truncate" />
    <div class="flex flex-wrap items-baseline gap-6">
        <x-ui.money-value amount="1299" currency="EUR" locale="nl-NL" size="sm" />
        <x-ui.money-value amount="1299" currency="EUR" locale="nl-NL" size="md" />
        <x-ui.money-value amount="1299" currency="EUR" locale="nl-NL" size="lg" />
        <x-ui.money-value amount="1299" currency="EUR" locale="nl-NL" size="xl" />
    </div>
</div>
modes.blade.php Blade
{{-- standard | accounting (parentheses) | compact (1.2M); `signed` colours a loss. --}}
<div class="flex flex-wrap items-baseline gap-6">
    <x-ui.money-value amount="-1234.50" currency="USD" mode="standard" />
    <x-ui.money-value amount="-1234.50" currency="USD" mode="accounting" signed />
    <x-ui.money-value amount="1250000" currency="USD" mode="compact" />
</div>
states.blade.php Blade
{{-- No amount, an invalid amount, and an RTL locale with a trailing symbol. --}}
<div class="flex flex-wrap items-baseline gap-6">
    <x-ui.money-value />
    <x-ui.money-value amount="not-a-number" />
    <div dir="rtl"><x-ui.money-value minor="129900" currency="AED" locale="ar-AE" /></div>
</div>

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
amount decimal-string | null null A decimal string; it is never converted to a float.
minor integer-string | null null An integer in currency minor units; it takes precedence over amount.
currency ISO 4217 code USD Currency code and minor-unit scale.
locale locale en-US Locale for symbols and separators.
mode standard | accounting | compact standard Display mode.
emptyLabel string — Localized empty value label.
invalidLabel string Invalid amount Localized invalid value label.
size sm | md | lg | xl md Display size; lg/xl for a total or a price hero.
signed boolean false Colour a negative amount in the destructive tone.

Slots

Default Blade slot only.

Data slots

Stable hooks for CSS overrides and browser tests.

money-value money-value-symbol

Behavior

  • Uses string operations for amount normalization and grouping.
  • Renders semantic data with a normalized decimal value and currency attribute.
  • Declares registry capability flags: a11y, authoredStateFixtures, responsive, rtl, darkMode, localized.

Guidance

Scannable data display

Present data for rapid scanning.

Use when

  • Use to summarize, sequence, or present data so users can scan it quickly.
  • Display a server-provided monetary value with an ISO 4217 currency.

Avoid when

  • Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
  • Use currency-input when a person must edit an amount.
  • Do not use this component for billing, tax, conversion, or arithmetic.

Use instead

  • Table for exact comparison
  • Plain text for a single value

Anti-patterns

  • Adding display ornament without informational value
Anatomy
money-value
Theming hooks
Uses foreground and tabular-number tokens.

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
No component-owned keyboard interaction; native element behavior applies.
Focus
none
  • The visible value is text and never depends on color.
  • Empty and invalid inputs have explicit localized labels.
  • Semantic HTML and a stable data-slot attribute for styling and scripting hooks.
  • Focus-visible rings use the ring token, so keyboard focus is always visible.
  • Disabled and invalid states are conveyed to assistive tech, not by color alone.
  • Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
  • Labels go through __() and layout uses logical properties (ms-*, text-start), so it mirrors under dir="rtl" — flip the preview to RTL to confirm.
  • Dark mode uses the same semantic tokens under the dark class; high contrast follows forced-color system tokens.

Livewire

Safe

Livewire can update this component through forwarded wire:* attributes.

Source

The exact, editable file ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/money-value.blade.php Blade
@props([
    'amount' => null,
    'minor' => null,
    'currency' => 'USD',
    'locale' => 'en-US',
    'mode' => 'standard',
    'emptyLabel' => '—',
    'invalidLabel' => 'Invalid amount',
    // sm | md | lg | xl display size (lg/xl for a total or a price hero).
    'size' => 'md',
    // Colour a negative amount in the destructive tone.
    'signed' => false,
])

@php
    $currency = strtoupper((string) $currency);
    $locale = str_replace('-', '_', (string) $locale);
    $mode = in_array($mode, ['standard', 'accounting', 'compact'], true) ? $mode : 'standard';
    $size = in_array($size, ['sm', 'md', 'lg', 'xl'], true) ? $size : 'md';
    $sizeClass = ['sm' => 'text-xs', 'md' => 'text-sm', 'lg' => 'text-lg font-semibold', 'xl' => 'text-2xl font-semibold tracking-tight'][$size];
    $digits = in_array($currency, ['BHD', 'IQD', 'JOD', 'KWD', 'LYD', 'OMR', 'TND'], true)
        ? 3
        : (in_array($currency, ['CLP', 'DJF', 'GNF', 'ISK', 'JPY', 'KMF', 'KRW', 'PYG', 'RWF', 'UGX', 'UYI', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'], true) ? 0 : 2);
    $raw = $minor !== null ? (string) $minor : ($amount !== null ? (string) $amount : null);
    $valid = $raw !== null
        && strlen($raw) <= 128
        && preg_match('/^[A-Z]{3}$/', $currency) === 1
        && preg_match('/^-?\d+(?:\.\d+)?$/', $raw) === 1;
    $decimal = null;
    $formatted = null;

    if ($valid) {
        $negative = str_starts_with($raw, '-');
        $unsigned = ltrim($raw, '-');

        if ($minor !== null) {
            $unsigned = ltrim($unsigned, '0') ?: '0';
            $unsigned = str_pad($unsigned, $digits + 1, '0', STR_PAD_LEFT);
            $whole = $digits === 0 ? $unsigned : substr($unsigned, 0, -$digits);
            $fraction = $digits === 0 ? '' : substr($unsigned, -$digits);
        } else {
            [$whole, $fraction] = array_pad(explode('.', $unsigned, 2), 2, '');
            $whole = ltrim($whole, '0') ?: '0';
            $fraction = substr(str_pad($fraction, $digits, '0'), 0, $digits);
        }

        $decimal = ($negative ? '-' : '').$whole.($digits > 0 ? '.'.$fraction : '');
        $numberFormatter = class_exists(\NumberFormatter::class)
            ? new \NumberFormatter($locale, \NumberFormatter::DECIMAL)
            : null;
        $currencyFormatter = class_exists(\NumberFormatter::class)
            ? new \NumberFormatter($locale, \NumberFormatter::CURRENCY)
            : null;
        $group = $numberFormatter?->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL) ?: ',';
        $decimalSeparator = $numberFormatter?->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL) ?: '.';
        // The symbol belongs to the requested currency, not to the locale's own
        // default one: en-US with EUR must read "€", not "$".
        $currencyFormatter?->setTextAttribute(\NumberFormatter::CURRENCY_CODE, $currency);
        $symbol = $currencyFormatter?->getSymbol(\NumberFormatter::CURRENCY_SYMBOL) ?: $currency;
        $grouped = preg_replace('/\B(?=(\d{3})+(?!\d))/', $group, $whole);
        $displayNumber = $grouped.($digits > 0 ? $decimalSeparator.$fraction : '');

        if ($mode === 'compact' && strlen($whole) >= 4) {
            foreach ([12 => 'T', 9 => 'B', 6 => 'M', 3 => 'K'] as $power => $suffix) {
                if (strlen($whole) > $power) {
                    $head = substr($whole, 0, -$power);
                    $tail = substr($whole, -$power, 1);
                    $displayNumber = $head.($tail !== '0' ? $decimalSeparator.$tail : '').$suffix;
                    break;
                }
            }
        }

        // Symbol side AND spacing follow the locale's own currency pattern:
        // "€ 1.299,00" in nl-NL, "$42.50" in en-US, "1 299,00 €" in fr-FR.
        $sample = $currencyFormatter?->formatCurrency(0, $currency) ?: null;
        $symbolFirst = $sample === null || mb_strpos($sample, $symbol) <= mb_strpos($sample, '0');
        $spaced = $sample === null || preg_match('/\p{Zs}/u', $sample) === 1;
        $joiner = $spaced ? "\u{00A0}" : '';
        $formatted = $negative
            ? ($mode === 'accounting' ? '(' : '-')
            : '';
        $formatted .= $symbolFirst
            ? '<span data-slot="money-value-symbol" class="text-muted-foreground">'.e($symbol).'</span>'.$joiner.e($displayNumber)
            : e($displayNumber).$joiner.'<span data-slot="money-value-symbol" class="text-muted-foreground">'.e($symbol).'</span>';
        $formatted .= ($negative && $mode === 'accounting') ? ')' : '';
    }
    $state = $raw === null ? 'empty' : ($valid ? 'valid' : 'invalid');
    $toneClass = match (true) {
        $state !== 'valid' => 'text-muted-foreground',
        $signed && $negative => 'text-destructive-text',
        default => 'text-foreground',
    };
@endphp

{{-- One unbreakable run: the symbol never wraps away from its number. A tight
     cell truncates it (pass `class="block truncate"`) rather than folding it. --}}
<data
    data-slot="money-value"
    data-mode="{{ $mode }}"
    data-currency="{{ $currency }}"
    data-state="{{ $state }}"
    data-size="{{ $size }}"
    @if ($valid && $negative) data-negative="true" @endif
    @if ($decimal !== null) value="{{ $decimal }}" @endif
    {{ $attributes->merge(['class' => 'inline-block min-w-0 whitespace-nowrap tabular-nums '.$sizeClass.' '.$toneClass]) }}
>@if ($state === 'valid'){!! $formatted !!}@else{{ __($state === 'empty' ? $emptyLabel : $invalidLabel) }}@endif</data>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.1.3
Status
Stable
License
open
Accessibility reviewed
No review date recorded
Last breaking change
No date recorded
Deprecation
Not deprecated
Contract
v6
Foundation
≥ 1.0.0