Skip to content
Brok UI

Loading…

No results

Bento Grid

Open source

A responsive bento-box grid with span-controlled cells for showcasing features.

Version
v1.0.4
Stability
stable
License
MIT
Related
Grid
Card

Preview

Lightning fast

Server-rendered Blade with zero runtime JavaScript for the layout.

Composable

Drop cells in any order and let the grid flow around their spans.

Accessible

Semantic tokens and reduced-motion friendly out of the box.

Theme aware

Adapts to light and dark automatically.

Responsive

One column on mobile, two on tablet, full grid on desktop.

previews.components.bento.default.blade.php Blade
<x-ui.bento class="max-w-3xl">
    <x-ui.bento.item
        :colSpan="2"
        title="Lightning fast"
        description="Server-rendered Blade with zero runtime JavaScript for the layout."
    />
    <x-ui.bento.item
        :rowSpan="2"
        title="Composable"
        description="Drop cells in any order and let the grid flow around their spans."
    />
    <x-ui.bento.item
        title="Accessible"
        description="Semantic tokens and reduced-motion friendly out of the box."
    />
    <x-ui.bento.item
        title="Theme aware"
        description="Adapts to light and dark automatically."
    />
    <x-ui.bento.item
        :colSpan="2"
        title="Responsive"
        description="One column on mobile, two on tablet, full grid on desktop."
    />
</x-ui.bento>

Installation

terminal
php artisan ui:add bento

Registry contract

php artisan ui:add bento 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/bento.blade.php
  • blade resources/views/components/ui/bento/item.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.

bento.md
# Brok UI: Bento Grid (`bento`)

A responsive bento-box grid with span-controlled cells for showcasing features.

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

## Install

```bash
php artisan ui:add bento
```

## Usage

```blade
<x-ui.bento class="max-w-3xl">
    <x-ui.bento.item
        :colSpan="2"
        title="Lightning fast"
        description="Server-rendered Blade with zero runtime JavaScript for the layout."
    />
    <x-ui.bento.item
        :rowSpan="2"
        title="Composable"
        description="Drop cells in any order and let the grid flow around their spans."
    />
    <x-ui.bento.item
        title="Accessible"
        description="Semantic tokens and reduced-motion friendly out of the box."
    />
    <x-ui.bento.item
        title="Theme aware"
        description="Adapts to light and dark automatically."
    />
    <x-ui.bento.item
        :colSpan="2"
        title="Responsive"
        description="One column on mobile, two on tablet, full grid on desktop."
    />
</x-ui.bento>
```

## Props

- `cols` (int, default `3`) — Column count at the large breakpoint; smaller breakpoints fall back to a fixed 1/2 column ramp.
- `gap` (int, default `4`) — Gap between cells: 2, 3, 4, 6, or 8.
- `colSpan` (int, default `1`) — Declared by @props in the registry Blade source.
- `rowSpan` (int, default `1`) — Declared by @props in the registry Blade source.
- `title` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `description` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `icon` (mixed|null, default `null`) — Declared by @props in the registry Blade source.

## Use when

- Use to group related content into scannable units for browsing, prioritization, or rearrangement.
- Showcasing a set of features or highlights as an asymmetric grid of differently sized cells.
- Needing individual cells that can span multiple columns or rows to create visual emphasis.

## Avoid when

- Do not use card-like structures when dense row-by-row comparison would be clearer in a list or table.
- The grid should be uniform, with every cell the same size; use grid instead.
- The content is primarily data to scan or compare; use table or a plain card grid.

## Anti-patterns

- Turning every object into a card

## Rules

