Skip to content
Brok UI

Loading…

No results

Current Plan

Open source

A compact current-plan card for plan name, billing interval, renewal or cancellation state, bounded usage meters, and an application-owned management link.

Version
v1.1.2
Stability
stable
License
MIT
Related
Progress
Badge
Card

Preview

Current plan

Pro

For teams that ship every week.

Active

$29 per month (billed yearly)

Renews
Payment method
Visa ending 4242

Usage

Team members 7 / 10
Storage 84 / 100 GB
API requests 120,000 / 500,000

Included

  • Unlimited projects
  • Priority support
  • SSO and audit log
  • 90-day history
previews.components.current-plan.default.blade.php Blade
@php
    $usage = [
        ['label' => __('Team members'), 'used' => 7, 'limit' => 10],
        ['label' => __('Storage'), 'used' => 84, 'limit' => 100, 'unit' => 'GB'],
        ['label' => __('API requests'), 'used' => 120000, 'limit' => 500000],
    ];
    $features = [__('Unlimited projects'), __('Priority support'), __('SSO and audit log'), __('90-day history')];
@endphp
<x-ui.current-plan
    class="mx-auto w-full max-w-md"
    name="Pro"
    description="{{ __('For teams that ship every week.') }}"
    price="$29"
    price-note="{{ __('per month') }}"
    interval="{{ __('billed yearly') }}"
    state="active"
    :renews-at="now()->addYear()"
    payment-method="{{ __('Visa ending 4242') }}"
    :usage="$usage"
    :features="$features"
    action-url="#"
    secondary-action-url="#"
/>

Installation

terminal
php artisan ui:add current-plan

Registry contract

php artisan ui:add current-plan 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/current-plan.blade.php
Registry dependencies
card badge relative-time progress button
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.

current-plan.md
# Brok UI: Current Plan (`current-plan`)

A compact current-plan card for plan name, billing interval, renewal or cancellation state, bounded usage meters, and an application-owned management link.

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

## Install

```bash
php artisan ui:add current-plan
```

## Usage

```blade
@php
    $usage = [
        ['label' => __('Team members'), 'used' => 7, 'limit' => 10],
        ['label' => __('Storage'), 'used' => 84, 'limit' => 100, 'unit' => 'GB'],
        ['label' => __('API requests'), 'used' => 120000, 'limit' => 500000],
    ];
    $features = [__('Unlimited projects'), __('Priority support'), __('SSO and audit log'), __('90-day history')];
@endphp
<x-ui.current-plan
    class="mx-auto w-full max-w-md"
    name="Pro"
    description="{{ __('For teams that ship every week.') }}"
    price="$29"
    price-note="{{ __('per month') }}"
    interval="{{ __('billed yearly') }}"
    state="active"
    :renews-at="now()->addYear()"
    payment-method="{{ __('Visa ending 4242') }}"
    :usage="$usage"
    :features="$features"
    action-url="#"
    secondary-action-url="#"
/>
```

## Props

- `name` (string, default `required`) — Current plan name.
- `interval` (string|null, default `null`) — Server-formatted billing interval.
- `state` (active|trial|past_due|cancelled, default `active`) — Billing state for presentation.
- `renewsAt` (instant|null, default `null`) — Renewal or access-end instant.
- `usage` (list<UsageMeter>, default `[]`) — Up to eight usage meters: label, used, limit and an optional unit (GB). A meter turns warning at 80% and destructive at the limit.
- `actionUrl` (url|null, default `null`) — Application-owned plan management URL.
- `actionLabel` (string, default `Manage plan`) — Localized plan management label.
- `price` (string|null, default `null`) — Server-formatted price shown as the hero figure.
- `priceNote` (string|null, default `null`) — What the price is per, shown after it (per month).
- `description` (string|null, default `null`) — One line under the plan name.
- `features` (list<string>, default `[]`) — Up to eight included items, shown as a check list.
- `paymentMethod` (string|null, default `null`) — Server-formatted payment method (Visa ending 4242).
- `secondaryActionUrl` (url|null, default `null`) — A second, outline action (change plan, cancel).
- `secondaryActionLabel` (string, default `Change plan`) — Label of the second action.

## Use when

- Use when self-contained content objects benefit from a recognizable card-like presentation with local actions or metadata.
- Summarize subscription data that the billing backend already calculated.

## Avoid when

- Do not use card widgets where a simpler list, table, or plain text block would make comparison faster.
- Comparing multiple plans side by side; use blocks/comparison-plans-table instead of a single plan summary.
- Do not use this component to calculate invoices, renewals, or entitlements.

## Anti-patterns

- Using cards for dense cross-item comparison

