Settings
Account settings screens — a grouped account form, a notification-preferences matrix and a connected-apps integrations list.
Preview
@props([
'heading' => 'Security',
'subheading' => 'Manage your password, two-factor authentication, and active sessions.',
'action' => '#',
'twoFactorEnabled' => true,
'sessions' => null,
'headingTag' => 'h2',
])
@php
$sessions ??= [
['device' => 'desktop', 'browser' => 'Chrome 124', 'os' => 'macOS 14', 'location' => 'Sydney, AU', 'current' => true, 'lastActive' => null],
['device' => 'mobile', 'browser' => 'Safari 17', 'os' => 'iOS 17', 'location' => 'Sydney, AU', 'current' => false, 'lastActive' => '2 hours ago'],
['device' => 'desktop', 'browser' => 'Firefox 125', 'os' => 'Windows 11', 'location' => 'Melbourne, AU', 'current' => false, 'lastActive' => '3 days ago'],
];
@endphp
<x-ui.section space="md" {{ $attributes }}>
<div class="mx-auto max-w-3xl space-y-6 px-4">
<div class="mb-2">
<{{ $headingTag }} class="text-2xl font-bold tracking-tight">{{ __($heading) }}</{{ $headingTag }}>
<p class="mt-2 text-sm text-muted-foreground">{{ __($subheading) }}</p>
</div>
<x-ui.card>
<x-ui.card.header>
<x-ui.card.title>{{ __('Password') }}</x-ui.card.title>
<x-ui.card.description>{{ __('Use a long, random password that you do not use anywhere else.') }}</x-ui.card.description>
</x-ui.card.header>
<x-ui.card.content>
<form method="POST" action="{{ $action }}" class="space-y-4">
@csrf
@method('PUT')
<x-ui.field>
<x-ui.label for="sec-current-password" required>{{ __('Current password') }}</x-ui.label>
<x-ui.input id="sec-current-password" type="password" name="current_password" autocomplete="current-password" required />
</x-ui.field>
<x-ui.field>
<x-ui.label for="sec-new-password" required>{{ __('New password') }}</x-ui.label>
<x-ui.input id="sec-new-password" type="password" name="password" autocomplete="new-password" required />
</x-ui.field>
<x-ui.field>
<x-ui.label for="sec-confirm-password" required>{{ __('Confirm new password') }}</x-ui.label>
<x-ui.input id="sec-confirm-password" type="password" name="password_confirmation" autocomplete="new-password" required />
</x-ui.field>
<div class="flex justify-end pt-2">
<x-ui.button type="submit">{{ __('Update password') }}</x-ui.button>
</div>
</form>
</x-ui.card.content>
</x-ui.card>
<x-ui.card>
<x-ui.card.header>
<x-ui.card.title>{{ __('Two-factor authentication') }}</x-ui.card.title>
<x-ui.card.description>{{ __('Add an extra layer of security by requiring a one-time code on sign in.') }}</x-ui.card.description>
</x-ui.card.header>
<x-ui.card.content>
<x-ui.switch-field
name="two_factor_enabled"
:label="__('Authenticator app')"
:description="$twoFactorEnabled ? __('Two-factor authentication is active on your account.') : __('Protect your account with an authenticator app.')"
:checked="$twoFactorEnabled"
align="center"
>
<x-slot:badge>
<x-ui.badge variant="{{ $twoFactorEnabled ? 'success' : 'outline' }}">{{ $twoFactorEnabled ? __('Enabled') : __('Disabled') }}</x-ui.badge>
</x-slot:badge>
</x-ui.switch-field>
</x-ui.card.content>
</x-ui.card>
<x-ui.card>
<x-ui.card.header>
<x-ui.card.title>{{ __('Active sessions') }}</x-ui.card.title>
<x-ui.card.description>{{ __('These devices are currently signed in. Revoke any you do not recognise.') }}</x-ui.card.description>
</x-ui.card.header>
<x-ui.card.content class="p-0">
<ul role="list" class="divide-y divide-border">
@foreach ($sessions as $session)
<li class="flex items-center justify-between gap-4 px-6 py-4">
<div class="shrink-0 text-muted-foreground" aria-hidden="true">
@if ($session['device'] === 'mobile')
<svg class="size-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="2" width="14" height="20" rx="2" /><line x1="12" y1="18" x2="12.01" y2="18" /></svg>
@else
<svg class="size-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="14" rx="2" /><line x1="8" y1="21" x2="16" y2="21" /><line x1="12" y1="17" x2="12" y2="21" /></svg>
@endif
</div>
<div class="min-w-0 flex-1 space-y-0.5">
<p class="text-sm font-medium text-foreground">{{ $session['browser'] }} — {{ $session['os'] }}</p>
<p class="text-xs text-muted-foreground">{{ $session['location'] }}</p>
</div>
<div class="flex shrink-0 items-center gap-4">
@if ($session['current'])
<x-ui.badge variant="secondary" size="sm">{{ __('Current') }}</x-ui.badge>
@else
<span class="text-xs text-muted-foreground">{{ __($session['lastActive']) }}</span>
<x-ui.button type="button" variant="ghost" size="sm" class="text-destructive hover:bg-destructive/10 hover:text-destructive">{{ __('Revoke') }}</x-ui.button>
@endif
</div>
</li>
@endforeach
</ul>
</x-ui.card.content>
</x-ui.card>
<x-ui.card class="border-destructive/40">
<x-ui.card.header>
<x-ui.card.title>{{ __('Danger zone') }}</x-ui.card.title>
<x-ui.card.description>{{ __('Permanent and irreversible actions for your account.') }}</x-ui.card.description>
</x-ui.card.header>
<x-ui.card.content>
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="space-y-2">
<p class="text-sm font-medium text-foreground">{{ __('Delete account') }}</p>
<p class="text-sm text-muted-foreground">{{ __('Once you delete your account, all of your data will be permanently removed. This cannot be undone.') }}</p>
</div>
{{-- Hold-to-confirm: a slip-proof destructive action. A fill sweeps up
the button over 2s while pressed (pointer or Space/Enter held);
releasing early snaps it back. Completion dispatches a
`hold-confirm` event on the button — wire the real deletion there.
The bottom-up fill is axis-neutral, so it needs no RTL handling. --}}
<div
x-data="{
holding: false,
timer: null,
begin() {
if (this.timer) return;
this.holding = true;
this.timer = window.setTimeout(() => {
this.release();
this.$dispatch('hold-confirm');
}, 2000);
},
release() {
this.holding = false;
window.clearTimeout(this.timer);
this.timer = null;
},
destroy() { window.clearTimeout(this.timer) },
}"
class="shrink-0"
>
<x-ui.button
type="button"
variant="ghost"
class="relative w-full text-destructive select-none hover:bg-destructive/10 hover:text-destructive sm:w-auto"
aria-label="{{ __('Press and hold to delete your account') }}"
@pointerdown="begin()"
@pointerup="release()"
@pointerleave="release()"
@pointercancel="release()"
@keydown.enter.prevent="if (! $event.repeat) begin()"
@keydown.space.prevent="if (! $event.repeat) begin()"
@keyup.enter="release()"
@keyup.space="release()"
>
<span
aria-hidden="true"
data-slot="hold-confirm-fill"
class="pointer-events-none absolute inset-0 bg-destructive/15"
:style="holding
? 'clip-path: inset(0 0 0 0); transition: clip-path 2s linear'
: 'clip-path: inset(100% 0 0 0); transition: clip-path 240ms var(--ease-standard)'"
></span>
<span class="relative">{{ __('Hold to delete account') }}</span>
</x-ui.button>
</div>
</div>
</x-ui.card.content>
</x-ui.card>
</div>
</x-ui.section>
Installation
php artisan ui:add blocks/settings-security
Registry contract
Install confidence
php artisan ui:add blocks/settings-security
writes only the generated targets below. The CLI validates each file hash before writing and
prompts before replacing local changes unless --force is used.
- Version
- 1.2.5
- License
- open
- Stability
- stable
- Contract
- v1
- Foundation
- ≥ 1.0.0
| Type | Generated target |
|---|---|
| blade | resources/views/blocks/settings-security.blade.php |
Registry dependencies
Package dependencies
composer: jml/brok:^0.2
Use with AI
A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.
# Brok UI block: Settings Security (`settings-security`)
A security settings screen — a password form, a two-factor toggle, an active-sessions list and a destructive danger zone.
Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.
## Install
```bash
php artisan ui:add blocks/settings-security
```
## Render it
```blade
<x-blocks.settings-security />
```
## Props
- `heading` (string, default `Security`) — Section heading.
- `subheading` (string, default `Manage your password, two-factor authentication, and active sessions.`) — Supporting copy under the heading.
- `action` (string, default `#`) — Password form submission endpoint.
- `two-factor-enabled` (bool, default `true`) — Initial state of the two-factor toggle.
- `sessions` (mixed|null, default `null`) — Active session rows with device, browser, location and last-active time; null falls back to sample data.
- `heading-tag` (string, default `h2`) — HTML heading tag rendered for the title.
## Use when
- Building a security settings screen with a password-change form, a two-factor toggle, an active-sessions list and a destructive danger zone.
- Dedicating a page to account security rather than general profile settings.
## Avoid when
- Managing general profile or notification settings; use settings-account or settings-notifications instead.
## Rules
- Render the installed block with `<x-blocks.settings-security />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.
## Links
- Docs: https://brokui.dev/blocks/settings-security
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/settings-security.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Guidance
Use when
- Building a security settings screen with a password-change form, a two-factor toggle, an active-sessions list and a destructive danger zone.
- Dedicating a page to account security rather than general profile settings.
Avoid when
- Managing general profile or notification settings; use settings-account or settings-notifications instead.
Anti-patterns
- Do not replace semantic props with conflicting utility classes.
- Do not remove labels, focus styles, or state attributes.
- Anatomy
Usage
Render the block as a component, passing data where useful:
<x-blocks.settings-security />
Built from primitives
This block composes installed <brok:*> primitives and semantic design
tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by
editing CSS variables; flip the preview to RTL to confirm it mirrors.
Source
The exact, editable file ui:add writes
into your app. The preview above renders this same source — there are no preview-only blocks.
@props([
'heading' => 'Security',
'subheading' => 'Manage your password, two-factor authentication, and active sessions.',
'action' => '#',
'twoFactorEnabled' => true,
'sessions' => null,
'headingTag' => 'h2',
])
@php
$sessions ??= [
['device' => 'desktop', 'browser' => 'Chrome 124', 'os' => 'macOS 14', 'location' => 'Sydney, AU', 'current' => true, 'lastActive' => null],
['device' => 'mobile', 'browser' => 'Safari 17', 'os' => 'iOS 17', 'location' => 'Sydney, AU', 'current' => false, 'lastActive' => '2 hours ago'],
['device' => 'desktop', 'browser' => 'Firefox 125', 'os' => 'Windows 11', 'location' => 'Melbourne, AU', 'current' => false, 'lastActive' => '3 days ago'],
];
@endphp
<x-ui.section space="md" {{ $attributes }}>
<div class="mx-auto max-w-3xl space-y-6 px-4">
<div class="mb-2">
<{{ $headingTag }} class="text-2xl font-bold tracking-tight">{{ __($heading) }}</{{ $headingTag }}>
<p class="mt-2 text-sm text-muted-foreground">{{ __($subheading) }}</p>
</div>
<x-ui.card>
<x-ui.card.header>
<x-ui.card.title>{{ __('Password') }}</x-ui.card.title>
<x-ui.card.description>{{ __('Use a long, random password that you do not use anywhere else.') }}</x-ui.card.description>
</x-ui.card.header>
<x-ui.card.content>
<form method="POST" action="{{ $action }}" class="space-y-4">
@csrf
@method('PUT')
<x-ui.field>
<x-ui.label for="sec-current-password" required>{{ __('Current password') }}</x-ui.label>
<x-ui.input id="sec-current-password" type="password" name="current_password" autocomplete="current-password" required />
</x-ui.field>
<x-ui.field>
<x-ui.label for="sec-new-password" required>{{ __('New password') }}</x-ui.label>
<x-ui.input id="sec-new-password" type="password" name="password" autocomplete="new-password" required />
</x-ui.field>
<x-ui.field>
<x-ui.label for="sec-confirm-password" required>{{ __('Confirm new password') }}</x-ui.label>
<x-ui.input id="sec-confirm-password" type="password" name="password_confirmation" autocomplete="new-password" required />
</x-ui.field>
<div class="flex justify-end pt-2">
<x-ui.button type="submit">{{ __('Update password') }}</x-ui.button>
</div>
</form>
</x-ui.card.content>
</x-ui.card>
<x-ui.card>
<x-ui.card.header>
<x-ui.card.title>{{ __('Two-factor authentication') }}</x-ui.card.title>
<x-ui.card.description>{{ __('Add an extra layer of security by requiring a one-time code on sign in.') }}</x-ui.card.description>
</x-ui.card.header>
<x-ui.card.content>
<x-ui.switch-field
name="two_factor_enabled"
:label="__('Authenticator app')"
:description="$twoFactorEnabled ? __('Two-factor authentication is active on your account.') : __('Protect your account with an authenticator app.')"
:checked="$twoFactorEnabled"
align="center"
>
<x-slot:badge>
<x-ui.badge variant="{{ $twoFactorEnabled ? 'success' : 'outline' }}">{{ $twoFactorEnabled ? __('Enabled') : __('Disabled') }}</x-ui.badge>
</x-slot:badge>
</x-ui.switch-field>
</x-ui.card.content>
</x-ui.card>
<x-ui.card>
<x-ui.card.header>
<x-ui.card.title>{{ __('Active sessions') }}</x-ui.card.title>
<x-ui.card.description>{{ __('These devices are currently signed in. Revoke any you do not recognise.') }}</x-ui.card.description>
</x-ui.card.header>
<x-ui.card.content class="p-0">
<ul role="list" class="divide-y divide-border">
@foreach ($sessions as $session)
<li class="flex items-center justify-between gap-4 px-6 py-4">
<div class="shrink-0 text-muted-foreground" aria-hidden="true">
@if ($session['device'] === 'mobile')
<svg class="size-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="2" width="14" height="20" rx="2" /><line x1="12" y1="18" x2="12.01" y2="18" /></svg>
@else
<svg class="size-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="14" rx="2" /><line x1="8" y1="21" x2="16" y2="21" /><line x1="12" y1="17" x2="12" y2="21" /></svg>
@endif
</div>
<div class="min-w-0 flex-1 space-y-0.5">
<p class="text-sm font-medium text-foreground">{{ $session['browser'] }} — {{ $session['os'] }}</p>
<p class="text-xs text-muted-foreground">{{ $session['location'] }}</p>
</div>
<div class="flex shrink-0 items-center gap-4">
@if ($session['current'])
<x-ui.badge variant="secondary" size="sm">{{ __('Current') }}</x-ui.badge>
@else
<span class="text-xs text-muted-foreground">{{ __($session['lastActive']) }}</span>
<x-ui.button type="button" variant="ghost" size="sm" class="text-destructive hover:bg-destructive/10 hover:text-destructive">{{ __('Revoke') }}</x-ui.button>
@endif
</div>
</li>
@endforeach
</ul>
</x-ui.card.content>
</x-ui.card>
<x-ui.card class="border-destructive/40">
<x-ui.card.header>
<x-ui.card.title>{{ __('Danger zone') }}</x-ui.card.title>
<x-ui.card.description>{{ __('Permanent and irreversible actions for your account.') }}</x-ui.card.description>
</x-ui.card.header>
<x-ui.card.content>
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="space-y-2">
<p class="text-sm font-medium text-foreground">{{ __('Delete account') }}</p>
<p class="text-sm text-muted-foreground">{{ __('Once you delete your account, all of your data will be permanently removed. This cannot be undone.') }}</p>
</div>
{{-- Hold-to-confirm: a slip-proof destructive action. A fill sweeps up
the button over 2s while pressed (pointer or Space/Enter held);
releasing early snaps it back. Completion dispatches a
`hold-confirm` event on the button — wire the real deletion there.
The bottom-up fill is axis-neutral, so it needs no RTL handling. --}}
<div
x-data="{
holding: false,
timer: null,
begin() {
if (this.timer) return;
this.holding = true;
this.timer = window.setTimeout(() => {
this.release();
this.$dispatch('hold-confirm');
}, 2000);
},
release() {
this.holding = false;
window.clearTimeout(this.timer);
this.timer = null;
},
destroy() { window.clearTimeout(this.timer) },
}"
class="shrink-0"
>
<x-ui.button
type="button"
variant="ghost"
class="relative w-full text-destructive select-none hover:bg-destructive/10 hover:text-destructive sm:w-auto"
aria-label="{{ __('Press and hold to delete your account') }}"
@pointerdown="begin()"
@pointerup="release()"
@pointerleave="release()"
@pointercancel="release()"
@keydown.enter.prevent="if (! $event.repeat) begin()"
@keydown.space.prevent="if (! $event.repeat) begin()"
@keyup.enter="release()"
@keyup.space="release()"
>
<span
aria-hidden="true"
data-slot="hold-confirm-fill"
class="pointer-events-none absolute inset-0 bg-destructive/15"
:style="holding
? 'clip-path: inset(0 0 0 0); transition: clip-path 2s linear'
: 'clip-path: inset(100% 0 0 0); transition: clip-path 240ms var(--ease-standard)'"
></span>
<span class="relative">{{ __('Hold to delete account') }}</span>
</x-ui.button>
</div>
</div>
</x-ui.card.content>
</x-ui.card>
</div>
</x-ui.section>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.2.5 - Status
- Stable
- License
-
open - Deprecation
- Not deprecated
- Contract
-
v1 - Foundation
-
≥ 1.0.0