@chronogrove/ui
v0.85.5
Published
Chronogrove Theme UI theme, color mode helpers, and shared UI primitives
Readme
@chronogrove/ui
Shared Theme UI theme, color-mode (light/dark) helpers, Emotion cache utilities, and small Gatsby-independent components used by gatsby-theme-chronogrove.
Install
In this monorepo the theme uses workspace:*. In another project (after publish):
pnpm add @chronogrove/uiUse pnpm publish for releases so workspace: dependencies in dependents are rewritten; see pnpm workspaces — publishing.
Shared dependencies with gatsby-theme-chronogrove: both packages depend on Theme UI, Emotion, three (where WebGL backgrounds or artwork import it), and related libraries, with versions driven by the root pnpm catalog. When you bump those catalog entries, update packages/ui, packages/theme, and any other workspace package.json files that reference catalog: for the same keys in the same change so installs stay aligned and you avoid duplicate or mismatched trees.
Subpath exports
Prefer deep imports so bundles stay lean:
| Import path | Contents |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| @chronogrove/ui | ChronogroveThemeProvider |
| @chronogrove/ui/theme | Default Theme UI theme object + named exports |
| @chronogrove/ui/provider | ChronogroveThemeProvider |
| @chronogrove/ui/color-mode | Storage key, reconcile event, SSR inline builders, chronogroveHeadTheme (RSC-safe), resolveChronogroveSurfaceColors, useDocumentColorModeSurface, browser sync, reconcileThemeUiColorModeOnNavigation |
| @chronogrove/ui/animated-page-background | ChronogroveAnimatedPageBackground — same stack as the Gatsby home: fixed z-index: 0, light = solid theme background, dark = three.js Color Bends + scroll-linked gradient overlay and parallax (three is a dependency of this package). Animation timing uses THREE.Timer (not deprecated Clock, three.js r183+). |
| @chronogrove/ui/color-bends | ColorBends — lower-level three.js gradient background used inside ChronogroveAnimatedPageBackground. Prefer the full animated-page-background or @chronogrove/ui/next shell unless you need the raw component. |
| @chronogrove/ui/next | Next.js App Router helpers: ChronogroveNextRootLayoutHead (RSC <head> injections), ChronogroveNextEmotionRegistry, ChronogroveNextAppShell (theme + three.js background + surface sync + soft-nav reconcile), ChronogroveNextThemeUiColorModeRouteSync (standalone). Requires next (peer, optional for the rest of the package). |
| @chronogrove/ui/action-card-layout | actionCardPinnedLayoutSx — layout sx for Card variant="actionCard" (matches GitHub pinned cards). |
| @chronogrove/ui/emotion-cache | createChronogroveEmotionCache, getChronogroveEmotionCache |
| @chronogrove/ui/button | Theme UI components button |
| @chronogrove/ui/color-toggle | Theme UI + @theme-toggles/react Expand |
| @chronogrove/ui/color-toggle-styles | CSS for the toggle (Expand.css + sizing); @import once in global CSS (see examples/chronogrove-next/app/globals.css) |
| @chronogrove/ui/skip-nav | SkipNavLink, SkipNavContent |
| @chronogrove/ui/is-dark-mode | colorMode === 'dark' helper |
| @chronogrove/ui/color-utils | hexToRgb, hexToRgba, BUTTON_PRIMARY_COLORS |
| @chronogrove/ui/action-button | Outline CTA as <button> or <a> |
| @chronogrove/ui/pagination-button | Compact paginator control |
| @chronogrove/ui/lazy-load | Defer children until in viewport (react-intersection-observer) |
| @chronogrove/ui/header | Masthead shell (variant: styles.Header) |
| @chronogrove/ui/page-header | Blog-style h1 heading (p-name) |
| @chronogrove/ui/profile-metrics-badge | Metrics row (Badge variant="metrics") for dashboard widget headers |
| @chronogrove/ui/widget-header | Widget section title row (optional Font Awesome icon, aside slot, optional metrics) |
| @chronogrove/ui/gatsby | Color-mode Gatsby SSR/browser helpers |
Additional subpaths: The table lists the most common entry points. Also published: pagination (full bar; composes pagination-button), category-label, metric-badge, metric-card, muted-card-footer, status-card, widget-section (dashboard <section>; when id is passed, tabIndex={-1} defaults so hosts can focus the landmark after hash / in-page navigation), widget-call-to-action, external-link-icon, thumbnail-strip, image-thumbnails (optimizeSrc for CDN resizing; Gatsby passes a Cloudinary helper). The authoritative list is package.json → exports.
Next.js (App Router)
Reference app: examples/chronogrove-next (chronogrove-next). Run pnpm --filter chronogrove-next dev from the repo root.
Prefer @chronogrove/ui/next for the standard wiring: ChronogroveNextRootLayoutHead in <head>, ChronogroveNextEmotionRegistry wrapping the body tree, and ChronogroveNextAppShell (or compose the lower-level exports yourself).
Server vs client
- Server
layout: Keep the root layout a Server Component. Do not import@chronogrove/ui/themehere—it loadstheme-ui’smergeand triggers ReactcreateContext, which Next.js disallows in RSC. UseChronogroveNextRootLayoutHeadfrom@chronogrove/ui/next, or manually passchronogroveHeadThemefrom@chronogrove/ui/color-modetoresolveChronogroveSurfaceColorsand emit in order:<meta name="emotion-insertion-point" content="" />, then the Theme UI no-flash script, HTML background script, and fallback CSS (same composition asbuildThemeUiColorModeHeadComponentsin@chronogrove/ui/gatsby). - Client Emotion registry + theme: Use
ChronogroveNextEmotionRegistryfrom@chronogrove/ui/next, or wrap the app in Emotion’sCacheProviderwith a per-request cache withkey: 'css'and Next’suseServerInsertedHTML(Next.js: CSS-in-JS). Do not rely ongetChronogroveEmotionCache()for SSR—it is a browser-oriented singleton. Import the full theme from@chronogrove/ui/themeonly inside client components. Anything usinguseColorMode,useThemeUI, orChronogroveThemeProvidermust be'use client'. Order:CacheProvider(registry) outsideChronogroveThemeProviderinside<body>. - Document surface (match Gatsby
RootWrapper):ChronogroveNextAppShellcallsuseDocumentColorModeSurfacefrom@chronogrove/ui/color-modeonce. It syncsdocument.documentElement’stheme-ui-*class,data-theme-ui-color-mode, and inline page background from the resolved Theme UI theme (rawColors/colors.background). Without it, Emotion can win the cascade over head fallback CSS and panel tokens (bg: 'panel-background') may not update in dark mode after hydration. - Animated page background (match Gatsby home):
ChronogroveNextAppShellincludesChronogroveAnimatedPageBackground(three.js Color Bends). Content should sit in aposition: relative; z-index: 1wrapper above the fixed background layer. WidgetHeader+ icons: This package depends on@fortawesome/fontawesome-svg-coreand@fortawesome/react-fontawesome. Icon definitions (e.g.faGithub) come from a Font Awesome kit you add to your app—typically@fortawesome/free-brands-svg-iconsfor brands—the same pattern asgatsby-theme-chronogrovewidgets.MetricCardloading state: PassloadingorshowPlaceholder(equivalent) for the built-in pulse placeholder (orloadingSlotto override). The default busy chrome renders as anoutputelement (live-region friendly) rather than arole="status"wrapper.- Soft navigations:
ChronogroveNextAppShellincludesChronogroveNextThemeUiColorModeRouteSync, which callsreconcileThemeUiColorModeOnNavigationafter pathname changes (not on initial mount). Running reconcile on mount can fightuseColorModetoggles. Gatsby’s equivalent isonRouteUpdateThemeUiColorMode(no initialonRouteUpdate). - Hydration: Set
suppressHydrationWarningon<html>and<body>. The inline no-flash / background scripts run before React hydrates and update<html>(theme-ui-*classes,data-theme-ui-color-mode, background), so the DOM no longer matches the server-rendered markup—React would warn without this flag (similar to next-themes on Next.js App Router).
Imports: Prefer @chronogrove/ui/color-mode for head builders and SPA reconcile. Reserve @chronogrove/ui/gatsby for Gatsby hooks (onPreRenderHTML, onRenderBody helpers).
JSX + bundlers: Primitives such as button use Box from @theme-ui/components with an as prop for native elements, so sx works with Gatsby’s classic JSX runtime and Next’s SWC without a Theme UI file pragma. Jest uses babel.config.cjs (automatic JSX).
Global CSS, Prism / third-party CSS, and fonts
Styles load in three layers. Understanding the split prevents accidental duplicates and keeps each host's build minimal.
Layer order
| # | Layer | Mechanism |
| --- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Head — color-mode + Emotion insertion point | Gatsby: packages/theme/gatsby-ssr.js + @chronogrove/ui/gatsby helpers. Next: ChronogroveNextRootLayoutHead from @chronogrove/ui/next. Emit before any <style> tags so Emotion and Theme UI no-flash scripts run first. |
| 2 | Theme UI globals (theme.global) | ChronogroveThemeProvider renders <Global styles={theme.global} /> via Emotion. This applies identical baseline CSS under both Gatsby and Next as long as the provider wraps the app. No extra import needed. |
| 3 | Host global CSS | Each host owns its plain-CSS files. See below for Gatsby and Next specifics. |
Gatsby
The theme's own global CSS entry is the side-effect import block in packages/theme/gatsby-browser.js:
import './src/styles/global.css' // layout, .sr-only, Prism overrides
import 'lightgallery/css/lightgallery.css'
import 'lightgallery/css/lg-thumbnail.css'
import 'lightgallery/css/lg-zoom.css'
import 'prismjs/themes/prism-solarizedlight.css'
import 'prismjs/plugins/line-numbers/prism-line-numbers.css'packages/theme/src/styles/global.css also contains @import '@chronogrove/ui/color-toggle-styles' for the theme toggle animation.
Prism is tightly coupled to gatsby-remark-prismjs (configured in packages/theme/gatsby-config.js). The CSS theme and helper overrides (.gatsby-highlight, line-number padding, etc.) only make sense when that plugin is active. They are intentionally kept in the Gatsby theme rather than in this package.
Sites that shadow or extend the theme can add additional side-effect imports in their own gatsby-browser.js alongside (not instead of) the theme's entry.
Next.js (App Router)
The reference app is examples/chronogrove-next. Its app/globals.css shows the minimal baseline:
/* Required if you use ColorToggle */
@import '@chronogrove/ui/color-toggle-styles';
html {
scroll-behavior: smooth;
}
body {
background-color: var(--theme-ui-colors-background);
-webkit-font-smoothing: antialiased;
}Import globals.css once from the root layout.jsx/layout.tsx — the same file that renders ChronogroveNextRootLayoutHead and ChronogroveNextEmotionRegistry. See examples/chronogrove-next/app/layout.jsx.
For syntax highlighting in a Next MDX pipeline, use whatever highlighter fits your setup (e.g. Shiki via rehype-pretty-code) and load only its CSS. There is no dependency on prismjs in this package, and you should not add the Prism CSS from the Gatsby theme to a Next app unless your MDX pipeline explicitly emits Prism class names.
Fonts
The default Chronogrove theme uses system font stacks only (defined in src/theme.js):
sans: '-apple-system, BlinkMacSystemFont, avenir next, ..., sans-serif'
mono: 'Menlo, Consolas, ..., monospace'No @font-face rules or external font URLs are shipped by this package. Web fonts are host-owned:
- Next.js: use
next/fontto load faces with zero layout shift; then pass the resulting CSS variable or family name into a theme override:{ fonts: { body: 'var(--font-inter), sans-serif' } }. - Gatsby: use
gatsby-plugin-google-gtagor a<link>ingatsby-ssr.js, then extendtheme.fontsin yourgatsby-config.jstheme options.
Changing theme.fonts is the only hook needed; ChronogroveThemeProvider merges the override automatically via Theme UI.
Font Awesome (icons)
@chronogrove/ui depends on @fortawesome/fontawesome-svg-core and @fortawesome/react-fontawesome for WidgetHeader. Icon definitions (e.g. faGithub) come from a kit you add to your own app — see the WidgetHeader + icons bullet in the Next.js section above.
Changelog
Releases are recorded in the repository root CHANGELOG.md.
License
MIT (same as the parent repository).
