Skip to content

easy-web-content-blocks

Terminal window
pnpm add @easy-web/content-blocks

@easy-web/content-blocks ships twenty-two pure-Astro components covering every standard page section: four header variants with responsive mobile menus, a footer, a theme toggle, a language switcher, hero/CTA/contact sections, structural helpers (Section, CardGrid, Card), a CMS-driven gallery system with six rendering variants, blog post cards, legal prose layouts, and notice banners. All components use the --ew-* design tokens from @easy-web/theme-core and carry no styling system of their own.

Components are not exported from a JavaScript barrel. Import each one from its component path using the ./components/* exports map:

---
import Header from '@easy-web/content-blocks/components/Header';
import Footer from '@easy-web/content-blocks/components/Footer';
---
  • astro >= 6.0.0
  • @easy-web/theme-core — provides the --ew-* token CSS
src/layouts/Base.astro
---
import '@easy-web/theme-core/tokens.css';
import { noFlashScript } from '@easy-web/theme-core';
import Header from '@easy-web/content-blocks/components/Header';
import Footer from '@easy-web/content-blocks/components/Footer';
---
<html lang="de">
<head>
<script is:inline set:html={noFlashScript}></script>
</head>
<body>
<Header siteName="My Site" navItems={navItems} currentLang="de" pathname={pathname} />
<slot />
<Footer siteName="My Site" legalLinks={legalLinks} />
</body>
</html>

Sticky responsive header with an actions slot. Mobile hamburger menu via scoped vanilla JS. Default slot fallback renders ThemeToggle + LanguageSwitch automatically.

Import: import Header from '@easy-web/content-blocks/components/Header'

| Prop | Type | Required | Default | | :--- | :--- | :---: | :--- | | siteName | string | ✓ | — | | navItems | NavItem[] | ✓ | — | | currentLang | string | ✓ | — | | pathname | string | ✓ | — | | alternateHref | string | | — | | menuLabel | string | | — |

Variants: HeaderCentered, HeaderHideOnScroll, HeaderFlyout — same props, different layout behaviours. HeaderFlyout additionally supports children[] on nav items for dropdown panels.

---
import Header from '@easy-web/content-blocks/components/Header';
import HeaderFlyout from '@easy-web/content-blocks/components/HeaderFlyout';
---
<!-- Standard header -->
<Header siteName="My Site" navItems={navItems} currentLang="de" pathname={pathname} />
<!-- Header with custom actions slot (replaces ThemeToggle + LanguageSwitch) -->
<Header siteName="My Site" navItems={navItems} currentLang="de" pathname={pathname}>
<fragment slot="actions">
<MyLoginButton />
</fragment>
</Header>
<!-- Flyout header with nested nav items -->
<HeaderFlyout siteName="My Site" navItems={navItemsWithChildren} currentLang="de" pathname={pathname} />

Simple copyright footer with legal-link navigation.

Import: import Footer from '@easy-web/content-blocks/components/Footer'

| Prop | Type | Required | | :--- | :--- | :---: | | siteName | string | ✓ | | legalLinks | Array<{ label: string; href: string }> | ✓ |

Light / dark / system three-way toggle button. Reads and writes theme state via @easy-web/theme-core.

Import: import ThemeToggle from '@easy-web/content-blocks/components/ThemeToggle'

No props. Renders a trio of buttons; the active state is reflected via data-theme on <html>.

DE ↔ EN switcher. If alternateHref is provided, links there directly; otherwise infers the alternate path from pathname.

Import: import LanguageSwitch from '@easy-web/content-blocks/components/LanguageSwitch'

| Prop | Type | Required | | :--- | :--- | :---: | | currentLang | string | ✓ | | pathname | string | ✓ | | alternateHref | string | |

Full-width page banner with optional CTA button.

Import: import Hero from '@easy-web/content-blocks/components/Hero'

| Prop | Type | Required | Notes | | :--- | :--- | :---: | :--- | | title | string | ✓ | — | | subtitle | string | | — | | ctaLabel | string | | Both ctaLabel and ctaHref must be present for the button to render | | ctaHref | string | | — | | variant | 'centered' \| 'left-aligned' | | — |

---
import Hero from '@easy-web/content-blocks/components/Hero';
---
<Hero
title="Willkommen"
subtitle="Der beste Ort für Harley-Davidson Vermietung in Florida"
ctaLabel="Jetzt buchen"
ctaHref="#booking"
variant="centered"
/>

Standalone call-to-action banner with optional body text.

Import: import CtaSection from '@easy-web/content-blocks/components/CtaSection'

| Prop | Type | Required | Notes | | :--- | :--- | :---: | :--- | | heading | string | ✓ | — | | buttonLabel | string | ✓ | — | | buttonHref | string | ✓ | — | | body | string | | — | | variant | 'default' \| 'muted' \| 'primary' | | primary inverts to brand colour |

Centered mailto-based contact CTA. Button href is mailto:${email}.

Import: import ContactSection from '@easy-web/content-blocks/components/ContactSection'

| Prop | Type | Required | | :--- | :--- | :---: | | heading | string | ✓ | | email | string | ✓ | | buttonLabel | string | ✓ | | body | string | |

Generic slot-based content wrapper. id enables anchor-link targets.

Import: import Section from '@easy-web/content-blocks/components/Section'

Responsive CSS grid (3 → 2 → 1 columns by breakpoint). Slot in Card children.

Import: import CardGrid from '@easy-web/content-blocks/components/CardGrid'

Content card. Renders as <a> when href is provided, plain <div> otherwise. Image is loading="lazy".

Import: import Card from '@easy-web/content-blocks/components/Card'

| Prop | Type | Required | | :--- | :--- | :---: | | title | string | ✓ | | description | string | | | href | string | | | image | string | | | imageAlt | string | |

---
import Section from '@easy-web/content-blocks/components/Section';
import CardGrid from '@easy-web/content-blocks/components/CardGrid';
import Card from '@easy-web/content-blocks/components/Card';
---
<Section id="services">
<h2>Unsere Leistungen</h2>
<CardGrid>
<Card title="Tagestouren" description="Entdecken Sie Florida auf zwei Rädern." href="/touren" />
<Card title="Wochenend-Pakete" description="Drei Tage, ein Abenteuer." href="/pakete" />
<Card title="Gruppen-Buchungen" description="Für Gruppen ab 4 Personen." href="/gruppen" />
</CardGrid>
</Section>

The gallery system is CMS-driven. Content authors create gallery entries in src/content/galleries/; pages load the entry and pass it to GallerySection, which dispatches to the correct variant based on entry.data.kind.

src/content.config.ts
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { gallerySchema } from '@easy-web/content-blocks/schemas/galleries';
const galleries = defineCollection({
loader: glob({ pattern: '**/*.{yml,yaml}', base: './src/content/galleries' }),
schema: gallerySchema,
});
export const collections = { galleries };

