# Frontend Widgets

Frontend widgets are registered public components that Capell can render in normal content, lazy interaction targets, and package-owned experiences. A widget definition answers four questions:

| Question | Defined by |
| --- | --- |
| Which widget key can editors choose? | `WidgetDefinitionData::$key` |
| Which Blade/Livewire component renders it? | `WidgetDefinitionData::$component` and target |
| Which frontend assets does it need? | `resourceGroups` |
| How should it present, load, and expose interactions by default? | `defaultPresentationSettings` and `defaultInteractionTriggers` |

Content Sections and Layout Builder use this same surface for editor-managed content. The registry lives in the core/frontend boundary so packages can ship reusable widget targets without inventing their own modal systems, asset loaders, or public routes.

## Register A Widget

Use `WidgetRegistry::registerDefinition()` when the widget needs presentation defaults, runtime resource groups, or interaction defaults.

```php
use Capell\Core\Data\Widgets\WidgetDefinitionData;
use Capell\Core\Support\Widgets\WidgetRegistry;

public function boot(WidgetRegistry $widgets): void
{
    $widgets->registerDefinition(WidgetDefinitionData::frontendBlade(
        key: 'video-player',
        component: 'vendor-video::widgets.player',
        resourceGroups: ['vendor-video.player'],
        defaultPresentationSettings: [
            'width_mode' => 'container',
            'loading_strategy' => 'interaction',
        ],
    ));
}
```

`WidgetRegistry::register($name, WidgetTarget::FrontendBlade, $component)` still works for simple widgets. New package code should prefer definitions because the rendering component, resource groups, presentation defaults, and interaction defaults stay in one place.

## Instance State

Widget instance data has two layers:

| Layer | Purpose | Public component receives it? |
| --- | --- | --- |
| `data.*` | The widget's own content props | Yes |
| `data.__capell.*` | Capell runtime, presentation, and interaction metadata | No |

For example:

```php
[
    'type' => 'video-player',
    'data' => [
        'title' => 'Product walkthrough',
        'video_url' => 'https://example.com/video.mp4',
        '__capell' => [
            'presentation' => [
                'loading_strategy' => 'visible',
            ],
            'interactions' => [
                [
                    'label' => 'Open transcript',
                    'target_type' => 'widget',
                    'behavior' => 'modal',
                    'target_widget' => [
                        ['type' => 'content', 'data' => ['content' => '<p>Transcript...</p>']],
                    ],
                ],
            ],
        ],
    ],
]
```

The public renderer strips `data.__capell` before passing props to the widget component. A video widget can receive `title` and `video_url`; it must not receive its presentation settings, nested target widgets, editor metadata, or interaction internals.

## Presentation Defaults And Overrides

Widget definitions can provide type defaults through `defaultPresentationSettings`. Editors can override those settings on one widget instance under `data.__capell.presentation`.

Resolution order is:

1. instance override in `data.__capell.presentation`;
2. widget definition default;
3. presentation preset default;
4. system default.

The system default keeps existing content server-rendered. Only opt a widget or block into lazy behaviour when it should genuinely wait for visibility, idle time, or visitor interaction.

## Interaction Targets

Widgets can expose interaction triggers through `data.__capell.interactions` or type defaults in `WidgetDefinitionData::$defaultInteractionTriggers`.

Supported target types:

| Target | Use |
| --- | --- |
| `widget` | Render a registered widget through the lazy widget endpoint. |
| `fragment` | Render an encrypted Layout Builder block fragment. |
| `url` | Link to a safe URL. |
| `public_action` | Use a safe fallback URL unless a package renders the action elsewhere. |

Supported behaviours for lazy targets are `modal`, `slide_over`, `inline_reveal`, and `replace_region`.

Widget targets render through `/_capell/widgets/{reference}`. The reference is encrypted JSON containing the widget type and data. The public trigger does not expose the widget type, component name, package name, target content, model IDs, field paths, or editor metadata.

Use a widget target when the visitor is opening a separate experience, such as a video player, form, gallery, quote calculator, or comparison panel. Use a Layout Builder fragment target when the visitor is loading a public block fragment from the current layout.

## Example: Button Opens A Video Widget

The editor-facing shape for a trigger that opens a video in a modal looks like this:

```php
[
    'label' => 'Watch tour',
    'icon' => 'heroicon-o-play-circle',
    'style' => 'primary',
    'target_type' => 'widget',
    'behavior' => 'modal',
    'modal_size' => 'lg',
    'target_widget' => [
        [
            'type' => 'video-player',
            'data' => [
                'title' => 'Product tour',
                'video_url' => 'https://example.com/product-tour.mp4',
            ],
        ],
    ],
]
```

The public page renders a safe trigger and an encrypted lazy widget URL. It does not render the target widget content until the visitor clicks.

## Runtime Resources

Use `FrontendResourceRegistry` when a widget needs CSS or JavaScript that should only load when the widget appears or when an interaction target opens.

```php
use Capell\Core\Enums\PresentationLoadingStrategy;
use Capell\Frontend\Support\Assets\FrontendResourceRegistry;

public function boot(FrontendResourceRegistry $resources): void
{
    $resources
        ->group('vendor-video.player')
        ->css('resources/css/player.css', buildPath: 'vendor/vendor-video')
        ->js('resources/js/player.js', buildPath: 'vendor/vendor-video', loading: PresentationLoadingStrategy::Interaction);
}
```

Public HTML receives generated resource IDs. Resource group keys and package names stay out of the rendered page.

## Choosing Loading Behaviour

| Loading strategy | Use when |
| --- | --- |
| `eager` | The widget is visible and needed for the first render. |
| `visible` | The widget can wait until it enters the viewport. |
| `interaction` | The widget is only needed after a click, focus, or similar action. |
| `idle` | The widget is useful soon, but not critical to initial rendering. |

For interaction targets, prefer `interaction` resources unless the target also appears server-rendered elsewhere on the page.

## Public Output Rules

Widget HTML and interaction placeholders must not expose:

- admin/editor controls;
- model IDs;
- field paths;
- block keys;
- component names;
- package namespaces;
- signed URLs;
- raw target widget data.

Use [Public HTML safety](public-html-safety.md), [Presentation delivery](../../packages/frontend/docs/presentation-delivery.md), and [Frontend extensions](../packages/frontend-extensions.md) when changing widget rendering.