Skip to content
Brok UI

Loading…

No results

Rich Text

Open source

Long-form prose sections — an article, policy or terms body with headings, lists and a pull-quote.

Preview

resources/views/blocks/rich-text-article.blade.php Blade
@props([
    'eyebrow'  => 'Last updated, April 2026',
    'title'    => 'Our commitment to privacy and transparency',
    'lead'     => 'We believe that trust is earned through clarity. This page explains how we collect, use, and protect your information — written in plain language so you always know where you stand.',
    'tags'     => null,
    // An ordered list of typed content blocks, rendered in place — this
    // is the data API; there is no raw-HTML escape hatch (see rich-text-with-toc
    // for a heading-grouped alternative shape). Each block is one of:
    //   ['type' => 'paragraph', 'text' => '…', 'link' => ['label','href'], 'after' => '…']
    //     `link`/`after` are optional: append an inline link then trailing text.
    //   ['type' => 'heading', 'text' => '…']
    //   ['type' => 'list', 'ordered' => bool, 'items' => [['label' => '…', 'text' => '…'], …]]
    //   ['type' => 'quote', 'text' => '…']
    'sections' => null,
])

@php
    $tags ??= ['Privacy', 'Legal', 'Policy'];

    $sections ??= [
        ['type' => 'paragraph', 'text' => 'When you use our products, you place your trust in us. That trust comes with real responsibilities — and we take them seriously. This document describes the information we collect, why we collect it, and the choices you have regarding how it is used. We have written it to be as straightforward as possible, because we believe everyone deserves to understand the tools they use every day.'],
        ['type' => 'paragraph', 'text' => 'Our practices are guided by the principle of data minimisation: we collect only what is genuinely necessary to provide the service. Where we rely on third-party processors, we apply the same scrutiny to their practices that we apply to our own. You can read more in our', 'link' => ['label' => 'sub-processor register', 'href' => '#sub-processors'], 'after' => ', which we keep up to date whenever our vendor relationships change.'],
        ['type' => 'heading', 'text' => 'What we collect and why'],
        ['type' => 'paragraph', 'text' => 'The information we process falls into two broad categories: information you provide directly, and information generated automatically as part of normal service operation. Both are described below, along with the legal basis we rely on for each.'],
        ['type' => 'list', 'ordered' => false, 'items' => [
            ['label' => 'Account information', 'text' => '— your name, email address, and password hash. Required to create and authenticate your account.'],
            ['label' => 'Usage data', 'text' => '— pages visited, features used, and session duration. Collected in aggregate to help us improve the product.'],
            ['label' => 'Support correspondence', 'text' => '— messages you send our team. Retained for 24 months to provide context for ongoing conversations.'],
            ['label' => 'Payment metadata', 'text' => '— transaction identifiers and invoice records. Raw card data is handled exclusively by our PCI-compliant payment processor.'],
        ]],
        ['type' => 'quote', 'text' => '"Privacy is not about having something to hide. It is about having the power to choose what you share, with whom, and on what terms."'],
        ['type' => 'heading', 'text' => 'Your rights and how to exercise them'],
        ['type' => 'paragraph', 'text' => 'Depending on your location, you may have certain rights regarding your personal data. We honour these rights regardless of jurisdiction, because we believe they reflect a fair baseline for everyone. To submit a request, contact us at the address listed at the foot of this page. We aim to respond within 30 days.'],
        ['type' => 'list', 'ordered' => true, 'items' => [
            ['label' => 'Right of access', 'text' => '— request a copy of the personal data we hold about you, along with how it is used.'],
            ['label' => 'Right to rectification', 'text' => '— ask us to correct inaccurate or incomplete information without undue delay.'],
            ['label' => 'Right to erasure', 'text' => '— request deletion of your data where there is no compelling reason for us to keep it.'],
            ['label' => 'Right to portability', 'text' => '— receive your data in a structured, machine-readable format to transfer elsewhere.'],
            ['label' => 'Right to object', 'text' => '— opt out of processing based on legitimate interests, including direct marketing, at any time.'],
        ]],
        ['type' => 'paragraph', 'text' => 'We will never sell your personal data to third parties, and we will never use it to build advertising profiles. These are not policies that may change with a product update — they are founding principles.'],
    ];
@endphp

