Skip to content
AstroCraft Docs
On this theme

Layout

Medice’s page rhythm comes from two classes and one layout component. Once you know them, every band on the site is predictable.

The container and the band

.site-container { @apply mx-auto w-full max-w-6xl px-6 lg:px-8; }
.section        { @apply py-14 md:py-22; }

Every band in the design is py-[88px], which .section expresses as py-22 at md and up, relaxing to py-14 on small screens. The container owns the horizontal gutter; the section owns the vertical rhythm. Nothing else should set page-level padding.

A typical section is therefore:

<section class="section">
  <div class="site-container">
    <!-- content -->
  </div>
</section>

Alternating bands set bg-background-alt on the outer <section>, which is why the container is a separate inner element — the tint runs full-bleed while the content stays in the measure.

Breakpoints

The design’s breakpoint/* values are Tailwind’s defaults, with one addition:

Name Width
xs 400px
sm 640px
md 768px
lg 1024px
xl 1280px
2xl 1536px

xs exists for the narrowest phones, where a two-column card grid still fits but a three-column one does not. It is used sparingly; most responsive work happens at md and lg.

The two layouts

src/layouts/BaseLayout.astro is the whole shell: <!doctype html>, <html lang> from siteSettings, the <head> delegated to BaseHead, and a <body> with a single <slot />. It is the only place global.css is imported.

src/layouts/BaseHead.astro owns everything in <head> — meta, OG, JSON-LD, favicons, the font preload and the sitemap link. See SEO.

Both are intentionally chrome-free. Neither renders a header or a footer; those are Sections/Global/Header.astro and Sections/Global/Footer.astro, composed per page. That means a page can legitimately omit them — the dev-only UI catalog does — without a special layout variant.

BaseLayout’s props are title, description, optional image and noindex, and the SEO pass-throughs schema and article.

Composing a page

---
import BaseLayout from "@layouts/BaseLayout.astro";
import Header from "@components/Sections/Global/Header.astro";
import Footer from "@components/Sections/Global/Footer.astro";
import Hero from "@components/Sections/Home/Hero.astro";
---

<BaseLayout title="…" description="…">
  <Header />
  <main>
    <Hero />
    <!-- more sections -->
  </main>
  <Footer />
</BaseLayout>

Every route file in the theme is this shape. The route owns the URL, the SEO props and the section order; the sections own the markup.

The sticky header

The header is sticky at 114px, and two things in the codebase depend on that number.

.article-prose headings carry scroll-mt-32 so an anchor jump from the “On this page” panel does not land the heading underneath the header. Any other in-page anchor target needs the same treatment.

If you change the header’s height, scroll-mt-32 is what to change with it.

The Global sections

src/components/Sections/Global/ holds the blocks two or more page groups draw. It is worth knowing what is there before building something that already exists:

Header and PrimaryNav · Footer · Wordmark · PageHero and DetailHero · SectionHead and Eyebrow · CtaBand · FilterBar and ListingEmpty · Gallery · Testimonials · Timeline · StepsBand · AccordionBand · ChecklistPanel · FeaturePanel · PricingPanel · ProsePanel · Diagnostics · Differentiators · CredentialGrid · ClinicianGrid · LocationMap · NewsletterBand · HelpNote

A section arrives in Global/ on its second page group, not before, and it brings no page data with it — both callers pass their own strings as props. A Global/ section still reading one page’s config is only half promoted.

The listing band

Three pages draw the same filter band — the specialty grid, the clinician directory and the health library index — and it is one implementation: a search box, a row of selects, a row of shortcut chips, a live count, a grid of cards already in the DOM, and an empty state.

The data-* contract and the matching logic are in Sections/Global/_listing.ts, which is pure and checked by _listing.test.ts. _listingFilter.ts is the half that touches the document, split out because it imports ui/_client, which reads document at import time and so cannot load under plain Node.

Only the facets differ between the three pages. If you add a fourth listing, add its facets — do not copy the band.

Decorative paint

.decorative-glow reproduces the design’s mint glow — an ellipse at 24% under a blur — as a radial-gradient rather than an exported SVG. Same read, no asset, and it follows a rebrand because the color is still the token. It is pointer-events-none and -z-10; size and place it with normal utilities.

.main-text-gradient is the primary-to-mint gradient text used on accent headings and tags.

NEXT STEPComponents