Skip to content
Brok UI

Loading…

No results

Credit Card

Open source

A visual credit/debit card surface showing brand mark, chip, masked number, holder name, and expiry.

Version
v1.1.4
Stability
stable
License
MIT
Related
Card
Current Plan
Currency Input

Preview

Variant
previews.components.credit-card.default.blade.php Blade
<x-ui.credit-card brand="visa" variant="primary" number="4242 4242 4242 4242" holder="Jordan Reyes" expiry="08/27" />
Default Current
Muted Current
Primary Current

Installation

terminal
php artisan ui:add credit-card

Registry contract

php artisan ui:add credit-card 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/credit-card.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.

credit-card.md
# Brok UI: Credit Card (`credit-card`)

A visual credit/debit card surface showing brand mark, chip, masked number, holder name, and expiry.

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

## Install

```bash
php artisan ui:add credit-card
```

## Usage

```blade
<x-ui.credit-card brand="visa" variant="primary" number="4242 4242 4242 4242" holder="Jordan Reyes" expiry="08/27" />
```

## Props

- `brand` (string, default `generic`)
- `number` (string, default `4242 4242 4242 4242`)
- `holder` (string, default `A. Cardholder`)
- `expiry` (string, default `12/28`)
- `variant` (default|muted|primary, default `default`)

## Use when

- Use to group related content into scannable units for browsing, prioritization, or rearrangement.
- Showing a saved payment method in a wallet, checkout or billing settings.
- Illustrating a card in marketing or onboarding, with a brand mark and masked number.

## Avoid when

- Do not use card-like structures when dense row-by-row comparison would be clearer in a list or table.
- Collecting card details; use a payment provider's element.
- Displaying a full card number: pass it masked, the card renders what it is given.

## Anti-patterns

- Turning every object into a card

## Rules

- Use the `<brok:credit-card>` tag (or `<x-ui.credit-card>`) 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/credit-card
- Registry JSON (files, props, contract): https://brokui.dev/r/open/credit-card.json

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

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
brand string generic
number string 4242 4242 4242 4242
holder string A. Cardholder
expiry string 12/28
variant default | muted | primary default

Slots

  • default —

Data slots

Stable hooks for CSS overrides and browser tests.

credit-card

Behavior

  • brand selects the mark; number, holder and expiry render as given, and variant switches the surface look.
  • The card is static and has no interaction of its own.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.

Guidance

Card-based organization

Organize browsable objects into scannable units.

Use when

  • Use to group related content into scannable units for browsing, prioritization, or rearrangement.
  • Showing a saved payment method in a wallet, checkout or billing settings.
  • Illustrating a card in marketing or onboarding, with a brand mark and masked number.

Avoid when

  • Do not use card-like structures when dense row-by-row comparison would be clearer in a list or table.
  • Collecting card details; use a payment provider's element.
  • Displaying a full card number: pass it masked, the card renders what it is given.

Use instead

  • Table for exact comparison
  • List for compact scanning

Anti-patterns

  • Turning every object into a card
Anatomy
root
Theming hooks
credit-card

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 card is role="img" with an accessible name, so a screen reader hears the card rather than its decorations.
  • The chip and brand marks are aria-hidden.
  • 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/credit-card.blade.php Blade
@props([
    'brand' => 'generic',
    'number' => '4242 4242 4242 4242',
    'holder' => 'A. Cardholder',
    'expiry' => '12/28',
    'variant' => 'default',
])

@php
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
    $variant = $styles['normalizeVariant']($variant);
    $brand = in_array($brand, ['visa', 'mastercard', 'amex', 'generic'], true) ? $brand : 'generic';
    $variant = in_array($variant, ['default', 'muted', 'primary'], true) ? $variant : 'default';

    // Token-only surfaces — gradients use from-/to- token classes, never raw colour.
    $surfaces = [
        'default' => 'bg-gradient-to-br from-card to-muted text-card-foreground border border-border',
        'muted' => 'bg-gradient-to-br from-muted to-muted/60 text-foreground border border-border',
        'primary' => 'bg-gradient-to-br from-primary to-primary/70 text-primary-foreground border border-primary/20',
    ];
    $surface = $surfaces[$variant];

    // Group the number into 4-digit blocks, masking all but the last group.
    $digits = preg_replace('/\D/', '', (string) $number);
    $last4 = substr($digits, -4);
    $masked = trim('•••• •••• •••• ' . $last4);

    $brandLabel = match ($brand) {
        'visa' => 'Visa',
        'mastercard' => 'Mastercard',
        'amex' => 'American Express',
        default => 'Card',
    };
@endphp

<figure
    data-slot="credit-card"
    data-brand="{{ $brand }}"
    data-variant="{{ $variant }}"
    role="img"
    aria-label="{{ __(':brand card ending in :last4', ['brand' => $brandLabel, 'last4' => $last4]) }}"
    {{ $attributes->merge(['class' => 'relative flex aspect-[1.586] w-full max-w-sm flex-col justify-between overflow-hidden rounded-lg p-6 shadow-lg ' . $surface]) }}
>
    {{-- Top row: brand mark --}}
    <div class="flex items-start justify-between">
        <span class="text-sm font-medium uppercase tracking-widest opacity-70">{{ __('Card') }}</span>

        <span class="inline-flex items-center" aria-hidden="true">
            @if ($brand === 'visa')
                <span class="text-xl font-bold italic tracking-tight">VISA</span>
            @elseif ($brand === 'mastercard')
                <span class="flex items-center -space-x-2">
                    <span class="size-6 rounded-full bg-current opacity-80"></span>
                    <span class="size-6 rounded-full bg-current opacity-50"></span>
                </span>
            @elseif ($brand === 'amex')
                <span class="text-sm font-semibold uppercase tracking-tight">AMEX</span>
            @else
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="size-6 opacity-80">
                    <rect x="2" y="5" width="20" height="14" rx="2" />
                    <path d="M2 10h20" />
                </svg>
            @endif
        </span>
    </div>

    {{-- Chip + number --}}
    <div class="space-y-3">
        <div class="flex h-7 w-10 items-center justify-center rounded-md border border-border bg-muted" aria-hidden="true">
            <div class="h-4 w-7 rounded-sm border border-border/60"></div>
        </div>

        <p class="font-mono text-lg tracking-[0.18em] tabular-nums sm:text-xl">{{ $masked }}</p>
    </div>

    {{-- Bottom row: holder + expiry --}}
    <dl class="flex items-end justify-between text-sm uppercase">
        <div class="space-y-0.5">
            <dt class="font-medium tracking-widest opacity-60">{{ __('Card holder') }}</dt>
            <dd class="text-sm font-medium normal-case tracking-normal">{{ $holder }}</dd>
        </div>
        <div class="space-y-0.5 text-end">
            <dt class="font-medium tracking-widest opacity-60">{{ __('Expires') }}</dt>
            <dd class="text-sm font-medium tabular-nums tracking-normal">{{ $expiry }}</dd>
        </div>
    </dl>
</figure>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.1.4
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