Credit Card
A visual credit/debit card surface showing brand mark, chip, masked number, holder name, and expiry.
Preview
•••• •••• •••• 4242
- Card holder
- Jordan Reyes
- Expires
- 08/27
<x-ui.credit-card brand="visa" variant="primary" number="4242 4242 4242 4242" holder="Jordan Reyes" expiry="08/27" />
Installation
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.
-
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.
# 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
Props
| 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.
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
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
- Theming hooks
Accessibility
- 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-slotattribute for styling and scripting hooks. - Focus-visible rings use the
ringtoken, 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 underdir="rtl"— flip the preview to RTL to confirm. - Dark mode uses the same semantic tokens under the
darkclass; high contrast follows forced-color system tokens.
Livewire
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.
@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