Files
emdash-patch-imageupload/templates/marketing/src/components/blocks/Hero.astro
kunthawat 2d1be52177 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
2026-05-03 10:44:54 +07:00

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>