🖼 Component
Banner
Full-bleed hero banner with image or video background, title, subtitle, CTA button, and loading shimmer.
Interactive Demo Playground
Configure Properties
Usage Code Generator
import Banner from '@contentvidya/ui/react/Banner';
import '@contentvidya/ui/theme.css';
// import { defaultBackgroundEffectPlugin } from '@contentvidya/ui/react/utils/backgroundEffects';
// Optionally override the canvas animation engine per-instance:
// config={{ ..., backgroundEffectPlugin: { start: myStart, stop: myStop } }}
// Every hotspot shape renders as a real SVG rect/ellipse/polygon (not
// clip-path), each with its own keyboard-focusable <a> sized to at least
// config.hotspotMinTargetSize CSS px (WCAG 2.5.8), a visible focus outline
// (2.4.7), and an optional hover/focus tooltip (1.4.13).
<Banner
title="Summer Collection 2026"
subtitle="Discover the new season"
ctaText="Shop Now"
media={{ type: 'image', url: '/hero.jpg' }}
hotspots={[
{ id: 'h1', altText: 'Jacket', shape: 'rect', coords: { x: 8, y: 18, width: 14, height: 24 }, action: { type: 'link', url: '/products/jacket' }, pulse: true, showTooltip: true, label: 'Jacket' },
{ id: 'h2', altText: 'Sunglasses', shape: 'oval', coords: { x: 34, y: 12, width: 10, height: 8 }, action: { type: 'link', url: '/products/sunglasses' }, pulse: true, showTooltip: true, label: 'Sunglasses' },
{ id: 'h3', altText: 'Sneakers', shape: 'polygon', coords: { x: 55, y: 60, width: 20, height: 15 }, points: [{ x: 55, y: 68 }, { x: 62, y: 60 }, { x: 75, y: 62 }, { x: 70, y: 75 }], action: { type: 'link', url: '/products/sneakers' }, pulse: true, showTooltip: true, label: 'Sneakers' }
]}
ctaLink="/shop"
lazyLoad
config={{
align: 'center',
padding: 'lg',
bgGradient: 'linear-gradient(to right, black, transparent)',
height: 'auto',
bgPosition: 'center',
hotspotMinTargetSize: 24,
backgroundEffect: 'rain'
}}
/>
<script lang="ts">
import Banner from '@contentvidya/ui/svelte/Banner.svelte';
import '@contentvidya/ui/theme.css';
// import { defaultBackgroundEffectPlugin } from '@contentvidya/ui/svelte/utils/backgroundEffects';
// Optionally override the canvas animation engine per-instance:
// config={{ ..., backgroundEffectPlugin: { start: myStart, stop: myStop } }}
</script>
<!-- Rect, oval and polygon hotspots all render as native SVG shapes with a
matching pulse ring and an accessible, min-24px hit target. -->
<Banner
title="Summer Collection 2026"
subtitle="Discover the new season"
ctaText="Shop Now"
media={{ type: 'image', url: '/hero.jpg' }}
hotspots={[
{ id: 'h1', altText: 'Jacket', shape: 'rect', coords: { x: 8, y: 18, width: 14, height: 24 }, action: { type: 'link', url: '/products/jacket' }, pulse: true, showTooltip: true, label: 'Jacket' },
{ id: 'h2', altText: 'Sunglasses', shape: 'oval', coords: { x: 34, y: 12, width: 10, height: 8 }, action: { type: 'link', url: '/products/sunglasses' }, pulse: true, showTooltip: true, label: 'Sunglasses' },
{ id: 'h3', altText: 'Sneakers', shape: 'polygon', coords: { x: 55, y: 60, width: 20, height: 15 }, points: [{ x: 55, y: 68 }, { x: 62, y: 60 }, { x: 75, y: 62 }, { x: 70, y: 75 }], action: { type: 'link', url: '/products/sneakers' }, pulse: true, showTooltip: true, label: 'Sneakers' }
]}
ctaLink="/shop"
lazyLoad={true}
config={{
align: 'center',
padding: 'lg',
bgGradient: 'linear-gradient(to right, black, transparent)',
height: 'auto',
bgPosition: 'center',
hotspotMinTargetSize: 24,
backgroundEffect: 'rain'
}}
/>
<link rel="stylesheet" href="node_modules/@contentvidya/ui/theme.css">
<script type="module"
src="node_modules/@contentvidya/ui/webcomponents/Banner.js"></script>
<!-- backgroundEffectPlugin ({ start, stop } functions) can't be passed as an
HTML attribute string — set it as a JS property on the element instead:
document.querySelector('banner').config = { ...existingConfig, backgroundEffectPlugin: { start: myStart, stop: myStop } }; -->
<banner
title="Summer Collection 2026"
subtitle="Discover the new season"
cta-text="Shop Now"
media='{"type":"image","url":"/hero.jpg"}'
hotspots='[{"id":"h1","altText":"Jacket","shape":"rect","coords":{"x":8,"y":18,"width":14,"height":24},"action":{"type":"link","url":"/products/jacket"},"pulse":true,"showTooltip":true,"label":"Jacket"},{"id":"h2","altText":"Sunglasses","shape":"oval","coords":{"x":34,"y":12,"width":10,"height":8},"action":{"type":"link","url":"/products/sunglasses"},"pulse":true,"showTooltip":true,"label":"Sunglasses"},{"id":"h3","altText":"Sneakers","shape":"polygon","coords":{"x":55,"y":60,"width":20,"height":15},"points":[{"x":55,"y":68},{"x":62,"y":60},{"x":75,"y":62},{"x":70,"y":75}],"action":{"type":"link","url":"/products/sneakers"},"pulse":true,"showTooltip":true,"label":"Sneakers"}]'
cta-link="/shop"
lazy-load="true"
config='{"align":"center","padding":"lg","height":"auto","bgPosition":"center","hotspotMinTargetSize":24,"backgroundEffect":"rain"}'
></banner>
Props API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | undefined | Unique identifier passed to the root element |
| title | string | undefined | Heading text rendered in the banner overlay |
| subtitle | string | undefined | Sub-heading text displayed below the title |
| ctaText | string | undefined | Call-to-action button label; renders an anchor when set |
| media | BannerMedia | undefined | Background media: { type: 'image'|'video', url: string } |
| mapLinks | MapLink[] | undefined | Link objects; first entry used as the CTA href |
| textAlignment | 'left' | 'center' | 'right' | undefined | Text alignment for all banner content |
| className | string | undefined | Additional CSS class names on the root element |
| isLoading | boolean | undefined | Applies the contentvidya-image-shimmer skeleton animation |
| hotspots | Hotspot[] | undefined | Clickable SVG image-map regions (rect/oval/polygon) overlaid on the media, each linking to a URL or deeplink. Renders as a real SVG shape (not clip-path), with a separately-sized keyboard-focusable hit target per hotspot |
| config.hotspotMinTargetSize | number | undefined | Minimum hotspot hit-target size in CSS px, enforced independently of the drawn shape size (WCAG 2.5.8). Default: 24 |
| config.backgroundEffect | 'none' | 'particles' | 'waves' | 'rain' | 'thunderstorm' | 'sunrise' | 'sunset' | 'fog' | 'autumn' | 'festival' | 'santa' | 'sea' | undefined | Animated canvas layer rendered behind the media/overlay. Shared plugin engine (src/utils/backgroundEffects.ts) also used by SlidingBanner — new effects can be added there without touching this component. 'rain'/'thunderstorm' include a water band along the bottom edge with landing ripples; 'thunderstorm' additionally adds periodic lightning flashes/bolts. 'fog' is winter fog drifting with wind, light snow, plus cinematic frost shards. 'autumn' is falling, swaying autumn leaves. 'festival' is an Indian-festival (Diwali-style) look with ambient diya sparkle and periodic firework bursts. 'santa' has twinkling string lights, snowfall mixed with tumbling candy canes and peppermints, a galloping-reindeer sleigh flyby (with a glowing Rudolph nose leading) that drops a trail of tumbling candy canes and mints as it flies, loops back right-to-left through the bottom half of the frame after its first pass instead of just vanishing, and a playful Santa cap that randomly peeks in and out from one of the four frame edges like hide-and-seek. 'sea' is an aerial-view waterline wash — the effect stays transparent everywhere except a thin translucent, sea-tinted band (~10% of the banner height) along the bottom edge, so the banner's own background image reads as the seashore/beach itself. The band breathes in and out on a natural (non-mechanical) surge rhythm, with drifting aerial-style foam blobs and lacy filament streaks, like a top-down drone shot of waves lapping the sand. Default: 'none' |
| config.backgroundEffectPlugin | BackgroundEffectPlugin | undefined | Optional { start, stop } override for the canvas animation engine, letting a consumer (e.g. the CMS page editor) inject its own effect renderer per-instance instead of the built-in one. Falls back to defaultBackgroundEffectPlugin exported from src/utils/backgroundEffects.ts when omitted. |
| lazyLoad | boolean | undefined | Defer mounting media/hotspots until the banner scrolls near the viewport (default: true) |
| lazyThreshold | number | undefined | IntersectionObserver threshold, 0-1 (default: 0.1) |
| lazyRootMargin | string | undefined | IntersectionObserver rootMargin, e.g. preload distance (default: '200px') |
Theming: Override
--contentvidya-color-primary in your stylesheet to change the CTA button colour.Video background: Set
media.type = 'video' — the component renders a muted, autoplayed, looped <video> positioned absolutely behind the overlay.Hotspots: Pass
hotspots (rect/oval/polygon regions with coords as percentages) to overlay clickable image-map regions on the media, each linking to a URL or app deeplink. Shapes render as real, anti-aliased SVG (not CSS clip-path), so polygon edges and the pulse ring look correct for every shape — matching what you see in the Hotspot Designer. backgroundImageUrl remains supported as a legacy alternative to media.Accessibility: Each hotspot is a real, keyboard-focusable
<a> with an aria-label from altText/label, a visible :focus-visible outline, and a hit target that's always at least config.hotspotMinTargetSize CSS px (default 24, per WCAG 2.5.8) regardless of the drawn shape's size. Set showTooltip for a hover/focus tooltip that meets WCAG 1.4.13. The pulse animation automatically pauses under prefers-reduced-motion: reduce.Background effects: Set
config.backgroundEffect to 'particles', 'waves', 'rain', 'thunderstorm', 'sunrise', 'sunset', 'fog', 'autumn', 'festival', 'santa', or 'sea' to render an animated canvas layer behind the banner's media/overlay. This is powered by a standalone plugin engine at src/utils/backgroundEffects.ts (also used by SlidingBanner) — it's a plain registry of canvas renderers, so new effects can be added there once and become available to every component that uses it, without any per-component changes.Custom animation engine: Pass
config.backgroundEffectPlugin={{ start, stop }} to inject your own animation implementation per-instance instead of the built-in canvas renderers — useful when a consumer (e.g. the CMS page editor) wants to control the effect engine itself. Omit it and the component falls back to defaultBackgroundEffectPlugin, exported alongside the built-in effects.Lazy loading: By default the banner defers mounting its media/hotspots until it scrolls near the viewport (shared
IntersectionObserver per threshold across all widgets on the page). Pass lazyLoad={false} to disable, or tune lazyThreshold / lazyRootMargin.