Dispatcher component. Renders the correct variant based on entry.data.kind. Renders nothing when entry is null or undefined.

Import: import GallerySection from '@easy-web/content-blocks/components/GallerySection'

---
import GallerySection from '@easy-web/content-blocks/components/GallerySection';
import { getEntry } from 'astro:content';
const heroEntry = await getEntry('galleries', 'home-hero-de');
---
<GallerySection entry={heroEntry} />

| kind | Component | Use case | | :--- | :--- | :--- | | image-grid | GalleryImageGrid | Static N-column uniform grid | | hero-slider | GalleryHeroSlider | Full-bleed editorial carousel | | carousel | GalleryCarousel | Constrained-width image slider | | masonry-grid | GalleryMasonryGrid | Pinterest-style variable-height grid | | feature-highlight | GalleryFeatureHighlight | Alternating image+text rows | | lightbox-grid | GalleryLightboxGrid | Grid with click-to-expand modal |

All gallery components are also importable directly if you want to bypass the CMS entry abstraction.

Linked card for blog index pages. Date is formatted with Intl.DateTimeFormat using the provided locale.

Import: import BlogPostCard from '@easy-web/content-blocks/components/BlogPostCard'

| Prop | Type | Required | | :--- | :--- | :---: | | title | string | ✓ | | description | string | ✓ | | href | string | ✓ | | pubDate | Date | ✓ | | heroImage | string | | | heroImageAlt | string | | | locale | string | |

Narrow content wrapper for impressum / datenschutz / AGB pages. Constrains line-length for legal prose.

Import: import LegalLayout from '@easy-web/content-blocks/components/LegalLayout'

Slot-based — place your Markdown content or <Prose> inside.

Styled wrapper that applies --ew-* typography tokens to raw HTML elements (<h1>–<h6>, <p>, <ul>, <a>, <blockquote>, <code>, etc.).

Import: import Prose from '@easy-web/content-blocks/components/Prose'

---
import LegalLayout from '@easy-web/content-blocks/components/LegalLayout';
import Prose from '@easy-web/content-blocks/components/Prose';
import { Content } from '../content/impressum.md';
---
<LegalLayout>
<Prose>
<Content />
</Prose>
</LegalLayout>

Banner notice for draft content. Slot-based; pass a short status message.

Import: import DraftBanner from '@easy-web/content-blocks/components/DraftBanner'

Banner notice shown when a page is displayed in the non-current locale or has a partial translation.

Import: import LanguageNotice from '@easy-web/content-blocks/components/LanguageNotice'

All components reference @easy-web/theme-core CSS custom properties exclusively. To re-skin, override --ew-* variables on :root (or a scoped ancestor) — do not edit the component files. Class names follow BEM with an ew- prefix (e.g. .ew-hero, .ew-hero__title, .ew-hero--centered).