Skip to content
Brok UI

Loading…

No results

Integrations

Open source

Integration sections — a card grid, a split layout, a marquee and an orbit diagram.

Preview

4 of 5

resources/views/blocks/integrations-orbit.blade.php Blade
@props([
    'eyebrow' => 'Integrations',
    'heading' => 'Connect your entire stack',
    'subheading' => 'One platform at the center of your tools. Sync data across the apps your team already uses — no glue code required.',
    'brand' => 'Acme',
    'innerNodes' => null,
    'outerNodes' => null,
])

@php
    $innerNodes ??= [
        ['label' => 'Slack', 'initials' => 'SL'],
        ['label' => 'Notion', 'initials' => 'NO'],
        ['label' => 'Linear', 'initials' => 'LI'],
        ['label' => 'GitHub', 'initials' => 'GH'],
    ];

    $outerNodes ??= [
        ['label' => 'Stripe', 'initials' => 'ST'],
        ['label' => 'Figma', 'initials' => 'FI'],
        ['label' => 'Zapier', 'initials' => 'ZA'],
        ['label' => 'HubSpot', 'initials' => 'HS'],
        ['label' => 'Segment', 'initials' => 'SE'],
        ['label' => 'Vercel', 'initials' => 'VE'],
    ];

    // Evenly space chips around a circle. Returns inline `left/top` percentages
    // (position only — no colour — so the raw-colour guard is satisfied), centred
    // with -translate-1/2. Radius is a % of the square container's half-size.
    $placeOnRing = function (int $count, float $radius, float $startDeg = -90.0): array {
        $out = [];
        for ($k = 0; $k < $count; $k++) {
            $rad = deg2rad($startDeg + $k * (360 / $count));
            $out[] = sprintf('left:%.3f%%;top:%.3f%%', 50 + $radius * cos($rad), 50 + $radius * sin($rad));
        }
        return $out;
    };

    // Outer chips sit on the outer ring; inner chips offset by half a step so the
    // two rings interleave rather than line up radially.
    $outerCoords = $placeOnRing(count($outerNodes), 44);
    $innerStep = count($innerNodes) > 0 ? 180 / count($innerNodes) : 0;
    $innerCoords = $placeOnRing(count($innerNodes), 25, -90 + $innerStep);
@endphp

<section {{ $attributes->merge(['class' => 'relative isolate overflow-hidden bg-gradient-to-b from-primary/5 via-background to-background py-16 text-foreground sm:py-24']) }}>
    <div class="mx-auto max-w-3xl px-4 text-center">
        <x-ui.badge variant="secondary" class="mb-6">{{ __($eyebrow) }}</x-ui.badge>

        <h2 class="text-balance text-3xl font-bold tracking-tight sm:text-4xl">
            {{ __($heading) }}
        </h2>

        <p class="mx-auto mt-4 max-w-2xl text-pretty text-xl text-muted-foreground">
            {{ __($subheading) }}
        </p>
    </div>

    <div class="mt-16 flex items-center justify-center px-4">
        <div class="relative aspect-square w-full max-w-md sm:max-w-lg">
            {{-- Outer ring: dashed border + outer chips, orbiting slowly clockwise.
                 Each chip gets a nested, inversely-animated wrapper so the ring
                 rotates while the chip label stays upright. --}}
            <div class="absolute inset-0 motion-safe:animate-[spin_60s_linear_infinite]" aria-hidden="true">
                <div class="absolute inset-[6%] rounded-full border border-dashed border-border/70"></div>
            </div>

            {{-- Inner ring: solid border + inner chips, orbiting slowly counter-clockwise.
                 The direction keyword is folded into the single arbitrary `animate-[…]`
                 value (rather than a sibling `[animation-direction:reverse]` utility)
                 because Tailwind groups every `motion-safe:` utility into one trailing
                 @media block — a separate, unprefixed direction longhand would land
                 earlier in the stylesheet and be reset back to `normal` by the later
                 `animation` shorthand it shares an element with. --}}
            <div class="absolute inset-0 motion-safe:animate-[spin_40s_linear_infinite_reverse]" aria-hidden="true">
                <div class="absolute inset-[25%] rounded-full border border-border"></div>
            </div>

            {{-- Outer integration chips, evenly spaced on the outer ring --}}
            <div class="absolute inset-0 motion-safe:animate-[spin_60s_linear_infinite]">
                @foreach ($outerNodes as $i => $node)
                    <div class="absolute -translate-x-1/2 -translate-y-1/2" style="{{ $outerCoords[$i] }}">
                        <div class="motion-safe:animate-[spin_60s_linear_infinite_reverse]">
                            <x-ui.card class="flex size-12 items-center justify-center rounded-full p-0 shadow-sm sm:size-14">
                                <span class="text-xs font-semibold tracking-tight text-muted-foreground sm:text-sm">{{ $node['initials'] }}</span>
                                <span class="sr-only">{{ __($node['label']) }}</span>
                            </x-ui.card>
                        </div>
                    </div>
                @endforeach
            </div>

            {{-- Inner integration chips, on the inner ring, orbiting the opposite direction --}}
            <div class="absolute inset-0 motion-safe:animate-[spin_40s_linear_infinite_reverse]">
                @foreach ($innerNodes as $i => $node)
                    <div class="absolute -translate-x-1/2 -translate-y-1/2" style="{{ $innerCoords[$i] }}">
                        <div class="motion-safe:animate-[spin_40s_linear_infinite]">
                            <x-ui.card class="flex size-11 items-center justify-center rounded-full p-0 shadow-sm sm:size-12">
                                <span class="text-xs font-semibold tracking-tight text-card-foreground">{{ $node['initials'] }}</span>
                                <span class="sr-only">{{ __($node['label']) }}</span>
                            </x-ui.card>
                        </div>
                    </div>
                @endforeach
            </div>

            {{-- Central brand node --}}
            <div class="absolute inset-0 m-auto flex size-20 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg ring-8 ring-background sm:size-24">
                <span class="text-xl font-bold tracking-tight sm:text-2xl">{{ $brand }}</span>
            </div>
        </div>
    </div>
