ComponentsSite blocks

Site shell

The frame of a whole website block: its own look (an accent from one brand colour, plus font, material and density) scoped to the site root, an EN / TR switch and inner pages — all from server-rendered blocks. Links like ?page=pricing, ?page=home#faq and ?lang=tr inside the shell switch in place; a page change scrolls to the top, moves focus to the page’s h1, marks matching links aria-current="page" and follows back/forward. Overlays opened inside keep the site’s theme.

ThemeScope blocks/site/site-shell.tsx, blocks/site/shared/site-accent.ts, blocks/site/shared/site-routing.tstheme · material · accent · density · font
Skip to content

Letterpress studio · Kadıköy

Ink you can feel with a fingertip.

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { SiteShell, SiteShellLocale, SiteShellPage, Navbar, Hero, PageIntro, sitePageHref } from "@vamos-ui/react/blocks";

export default function Site() {
  return (
    <SiteShell accent="#B4532A" font="editorial" material="solid" density="comfortable" pages={["home", "shop"]}>
      <SiteShellLocale code="en">
        <Navbar
          items={[{ label: "Shop", href: sitePageHref("shop") }, { label: "FAQ", href: sitePageHref("home", "faq") }]}
          locales={[{ code: "en", href: "?lang=en" }, { code: "tr", href: "?lang=tr" }]}
          …
        />
        <SiteShellPage id="home"><Hero … /></SiteShellPage>
        <SiteShellPage id="shop"><PageIntro title="Shop" … /></SiteShellPage>
      </SiteShellLocale>
      <SiteShellLocale code="tr">…</SiteShellLocale>
    </SiteShell>
  );
}

Every language and page is rendered on the server; the shell only shows one. For a real multi-route site, route per page and locale and keep SiteShell for the look (or use ThemeScope).

Plain #anchor links keep working on the current page. Links to another page's section use sitePageHref("home", "faq").

Examples

Accent from one colour

siteAccent() is the same OKLCH maths as `vamos-ui brand`: the fill keeps the brand colour where it can, text on it is white or dark ink, and accent ink is darkened (light) or lightened (dark) until it reaches 4.5:1.

Clay · #B4532A

Accent ink for links and highlights

text on fill 4.99:1ink 6.12:1

Mustard · #CA8A04

Accent ink for links and highlights

text on fill 6.41:1ink 5.64:1

Ink blue · #2B4C7E

Accent ink for links and highlights

text on fill 8.62:1ink 7.5:1

EN / TR switch

Each SiteShellLocale renders only while active and carries its own lang attribute. Keep one locale per page for money, dates and times (12- or 24-hour, never both).

Skip to content

Book an appointment

General practice for the whole family. Same-week appointments, Monday to Saturday.

Very dark brand colour on a dark site

Colours too dark for dark surfaces get a lighter dark-mode fill; light sections inside get the brand fill back.

Quarterly review

Your numbers, closed by the 5th

Fixed monthly fee. No hourly billing.

Light section, brand fill

Own page navigation (client)

useSiteShell() gives page, locale and navigate(); SiteShellLink sets aria-current for you.

Interface Days 2027

Two days of design and engineering talks, 14–15 May, Istanbul.

API

SiteShell

PropTypeDefault
accent
A preset name (lime, ocean, ember, rose, graphite, or your own like brand) → data-accent. A colour (#hex, rgb(), oklch()) → the four accent variables on the root, contrast-checked.
string—
accentDark
Explicit dark-mode fill for a colour accent; derived when the colour is too dark.
string—
font
→ data-font (editorial, grotesk, corporate, system or your own).
string—
material
→ data-material. Websites usually want solid.
"glass" | "solid"—
density
→ data-density.
"comfortable" | "compact" | "spacious"—
theme
Force a mode for the whole site. Default: inherit.
"light" | "dark"—
dir
Direction (also for Radix parts).
"ltr" | "rtl"—
locales / defaultLocale
Locale codes; the default is left out of the URL.
string[] / string["en", "tr"] / first
locale / onLocaleChange
Controlled locale.
string / (locale) => void—
pages / defaultPage
Page ids; the default page has a clean URL.
string[] / string["home"] / first
page / onPageChange
Controlled page.
string / (page) => void—
routing
Where the page lives: ?page=, #page= or nowhere (previews, docs). The locale uses ?lang= unless memory.
"search" | "hash" | "memory""search"
pageParam / localeParam
URL keys.
string"page" / "lang"
className
Merged onto the root (vb-scope font-sans text-fg). Add the scene colour and min height here.
string—

SiteShellPage

PropTypeDefault
id
Page id from pages; rendered only while current.
string—
title
document.title after navigating to this page.
string—

SiteShellLocale

PropTypeDefault
code
Locale code; rendered only while current, inside a lang wrapper.
string—

SiteShellLink

PropTypeDefault
page / anchor
Target page and optional in-page anchor; aria-current when it is the current page.
string—

useSiteShell()

PropTypeDefault
returns
For client components inside the shell.
{ page, locale, pages, locales, navigate(to), hrefFor(page, anchor?) }—

Helpers (server-safe)

PropTypeDefault
sitePageHref(page, anchor?)
"?page=shop", "?page=home#faq" — for Navbar, Footer and CTA hrefs in server components.
string—
siteLocaleHref(code)
"?lang=tr".
string—
siteAccent(colour, { dark? })
base, fg, inkLight, inkDark, dark?, measured contrast and warnings. Throws on a non-colour.
SiteAccentPalette—
siteAccentCss(palette, key)
The scoped stylesheet under [data-site-accent="key"] (the shell renders it for you).
string—