Skip to content
Brok UI

Loading…

No results

Product Quick View

Open source

Quick-view dialog panels — a compact modal-style product card with image, pricing, rating and add-to-cart.

Preview

resources/views/blocks/product-quick-view-modal.blade.php Blade
@props([
    'name' => 'Aero Knit Runner',
    'brand' => 'Atlas Athletics',
    'badge' => 'New arrival',
    'price' => '$148',
    'compareAtPrice' => '$185',
    'rating' => 4.5,
    'reviewCount' => 312,
    'description' => 'A breathable engineered-knit upper over a responsive foam midsole — built for daily miles and easy enough to wear straight off the run.',
    'sizes' => null,
    'selectedSize' => '9',
    'detailsHref' => '#',
    'closeHref' => '#',
    'imageAlt' => 'Aero Knit Runner shown from the side',
])

@php
    $sizes ??= ['7', '8', '9', '10', '11', '12'];
@endphp

<section {{ $attributes->merge(['class' => 'flex min-h-svh items-center justify-center bg-muted/40 px-4 py-12 text-foreground']) }}>
    <x-ui.card
        role="dialog"
        aria-modal="true"
        aria-label="{{ __('Product quick view') }}"
        class="relative w-full max-w-3xl overflow-hidden rounded-lg border border-border bg-card p-0 shadow-xl"
    >
        {{-- Close (top end) --}}
        <x-ui.button
            :href="$closeHref"
            variant="ghost"
            size="icon"
            class="absolute end-3 top-3 z-10 size-9 rounded-full text-muted-foreground hover:text-foreground"
            aria-label="{{ __('Close quick view') }}"
        >
            <svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                <path d="M18 6 6 18" />
                <path d="m6 6 12 12" />
            </svg>
        </x-ui.button>

        <div class="grid grid-cols-1 gap-0 sm:grid-cols-2">
            {{-- Image (left) --}}
            <div class="relative aspect-square w-full overflow-hidden bg-muted">
                @if ($badge)
                    <x-ui.badge class="absolute start-3 top-3 z-10 shadow-sm">{{ __($badge) }}</x-ui.badge>
                @endif
                <svg
                    class="size-full text-muted-foreground/40"
                    viewBox="0 0 200 200"
                    fill="none"
                    role="img"
                    aria-label="{{ __($imageAlt) }}"
                >
                    <rect width="200" height="200" fill="currentColor" fill-opacity="0.08" />
                    <path
                        d="M28 132c14-6 22-18 30-30 6-9 11-16 21-16 7 0 11 5 18 9 6 3 13 4 22 4 14 0 24 7 33 16l20 21H28v-4Z"
                        fill="currentColor"
                        fill-opacity="0.55"
                    />
                    <circle cx="70" cy="74" r="9" fill="currentColor" fill-opacity="0.5" />
                </svg>
            </div>

            {{-- Details (right) --}}
            <div class="flex flex-col gap-4 p-6 sm:p-8">
                <div class="flex flex-col gap-2 pe-8">
                    @if ($brand)
                        <p class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">{{ __($brand) }}</p>
                    @endif
                    <h2 class="text-pretty text-xl font-semibold leading-tight tracking-tight sm:text-2xl">{{ __($name) }}</h2>
                </div>

                <div class="flex items-center gap-4">
                    <span class="text-xl font-bold tracking-tight">{{ $price }}</span>
                    @if ($compareAtPrice)
                        <span class="text-base text-muted-foreground line-through">{{ $compareAtPrice }}</span>
                    @endif
                </div>

                <div class="flex items-center gap-2">
                    <x-ui.rating :value="$rating" size="sm" read-only />
                    <span class="text-sm text-muted-foreground">
                        {{ number_format((float) $rating, 1) }}
                        ({{ trans_choice('{0}No reviews|{1}:count review|[2,*]:count reviews', $reviewCount, ['count' => number_format($reviewCount)]) }})
                    </span>
                </div>

                <p class="line-clamp-2 text-pretty text-sm leading-relaxed text-muted-foreground">
                    {{ __($description) }}
                </p>

                <x-ui.separator class="my-2" />

                {{-- Size selector --}}
                <div class="flex flex-col gap-2">
                    <span class="text-sm font-medium text-foreground" aria-hidden="true">{{ __('Size') }}</span>
                    <x-ui.option-group
                        name="size"
                        :options="$sizes"
                        :selected="$selectedSize"
                        size="sm"
                        :label="__('Size')"
                    />
                </div>

                {{-- Quantity --}}
                <div class="flex flex-col gap-2">
                    <span class="text-sm font-medium text-foreground" aria-hidden="true">{{ __('Quantity') }}</span>
                    <x-ui.number-field
                        variant="stepper"
                        size="md"
                        :value="1"
                        :min="1"
                        :max="10"
                        :label="__('Quantity')"
                        class="w-fit"
                    />
                </div>

                <div class="mt-2 flex flex-col gap-4">
                    <x-ui.button type="button" size="lg" class="w-full">
                        <svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                            <circle cx="8" cy="21" r="1" />
                            <circle cx="19" cy="21" r="1" />
                            <path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" />
                        </svg>
                        {{ __('Add to cart') }}
                    </x-ui.button>

                    <a href="{{ $detailsHref }}" class="inline-flex items-center justify-center gap-2 text-sm font-medium text-link hover:underline">
                        {{ __('View full details') }}
                        <svg class="size-4 rtl:-scale-x-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                            <path d="M5 12h14" />
                            <path d="m12 5 7 7-7 7" />
                        </svg>
                    </a>
                </div>
            </div>
        </div>
    </x-ui.card>
