Wordmark photo 01
A rounded photo card with a stacked heading, a line and a round button, a giant wordmark across the bottom and a scrolling strip of claims under it.
Last updated
---
/**
* Wordmark photo 01: A rounded photo card with a stacked heading, a line and a round button, a giant wordmark across the bottom and a scrolling strip of claims under it.
*
* @block hero
* @order 61
*/
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 Image from '@/components/apogee/Image/Image.astro'
import Marquee from '@/components/apogee/Marquee/Marquee.astro'
import { plain, type HeroContent } from '@/components/apogee/sections/_shared/spine.ts'
/** @composable */
interface Props extends HeroContent {
/** The giant word across the bottom. @category content */
wordmark?: string | null
/** The scrolling strip under the card. @category content */
claims?: string[] | null
}
// `heading` is three stacked lines; separate them with \n.
const {
heading = 'Real food.\nMade for dogs.\nBetter every day.',
description = 'Fresh, gently cooked meals with the extras dogs need for good digestion, strong joints and a shiny coat.',
primaryAction = { label: 'Shop meals', href: '#' },
media = { src: 'https://picsum.photos/id/237/2000/1400', alt: '', width: 2000, height: 1400 },
wordmark = 'woofly',
claims = ['Real ingredients', 'Vet approved', 'Small batches', 'No fillers', 'Taste tested', 'Made fresh'],
} = Astro.props
---
<Section spacing="small" containerWidth="full">
<!-- Always dark: the text sits on a photo. -->
<div data-theme="dark" class="relative isolate flex min-h-[44rem] flex-col justify-between overflow-hidden rounded-2xl text-neutral md:min-h-[52rem]">
{media && <Image src={media.src} alt={media.alt} width={media.width} height={media.height} fit="cover" loading="eager" class="absolute inset-0 -z-10 size-full" />}
<div class="absolute inset-0 -z-10 bg-[color-mix(in_oklab,var(--background-page)_55%,transparent)]" aria-hidden="true"></div>
<div class="grid gap-8 px-6 pt-16 md:grid-cols-2 md:gap-16 md:px-12 md:pt-28">
<Heading as="h1" size="h1" class="flex flex-col md:items-end md:text-end">{plain(heading).split('\n').map((line) => <span>{line}</span>)}</Heading>
<div class="flex max-w-md flex-col items-start gap-6">
{description && <p class="text-lg">{description}</p>}
{primaryAction && <Button href={primaryAction.href} size="large" rounded>{primaryAction.label}</Button>}
</div>
</div>
{wordmark && <p class="px-4 pb-[0.2em] text-center text-[clamp(5rem,0rem+22vw,22rem)] font-black leading-none tracking-tighter" aria-hidden="true">{wordmark}</p>}
</div>
{claims && (
<div class="mt-2 rounded-full bg-neutral-faded py-2">
<Marquee label="What goes into every meal">
{claims.map((c, i) => <span class:list={['px-4 text-sm', i % 2 === 0 ? 'font-semibold' : 'text-neutral-faded']}>{c}</span>)}
</Marquee>
</div>
)}
</Section>
Show codeHide code
---
/**
* Wordmark photo 01: A rounded photo card with a stacked heading, a line and a round button, a giant wordmark across the bottom and a scrolling strip of claims under it.
*
* @block hero
* @order 61
*/
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 Image from '@/components/apogee/Image/Image.astro'
import Marquee from '@/components/apogee/Marquee/Marquee.astro'
import { plain, type HeroContent } from '@/components/apogee/sections/_shared/spine.ts'
/** @composable */
interface Props extends HeroContent {
/** The giant word across the bottom. @category content */
wordmark?: string | null
/** The scrolling strip under the card. @category content */
claims?: string[] | null
}
// `heading` is three stacked lines; separate them with \n.
const {
heading = 'Real food.\nMade for dogs.\nBetter every day.',
description = 'Fresh, gently cooked meals with the extras dogs need for good digestion, strong joints and a shiny coat.',
primaryAction = { label: 'Shop meals', href: '#' },
media = { src: 'https://picsum.photos/id/237/2000/1400', alt: '', width: 2000, height: 1400 },
wordmark = 'woofly',
claims = ['Real ingredients', 'Vet approved', 'Small batches', 'No fillers', 'Taste tested', 'Made fresh'],
} = Astro.props
---
<Section spacing="small" containerWidth="full">
<!-- Always dark: the text sits on a photo. -->
<div data-theme="dark" class="relative isolate flex min-h-[44rem] flex-col justify-between overflow-hidden rounded-2xl text-neutral md:min-h-[52rem]">
{media && <Image src={media.src} alt={media.alt} width={media.width} height={media.height} fit="cover" loading="eager" class="absolute inset-0 -z-10 size-full" />}
<div class="absolute inset-0 -z-10 bg-[color-mix(in_oklab,var(--background-page)_55%,transparent)]" aria-hidden="true"></div>
<div class="grid gap-8 px-6 pt-16 md:grid-cols-2 md:gap-16 md:px-12 md:pt-28">
<Heading as="h1" size="h1" class="flex flex-col md:items-end md:text-end">{plain(heading).split('\n').map((line) => <span>{line}</span>)}</Heading>
<div class="flex max-w-md flex-col items-start gap-6">
{description && <p class="text-lg">{description}</p>}
{primaryAction && <Button href={primaryAction.href} size="large" rounded>{primaryAction.label}</Button>}
</div>
</div>
{wordmark && <p class="px-4 pb-[0.2em] text-center text-[clamp(5rem,0rem+22vw,22rem)] font-black leading-none tracking-tighter" aria-hidden="true">{wordmark}</p>}
</div>
{claims && (
<div class="mt-2 rounded-full bg-neutral-faded py-2">
<Marquee label="What goes into every meal">
{claims.map((c, i) => <span class:list={['px-4 text-sm', i % 2 === 0 ? 'font-semibold' : 'text-neutral-faded']}>{c}</span>)}
</Marquee>
</div>
)}
</Section>Add
npx @ava-ui/apogee add hero/wordmark-photo-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.