<x-ui.section space="xl" {{ $attributes }}>
    <div class="mx-auto max-w-3xl px-4">
        @if (! empty($tags))
            <div class="mb-4 flex flex-wrap gap-2">
                @foreach ($tags as $tag)
                    <x-ui.badge variant="secondary">{{ __($tag) }}</x-ui.badge>
                @endforeach
            </div>
        @endif

        @if ($eyebrow)
            <p class="mb-4 text-sm font-medium text-muted-foreground">{{ __($eyebrow) }}</p>
        @endif
        <h2 class="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">{{ __($title) }}</h2>
        <p class="text-pretty text-xl leading-relaxed text-muted-foreground">{{ __($lead) }}</p>

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

        <div class="space-y-6 text-pretty leading-relaxed">
            @foreach ($sections as $block)
                @switch($block['type'] ?? 'paragraph')
                    @case('heading')
                        <h3 class="text-xl font-semibold text-foreground">{{ __($block['text']) }}</h3>
                        @break

                    @case('list')
                        @if (! empty($block['ordered']))
                            <ol class="ms-6 list-decimal space-y-2 text-muted-foreground">
                                @foreach ($block['items'] ?? [] as $item)
                                    <li><span class="font-medium text-foreground">{{ __($item['label']) }}</span> {{ __($item['text']) }}</li>
                                @endforeach
                            </ol>
                        @else
                            <ul class="ms-6 list-disc space-y-2 text-muted-foreground">
                                @foreach ($block['items'] ?? [] as $item)
                                    <li><span class="font-medium text-foreground">{{ __($item['label']) }}</span> {{ __($item['text']) }}</li>
                                @endforeach
                            </ul>
                        @endif
                        @break

                    @case('quote')
                        <blockquote class="border-s-2 border-primary ps-4 italic text-foreground">{{ __($block['text']) }}</blockquote>
                        @break

                    @default
                        <p class="text-muted-foreground">
                            {{ __($block['text']) }}
                            @if (! empty($block['link']))
                                <a href="{{ $block['link']['href'] ?? '#' }}" class="font-medium text-link underline underline-offset-4">{{ __($block['link']['label']) }}</a>{{ __($block['after'] ?? '') }}
                            @endif
                        </p>
                @endswitch
            @endforeach
        </div>
    </div>
</x-ui.section>

Installation

terminal
php artisan ui:add blocks/rich-text-article

Installs these dependencies automatically:

Registry contract

Install confidence

php artisan ui:add blocks/rich-text-article 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/rich-text-article.blade.php

Registry dependencies

badge section separator

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.

rich-text-article.md
# Brok UI block: Rich Text (`rich-text-article`)