</section>

Installation

terminal
php artisan ui:add blocks/product-quick-view-modal

Registry contract

Install confidence

php artisan ui:add blocks/product-quick-view-modal 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.1.2
License
open
Stability
stable
Contract
v1
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/product-quick-view-modal.blade.php

Registry dependencies

card button badge rating separator option-group number-field

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.

product-quick-view-modal.md
# Brok UI block: Product Quick View (`product-quick-view-modal`)

A centered quick-view dialog panel on a muted backdrop with a top-end close button, a square product image on the left, and details on the right: title, badge, price, rating, description, size selector, quantity stepper, add-to-cart button, and a view-full-details link.

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/product-quick-view-modal
```

## Render it

```blade
<x-blocks.product-quick-view-modal />
```

## Props

- `name` (string, default `Aero Knit Runner`) — Product name shown as the dialog heading.
- `brand` (string, default `Atlas Athletics`) — Brand label shown above the product name.
- `badge` (string, default `New arrival`) — Badge shown on the product image; falsy hides it.
- `price` (string, default `$148`) — Current price, pre-formatted.
- `compare-at-price` (string, default `$185`) — Original price shown struck through; falsy hides it.
- `rating` (float, default `4.5`) — Average star rating value.
- `review-count` (int, default `312`) — Number of reviews shown next to the rating.
- `description` (string, default `A breathable engineered-knit upper over a responsive foam midsole — built for daily miles and easy enough to wear straight off the run.`) — Short product description shown under the rating.
- `sizes` (mixed|null, default `null`) — Size options for the option-group selector; null falls back to a default sample set.
- `selected-size` (string, default `9`) — Initially selected size value.
- `details-href` (string, default `#`) — Destination for the view-full-details link.
- `close-href` (string, default `#`) — Destination for the close button.
- `image-alt` (string, default `Aero Knit Runner shown from the side`) — Accessible label for the product image placeholder.

## Use when

- Opening a centered quick-view dialog for a single product with one square image, price, rating, a size selector, quantity stepper and an add-to-cart action.
- Use when the shopper needs a fast add-to-cart path from a single image, not a photo gallery or colour picker.

## Avoid when

- Needing an image gallery with a thumbnail strip and a colour swatch selector; use product-quick-view-gallery instead.

## Rules

- Render the installed block with `<x-blocks.product-quick-view-modal />` 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/product-quick-view-modal
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/product-quick-view-modal.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

  • Opening a centered quick-view dialog for a single product with one square image, price, rating, a size selector, quantity stepper and an add-to-cart action.
  • Use when the shopper needs a fast add-to-cart path from a single image, not a photo gallery or colour picker.

Avoid when

  • Needing an image gallery with a thumbnail strip and a colour swatch selector; use product-quick-view-gallery instead.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
dialog card close button product image price and rating size selector quantity stepper add-to-cart button

Usage

Render the block as a component, passing data where useful:

resources/views/welcome.blade.php Blade
<x-blocks.product-quick-view-modal />

Built from primitives

Composes <brok:*> Semantic tokens Re-themeable RTL-ready

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.

resources/views/blocks/product-quick-view-modal.blade.php Blade
@props([
    'name' => 'Aero Knit Runner',
    'brand' => 'Atlas Athletics',
    'badge' => 'New arrival',
    'price' => '$148',
    'compareAtPrice' => '$185',
    'rating' => 4.5,
    'reviewCount' => 312,
    'description' => 'A breathable engineered-knit upper over a responsive foam midsole — built for daily miles and easy enough to wear straight off the run.',
    'sizes' => null,
    'selectedSize' => '9',
    'detailsHref' => '#',
    'closeHref' => '#',
    'imageAlt' => 'Aero Knit Runner shown from the side',
])

@php
    $sizes ??= ['7', '8', '9', '10', '11', '12'];
@endphp

