Reusable blocks
Components
Drop-in components built on framework tokens. Tone + size variants where they matter. Pages compose Sections inside the bare <main>; the closing CTA comes from the layout. For raw color / spacing / type tokens, see Style guide.
Navigation2
Sections7
Section
The page building block. Full-bleed outer owns gutter + rhythm, inner wrapper owns the width. Props: eyebrow?, title?, sub?, level: 2 | 3,width: "site" | "wide" | "content" | "narrow" | "prose", align: "start" | "center",tone: "plain" | "raised" | "brand" | "inverse", space: "default" | "tight" | "none",id?, plus any <section> attribute. Slots: default, aside. Renders a <section> when a title is present, otherwise a <div>.
tone="plain" · eyebrow + title + sub
Eyebrow
A plain section
No background. Width defaults to site (--site-width, the header pill's column); rhythm is --section-space-md top and bottom.
Default-slot content follows the head. Unclassed lists, paragraphs and tables pick up the base defaults.
tone="raised" · width="narrow" · space="tight"
Raised, narrow, tight
Background --surface-raised (the body already sits on --neutral-ultra-light, so that shade would be invisible). Inner width --content-width-sm.
Use space="tight" when two sections of the same tone sit next to each other.
tone="brand" · aside slot
Brand tone
Head with an aside
The aside sits beside the copy from the medium breakpoint up, bottom-aligned; below that it stacks under the sub.
Typical use: a section title on the left, a "See all" link or a filter on the right.
Hero
Props: eyebrow?, title, lede?,align: "start" | "center", tone: "default" | "dark" | "brand",level?: 1 | 2. Slot: actions. Full-bleed band; as the first block in main it clears the fixed header by itself.
tone="default" · align="start"
Default hero
A starter you can actually launch from.
Opinionated defaults, OKLCH color engine, fluid type. Edit override.scss, you're done.
tone="brand" · align="center"
Brand tone
Build something worth shipping.
Primary ultra-light background, full-bleed band, generous padding.
tone="dark" · align="center"
Dark tone
For high-contrast moments.
Neutral ultra-dark background, primary-light eyebrow, inverted text.
CTA
The closing call-to-action on every page is the layout's SiteCta — aSection with align="center" and width="narrow", rendered after<main>. Pages do not compose it; they pass cta to Base(hide, or title / body / primary / secondary overrides).SiteCta props: title, body, primary, secondary({label, href}), tone: "brand" | "inverse".
CTA below is the in-page block for mid-page prompts. Props: title, body?,tone: "default" | "brand" | "dark" | "accent",align: "start" | "center". Slot: actions.
SiteCta · tone="inverse" (layout-owned; shown here with overrides)
CTA · tone="default"
tone="brand"
Make it yours in minutes.
Tune brand colors in override.scss — the whole palette rebuilds via OKLCH.
tone="accent"
Got feedback?
Tell us what's missing — astrobase grows from real project feedback.
tone="dark"
Still reading?
Clone the repo and see how little configuration it takes to ship something clean.
Feature grid
Props: features (array of {icon?, title, body}),columns: 2 | 3 | 4, align: "start" | "center",tone: "default" | "muted".
columns=3 · align="start"
OKLCH palette
10 families × 8 shades, generated perceptually uniform from hue alone.
Fluid scale
Spacing + type interpolate between mobile and desktop via clamp().
One file tuning
Edit override.scss, the whole system rebuilds. Framework file stays untouched.
columns=3 · align="center" · tone="muted"
OKLCH palette
10 families × 8 shades, generated perceptually uniform from hue alone.
Fluid scale
Spacing + type interpolate between mobile and desktop via clamp().
One file tuning
Edit override.scss, the whole system rebuilds. Framework file stays untouched.
Astro native
SCSS compiled by Astro — no extra build step, no runtime CSS-in-JS.
Dark mode
Flip the palette via [data-theme='dark']. All components follow automatically.
Three layers
framework.scss upstream; override.scss is your tuning surface; base.scss is opt-in reset.
columns=4
OKLCH palette
10 families × 8 shades, generated perceptually uniform from hue alone.
Fluid scale
Spacing + type interpolate between mobile and desktop via clamp().
One file tuning
Edit override.scss, the whole system rebuilds. Framework file stays untouched.
Astro native
SCSS compiled by Astro — no extra build step, no runtime CSS-in-JS.
Stats
Props: stats (array of {value, label, hint?}),columns: 2 | 3 | 4, align: "start" | "center",tone: "default" | "muted". Uses <dl>/<dt>/<dd> + tabular-nums for aligned values.
columns=4 · align="start"
- Color tokens
- 10×8
- Families × shades generated from hue + lightness + chroma.
- Body baseline
- 18px
- Fluid to 16px on mobile. Type scale 1.25 / 1.2.
- Versioned
- SemVer
- Every framework.scss change bumps the version; history in css-framework/CHANGELOG.md.
- Runtime CSS
- 0ms
- Static SCSS → plain CSS. Zero CSS-in-JS overhead.
columns=4 · align="center" · tone="muted"
- Color tokens
- 10×8
- Families × shades generated from hue + lightness + chroma.
- Body baseline
- 18px
- Fluid to 16px on mobile. Type scale 1.25 / 1.2.
- Versioned
- SemVer
- Every framework.scss change bumps the version; history in css-framework/CHANGELOG.md.
- Runtime CSS
- 0ms
- Static SCSS → plain CSS. Zero CSS-in-JS overhead.
Testimonial
Props: quote, author, role?,avatar?, tone: "default" | "muted" | "brand" | "dark".
tone="default"
The OKLCH palette is the cleanest color system I've shipped against. Edit one hue, the whole palette re-derives.
tone="brand"
Fluid type and spacing out of the box. I stopped writing media queries and started writing features.
tone="dark"
Clone, tune override.scss, push. First marketing site of the year that didn't turn into a config swamp.
Logo cloud
Props: title?, logos (array of {name, src?, href?}),align: "start" | "center", tone: "default" | "muted". Text fallback when src omitted.
align="center"
Trusted by teams at
- Acme
- Corta
- Nimbus
- Folio
- Relay
- Pilot
align="start" · tone="muted"
Powering product launches
- Acme
- Corta
- Nimbus
- Folio
- Relay
- Pilot
Content3
Card
Props: eyebrow?, title?, href?,tone: "default" | "muted" | "brand",padding: "md" | "lg". Slots: default (body), media.
Default trio
Guide
Getting started
Clone astrobase, run npm install, tune override.scss, and you're shipping.
Reference
Style guide
Every token live-rendered — colors, spacing, type, radius, and primitives.
Framework
Three layers
framework.scss is upstream. override.scss is yours. base.scss is opt-in.
tone="brand" · pad-lg
Launch
Ready when you are
Primary-tinted card for emphasis. Use sparingly — hero piece of a grid.
Accordion
Props: items (array of {question, answer}),exclusive? (only one item open at a time — native <details name> group, Chromium 120+/Safari 17+). Uses native <details> so it works without JS.
default (independent)
Can I use a different font?
@font-face block in main.scss and update the $font-family-* tokens. The framework itself is font-agnostic.How do I customize the palette?
$color-bases map in override.scss. Each family generates a full 8-shade scale from a single hue, lightness, and chroma in OKLCH.Do I style each component twice for dark mode?
[data-theme="dark"] flips the whole shade palette; any component that uses palette tokens follows automatically — no per-component overrides required.What do I change when cloning astrobase?
override.scss for your brand, build pages under src/pages, and point a deploy webhook at main. That's the whole setup.exclusive=true
Can I use a different font?
@font-face block in main.scss and update the $font-family-* tokens. The framework itself is font-agnostic.How do I customize the palette?
$color-bases map in override.scss. Each family generates a full 8-shade scale from a single hue, lightness, and chroma in OKLCH.Do I style each component twice for dark mode?
[data-theme="dark"] flips the whole shade palette; any component that uses palette tokens follows automatically — no per-component overrides required.What do I change when cloning astrobase?
override.scss for your brand, build pages under src/pages, and point a deploy webhook at main. That's the whole setup.Alert
Props: variant: "info" | "success" | "warning" | "danger",title?, icon? (overrides default glyph). Static content, so no live-region role is set; pass role="alert" orrole="status" through when you inject one after load.
All four variants
Heads up
This is an info alert. Rendered statically, so it carries no live-region role.
Deploy complete
Your site is live at astrobase.florianstangl.com.
Pending action
The latest framework minor changed the default palette. Review your override.scss.
Build failed
SCSS compilation errored at framework.scss:352. Check the CI logs.
Title only (no body)
A new framework version is live.
All systems operational.
Primitives2
Badge
Props: variant (9 families, all soft by default),size: "sm" | "md", solid?.
Soft (default) — 9 variants
Solid
Sizes