- Use the `<brok:bento>` tag (or `<x-ui.bento>`) 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/bento
- Registry JSON (files, props, contract): https://brokui.dev/r/open/bento.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
cols int 3 Column count at the large breakpoint; smaller breakpoints fall back to a fixed 1/2 column ramp.
gap int 4 Gap between cells: 2, 3, 4, 6, or 8.
colSpan int 1 Declared by @props in the registry Blade source.
rowSpan int 1 Declared by @props in the registry Blade source.
title mixed | null null Declared by @props in the registry Blade source.
description mixed | null null Declared by @props in the registry Blade source.
icon mixed | null null Declared by @props in the registry Blade source.

Slots

  • default — bento.item cells composing the grid.
  • x-ui.bento.item — Installed subcomponent from the registry item.

Data slots

Stable hooks for CSS overrides and browser tests.

bento bento-item bento-item-icon

Behavior

  • The large-breakpoint column count is driven by a CSS custom property so the grid stays Tailwind-purge safe with a dynamic cols value.
  • Each item's grid-column and grid-row spans are set via inline style, also for the same purge-safety reason, so spans can be any integer without pre-registering classes.
  • 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.
  • Showcasing a set of features or highlights as an asymmetric grid of differently sized cells.
  • Needing individual cells that can span multiple columns or rows to create visual emphasis.

Avoid when

  • Do not use card-like structures when dense row-by-row comparison would be clearer in a list or table.
  • The grid should be uniform, with every cell the same size; use grid instead.
  • The content is primarily data to scan or compare; use table or a plain card grid.

Use instead

  • Table for exact comparison
  • List for compact scanning

Anti-patterns

  • Turning every object into a card
Anatomy
root bento-item bento-item-icon
Theming hooks
bento bento-item bento-item-icon

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
  • Cells render as plain divs; give interactive cells their own link or button semantics rather than relying on the bento grid for interactivity.
  • 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 files ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/bento.blade.php Blade
@props([
    'cols' => 3,
    'gap' => 4,
])

@php
    // The large-breakpoint column count is dynamic, so it is fed through a CSS
    // custom property + an arbitrary-value class to stay Tailwind-purge safe.
    // Small/medium breakpoints fall back to a fixed 1/2 column ramp.
    $gapClass = match ((int) $gap) {
        2 => 'gap-2',
        3 => 'gap-3',
        6 => 'gap-6',
        8 => 'gap-8',
        default => 'gap-4',
    };
@endphp

<div
    data-slot="bento"
    style="--bento-cols: {{ (int) $cols }};"
    {{ $attributes->merge(['class' => 'grid grid-cols-1 sm:grid-cols-2 lg:[grid-template-columns:repeat(var(--bento-cols),minmax(0,1fr))] '.$gapClass]) }}
>
    {{ $slot }}
</div>
resources/views/components/ui/bento/item.blade.php Blade
@props([
    'colSpan' => 1,
    'rowSpan' => 1,
    'title' => null,
    'description' => null,
    'icon' => null,
])

@php
    // Spans are dynamic, so they go through inline grid-* styles rather than
    // dynamic class names (which Tailwind's JIT purge would strip).
    $hasHeader = filled($title) || filled($description) || $icon;
@endphp

<div
    data-slot="bento-item"
    style="grid-column: span {{ (int) $colSpan }}; grid-row: span {{ (int) $rowSpan }};"
    {{ $attributes->merge(['class' => 'group relative overflow-hidden rounded-lg border border-border bg-card p-6 transition-colors hover:bg-accent/50 motion-reduce:transition-none']) }}
>
    @if ($icon)
        <div data-slot="bento-item-icon" class="mb-4 text-primary">
            {{ $icon }}
        </div>
    @endif

    @if (filled($title))
        <h3 class="font-semibold text-foreground">{{ $title }}</h3>
    @endif

    @if (filled($description))
        <p class="text-sm text-muted-foreground @if (filled($title)) mt-1 @endif">{{ $description }}</p>
    @endif

    @if ($hasHeader && trim($slot) !== '')
        <div class="mt-4">{{ $slot }}</div>
    @else
        {{ $slot }}
    @endif
</div>

Ownership & lifecycle

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