Page Sections

Integration Hub Hero

A grid of integration icons surrounds a central product icon while beams flow along the connectors towards the centre in turn; hovering a service lights up its own connector.

A landing-page hero: a centred eyebrow, headline, supporting copy and two CTAs on top, and below them a grid of integration icons with your product icon in the centre cell (larger, with an accent outline and soft glow). Every service tile is linked to the centre by a smooth curve, beams flow along the connectors towards the centre in turn (or outwards), and the centre icon breathes gently to the beam rhythm. Hovering a service (or focusing it with the keyboard) turns its connector into a solid accent line and speeds up its beam while the other beams fade back. The grid size is picked from the number of services (3×3, 5×3, 7×3, 7×5…), so the centre cell is always in the middle.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add integration-hub-hero.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

2.4
600
Active edited usageDefault props
<IntegrationHubHero/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/integration-hub-hero.en.json

Or, once registries are configured in components.json, install it as @webberui/integration-hub-hero.

Usage

import { Cloud, CreditCard, Database, Mail } from "lucide-react";
import { IntegrationHubHero } from "@/components/ui/integration-hub-hero";

<IntegrationHubHero
  eyebrow="接點雲 · 40+ 種整合"
  title="把每一套工具,接到同一個地方"
  subtitle="資料、收款、通知與排程自動串在一起。"
  primaryAction={{ label: "免費開始", href: "/signup" }}
  secondaryAction={{ label: "瀏覽整合清單", href: "/integrations" }}
  centerLabel="接點雲"
  integrations={[
    { label: "雲庫資料", icon: Database },
    { label: "信鴿郵件", icon: Mail },
    { label: "收款站", icon: CreditCard },
    { label: "雲端倉", icon: Cloud },
    // up to 8 fit a 3×3 grid, up to 14 fit 5×3
  ]}
/>

The sample copy reads: eyebrow "Junction Cloud · 40+ integrations", headline "Plug every tool into one place", buttons "Start free" and "Browse integrations"; the services are "Vault DB", "Pigeon Mail", "PayPost" and "Cloudshed".

Props

PropTypeDefaultDescription
titleReactNode—Headline (required)
eyebrowReactNode—Small label above the headline
subtitleReactNode—Supporting copy below the headline
primaryActionIntegrationHubHeroAction—Primary button, filled with the accent colour
secondaryActionIntegrationHubHeroAction—Secondary button, outlined
as"h1" | "h2""h1"Heading level
minHeightnumber560Minimum section height (px)
integrationsIntegrationHubHeroItem[]14 built-in fictional servicesIntegration services; the grid size follows the count
centerIconComponentTypeLayersCentral product icon
centerLabelstring"你的產品" (Your product)Central product name (accessible name)
beamDirection"in" | "out""in"Beams flow from the services to the centre (in) or out from the centre (out)
showLabelsbooleanfalseAlways show names under the icons; when off, names only appear as a tooltip on hover / focus
activeIndexnumber | null—Controlled: index of the lit-up service
defaultActiveIndexnumber | nullnullUncontrolled: initially lit-up service index
onActiveIndexChange(index: number | null) => void—Called when the lit-up service changes
onIntegrationClick(item, index) => void—Click on a service tile; when set, tiles render as button
classNamestring—Classes merged into the root section

IntegrationHubHeroItem

FieldTypeDefaultDescription
labelstring—Service name, also the tooltip and accessible name
iconComponentType—Service icon (a lucide icon or any component that accepts className); when omitted, the first letter of the name is shown

IntegrationHubHeroAction

FieldTypeDefaultDescription
labelstring—Button label
hrefstring—Renders an a link when set, otherwise type="button"
onClick() => void—Click callback

How it works

  • The connectors are an SVG layer under the grid: a ResizeObserver measures each cell centre and draws an S-curve that leaves and enters sideways from each service tile to the centre cell, recomputed whenever the layout changes.
  • Each beam is a linearGradient swept along the connector's direction, about 20% of the connector long. Its period reads --wb-integration-hub-hero-beam (default 2.4s, read when the component mounts), and phases are spread evenly by index so beams reach the centre in turn; the hovered one speeds up to half the period.
  • The centre icon breathes 1 → 1.05 → 1 on the same period.
  • Entrance: eyebrow, headline, copy and CTAs rise 16px and fade in one after another, 80ms apart, over --wb-integration-hub-hero-duration (default 600ms, easing cubic-bezier(0.22, 1, 0.36, 1)); grid cells fade in ring by ring from the centre outwards, 40ms per ring, the connectors draw once the cells are in place, and only then do the beams start.
  • Accent: --wb-integration-hub-hero-accent (default #0d9488 in light mode, #2dd4bf in dark), used for the eyebrow, primary button, centre outline and glow, beams and the lit-up connector.
  • When there are more services than the grid holds, the grid grows automatically; spare cells stay empty.

Accessibility

  • The root is a section labelled by the headline via aria-labelledby; the heading level comes from as.
  • The grid is a ul with role="list"; each service tile and the centre cell is an li with an aria-label. Empty cells and the connector SVG are aria-hidden.
  • With onIntegrationClick, service tiles are type="button", reachable with Tab; focus lights up the connector just like hover and shows a focus-visible ring. Without it, tiles are not focusable, so the hero does not fill up with Tab stops that do nothing.
  • CTAs are a or type="button" with a clear focus-visible ring.
  • When the system's reduce motion setting is on, beams and the centre breathing stop and the connectors appear without drawing, leaving a static composition; the entrance becomes a 150ms fade, applied from the first paint by a CSS media query.