A photo framed like a frame of cine film, with the next frames peeking in above and below, a centred two-part heading and two dotted links.

Last updated

Open block
Show code
---
/**
 * Film strip 01: A photo framed like a frame of cine film, with the next frames peeking in above and below, a centred two-part heading and two dotted links.
 *
 * @block hero
 * @order 63
 */
import Section from '@/components/apogee/Section/Section.astro'
import Heading from '@/components/apogee/Heading/Heading.astro'
import Link from '@/components/apogee/Link/Link.astro'
import { srcUrl, accentRuns, type HeroContent } from '@/components/apogee/sections/_shared/spine.ts'

/** @composable */
interface Props extends HeroContent {}

const {
  heading = 'A drink for *easy evenings*',
  primaryAction = { label: 'Buy a case', href: '#' },
  secondaryAction = { label: 'Find a stockist', href: '#' },
  media = { src: 'https://picsum.photos/id/292/1800/1100', alt: '' },
} = Astro.props
const src = media && (srcUrl(media.src))
---

<!-- Always dark: a strip of film, light text on the photo. -->
<Section spacing="none" container={false} theme="dark" class="overflow-hidden">
  <div class="relative flex flex-col gap-3 overflow-hidden px-3 md:px-24">
    <!-- Sprocket notch on the start edge. -->
    <span class="absolute start-0 top-1/2 hidden h-48 w-10 -translate-y-1/2 rounded-e-2xl border border-neutral-faded bg-page md:block" aria-hidden="true"></span>
    <div class="h-10 overflow-hidden rounded-b-3xl md:h-16" aria-hidden="true">{media && <img src={src} alt="" class="size-full object-cover object-bottom opacity-60" />}</div>
    <div class="relative isolate grid aspect-[4/5] place-items-center overflow-hidden rounded-3xl sm:aspect-16/10">
      {media && <img src={src} alt={media.alt} loading="eager" class="absolute inset-0 -z-10 size-full object-cover" />}
      <div class="absolute inset-0 -z-10 bg-[color-mix(in_oklab,var(--background-page)_45%,transparent)]" aria-hidden="true"></div>
      <div class="flex flex-col items-center gap-12 px-6 text-center md:gap-20">
        <Heading as="h1" size="display" class="text-balance">{accentRuns(heading).map((r) => (r.accent ? <span class="font-light italic">{r.text}</span> : r.text))}</Heading>
        {(primaryAction || secondaryAction) && (
        <div class="flex flex-wrap justify-center gap-x-16 gap-y-4 text-xl md:text-2xl">
          {primaryAction && <Link href={primaryAction.href} color="inherit" class="decoration-dotted underline-offset-8">{primaryAction.label}</Link>}
          {secondaryAction && <Link href={secondaryAction.href} color="inherit" class="decoration-dotted underline-offset-8">{secondaryAction.label}</Link>}
        </div>
        )}
      </div>
    </div>
    <div class="h-10 overflow-hidden rounded-t-3xl md:h-16" aria-hidden="true">{media && <img src={src} alt="" class="size-full object-cover object-top opacity-60" />}</div>
  </div>
</Section>

Add

npx @ava-ui/apogee add hero/film-strip-01

Every piece of copy is a prop, with the sample copy as its default. Pass yours, or null to hide a part; see copy is props.