Emdash source with visual editor image upload fix
Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
This commit is contained in:
180
templates/marketing/src/components/blocks/Hero.astro
Normal file
180
templates/marketing/src/components/blocks/Hero.astro
Normal file
@@ -0,0 +1,180 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user