<section {{ $attributes->merge(['class' => 'flex min-h-svh items-center justify-center bg-muted/40 px-4 py-12 text-foreground']) }}>
    <x-ui.card
        role="dialog"
        aria-modal="true"
        aria-label="{{ __('Product quick view') }}"
        class="relative w-full max-w-3xl overflow-hidden rounded-lg border border-border bg-card p-0 shadow-xl"
    >
        {{-- Close (top end) --}}
        <x-ui.button
            :href="$closeHref"
            variant="ghost"
            size="icon"
            class="absolute end-3 top-3 z-10 size-9 rounded-full text-muted-foreground hover:text-foreground"
            aria-label="{{ __('Close quick view') }}"
        >
            <svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                <path d="M18 6 6 18" />
                <path d="m6 6 12 12" />
            </svg>
        </x-ui.button>

        <div class="grid grid-cols-1 gap-0 sm:grid-cols-2">
            {{-- Image (left) --}}
            <div class="relative aspect-square w-full overflow-hidden bg-muted">
                @if ($badge)
                    <x-ui.badge class="absolute start-3 top-3 z-10 shadow-sm">{{ __($badge) }}</x-ui.badge>
                @endif
                <svg
                    class="size-full text-muted-foreground/40"
                    viewBox="0 0 200 200"
                    fill="none"
                    role="img"
                    aria-label="{{ __($imageAlt) }}"
                >
                    <rect width="200" height="200" fill="currentColor" fill-opacity="0.08" />
                    <path
                        d="M28 132c14-6 22-18 30-30 6-9 11-16 21-16 7 0 11 5 18 9 6 3 13 4 22 4 14 0 24 7 33 16l20 21H28v-4Z"
                        fill="currentColor"
                        fill-opacity="0.55"
                    />
                    <circle cx="70" cy="74" r="9" fill="currentColor" fill-opacity="0.5" />
                </svg>
            </div>

            {{-- Details (right) --}}
            <div class="flex flex-col gap-4 p-6 sm:p-8">
                <div class="flex flex-col gap-2 pe-8">
                    @if ($brand)
                        <p class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">{{ __($brand) }}</p>
                    @endif
                    <h2 class="text-pretty text-xl font-semibold leading-tight tracking-tight sm:text-2xl">{{ __($name) }}</h2>
                </div>

                <div class="flex items-center gap-4">
                    <span class="text-xl font-bold tracking-tight">{{ $price }}</span>
                    @if ($compareAtPrice)
                        <span class="text-base text-muted-foreground line-through">{{ $compareAtPrice }}</span>
                    @endif
                </div>

                <div class="flex items-center gap-2">
                    <x-ui.rating :value="$rating" size="sm" read-only />
                    <span class="text-sm text-muted-foreground">
                        {{ number_format((float) $rating, 1) }}
                        ({{ trans_choice('{0}No reviews|{1}:count review|[2,*]:count reviews', $reviewCount, ['count' => number_format($reviewCount)]) }})
                    </span>
                </div>

                <p class="line-clamp-2 text-pretty text-sm leading-relaxed text-muted-foreground">
                    {{ __($description) }}
                </p>

                <x-ui.separator class="my-2" />

                {{-- Size selector --}}
                <div class="flex flex-col gap-2">
                    <span class="text-sm font-medium text-foreground" aria-hidden="true">{{ __('Size') }}</span>
                    <x-ui.option-group
                        name="size"
                        :options="$sizes"
                        :selected="$selectedSize"
                        size="sm"
                        :label="__('Size')"
                    />
                </div>

                {{-- Quantity --}}
                <div class="flex flex-col gap-2">
                    <span class="text-sm font-medium text-foreground" aria-hidden="true">{{ __('Quantity') }}</span>
                    <x-ui.number-field
                        variant="stepper"
                        size="md"
                        :value="1"
                        :min="1"
                        :max="10"
                        :label="__('Quantity')"
                        class="w-fit"
                    />
                </div>

                <div class="mt-2 flex flex-col gap-4">
                    <x-ui.button type="button" size="lg" class="w-full">
                        <svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                            <circle cx="8" cy="21" r="1" />
                            <circle cx="19" cy="21" r="1" />
                            <path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" />
                        </svg>
                        {{ __('Add to cart') }}
                    </x-ui.button>

                    <a href="{{ $detailsHref }}" class="inline-flex items-center justify-center gap-2 text-sm font-medium text-link hover:underline">
                        {{ __('View full details') }}
                        <svg class="size-4 rtl:-scale-x-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                            <path d="M5 12h14" />
                            <path d="m12 5 7 7-7 7" />
                        </svg>
                    </a>
                </div>
            </div>
        </div>
    </x-ui.card>
</section>

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
Deprecation
Not deprecated
Contract
v1
Foundation
≥ 1.0.0