A centred heading, line and round button over an app screenshot that sits between two rolling hills.

Last updated

Open block
Show code
---
/**
 * Screen hills 01: A centred heading, line and round button over an app screenshot that sits between two rolling hills.
 *
 * @block hero
 * @order 60
 */
import Section from '@/components/apogee/Section/Section.astro'
import Heading from '@/components/apogee/Heading/Heading.astro'
import Button from '@/components/apogee/Button/Button.astro'
import DeviceFrame from '@/components/apogee/DeviceFrame/DeviceFrame.astro'
import { plain, type HeroContent, type Media } from '@/components/apogee/sections/_shared/spine.ts'

/** @composable */
interface Props extends HeroContent {
  /** The screenshot; `srcDark` swaps in when the page is dark. @category content */
  media?: Media | null
}

const {
  heading = 'Show your designs in motion',
  description = 'Pick a template, drop in images or clips, tune the timing and export a video. All in the browser, nothing to install.',
  primaryAction = { label: 'Try it free', href: '#' },
  media = { src: '/mockups/app-desktop-light.jpg', srcDark: '/mockups/app-desktop-dark.jpg', alt: 'The editor with templates, a preview and settings' },
} = Astro.props
---

<Section spacing="none" container={false} class="overflow-hidden">
  <div class="flex flex-col items-center gap-6 px-gutter pt-(--spacing-section-lg) text-center">
    <Heading as="h1" size="display" class="max-w-4xl text-balance">{plain(heading)}</Heading>
    {description && <p class="max-w-2xl text-lg md:text-xl text-neutral-faded">{description}</p>}
    {primaryAction && <Button href={primaryAction.href} size="large" rounded class="px-8">{primaryAction.label}</Button>}
  </div>

  <div class="relative mt-16 md:mt-20">
    <div class="relative mx-auto max-w-5xl px-gutter">{media && <DeviceFrame device="browser" src={media.src} srcDark={media.srcDark} alt={media.alt} width={media.width} height={media.height}  />}</div>
    <!-- Two hills rise over the screenshot's base; the primary tint mixes into the page so they follow the theme. -->
    <svg class="pointer-events-none relative -mt-[18%] block w-full" viewBox="0 0 1440 420" preserveAspectRatio="none" aria-hidden="true">
      <defs>
        <linearGradient id="screen-hills-fill" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stop-color="color-mix(in oklab, var(--background-primary) 30%, var(--background-page))" />
          <stop offset="1" stop-color="var(--background-page)" />
        </linearGradient>
      </defs>
      <path d="M0 40C180 40 300 160 420 300C500 390 560 420 720 420H0Z" fill="url(#screen-hills-fill)" />
      <path d="M1440 40C1260 40 1140 160 1020 300C940 390 880 420 720 420H1440Z" fill="url(#screen-hills-fill)" />
      <rect y="400" width="1440" height="20" fill="var(--background-page)" />
    </svg>
  </div>
</Section>

Add

npx @ava-ui/apogee add hero/screen-hills-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.