@kblack0610/site-shell (0.1.0)
Installation
@kblack0610:registry=npm install @kblack0610/site-shell@0.1.0"@kblack0610/site-shell": "0.1.0"About this package
@kblack0610/site-shell
The marketing-site shell shared by blacknbrownstudios.com, kennethblack.me and kblack.dev: nav, footer, hero, CTA, cards, metadata builders, and a CSS-variable theming layer.
Separate from @blacknbrownstudios/ui on purpose. ui is dual-target and ships a React Native entry consumed by four Expo apps; these components import next/link, next/navigation and next-themes. Putting them in ui would place Next in the import graph of every native build.
Install
pnpm add @kblack0610/site-shell
Peers: react, next, next-themes, lucide-react. tailwindcss is an optional peer, needed only for ./tailwind.
Entry points
| Import | Contents |
|---|---|
@kblack0610/site-shell |
Server components: SiteShell, SiteFooter, HeroSection, CTASection, SectionBand, SectionHeading, ItemCard, TagBadge, TagList, SocialLinkRow, cn |
@kblack0610/site-shell/client |
SiteHeader, ThemeModeToggle, SiteThemeProvider |
@kblack0610/site-shell/theme |
defineSiteTheme, siteThemeToCssVars, types |
@kblack0610/site-shell/tailwind |
withSiteShell, siteShellPreset, contentGlobs, emittedClasses |
@kblack0610/site-shell/metadata |
siteMetadata, pageMetadata |
The root entry carries no "use client" banner, so ItemCard can take a ReactNode icon from a server page. Anything interactive lives in /client.
Tailwind: use the wrapper, not the preset
// tailwind.config.js
const { withSiteShell } = require('@kblack0610/site-shell/tailwind');
const { myTheme } = require('./src/site.theme.cjs');
module.exports = withSiteShell({
siteShell: { themes: [myTheme], defaultThemeId: 'my-site' },
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
});
Two rules, both enforced at runtime:
-
Do not set
darkModein your config. The preset owns it (class). Tailwind lets an app-leveldarkModeclobber a preset's, which silently reverts dark mode to the OS setting and makes every theme toggle a no-op.withSiteShellthrows rather than let that happen. -
Do not hand-write the package's content glob. Tailwind v3 presets replace
content, they do not merge it, so a preset cannot contribute file globs.withSiteShellconcatenates them for you. A written-out./node_modules/...path is also wrong under pnpm's isolated linking.
Theming
Each site passes a SiteTheme; the preset emits CSS variables under [data-site-theme="<id>"] and .dark, and maps them into Tailwind colours. Set the id once on <html>:
<html lang="en" data-site-theme="my-site" suppressHydrationWarning>
Components address semantic tokens only and never write dark: variants: bg-surface, bg-surface-alt, bg-surface-raised, text-content, text-content-muted, text-content-subtle, border-border-token, plus the brand-* and accent-* scales. That is what lets one component render correctly across sites with unrelated palettes.
Colours are emitted as r g b channel triplets, not hex. A var() holding a hex makes bg-brand-500/20 compile to rgb(#78716c / 0.2), which is invalid, so the browser drops the declaration and the element renders fully transparent.
Verifying a class survived the build
A purged class leaves the markup byte-identical and changes only the pixels, so an HTML diff cannot catch it. emittedClasses lists the vocabulary to assert against the compiled CSS:
const { emittedClasses } = require('@kblack0610/site-shell/tailwind');
No theme-init script
next-themes already injects its own blocking inline script, so a hand-rolled FOUC IIFE in your layout is redundant: delete it. SiteThemeProvider passes attribute={['class', 'data-mode']}, so the .dark class and data-mode stay in sync on every toggle. The hand-rolled scripts set data-mode once at load and never again, which is why [data-mode='dark'] CSS only responded to a reload.
Dependencies
Dependencies
| ID | Version |
|---|---|
| clsx | ^2.1.1 |
| tailwind-merge | ^2.6.0 |
Development dependencies
| ID | Version |
|---|---|
| @types/react | ^19.2.0 |
| lucide-react | ^0.554.0 |
| next | ^16.1.1 |
| next-themes | ^0.4.6 |
| playwright | ^1.55.1 |
| react | 19.2.4 |
| tailwindcss | ^3.4.17 |
| tsup | ^8.0.0 |
| typescript | 5.8.3 |
| vitest | ^4.0.16 |
Peer dependencies
| ID | Version |
|---|---|
| lucide-react | >=0.400.0 |
| next | >=15.0.0 <17 |
| next-themes | ^0.4.6 |
| react | >=19.0.0 <20 |
| tailwindcss | ^3.4.0 |