Product Ingredients
Ingredient and spec breakdowns — icon-tile ingredient cards with a full-list accordion and trust claims.
Preview
@props([
'heading' => 'What\'s inside',
'subheading' => 'Clean ingredients, nothing hidden.',
'ingredients' => null,
'fullList' => null,
'claims' => null,
])
@php
$ingredients ??= [
[
'name' => 'Hyaluronic Acid',
'benefit' => 'Draws in moisture for plumper, dewy-looking skin.',
'amount' => '2%',
'icon' => 'M12 3c4 4 6 7 6 10a6 6 0 0 1-12 0c0-3 2-6 6-10Z',
],
[
'name' => 'Vitamin C',
'benefit' => 'Brightens tone and defends against daily stressors.',
'amount' => '15%',
'icon' => 'M12 3v2 M12 19v2 M5 12H3 M21 12h-2 M6 6l1.5 1.5 M16.5 16.5 18 18 M6 18l1.5-1.5 M16.5 7.5 18 6 M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8Z',
],
[
'name' => 'Niacinamide',
'benefit' => 'Refines pores and evens out the appearance of skin.',
'amount' => '500mg',
'icon' => 'M12 3a9 9 0 1 0 9 9 7 7 0 0 1-9-9Z',
],
[
'name' => 'Squalane',
'benefit' => 'Lightweight plant oil that softens and seals in moisture.',
'amount' => '5%',
'icon' => 'M3 12c3-4 6-4 9 0s6 4 9 0 M3 17c3-4 6-4 9 0s6 4 9 0',
],
[
'name' => 'Green Tea Extract',
'benefit' => 'Antioxidant-rich botanical that calms and soothes.',
'amount' => '3%',
'icon' => 'M4 20c0-7 5-13 16-15-2 11-8 15-13 15 M9 17c1-4 3-6 6-8',
],
[
'name' => 'Ceramides',
'benefit' => 'Rebuilds the moisture barrier for resilient skin.',
'amount' => '250mg',
'icon' => 'M12 3 21 8v8l-9 5-9-5V8l9-5Z M12 3v18 M3 8l9 5 9-5',
],
];
$fullList ??= [
'Aqua (Water)',
'Glycerin',
'Sodium Hyaluronate',
'Ascorbic Acid (Vitamin C)',
'Niacinamide',
'Squalane',
'Camellia Sinensis (Green Tea) Leaf Extract',
'Ceramide NP',
'Tocopherol (Vitamin E)',
'Panthenol (Pro-Vitamin B5)',
'Citric Acid',
'Sodium Benzoate',
];
$claims ??= [
'Vegan',
'Cruelty-free',
'No parabens',
'Fragrance-free',
'Dermatologist-tested',
];
@endphp
<x-ui.section
data-slot="product-ingredients-01"
space="xl" {{ $attributes }}
>
<x-ui.container gutter="sm">
<x-ui.section-heading align="center" :title="$heading" :subtitle="$subheading" />
<ul role="list" class="mt-12 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
@foreach ($ingredients as $ingredient)
<li>
<x-ui.card class="h-full p-6">
<div class="flex items-start justify-between gap-4">
<span class="flex size-12 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
<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"
>
<path d="{{ $ingredient['icon'] }}" />
</svg>
</span>
<x-ui.badge variant="outline" class="font-mono">
{{ __($ingredient['amount']) }}
</x-ui.badge>
</div>
<h3 class="mt-4 text-sm font-semibold tracking-tight text-foreground">
{{ __($ingredient['name']) }}
</h3>
<p class="mt-2 text-pretty text-sm text-muted-foreground">
{{ __($ingredient['benefit']) }}
</p>
</x-ui.card>
</li>
@endforeach
</ul>
<div class="mx-auto mt-12 max-w-2xl">
<x-ui.accordion>
<x-ui.accordion.item value="full-ingredient-list">
<x-ui.accordion.trigger>{{ __('Full ingredient list') }}</x-ui.accordion.trigger>
<x-ui.accordion.content>
<p class="text-sm text-muted-foreground">
{{ __('Complete INCI breakdown, in descending order of concentration.') }}
</p>
<ul role="list" class="mt-4 flex flex-wrap gap-x-2 gap-y-2 text-sm text-foreground">
@foreach ($fullList as $i => $entry)
<li class="inline-flex items-center">
{{ __($entry) }}@if (! $loop->last)<span class="text-muted-foreground" aria-hidden="true">,</span>@endif
</li>
@endforeach
</ul>
</x-ui.accordion.content>
</x-ui.accordion.item>
</x-ui.accordion>
<x-ui.separator class="my-6" />
<ul role="list" class="flex flex-wrap items-center justify-center gap-2">
@foreach ($claims as $claim)
<li>
<x-ui.badge variant="secondary">{{ __($claim) }}</x-ui.badge>
</li>
@endforeach
</ul>
</div>
</x-ui.container>
</x-ui.section>
Installation
php artisan ui:add blocks/product-ingredients-grid
Registry contract
Install confidence
php artisan ui:add blocks/product-ingredients-grid
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.0.5
- License
- open
- Stability
- stable
- Contract
- v1
- Foundation
- ≥ 1.0.0
| Type | Generated target |
|---|---|
| blade | resources/views/blocks/product-ingredients-grid.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: Product Ingredients (`product-ingredients-grid`)
A "What's inside" ingredients and specs breakdown for a product page: a centered heading and reassurance subheading, a responsive grid of ingredient cards each with a tokened icon tile, the ingredient name, a one-line benefit, and an amount/percentage badge. Below the grid an accordion reveals the full INCI / nutrition-style ingredient list, followed by a centered row of trust badges (vegan, cruelty-free, no parabens).
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-ingredients-grid
```
## Render it
```blade
<x-blocks.product-ingredients-grid />
```
## Props
- `heading` (string, default `What\'s inside`) — Section heading.
- `subheading` (string, default `Clean ingredients, nothing hidden.`) — Supporting reassurance copy under the heading.
- `ingredients` (mixed|null, default `null`) — Array of {name, benefit, amount, icon} ingredient card entries.
- `full-list` (mixed|null, default `null`) — Array of full INCI/nutrition-style ingredient list line strings shown inside the accordion.
- `claims` (mixed|null, default `null`) — Array of trust badge label strings (e.g. vegan, cruelty-free).
## Use when
- Building a "What's inside" ingredients/specs section for a product page: a centred heading, a grid of ingredient cards (icon, name, benefit line, amount badge), and a row of trust badges (vegan, cruelty-free, no parabens).
- Wanting the full INCI/nutrition-style ingredient list tucked behind a collapsible accordion rather than always shown.
## Avoid when
- Needing a specifications table for technical product attributes rather than an ingredients grid; use product-overview-tabs's specs table instead.
## Rules
- Render the installed block with `<x-blocks.product-ingredients-grid />` 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-ingredients-grid
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/product-ingredients-grid.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 "What's inside" ingredients/specs section for a product page: a centred heading, a grid of ingredient cards (icon, name, benefit line, amount badge), and a row of trust badges (vegan, cruelty-free, no parabens).
- Wanting the full INCI/nutrition-style ingredient list tucked behind a collapsible accordion rather than always shown.
Avoid when
- Needing a specifications table for technical product attributes rather than an ingredients grid; use product-overview-tabs's specs table 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.product-ingredients-grid />
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' => 'What\'s inside',
'subheading' => 'Clean ingredients, nothing hidden.',
'ingredients' => null,
'fullList' => null,
'claims' => null,
])
@php
$ingredients ??= [
[
'name' => 'Hyaluronic Acid',
'benefit' => 'Draws in moisture for plumper, dewy-looking skin.',
'amount' => '2%',
'icon' => 'M12 3c4 4 6 7 6 10a6 6 0 0 1-12 0c0-3 2-6 6-10Z',
],
[
'name' => 'Vitamin C',
'benefit' => 'Brightens tone and defends against daily stressors.',
'amount' => '15%',
'icon' => 'M12 3v2 M12 19v2 M5 12H3 M21 12h-2 M6 6l1.5 1.5 M16.5 16.5 18 18 M6 18l1.5-1.5 M16.5 7.5 18 6 M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8Z',
],
[
'name' => 'Niacinamide',
'benefit' => 'Refines pores and evens out the appearance of skin.',
'amount' => '500mg',
'icon' => 'M12 3a9 9 0 1 0 9 9 7 7 0 0 1-9-9Z',
],
[
'name' => 'Squalane',
'benefit' => 'Lightweight plant oil that softens and seals in moisture.',
'amount' => '5%',
'icon' => 'M3 12c3-4 6-4 9 0s6 4 9 0 M3 17c3-4 6-4 9 0s6 4 9 0',
],
[
'name' => 'Green Tea Extract',
'benefit' => 'Antioxidant-rich botanical that calms and soothes.',
'amount' => '3%',
'icon' => 'M4 20c0-7 5-13 16-15-2 11-8 15-13 15 M9 17c1-4 3-6 6-8',
],
[
'name' => 'Ceramides',
'benefit' => 'Rebuilds the moisture barrier for resilient skin.',
'amount' => '250mg',
'icon' => 'M12 3 21 8v8l-9 5-9-5V8l9-5Z M12 3v18 M3 8l9 5 9-5',
],
];
$fullList ??= [
'Aqua (Water)',
'Glycerin',
'Sodium Hyaluronate',
'Ascorbic Acid (Vitamin C)',
'Niacinamide',
'Squalane',
'Camellia Sinensis (Green Tea) Leaf Extract',
'Ceramide NP',
'Tocopherol (Vitamin E)',
'Panthenol (Pro-Vitamin B5)',
'Citric Acid',
'Sodium Benzoate',
];
$claims ??= [
'Vegan',
'Cruelty-free',
'No parabens',
'Fragrance-free',
'Dermatologist-tested',
];
@endphp
<x-ui.section
data-slot="product-ingredients-01"
space="xl" {{ $attributes }}
>
<x-ui.container gutter="sm">
<x-ui.section-heading align="center" :title="$heading" :subtitle="$subheading" />
<ul role="list" class="mt-12 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
@foreach ($ingredients as $ingredient)
<li>
<x-ui.card class="h-full p-6">
<div class="flex items-start justify-between gap-4">
<span class="flex size-12 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
<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"
>
<path d="{{ $ingredient['icon'] }}" />
</svg>
</span>
<x-ui.badge variant="outline" class="font-mono">
{{ __($ingredient['amount']) }}
</x-ui.badge>
</div>
<h3 class="mt-4 text-sm font-semibold tracking-tight text-foreground">
{{ __($ingredient['name']) }}
</h3>
<p class="mt-2 text-pretty text-sm text-muted-foreground">
{{ __($ingredient['benefit']) }}
</p>
</x-ui.card>
</li>
@endforeach
</ul>
<div class="mx-auto mt-12 max-w-2xl">
<x-ui.accordion>
<x-ui.accordion.item value="full-ingredient-list">
<x-ui.accordion.trigger>{{ __('Full ingredient list') }}</x-ui.accordion.trigger>
<x-ui.accordion.content>
<p class="text-sm text-muted-foreground">
{{ __('Complete INCI breakdown, in descending order of concentration.') }}
</p>
<ul role="list" class="mt-4 flex flex-wrap gap-x-2 gap-y-2 text-sm text-foreground">
@foreach ($fullList as $i => $entry)
<li class="inline-flex items-center">
{{ __($entry) }}@if (! $loop->last)<span class="text-muted-foreground" aria-hidden="true">,</span>@endif
</li>
@endforeach
</ul>
</x-ui.accordion.content>
</x-ui.accordion.item>
</x-ui.accordion>
<x-ui.separator class="my-6" />
<ul role="list" class="flex flex-wrap items-center justify-center gap-2">
@foreach ($claims as $claim)
<li>
<x-ui.badge variant="secondary">{{ __($claim) }}</x-ui.badge>
</li>
@endforeach
</ul>
</div>
</x-ui.container>
</x-ui.section>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.0.5 - Status
- Stable
- License
-
open - Deprecation
- Not deprecated
- Contract
-
v1 - Foundation
-
≥ 1.0.0