</section>

Installation

terminal
php artisan ui:add blocks/integrations-orbit

Installs these dependencies automatically:

Registry contract

Install confidence

php artisan ui:add blocks/integrations-orbit 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.1
License
open
Stability
stable
Contract
v1
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/integrations-orbit.blade.php

Registry dependencies

card badge

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.

integrations-orbit.md
# Brok UI block: Integrations Orbit (`integrations-orbit`)

A centered integrations diagram with a primary brand node ringed by two concentric orbits of integration logo chips and a slow CSS rotation.

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/integrations-orbit
```

## Render it

```blade
<x-blocks.integrations-orbit />
```

## Props

- `eyebrow` (string, default `Integrations`) — Small label shown above the heading inside a badge.
- `heading` (string, default `Connect your entire stack`) — Primary section heading.
- `subheading` (string, default `One platform at the center of your tools. Sync data across the apps your team already uses — no glue code required.`) — Supporting paragraph under the heading.
- `brand` (string, default `Acme`) — Label shown in the central brand node.
- `inner-nodes` (mixed|null, default `null`) — Array of {label, initials} chips placed on the inner orbit ring; falls back to a 4-item sample list when null.
- `outer-nodes` (mixed|null, default `null`) — Array of {label, initials} chips placed on the outer orbit ring; falls back to a 6-item sample list when null.

## Use when

- Use it when you want a visual 'hub and spoke' diagram with your brand as the central node ringed by two concentric orbits of integration logo chips.
- Reach for it as an illustrative centerpiece rather than a browsable or connectable list of integrations.

## Avoid when

- Avoid it when integrations need to be individually connectable or described; use integrations-grid or integrations-list instead.

## Rules

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

  • Use it when you want a visual 'hub and spoke' diagram with your brand as the central node ringed by two concentric orbits of integration logo chips.
  • Reach for it as an illustrative centerpiece rather than a browsable or connectable list of integrations.

Avoid when

  • Avoid it when integrations need to be individually connectable or described; use integrations-grid or integrations-list instead.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
centered heading and subheading outer dashed orbit ring inner orbit ring outer integration chips inner integration chips central brand node

Usage

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

resources/views/welcome.blade.php Blade
<x-blocks.integrations-orbit />

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/integrations-orbit.blade.php Blade
@props([
    'eyebrow' => 'Integrations',
    'heading' => 'Connect your entire stack',
    'subheading' => 'One platform at the center of your tools. Sync data across the apps your team already uses — no glue code required.',
    'brand' => 'Acme',
    'innerNodes' => null,
    'outerNodes' => null,
])

@php
    $innerNodes ??= [
        ['label' => 'Slack', 'initials' => 'SL'],
        ['label' => 'Notion', 'initials' => 'NO'],
        ['label' => 'Linear', 'initials' => 'LI'],
        ['label' => 'GitHub', 'initials' => 'GH'],
    ];

    $outerNodes ??= [
        ['label' => 'Stripe', 'initials' => 'ST'],
        ['label' => 'Figma', 'initials' => 'FI'],
        ['label' => 'Zapier', 'initials' => 'ZA'],
        ['label' => 'HubSpot', 'initials' => 'HS'],
        ['label' => 'Segment', 'initials' => 'SE'],
        ['label' => 'Vercel', 'initials' => 'VE'],
    ];

    // Evenly space chips around a circle. Returns inline `left/top` percentages
    // (position only — no colour — so the raw-colour guard is satisfied), centred
    // with -translate-1/2. Radius is a % of the square container's half-size.
    $placeOnRing = function (int $count, float $radius, float $startDeg = -90.0): array {
        $out = [];
        for ($k = 0; $k < $count; $k++) {
            $rad = deg2rad($startDeg + $k * (360 / $count));
            $out[] = sprintf('left:%.3f%%;top:%.3f%%', 50 + $radius * cos($rad), 50 + $radius * sin($rad));
        }
        return $out;
    };

    // Outer chips sit on the outer ring; inner chips offset by half a step so the
    // two rings interleave rather than line up radially.
    $outerCoords = $placeOnRing(count($outerNodes), 44);
    $innerStep = count($innerNodes) > 0 ? 180 / count($innerNodes) : 0;
    $innerCoords = $placeOnRing(count($innerNodes), 25, -90 + $innerStep);
@endphp

<section {{ $attributes->merge(['class' => 'relative isolate overflow-hidden bg-gradient-to-b from-primary/5 via-background to-background py-16 text-foreground sm:py-24']) }}>
    <div class="mx-auto max-w-3xl px-4 text-center">
        <x-ui.badge variant="secondary" class="mb-6">{{ __($eyebrow) }}</x-ui.badge>

        <h2 class="text-balance text-3xl font-bold tracking-tight sm:text-4xl">
            {{ __($heading) }}
        </h2>

        <p class="mx-auto mt-4 max-w-2xl text-pretty text-xl text-muted-foreground">
            {{ __($subheading) }}
        </p>
    </div>

    <div class="mt-16 flex items-center justify-center px-4">
        <div class="relative aspect-square w-full max-w-md sm:max-w-lg">
            {{-- Outer ring: dashed border + outer chips, orbiting slowly clockwise.
                 Each chip gets a nested, inversely-animated wrapper so the ring
                 rotates while the chip label stays upright. --}}
            <div class="absolute inset-0 motion-safe:animate-[spin_60s_linear_infinite]" aria-hidden="true">
                <div class="absolute inset-[6%] rounded-full border border-dashed border-border/70"></div>
            </div>

            {{-- Inner ring: solid border + inner chips, orbiting slowly counter-clockwise.
                 The direction keyword is folded into the single arbitrary `animate-[…]`
                 value (rather than a sibling `[animation-direction:reverse]` utility)
                 because Tailwind groups every `motion-safe:` utility into one trailing
                 @media block — a separate, unprefixed direction longhand would land
                 earlier in the stylesheet and be reset back to `normal` by the later
                 `animation` shorthand it shares an element with. --}}
            <div class="absolute inset-0 motion-safe:animate-[spin_40s_linear_infinite_reverse]" aria-hidden="true">
                <div class="absolute inset-[25%] rounded-full border border-border"></div>
            </div>

            {{-- Outer integration chips, evenly spaced on the outer ring --}}
            <div class="absolute inset-0 motion-safe:animate-[spin_60s_linear_infinite]">
                @foreach ($outerNodes as $i => $node)
                    <div class="absolute -translate-x-1/2 -translate-y-1/2" style="{{ $outerCoords[$i] }}">
                        <div class="motion-safe:animate-[spin_60s_linear_infinite_reverse]">
                            <x-ui.card class="flex size-12 items-center justify-center rounded-full p-0 shadow-sm sm:size-14">
                                <span class="text-xs font-semibold tracking-tight text-muted-foreground sm:text-sm">{{ $node['initials'] }}</span>
                                <span class="sr-only">{{ __($node['label']) }}</span>
                            </x-ui.card>
                        </div>
                    </div>
                @endforeach
            </div>

            {{-- Inner integration chips, on the inner ring, orbiting the opposite direction --}}
            <div class="absolute inset-0 motion-safe:animate-[spin_40s_linear_infinite_reverse]">
                @foreach ($innerNodes as $i => $node)
                    <div class="absolute -translate-x-1/2 -translate-y-1/2" style="{{ $innerCoords[$i] }}">
                        <div class="motion-safe:animate-[spin_40s_linear_infinite]">
                            <x-ui.card class="flex size-11 items-center justify-center rounded-full p-0 shadow-sm sm:size-12">
                                <span class="text-xs font-semibold tracking-tight text-card-foreground">{{ $node['initials'] }}</span>
                                <span class="sr-only">{{ __($node['label']) }}</span>
                            </x-ui.card>
                        </div>
                    </div>
                @endforeach
            </div>

            {{-- Central brand node --}}
            <div class="absolute inset-0 m-auto flex size-20 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg ring-8 ring-background sm:size-24">
                <span class="text-xl font-bold tracking-tight sm:text-2xl">{{ $brand }}</span>
            </div>
        </div>
    </div>
</section>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.1.1
Status
Stable
License
open
Deprecation
Not deprecated
Contract
v1
Foundation
≥ 1.0.0