## Rules

- Use the `<brok:current-plan>` tag (or `<x-ui.current-plan>`) 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/current-plan
- Registry JSON (files, props, contract): https://brokui.dev/r/open/current-plan.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
<div class="max-w-xs">
    <x-ui.current-plan name="{{ __('Enterprise collaboration and governance plan with a long translated name') }}" interval="{{ __('Billed once for permanent access') }}" :usage="[['label' => __('A long usage meter label'), 'used' => 42, 'limit' => 100]]" />
</div>
states.blade.php Blade
<div class="grid gap-4 md:grid-cols-2">
    <x-ui.current-plan name="Pro trial" interval="14 days" state="trial" :renews-at="now()->addDays(8)" />
    <x-ui.current-plan name="Pro" interval="Yearly" state="cancelled" :renews-at="now()->addDays(21)" />
</div>

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
name string required Current plan name.
interval string | null null Server-formatted billing interval.
state active | trial | past_due | cancelled active Billing state for presentation.
renewsAt instant | null null Renewal or access-end instant.
usage list<UsageMeter> [] Up to eight usage meters: label, used, limit and an optional unit (GB). A meter turns warning at 80% and destructive at the limit.
actionUrl url | null null Application-owned plan management URL.
actionLabel string Manage plan Localized plan management label.
price string | null null Server-formatted price shown as the hero figure.
priceNote string | null null What the price is per, shown after it (per month).
description string | null null One line under the plan name.
features list<string> [] Up to eight included items, shown as a check list.
paymentMethod string | null null Server-formatted payment method (Visa ending 4242).
secondaryActionUrl url | null null A second, outline action (change plan, cancel).
secondaryActionLabel string Change plan Label of the second action.

Slots

Default Blade slot only.

Data slots

Stable hooks for CSS overrides and browser tests.

current-plan current-plan-meter current-plan-price

Behavior

  • Presents billing view data without owning billing logic.
  • Declares registry capability flags: a11y, authoredStateFixtures, responsive, rtl, darkMode, localized.

Guidance

Card and content widget

Present a self-contained content object with local context.

Use when

  • Use when self-contained content objects benefit from a recognizable card-like presentation with local actions or metadata.
  • Summarize subscription data that the billing backend already calculated.

Avoid when

  • Do not use card widgets where a simpler list, table, or plain text block would make comparison faster.
  • Comparing multiple plans side by side; use blocks/comparison-plans-table instead of a single plan summary.
  • Do not use this component to calculate invoices, renewals, or entitlements.

Use instead

  • List for compact scanning
  • Table for exact comparison

Anti-patterns

  • Using cards for dense cross-item comparison
Anatomy
current-plan
Theming hooks
Uses card, badge, progress, and button 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
  • Plan state is visible text.
  • Usage meters expose accessible labels and values.
  • 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/current-plan.blade.php Blade
@props([
    'name',
    'interval' => null,
    'state' => 'active',
    'renewsAt' => null,
    'usage' => [],
    'actionUrl' => null,
    'actionLabel' => 'Manage plan',
    // Server-formatted price ("$29") and what it is per ("per month").
    'price' => null,
    'priceNote' => null,
    // One line under the name: what the plan is for.
    'description' => null,
    // What the plan includes, shown as a check list.
    'features' => [],
    // Server-formatted payment method ("Visa ending 4242").
    'paymentMethod' => null,
    // A second, quieter action (change plan, cancel).
    'secondaryActionUrl' => null,
    'secondaryActionLabel' => 'Change plan',
])

@php
    $stateVariants = ['active' => 'success', 'trial' => 'info', 'past_due' => 'warning', 'cancelled' => 'secondary'];
    $stateLabels = ['active' => __('Active'), 'trial' => __('Trial'), 'past_due' => __('Past due'), 'cancelled' => __('Cancelled')];
    $resolvedState = array_key_exists($state, $stateVariants) ? $state : 'active';
    $usage = array_slice(array_values(array_filter((array) $usage, 'is_array')), 0, 8);
    $features = array_slice(array_values(array_filter((array) $features, 'is_string')), 0, 8);
@endphp

{{-- Current plan: the plan, its price and state, when it renews and how it
     is paid, what it includes, the usage meters (a meter turns warning at
     80% and destructive at the limit) and the plan actions. --}}
