maverick-wave-astro
v1.14.0
Published
Shared frame for static Astro homepages built with MaverickWave: layout, SEO, design variants, schedule, building blocks.
Maintainers
Readme
maverick-wave-astro
Shared frame for the Astro homepages built with
MaverickWave. A site keeps its site.ts,
its content and what is truly its own.
Setup
npm install maverick-wave-astro maverick-wave astro// astro.config.mjs
import homepage from "maverick-wave-astro";
import { OG_IMAGE, SITE } from "./src/config/site.ts";
export default defineConfig({
site: "https://example.de",
integrations: [homepage({ site: SITE, ogImage: OG_IMAGE })],
});The integration sets the CSP-safe build options, the @ alias, the sitemap,
/design.css, /theme.js and the MaverickWave script. site.ts is typed with
satisfies Site (src/types.ts). Colours and fonts stay in the site's
main.scss.
After the build it writes robots.txt (site.aiCrawlers), llms.txt and, with
site.email, /.well-known/security.txt - only for a site at the root of its
host, a file in public/ wins. It warns about titles over 60 characters,
descriptions outside 140-160, pages without exactly one <h1> and images over
200 KB.
Design
design in site.ts takes the semantic values from src/design.ts, e.g.
{ corners: 'even', buttonShape: 'pill', spacing: 'airy' }, or the text from
the setup box of the MaverickWave overlay. The werkbank section with
schemas/design.schema.json overrides it value by value; a site not yet moved
there uses src/content/design.json.
Layout
The site wraps BaseLayout once and imports its stylesheet there:
<Frame {...Astro.props}>
<slot name="head" slot="head" />
<slot name="header" slot="header" />
<slot />
<slot name="footer" slot="footer" />
<Scripts slot="scripts" />
</Frame>Slot banner sits below the header; htmlData puts data-* on <html>.
bodyClass adds to site.bodyClass; bare renders only the default slot into
<body> - no skip link, no <main>, no header or footer.
AppShell is the frame of an app page: render it inside BaseLayout bare, it
brings its own skip link. nav groups AppLinks, tabbar takes the phone's
destinations, the aside slot a side panel, persist remembers rail and aside.
The toggles come from the MaverickWave script.
Components
- Frame:
Header,Footer(social links and notes above a bar with copyright, credit and the legallinks;dark,compact),ThemeToggle,LanguageSwitch,Announcement,PromoBar - Sections:
Hero(variantstart | fade),SplitHero(text beside a photo, no parallax),Parallax(a picture band between sections),SectionHead,IconCard,ValueList,OpeningHours,Address,Accordion,Calendar,CalendarLegend,EmptyState(inline) - Blocks:
BentowithBentoItem(sizewide | tall | lg | full),ActionBar(call, route, booking),Marquee,Compare,Statin aStatGroup,Meter,ProgressRing,Rating,Chat,Pagination - Options:
Modaldrawer(end | start),Progresswithoutvalueruns indeterminate,Timelinevariant="compact",Pictureshape(arch | signature | leaf),TablehoverandstickyFirst - Pages:
NotFound,LegalPage,ImprintCard,PrivacyCard Schedulehides expired entries in the browser (data-expires,data-starts,data-schedule-list,data-day)
Helpers live in src/ and import as maverick-wave-astro/<file>.
Presentations
A presentation is one JSON object typed Deck (maverick-wave-astro/deck):
format (landscape | portrait), brand, and slides with layout, tone,
image or drawing, head texts and blocks (facts, figures, list, chips,
steps, notes, jumps, table, packages, images, quote, price, text, actions,
swipe, chart). Deck renders it as a page of its own; the site wraps it once to
bring its stylesheet, like BaseLayout:
---
import "@/styles/main.scss";
import Frame from "maverick-wave-astro/Deck.astro";
---
<Frame deck={Astro.props.deck}><slot /></Frame>DeckFrame embeds such a page in an iframe (slot = text beside it), DeckCard
links to it. SCSS: components/deck and components/deck-frame.
Deck checks the JSON while building (validateDeck) and stops with the path
of every problem - an unknown block type, a jump to a missing slide id, a
drawing not in public/. The chrome over the slides wears the second colour;
accent: 'primary' keeps it to the first, design.accent: 'single' does so
site-wide.
Occasions
Seasonal decoration on the cards, panels and testimonials - snow, a garland,
leaves, bunting. Drawn by MaverickWave (components/occasions), switched on by
a file: with src/content/occasions.json, or a werkbank section with an
occasions.schema.json, the integration sets data-mw-occasions on <html>
and serves /occasions.js, MaverickWave's head script, as the first script in
<head>. It picks today's entry before the first paint.
{
"motion": "dynamic",
"size": "sm",
"entries": [
{
"effect": "christmas",
"from": "2026-12-18",
"until": "2026-12-26",
"yearly": true
}
]
}motion (static | dynamic) and the optional size (sm | lg) hold for
every entry. Needs maverick-wave 5.34 or later.
Werkbank
werkbank/struktur.json lists the languages and the editable sections with
their schemas; a site not yet moved has contentmanager/sections.json.
schemas/design.schema.json, schemas/promo.schema.json and
schemas/occasions.schema.json ship with the package. Put
mwa-check && astro build in the build script so a broken file stops the
build - it knows the formats date, email, uri and the keywords x-widget,
x-item-title, x-labels, x-empty, x-not-before, x-groups,
x-newest-first, x-translate. Photos uploaded in the werkbank land in
public/images/uploads/; the integration derives their sizes before the build
(maverick-wave-astro/uploads), skips a file sharp cannot read and removes the
originals from dist/, since they still carry EXIF and GPS. Tooling: re-export
maverick-wave-astro/eslint and /prettier.
Pages read sections and the texts of the code through
maverick-wave-astro/werkbank:
// src/texts.ts
const de = { hero: { title: "Willkommen" } };
const en: typeof de = { hero: { title: "Welcome" } };
export const textsFor = defineTexts<typeof de>({ de, en });
// a component
const { hero } = textsFor(Astro.url);
const workshops = content<Workshops>("workshops", Astro.url);A section comes from src/content/werkbank/<schluessel>.json, which the
werkbank writes before every build, or else from its start content - so
astro dev works without the werkbank. A language without texts, or a text
missing in one language, stops the build. The first language lives at /, every
other one under /<code>/; localized, alternates and switcher (the props
of LanguageSwitch) take Astro.url.
Releasing
tools/install-local.sh <site>|--allpacks and installs into sites without publishing./release.shpublishes the version frompackage.json, thentools/update-all.sh <version>moves all sites and builds each one