A centered long-form prose section (terms, policy or article body) with topic tags, a title and lead above a $sections prop of typed content blocks (paragraph, heading, list, quote) — the default value keeps the original privacy-policy copy.

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/rich-text-article
```

## Render it

```blade
<x-blocks.rich-text-article />
```

## Props

- `eyebrow` (string, default `Last updated, April 2026`) — Small label above the title, typically a last-updated date.
- `title` (string, default `Our commitment to privacy and transparency`) — Document title.
- `lead` (string, default `We believe that trust is earned through clarity. This page explains how we collect, use, and protect your information — written in plain language so you always know where you stand.`) — Supporting lead paragraph under the heading.
- `tags` (mixed|null, default `null`) — Topic badges shown under the title; null falls back to a default sample set.
- `sections` (mixed|null, default `null`) — Ordered list of typed content blocks (paragraph, heading, list, quote); null falls back to sample privacy-policy copy.

## Use when

- Publishing a long-form legal or policy document, such as a privacy policy or terms of service, as typed paragraph, heading, list and quote sections.
- Needing a single-column prose body with an optional lead paragraph and topic tags but no in-page navigation.

## Avoid when

- Needing a sticky on-this-page table of contents beside the prose; use rich-text-with-toc instead.

## Rules

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

  • Publishing a long-form legal or policy document, such as a privacy policy or terms of service, as typed paragraph, heading, list and quote sections.
  • Needing a single-column prose body with an optional lead paragraph and topic tags but no in-page navigation.

Avoid when

  • Needing a sticky on-this-page table of contents beside the prose; use rich-text-with-toc instead.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.

Usage

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

resources/views/welcome.blade.php Blade
<x-blocks.rich-text-article />

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/rich-text-article.blade.php Blade
@props([
    'eyebrow'  => 'Last updated, April 2026',
    'title'    => 'Our commitment to privacy and transparency',
    'lead'     => 'We believe that trust is earned through clarity. This page explains how we collect, use, and protect your information — written in plain language so you always know where you stand.',
    'tags'     => null,
    // An ordered list of typed content blocks, rendered in place — this
    // is the data API; there is no raw-HTML escape hatch (see rich-text-with-toc
    // for a heading-grouped alternative shape). Each block is one of:
    //   ['type' => 'paragraph', 'text' => '…', 'link' => ['label','href'], 'after' => '…']
    //     `link`/`after` are optional: append an inline link then trailing text.
    //   ['type' => 'heading', 'text' => '…']
    //   ['type' => 'list', 'ordered' => bool, 'items' => [['label' => '…', 'text' => '…'], …]]
    //   ['type' => 'quote', 'text' => '…']
    'sections' => null,
])

@php
    $tags ??= ['Privacy', 'Legal', 'Policy'];

    $sections ??= [
        ['type' => 'paragraph', 'text' => 'When you use our products, you place your trust in us. That trust comes with real responsibilities — and we take them seriously. This document describes the information we collect, why we collect it, and the choices you have regarding how it is used. We have written it to be as straightforward as possible, because we believe everyone deserves to understand the tools they use every day.'],
        ['type' => 'paragraph', 'text' => 'Our practices are guided by the principle of data minimisation: we collect only what is genuinely necessary to provide the service. Where we rely on third-party processors, we apply the same scrutiny to their practices that we apply to our own. You can read more in our', 'link' => ['label' => 'sub-processor register', 'href' => '#sub-processors'], 'after' => ', which we keep up to date whenever our vendor relationships change.'],
        ['type' => 'heading', 'text' => 'What we collect and why'],
        ['type' => 'paragraph', 'text' => 'The information we process falls into two broad categories: information you provide directly, and information generated automatically as part of normal service operation. Both are described below, along with the legal basis we rely on for each.'],
        ['type' => 'list', 'ordered' => false, 'items' => [
            ['label' => 'Account information', 'text' => '— your name, email address, and password hash. Required to create and authenticate your account.'],
            ['label' => 'Usage data', 'text' => '— pages visited, features used, and session duration. Collected in aggregate to help us improve the product.'],
            ['label' => 'Support correspondence', 'text' => '— messages you send our team. Retained for 24 months to provide context for ongoing conversations.'],
            ['label' => 'Payment metadata', 'text' => '— transaction identifiers and invoice records. Raw card data is handled exclusively by our PCI-compliant payment processor.'],
        ]],
        ['type' => 'quote', 'text' => '"Privacy is not about having something to hide. It is about having the power to choose what you share, with whom, and on what terms."'],
        ['type' => 'heading', 'text' => 'Your rights and how to exercise them'],
        ['type' => 'paragraph', 'text' => 'Depending on your location, you may have certain rights regarding your personal data. We honour these rights regardless of jurisdiction, because we believe they reflect a fair baseline for everyone. To submit a request, contact us at the address listed at the foot of this page. We aim to respond within 30 days.'],
        ['type' => 'list', 'ordered' => true, 'items' => [
            ['label' => 'Right of access', 'text' => '— request a copy of the personal data we hold about you, along with how it is used.'],
            ['label' => 'Right to rectification', 'text' => '— ask us to correct inaccurate or incomplete information without undue delay.'],
            ['label' => 'Right to erasure', 'text' => '— request deletion of your data where there is no compelling reason for us to keep it.'],
            ['label' => 'Right to portability', 'text' => '— receive your data in a structured, machine-readable format to transfer elsewhere.'],
            ['label' => 'Right to object', 'text' => '— opt out of processing based on legitimate interests, including direct marketing, at any time.'],
        ]],
        ['type' => 'paragraph', 'text' => 'We will never sell your personal data to third parties, and we will never use it to build advertising profiles. These are not policies that may change with a product update — they are founding principles.'],
    ];
@endphp

<x-ui.section space="xl" {{ $attributes }}>
    <div class="mx-auto max-w-3xl px-4">
        @if (! empty($tags))
            <div class="mb-4 flex flex-wrap gap-2">
                @foreach ($tags as $tag)
                    <x-ui.badge variant="secondary">{{ __($tag) }}</x-ui.badge>
                @endforeach
            </div>
        @endif

        @if ($eyebrow)
            <p class="mb-4 text-sm font-medium text-muted-foreground">{{ __($eyebrow) }}</p>
        @endif
        <h2 class="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">{{ __($title) }}</h2>
        <p class="text-pretty text-xl leading-relaxed text-muted-foreground">{{ __($lead) }}</p>

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

        <div class="space-y-6 text-pretty leading-relaxed">
            @foreach ($sections as $block)
                @switch($block['type'] ?? 'paragraph')
                    @case('heading')
                        <h3 class="text-xl font-semibold text-foreground">{{ __($block['text']) }}</h3>
                        @break

                    @case('list')
                        @if (! empty($block['ordered']))
                            <ol class="ms-6 list-decimal space-y-2 text-muted-foreground">
                                @foreach ($block['items'] ?? [] as $item)
                                    <li><span class="font-medium text-foreground">{{ __($item['label']) }}</span> {{ __($item['text']) }}</li>
                                @endforeach
                            </ol>
                        @else
                            <ul class="ms-6 list-disc space-y-2 text-muted-foreground">
                                @foreach ($block['items'] ?? [] as $item)
                                    <li><span class="font-medium text-foreground">{{ __($item['label']) }}</span> {{ __($item['text']) }}</li>
                                @endforeach
                            </ul>
                        @endif
                        @break

                    @case('quote')
                        <blockquote class="border-s-2 border-primary ps-4 italic text-foreground">{{ __($block['text']) }}</blockquote>
                        @break

                    @default
                        <p class="text-muted-foreground">
                            {{ __($block['text']) }}
                            @if (! empty($block['link']))
                                <a href="{{ $block['link']['href'] ?? '#' }}" class="font-medium text-link underline underline-offset-4">{{ __($block['link']['label']) }}</a>{{ __($block['after'] ?? '') }}
                            @endif
                        </p>
                @endswitch
            @endforeach
        </div>
    </div>
</x-ui.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