Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
181 lines
3.7 KiB
Plaintext
181 lines
3.7 KiB
Plaintext
---
|
|
interface Props {
|
|
node: {
|
|
headline: string;
|
|
subheadline?: string;
|
|
// CTAs are flattened to sibling fields because Block Kit has no
|
|
// object-group element. Empty strings mean "no CTA".
|
|
primaryCtaLabel?: string;
|
|
primaryCtaUrl?: string;
|
|
secondaryCtaLabel?: string;
|
|
secondaryCtaUrl?: string;
|
|
image?: { url: string; alt?: string };
|
|
centered?: boolean;
|
|
};
|
|
}
|
|
|
|
const { node } = Astro.props;
|
|
const { headline, subheadline, image, centered } = node;
|
|
|
|
const primaryCta =
|
|
node.primaryCtaLabel && node.primaryCtaUrl
|
|
? { label: node.primaryCtaLabel, url: node.primaryCtaUrl }
|
|
: undefined;
|
|
const secondaryCta =
|
|
node.secondaryCtaLabel && node.secondaryCtaUrl
|
|
? { label: node.secondaryCtaLabel, url: node.secondaryCtaUrl }
|
|
: undefined;
|
|
---
|
|
|
|
<section class:list={["hero", { "hero-centered": centered, "hero-with-image": !!image }]}>
|
|
<div class="hero-content">
|
|
<h1 class="hero-headline">{headline}</h1>
|
|
{subheadline && <p class="hero-subheadline">{subheadline}</p>}
|
|
{(primaryCta || secondaryCta) && (
|
|
<div class="hero-actions">
|
|
{primaryCta && (
|
|
<a href={primaryCta.url} class="btn btn-primary btn-lg">
|
|
{primaryCta.label}
|
|
</a>
|
|
)}
|
|
{secondaryCta && (
|
|
<a href={secondaryCta.url} class="btn btn-secondary btn-lg">
|
|
{secondaryCta.label}
|
|
</a>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
{image ? (
|
|
<div class="hero-image">
|
|
<img src={image.url} alt={image.alt || ""} loading="eager" />
|
|
</div>
|
|
) : !centered && (
|
|
<div class="hero-visual" aria-hidden="true">
|
|
<img src="/hero-visual.svg" alt="" width="800" height="800" />
|
|
</div>
|
|
)}
|
|
</section>
|
|
|
|
<style>
|
|
.hero {
|
|
max-width: var(--wide-width);
|
|
margin: 0 auto;
|
|
padding: var(--spacing-4xl) var(--spacing-lg);
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: var(--spacing-2xl);
|
|
align-items: center;
|
|
}
|
|
|
|
.hero-centered {
|
|
grid-template-columns: 1fr;
|
|
text-align: center;
|
|
max-width: var(--max-width);
|
|
padding-top: var(--spacing-4xl);
|
|
padding-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.hero-centered .hero-content {
|
|
max-width: 100%;
|
|
}
|
|
|
|
.hero-centered .hero-actions {
|
|
justify-content: center;
|
|
}
|
|
|
|
.hero-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-lg);
|
|
max-width: 560px;
|
|
}
|
|
|
|
.hero-headline {
|
|
font-size: var(--font-size-5xl);
|
|
font-weight: 800;
|
|
line-height: 1.1;
|
|
letter-spacing: -0.03em;
|
|
background: linear-gradient(135deg, var(--color-text) 0%, var(--color-muted) 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.hero-subheadline {
|
|
font-size: var(--font-size-xl);
|
|
line-height: 1.6;
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
.hero-actions {
|
|
display: flex;
|
|
gap: var(--spacing-md);
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.hero-image {
|
|
position: relative;
|
|
}
|
|
|
|
.hero-image img {
|
|
width: 100%;
|
|
height: auto;
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-xl);
|
|
}
|
|
|
|
.hero-image::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: -10px;
|
|
background: linear-gradient(135deg, var(--color-primary-light) 0%, var(--color-accent-light) 100%);
|
|
border-radius: var(--radius-lg);
|
|
z-index: -1;
|
|
opacity: 0.3;
|
|
filter: blur(20px);
|
|
}
|
|
|
|
/* Hero visual (external SVG image) */
|
|
.hero-visual {
|
|
position: relative;
|
|
width: 100%;
|
|
max-width: 550px;
|
|
justify-self: center;
|
|
}
|
|
|
|
.hero-visual img {
|
|
width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
@media (max-width: 1024px) {
|
|
.hero {
|
|
grid-template-columns: 1fr;
|
|
padding: var(--spacing-2xl) var(--spacing-lg);
|
|
gap: var(--spacing-2xl);
|
|
}
|
|
|
|
.hero-headline {
|
|
font-size: var(--font-size-4xl);
|
|
}
|
|
|
|
.hero-subheadline {
|
|
font-size: var(--font-size-lg);
|
|
}
|
|
|
|
.hero-with-image {
|
|
text-align: center;
|
|
}
|
|
|
|
.hero-with-image .hero-actions {
|
|
justify-content: center;
|
|
}
|
|
|
|
.hero-image,
|
|
.hero-visual {
|
|
order: -1;
|
|
}
|
|
}
|
|
</style>
|