npm install @contentvidya/ui

Interactive Demo Playground

<contentvidya-banner> Playground
⚙️ Configure Properties
Main heading text.
Secondary text below the title.
Call to action button text. Omit to hide the button.
Alignment of text content: "left", "center", or "right".
❌ Invalid JSON Formatting
Background media object. { type: "image" | "video", url: string }
❌ Invalid JSON Formatting
Link for the CTA. { url: string }
Applies shimmer loading skeleton.
Alternative/direct text alignment configuration: "left", "center", or "right".
Alternative/direct background image or video source link.
Alternative/direct destination URL for the CTA button.
Content alignment: "left", "center", or "right".
CSS background for the overlay layer, e.g. a gradient or rgba() color (default: 'rgba(0, 0, 0, 0.4)').
Overlay padding: "sm", "md", "lg", "xl", or a raw CSS value.
CSS height for the root element. 'auto' follows the media's natural aspect ratio via an in-flow image/video.
CSS background-position / object-position for the media.
24
Minimum hit-target size in CSS px for hotspot links (WCAG 2.5.8).
Canvas background effect: "none", "particles", "waves", "rain", "thunderstorm", "sunrise", "sunset", "fog", "autumn", "festival", "santa", or "sea".
❌ Invalid JSON Formatting
Clickable image-map regions overlaid on the media. Each: { id, altText, shape: 'rect'|'oval'|'polygon', coords: {x,y,width,height}, points?, action: {type,url,deeplink?}, showTooltip?, pulse? } (coords/points are % of the banner).
Defer mounting media/hotspots until scrolled near the viewport.

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

PropTypeDefaultDescription
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.