Screen hills 01
A centred heading, line and round button over an app screenshot that sits between two rolling hills.
Last updated
---
/**
* 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>
Show codeHide 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.