Page Sections

Angled Gallery Hero

A hero with the headline on the left and a gallery of images set at a 3D angle drifting slowly sideways on the right, two rows in opposite directions, pausing on hover.

A landing-page hero: an eyebrow, headline, supporting copy and two CTAs on the left, and a gallery tilted with rotateY on the right. Each row of cards loops slowly sideways, neighbouring rows run in opposite directions at slightly different speeds, and both edges fade out with a gradient mask. Hovering or keyboard-focusing the gallery pauses it, and the card under the pointer lifts forward. With no images supplied, it draws warm-gradient placeholders with mountain, sun and window-pane geometry.

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 angled-gallery-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.

-18
40
Active edited usageDefault props
<AngledGalleryHero/>

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/angled-gallery-hero.en.json

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

Usage

import { AngledGalleryHero } from "@/components/ui/angled-gallery-hero";

<AngledGalleryHero
  eyebrow="穗光攝影工作室"
  title="把每一次快門,排成一面會呼吸的牆"
  subtitle="婚禮、商品與街拍作品集中展示。"
  primaryAction={{ label: "預約拍攝", href: "/booking" }}
  secondaryAction={{ label: "看完整作品", href: "/works" }}
  images={[
    { src: "/works/01.jpg", alt: "海邊婚禮" },
    { src: "/works/02.jpg", alt: "手沖咖啡商品照" },
    // 6–12 images work well
  ]}
/>

The sample copy reads: eyebrow "Ember Grain Studio", headline "Every shutter click, arranged into a wall that breathes", buttons "Book a shoot" and "See all work".

Props

PropTypeDefaultDescription
titleReactNode—Headline (required)
eyebrowReactNode—Small label above the headline
subtitleReactNode—Supporting copy below the headline
primaryActionAngledGalleryHeroAction—Primary button, filled with the accent colour
secondaryActionAngledGalleryHeroAction—Secondary button, outlined
as"h1" | "h2""h1"Heading level
minHeightnumber560Minimum section height (px)
imagesAngledGalleryHeroImage[]8 placeholdersGallery images; items without src draw a placeholder
anglenumber-18Gallery rotateY angle (deg); when omitted, --wb-angled-gallery-hero-angle is used
rows1 | 2 | 32Number of rows; adjacent rows drift in opposite directions
pauseOnHoverbooleantruePause the drift while the pointer is over the gallery
galleryLabelstring"作品畫廊" (Work gallery)Accessible name of the gallery region
classNamestring—Classes merged into the root section

AngledGalleryHeroAction

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

AngledGalleryHeroImage

FieldTypeDefaultDescription
srcstring—Image URL; omit to draw a placeholder
altstring—Alt text, also the placeholder's accessible name

How it works

  • The gallery container has perspective: 1200px; the inner layer is rotateY(var(--wb-angled-gallery-hero-angle, -18deg)) rotateX(6deg).
  • Each row's content is duplicated, and translateX runs linearly from 0 to -50% before looping seamlessly. The period is --wb-angled-gallery-hero-duration (default 40s); row two runs in reverse at 1.25×, row three forward at 1.5×.
  • Entrance: eyebrow, headline, copy and CTAs rise 16px and fade in one after another, 80ms apart, over --wb-angled-gallery-hero-enter-duration (default 600ms).
  • Accent: --wb-angled-gallery-hero-accent (default #0d9488 in light mode, #2dd4bf in dark), used for the eyebrow, primary button and focus ring.
  • From md up it is a 5 / 7 two-column layout; on narrow screens the gallery moves below the headline.

Accessibility

  • The root is a section labelled by the headline via aria-labelledby; the heading level comes from as.
  • The gallery is a region with an aria-label and can be focused with Tab; focus always pauses the drift (regardless of pauseOnHover) so keyboard users can stop the moving content.
  • The duplicate loop copy and the cards in rows after the first are aria-hidden, so screen readers hear each image's alt text once.
  • CTAs are a or type="button" with a clear focus-visible ring.
  • When the system's reduce motion setting is on, all looping drift stops and a static composition is shown, and the entrance becomes a 150ms fade; a CSS media query applies this from the first paint.