<x-ui.card data-slot="current-plan" data-state="{{ $resolvedState }}" {{ $attributes }}>
    <x-ui.card.header class="flex-row flex-wrap items-start justify-between gap-3">
        <div class="min-w-0">
            <x-ui.card.description>{{ __('Current plan') }}</x-ui.card.description>
            <x-ui.card.title class="truncate">{{ $name }}</x-ui.card.title>
            @if (filled($description))
                <p class="mt-1 text-sm text-muted-foreground">{{ $description }}</p>
            @endif
        </div>
        <x-ui.badge :variant="$stateVariants[$resolvedState]" dot>{{ $stateLabels[$resolvedState] }}</x-ui.badge>
    </x-ui.card.header>
    <x-ui.card.content class="space-y-6">
        @if (filled($price) || filled($renewsAt) || filled($paymentMethod))
            <div class="flex flex-wrap items-end justify-between gap-3">
                @if (filled($price))
                    <p class="flex items-baseline gap-1">
                        <span data-slot="current-plan-price" class="text-2xl font-semibold tabular-nums tracking-tight">{{ $price }}</span>
                        @if (filled($priceNote))<span class="text-sm text-muted-foreground">{{ $priceNote }}</span>@endif
                        @if (filled($interval))<span class="text-sm text-muted-foreground">({{ $interval }})</span>@endif
                    </p>
                @endif
                <dl class="flex flex-col items-end gap-1 text-sm text-muted-foreground">
                    @if (filled($renewsAt))
                        <div class="flex items-center gap-2">
                            <svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2" /><path d="M16 2v4M8 2v4M3 10h18" /></svg>
                            <dt>{{ $resolvedState === 'cancelled' ? __('Access ends') : __('Renews') }}</dt>
                            <dd class="text-foreground"><x-ui.relative-time :datetime="$renewsAt" mode="date" /></dd>
                        </div>
                    @endif
                    @if (filled($paymentMethod))
                        <div class="flex items-center gap-2">
                            <svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="5" width="20" height="14" rx="2" /><path d="M2 10h20" /></svg>
                            <dt class="sr-only">{{ __('Payment method') }}</dt>
                            <dd class="text-foreground">{{ $paymentMethod }}</dd>
                        </div>
                    @endif
                </dl>
            </div>
        @endif

        @if ($usage !== [])
            <div class="space-y-4">
                <p class="text-2xs font-semibold uppercase tracking-wide text-muted-foreground">{{ __('Usage') }}</p>
                @foreach ($usage as $meter)
                    @php
                        $used = min(1000000000, max(0, (int) ($meter['used'] ?? 0)));
                        $limit = min(1000000000, max(0, (int) ($meter['limit'] ?? 0)));
                        $value = $limit > 0 ? min(100, (int) floor(($used * 100) / $limit)) : 0;
                        $unit = (string) ($meter['unit'] ?? '');
                        $tone = $value >= 100 ? 'destructive' : ($value >= 80 ? 'warning' : 'default');
                    @endphp
                    <div class="space-y-2" data-slot="current-plan-meter" data-tone="{{ $tone }}">
                        <div class="flex min-w-0 items-baseline justify-between gap-3 text-sm">
                            <span class="truncate text-foreground">{{ $meter['label'] ?? __('Usage') }}</span>
                            <span class="shrink-0 tabular-nums text-muted-foreground"><span class="font-medium text-foreground">{{ number_format($used) }}</span> / {{ number_format($limit) }}{{ $unit !== '' ? ' '.$unit : '' }}</span>
                        </div>
                        <x-ui.progress :value="$value" :tone="$tone" class="h-2" :aria-label="$meter['label'] ?? __('Usage')" />
                    </div>
                @endforeach
            </div>
        @endif

        @if ($features !== [])
            <div class="space-y-2">
                <p class="text-2xs font-semibold uppercase tracking-wide text-muted-foreground">{{ __('Included') }}</p>
                <ul class="grid gap-2 text-sm sm:grid-cols-2" role="list">
                    @foreach ($features as $feature)
                        <li class="flex gap-2">
                            <svg class="mt-px size-4 shrink-0 text-success" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 5 5L20 7" /></svg>
                            <span class="min-w-0">{{ $feature }}</span>
                        </li>
                    @endforeach
                </ul>
            </div>
        @endif
    </x-ui.card.content>
    @if (filled($actionUrl) || filled($secondaryActionUrl))
        <x-ui.card.footer class="flex-wrap gap-2 border-t border-border pt-6">
            @if (filled($actionUrl))
                <x-ui.button as="a" :href="$actionUrl">{{ __($actionLabel) }}</x-ui.button>
            @endif
            @if (filled($secondaryActionUrl))
                <x-ui.button as="a" :href="$secondaryActionUrl" variant="outline">{{ __($secondaryActionLabel) }}</x-ui.button>
            @endif
        </x-ui.card.footer>
    @endif
</x-ui.card>

Ownership & lifecycle

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