Winter collection. Boundary Waters, Minnesota.
Out past the last road.
Shells and sleep systems rated for minus thirty, tested by people who go there on purpose.
Full-bleed image or video with the headline set over it, bottom left, behind a soft scrim.
HeroSection1 dependency Original, MIT
Winter collection. Boundary Waters, Minnesota.
Shells and sleep systems rated for minus thirty, tested by people who go there on purpose.
Using Claude Code? Give it the agent prompt and skip these steps.
foundation.css holds the tokens every component reads.
---
// src/layouts/Layout.astro
import "../styles/foundation.css";
--- ---
import HeroPoster from "../components/viewership/hero-poster/HeroPoster.astro";
---
<HeroPoster /> | Prop | Type | Default | Description |
|---|---|---|---|
title | string | · | Headline. |
accent | string | · | Closing words of the headline. |
body | string | · | Supporting paragraph. |
primary | { label: string; href: string } | · | Main call to action (accent button). |
secondary | { label: string; href: string } | · | Second action. |
caption | string | · | Short line in the top corner. |
as | "h1" | "h2" | "h1" | Heading element. |
slot: media | slot | · | Background image or video, covers the section. |
---
/**
* HeroPoster: full-bleed media with the headline set over it, bottom left.
*
* A scrim keeps the type readable on any image. Text is always light here,
* whatever the theme mode, because it sits on the picture.
*
* @example
* <HeroPoster title="Out past" accent="the last road." primary={{ label: "Shop", href: "/shop" }}>
* <img slot="media" src="/night.webp" alt="" />
* </HeroPoster>
*/
import Button from "../button/Button.astro";
interface Cta {
label: string;
href: string;
}
interface Props {
title: string;
accent?: string;
body?: string;
primary?: Cta;
secondary?: Cta;
/** Short caption in the top corner: a place, a season, a credit. */
caption?: string;
as?: "h1" | "h2";
class?: string;
}
const { title, accent, body, primary, secondary, caption, as: Heading = "h1", class: className } = Astro.props;
---
<section class:list={["vs-hero-poster", className]}>
<div class="vs-hero-poster__media" aria-hidden={Astro.slots.has("media") ? undefined : "true"}>
<slot name="media" />
</div>
<div class="vs-hero-poster__inner">
{caption && <p class="vs-hero-poster__caption">{caption}</p>}
<div class="vs-hero-poster__copy">
<Heading class="vs-display vs-hero-poster__title">{title}{accent && <> <em>{accent}</em></>}</Heading>
{body && <p class="vs-hero-poster__body">{body}</p>}
{
(primary || secondary) && (
<div class="vs-hero-poster__actions">
{primary && <Button href={primary.href} variant="accent">{primary.label}</Button>}
{secondary && <Button href={secondary.href} variant="secondary">{secondary.label}</Button>}
</div>
)
}
</div>
</div>
</section>
<style>
.vs-hero-poster {
position: relative;
container-type: inline-size;
display: grid;
overflow: hidden;
background: #0d1118;
color: #f6f2ea;
font-family: var(--vs-font-sans);
font-stretch: var(--vs-font-stretch);
}
.vs-hero-poster__media {
position: absolute;
inset: 0;
}
.vs-hero-poster__media :global(:is(img, video)) {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.vs-hero-poster__media::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(to top, rgb(5 7 12 / 0.82) 0%, rgb(5 7 12 / 0.35) 45%, rgb(5 7 12 / 0) 75%);
}
.vs-hero-poster__inner {
position: relative;
display: flex;
min-height: clamp(460px, 62cqi, 760px);
flex-direction: column;
justify-content: space-between;
gap: 40px;
width: 100%;
max-width: var(--vs-container);
margin-inline: auto;
padding: clamp(24px, 4cqi, 40px) var(--vs-gutter) var(--vs-section-y);
}
.vs-hero-poster__caption {
margin: 0;
font-size: 0.8125rem;
letter-spacing: 0.02em;
opacity: 0.8;
}
.vs-hero-poster__copy {
max-width: 44rem;
margin-top: auto;
}
.vs-hero-poster__title {
font-size: clamp(2.5rem, 9cqi, 6.5rem);
}
.vs-hero-poster__title :global(em) {
color: inherit;
opacity: 0.75;
}
.vs-hero-poster__body {
max-width: 30rem;
margin: 1.25rem 0 0;
font-size: 1.0625rem;
line-height: 1.55;
opacity: 0.85;
}
.vs-hero-poster__actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 2rem;
}
</style> Original, MIT.