React components and CSS for Hraness apps and product sites: application shells, marketing sections, charts, themes, effects, and syntax highlighting. Built on @hraness/ui.
@hraness/ui supplies the accessible React Aria primitives: actions, form fields, overlays, collections, navigation, and basic surfaces. This package builds on them with application shells, loading and error pages, saved light and dark appearance, charts and instrument controls, haptics, decorative effects, server-side syntax highlighting, plain-site CSS, and a gallery you can run.
Pin a GitHub release tag:
{
"dependencies": {
"@hraness/design-kit": "github:hraness/design-kit#v0.41.2",
"@hraness/ui": "github:hraness/ui#v0.5.17"
}
}@hraness/ui is an explicit peer dependency with the supported range
>=0.5.16 <0.6.0; consumers should pin an immutable compatible release such as
v0.5.17 when using the stylesheet, React, or compiler-adopter entries. The peer is optional at
installation so the framework-neutral root and syntax highlighter can be used
on their own. React 18 or 19 and React DOM 18 or 19 are also peer dependencies.
In an existing React 18 or 19 application with the dependencies above installed, load the complete precompiled stylesheet once in your global CSS file:
@import "@hraness/design-kit/styles.css";If you use Tailwind, put its import first. This route needs no application StyleX compiler. It loads the shared presentation layers and default webfonts; do not combine it with the compiler-adopter route described in Load the presentation layer.
In App.tsx, render a header and your application content:
import { TopBar } from "@hraness/design-kit/react";
export default function App() {
return (
<>
<TopBar title="My workspace" />
<main>
<h1>Saved work</h1>
<p>Your application owns the content and state.</p>
</main>
</>
);
}Run your application's existing development command. You see a solid, non-sticky
header titled “My workspace” above “Saved work”. TopBar renders a semantic
header; it does not supply application routing or a main landmark for you.
- Load styles or adopt the StyleX compiler: choose one stylesheet route.
- Use application compositions: shells, navigation, and surfaces.
- Explain a technical product: marketing compositions and complete examples.
- Publish articles and article copy guidance: article structure and review requirements.
- Show status pages: error and not-found pages.
- Use charts and syntax: application-owned data and code display.
- Appearance and fonts and palette reference: themes and typography.
- Run the gallery or develop the package.
Version 0.41.2 refreshes the @hraness/design-kit/portfolio snapshot from registry commit 75818ad8. Soulscrape's canonical tagline becomes "Learn everything about anyone from their internet presence", inside the 60-character limit the launch kit enforces for the Product Hunt tagline.
Version 0.41.1 gives MarketingInterfaceGrid a shared link slot: pass link: { href, label } on any interface entry and the card renders the link at its bottom edge, aligned to the end, in the muted ink, so every card in the row keeps the same quiet reference link no matter how tall its example runs. Code inside interface cards sets at a smaller size, proof-frame chrome packs tighter, and MarketingProofFrame accepts chrome="terminal" or chrome="window" without a title; the chrome then shows only its window lights, dropping the generic "Terminal" label while keeping a named bar available when a real title is supplied. Existing call sites keep working: link is optional and a title still implies window chrome.
Version 0.41.0 adds product-landscape.css: one product-specific drawing behind
the marketing, documentation, and blog pages. The product supplies a grayscale
luminance mask and the kit tints it from the page's text and background, tiles
it down the page so long pages never run out, swaps in a narrow image on
phones, and hides it in forced colors and print. Mark a page with
data-hraness-landscape="page" (or MarketingPage landscape="page"), turn it
off on application routes with "off", or draw it behind one element with
"contained". Over the drawing, page wrappers clear, code blocks and tables
stay opaque with rounded edges, and small cards and callouts become frosted
panels that turn opaque without backdrop support, under reduced transparency,
and in forced colors. Product elements opt in with
data-hraness-landscape-surface="clear", "solid", or "glass". styles.css
and compiler-foundation.css include the stylesheet; nothing changes on a page
without a host. See Product landscape.
Version 0.40.3 keeps SlopCamera spelled consistently in install labels supplied by @hraness/design-kit/portfolio. The snapshot comes from registry commit 64d51361; technical IDs, commands, and domains are unchanged.
Version 0.40.2 refreshes the @hraness/design-kit/portfolio snapshot from registry commit 26b167cd. It adds the Alt, midi.place, and Textmock products and the runtime:xcb:aicharts:installs and runtime:gobstopper:aicharts:installs relations, which fold the retired contract:xcb:aicharts:exports-sessions-for entry into each installer's detail sentence. Sites that render a "How X uses aicharts" post can now resolve the relation their admission names.
Version 0.40.1 strengthens the dotted underline on embedded article author links during hover and keyboard focus. Account prompts in plain pages retain a readable Create account button and a dotted Sign in link, with visible keyboard focus and system colors in high contrast mode.
Version 0.40.0 keeps every StepThrough slide at the full height of its
frame. A slide's window grows to the stage even when product markup wraps it,
and its body takes the extra room, so a shorter slide never leaves an empty
band below it. On wide screens the stage is at least as tall as the step list,
so the selected step never hangs below the preview. A window inside a wrapper
no longer draws a second border inside the panel.
Step hints should be one sentence of at most 60 characters, and labels at
most three words; see MARKETING_COPY.md. assertWalkthroughCopy in
@hraness/design-kit/testing checks both in a product's own tests.
Version 0.39.0 adds MarketingMarquee and the matching static renderMarketingMarqueeHtml: a quiet band of provider marks and names under a label that counts them, such as “Works with 23 services”. The count is the number of items passed, so the label always matches the band. On screens that allow motion the band scrolls slowly; hovering or the round checkbox control pauses it. Under reduced motion and in print it is one wrapped, static list. Duplicate copies that fill the loop are hidden from assistive technology. See Explain a technical product for usage.
The provider registry adds marks for Bluesky, Facebook, GitHub, Gmail, Google, Instagram, Microsoft, Reddit, Substack, Telegram, Threads, TikTok, Twitch, X, Y Combinator (also matched by “Hacker News”), and YouTube, plus generic glyphs for CSV files, vCards, calendar files, storefronts, and websites. providerMark("google") now returns Google's mark; Gemini keeps gemini, “Google DeepMind”, and “Google AI”.
Version 0.38.0 draws StepThrough as one tabbed selector: the steps and the
preview share an edge. Below 720px the tabs sit on the preview's top edge
between one Back and one Next arrow, and the selected tab takes the window
title-bar color so it reads as part of the frame. When the labels do not fit,
the other tabs show only their numbers and keep their labels as accessible
names. At 720px and wider the steps stack on the preview's start edge, flush
with its square top corner, and Back and Next follow the list. Step
explanations now sit below the preview on small screens. Product CSS that
replaced the arrow glyphs, moved the tabs, or reset the preview's corner
radius should be deleted; it now draws a second arrow or breaks the shared
edge.
MockupBrandMark draws a product's mark from the provider-mark registry as an
app-icon tile or a plain glyph, and draws a neutral monogram for names without
a registered mark. The registry adds Apple (also matched by "Apple Contacts")
from Simple Icons and LinkedIn from Bootstrap Icons, with provenance in
vendor/provider-marks/UPSTREAM.md.
Version 0.37.0 updates the shared marketing walkthroughs. StepThrough puts plain step numbers, labels, and optional hint explanations
in a stacked selector beside the preview when the component is at least 720px
wide. On smaller screens, Back and Next bracket a scrolling tab strip; the
selected explanation sits above the preview in a space reserved for the longest
hint. Controls and frame titles keep the same sizes between steps. Arrow keys
follow the selector's orientation, Home and End select the first and last step,
and Tab reaches the active preview.
Illustrate the output or activity with a compact mock interface, document,
timeline, or diagram. BrowserFrame can show a conceptual report or saved
artifact; describe the illustration clearly without implying a shipped GUI.
Use terminal examples for commands that need to be read. In filled sequences,
presentation terminals share one type size that fits every authored state.
The full stylesheet includes the mockup rules and the public gallery shows a
graphical job walkthrough. Narrow compositions can still import mockups.css.
ModeShowcase accepts optional surface hints and reserves the longest complete
surface, mode, and option explanation, so changing a selection keeps the preview
in place.
Version 0.36.5 adds inspectMarketingHeader to the browser entry. Public-route checks now have a shared way to require a branded header before main, compare its home link and mark with the homepage, and verify visible layout and the final appearance control. Use the same product header on articles, documentation, and normal status pages.
import { inspectMarketingHeader } from "@hraness/design-kit/browser";
const header = await page.evaluate(inspectMarketingHeader, {
brandLabel: "Relay home",
brandMark: "/marks/relay.svg",
});
assert.deepEqual(header.problems, []);The inspector is read-only and returns serializable evidence. Set measure: false only for unrendered DOM unit tests; fixed-theme pages may explicitly set appearance: "omitted". Set stickyOffset: true after hydration to check live header clearance. The default sticky synchronizer now publishes the measured height to the document and enclosing preset scopes, so local fallback heights cannot mask it. Run the inspector on every public route at phone and desktop widths. Missing-header and missing-layout examples fail the shared browser tests.
Version 0.36.4 keeps the document’s system appearance when the final compiled stylesheet loads after the palette foundation. Explicit light and dark documents and nested examples keep their selected appearance.
Version 0.36.3 gives article sources, reading links, and comparison notes the same muted dotted underline as body links. Hover and keyboard focus strengthen the underline color; forced-color mode uses system link colors. Embedded articles and index footers receive the treatment without a surrounding prose class.
Version 0.36.2 preserves deliberately scaled graphics inside filled walkthroughs and gives icon cards room for complete copy on narrow screens and at enlarged text sizes. Version 0.36.1 makes filled showcases measure text and frames without inheriting site transitions or animations, keeping terminal content readable under reduced-motion resets. Version 0.36.0 adds an optional split hero with copy beside the product frame, and an icon slot to marketing cards for a product mark beside
complete, wrapping copy. Illustration cards keep their separate art slot.
StepThrough fit="fill" fills a stable stage with each slide, and
TerminalFrame density="presentation" uses the available space for larger
readable text. Complete source lines determine the fit before animation;
user text zoom can enlarge the stage. ModeShowcase fit="fill" reserves the
largest complete surface across every mode and option before a selection changes,
with height as a minimum. The provider registry adds sourced marks
for iMessage, WhatsApp, Beeper, Ollama, and Vercel.
In fill mode, ModeShowcase renders each authored surface/mode/option combination
as a hidden, inert, nonanimated measurement fixture. Render functions must be
pure and provide complete content. The limit is 128 combinations. Visible
presentation terminals share one fitted type size within that shared maximum;
changing a mode or option never changes the reserved height. At narrower widths
or larger user text settings, the stage can grow to keep the full content readable.
Natural mode retains its existing scaling and page-height behavior.
Version 0.35.3 brings the same folder tabs to mode showcases. Surface tabs attach to one panel containing the controls and preview, while mode and option selections persist across surfaces. A grid reserves the tallest preview so switching surfaces keeps the panel height stable; inactive previews remain hidden and inert. Single-surface showcases keep a complete rounded panel.
Version 0.35.2 lets evergreen articles, article indexes, and source lists hide visible dates with showDates: false. React and static renderers keep authors, review credits, and source publishers, remove empty date rows, and still require valid publication, update, and source-check metadata. Dates remain visible by default.
Version 0.35.1 adds AgentSetupPrompt.targetsPlacement="below" for a compact provider grid beneath the prompt. Prompt, agent-command, and platform-install copy actions share a 44-pixel control. Platform requirements appear in the terminal footer. Clipboard fallbacks, copy-before-open handoffs, keyboard controls, and no-script access remain available. The article guide also centers technique posts on the reader’s question, with accurate drafting notes and focused illustration guidance.
Version 0.35.0 aligns related tools with the studio's portfolio categories and compact product cards. It adds account sections and matching actions, a compact comparison table, shared SVG diagram conventions, open benefit grids, and an install-first hero slot. Walkthroughs use folder tabs and a stable stage with accessible chevron controls. Install commands use shell syntax colors and a quiet primary border; ordinary links use dotted underlines.
Version 0.34.0 extends the same finite columns API to MarketingTrustBoundary, MarketingInterfaceGrid, and MarketingRelated. Related-product groups can override the section’s column limit, so four-item groups can use two columns while a neighboring three-item group keeps three. The compiled and static styles share the adaptive sizing and nested-grid reset.
Version 0.33.0 adds a columns prop accepting 1, 2, 3, or 4 to MarketingCardRow and MarketingPrimitives. Set columns={2} for a balanced two-by-two collection. Each grid wraps within its own container, retaining readable card widths on phones and in narrow page sections. The prop uses compiled classes without inline styles; omitting it preserves automatic layout.
Version 0.32.0 pairs filled mockup controls with their semantic foreground, so selected labels remain readable across palettes, light and dark modes, nested themes, and forced colors. Solid provider marks choose foregrounds using measured contrast, including midtone brand colors. Showcase hints and figure captions are optional; figures keep accessible labels without adding a visible “Illustration” prefix or repeating alt text.
New client compositions AgentSetupPrompt and AgentCommandTabs share prompt previews, complete-source copying, provider marks, responsive actions, and keyboard navigation. The framework-neutral agentSetupTargets(prompt) helper centralizes documented computer-agent destinations. MarketingSection.headingContent places commands and actions beside a result preview, and MarketingActionLink uses the existing shared action recipe. The provider registry adds Obsidian, Supermemory, Mem0, and GitHub Copilot. ProviderMark gains tone="inherit" for glyphs that follow a control’s foreground. See AGENT_SETUP.md for usage and verified handoffs.
Version 0.31.1 includes Charm's complete license and copyright notices for the Crush HeartBit artwork in vendor/provider-marks/CRUSH-LICENSE.md. The package also retains the LobeHub MIT notice and identifies the exact Crush source commit.
Version 0.31.0 keeps caveats out of the social kit. buildSocialKit skips the limits beat and any beat marked social: false, so their text stays in the launch post and never reaches X, Bluesky, Threads, LinkedIn, or the Show HN and Product Hunt fact sheet. A beat can carry a socialPost, a narrower wording that is accurate without the caveat its post keeps; resolveLaunchBeats fills its placeholders and the checks apply to it. assertLaunchKit now rejects a kit that carries a launch-post-only beat. New exports: launchPostOnlyParts, isSocialBeat, socialBeats, and socialPostText. Kits built from beats without a limits beat are unchanged.
Version 0.30.3 refreshes the @hraness/design-kit/portfolio snapshot from the registry. Related-product cards now spell every product the way its own site does, including GhostGet, TextButler, SlopCamera, Soulscrape, Valhalla, and Excalibur (xcb), and names that were set in capitals, such as Stripe History and Lifecharts, now use their prose form. The snapshot adds icon.place, GhostGet Skills, System One Skills, and Pattern Language, with the current one-liners and relations. No export changes.
Version 0.30.2 keeps the narrow and stacked PlatformInstall layout in place when another package, such as @hraness/site-footer, compiles the same default atoms into a later cascade layer: the tab row and tabs now read their narrow and stacked values from private --_hraness-platform-install-* custom properties, so the tab row spans its column again and stacked tabs use their smaller label and padding. No prop or export changes. Version 0.30.1 keeps every PlatformInstall tab name whole in narrow columns. The component is now an inline-size container named hraness-platform-install, and its narrow rules follow the column it sits in rather than the viewport. At 30rem and below the tab row spans the column; at 17.5rem and below each mark stacks above its name, so macOS, Linux, and Windows stay whole down to a 200px tab row, such as a nested install panel on a 320px phone. No prop or export changes. Sites that mirror the hook classes in their own CSS should add container: hraness-platform-install / inline-size to .hraness-platform-install, move their narrow .hraness-platform-install__tabs and __tab rules from @media (max-width: 30rem) to @container hraness-platform-install (max-width: 30rem), and copy the stacked __tab rule. Version 0.30.0 adds site-shell.css with hraness-site-shell and hraness-site-shell__content hooks. Short pages place their footer at the viewport bottom; long pages keep it below their content. Plain and marketing document bodies use the shared layout by default. Version 0.29.3 keeps selected install-tab labels and marks readable in forced-colors mode. Selected tabs preserve the paired system selection colors across their descendants, including hovered tabs.
Version 0.29.2 keeps every PlatformInstall tab visible at 320 to 360px. Below 30rem the tab row spans its column, the tabs share its width and use tighter padding and a slightly smaller label, and a name is truncated only as a last resort, so the Windows tab no longer hides behind a sideways scroll. The component also defines each platform mark once as an SVG <symbol> and draws tabs and no-script labels by reference, which removes about 25 KB of repeated paths per instance. No prop or export changes. Sites that mirror the hook classes in their own CSS should copy the narrow rules for .hraness-platform-install__tabs, __tab, and the new __tab-label.
Version 0.29.1 keeps marketing action labels readable with system button colors, including accent sections and hovered controls. Older consumers can load the standalone @hraness/design-kit/marketing-forced-colors.css after their existing marketing styles without changing normal appearance. Vendored copies must retain the exact released file and its provenance; local action color overrides must respect forced-colors mode.
FoilMark is available from both React entry points, including the server-safe
@hraness/design-kit/react/server. Give it a transparent same-origin SVG (or a
data URL); the exact artwork alpha carries the material and the original image
remains underneath as a fallback. An optional fallback accepts the original
inline vector for currentColor behavior. The mark is decorative unless label
is supplied. Name its enclosing link once.
React consumers load @hraness/design-kit/components.css or the complete
styles.css entry. The raw marketing stylesheet alone styles authored HTML
hooks; it does not contain the compiled React atoms.
<MarketingSiteHeader brand="Relay" brandMark="/marks/relay.svg" links={[]} />
<FoilMark src="/marks/relay.svg" size={44} />Wordmarks and marks use contrast-bearing metal bands with a faint rainbow
reflection, including before hydration and on touch devices. The existing
attachFoil controller adds bounded pointer movement only when motion and
forced-color preferences permit it. --hraness-foil-reflection defaults to
14%; --hraness-foil-image can replace the shared text/mark paint. The
spectrum image belongs to wordmarks and marks only: .hraness-foil and
[data-emphasis="primary"] bordered surfaces keep a flat fill under one
theme-aware monochrome edge, black on light and white on dark, overridable
through --hraness-foil-edge. A blocked inline
mask style or unsupported masks retain the original image. Cross-origin masks
require the asset server's CORS permission; prefer local assets. Forced colors
remove the overlay entirely.
providerMarks (root entry) is the shared registry of coding-agent,
model-vendor, and web-service identities: display name, aliases, accent color,
monochrome glyph, and vendor-colored artwork where the source publishes one.
Marks of kind generic (csv, vcard, calendar, storefront, and
website) are neutral glyphs for sources without a brand. providerMark()
resolves any display name or alias by folded lowercase-alphanumeric match, so
"Claude Code", claude-code, and claudecode land on the same mark. Each
folded name belongs to exactly one mark.
import { providerMark } from "@hraness/design-kit";
import { ProviderMark, ProviderMarkChip } from "@hraness/design-kit/react";
<ProviderMark mark="claudecode" size={40} />
<ProviderMark mark="crush" tone="solid" size={40} />
<ProviderMarkChip mark="goose" />ProviderMark renders an accent-tinted tile by default, a saturated brand
tile with tone="solid", or the bare glyph with tone="plain". Use
tone="inherit" inside controls to follow their foreground. Solid marks
measure their ink at 4.5:1 or better; custom solid accents must use an opaque
hex color with six digits. Identity
artwork comes from vendored sources (vendor/provider-marks/: LobeHub
icons 1.95.1, Simple Icons 15.20.0, Bootstrap Icons 1.13.1, and the
first-party marks documented in its UPSTREAM.md);
marks without published color art keep a retinted glyph, and unknown names
fall back to a neutral monogram tile rather than invented artwork. In forced
colors the artwork yields to a system-color glyph or monogram. Non-React
consumers can build data URIs with providerMarkGlyphDataUri and
providerMarkArtDataUri.
PlatformInstall (@hraness/design-kit/react) shows one tab per operating
system with that platform's mark, its install command, and a Copy button.
Every CLI site should use it for install commands so they look and behave
the same.
import { PlatformBadges, PlatformInstall } from "@hraness/design-kit/react";
<PlatformInstall
platforms={[
{
id: "macos",
command: "brew install hraness/tap/relay",
shell: "Terminal",
note: "Apple silicon and Intel",
alternatives: [{ label: "npm", command: "npm install --global relay" }],
},
{
id: "linux",
command: "curl -fsSL https://relay.example/install.sh | sh",
shell: "Terminal",
note: "x86_64 and ARM64, glibc 2.34+",
},
{
id: "windows",
unavailable: true,
unavailableNote: "No native build yet. Relay runs in WSL2.",
command: "curl -fsSL https://relay.example/install.sh | sh",
shell: "WSL2 terminal",
},
]}
/>
<PlatformBadges platforms={["macos", "linux", { id: "windows", note: "via WSL2" }]} />The server render selects defaultPlatform (the first platform when
omitted). After hydration the component selects the visitor's operating
system when it is listed; pass detect={false} to keep the default. The tabs
follow the ARIA tab pattern: arrow keys, Home, and End move between
platforms. Copy writes the exact command, shows "Copied", and announces the
result to screen readers; when the clipboard is blocked it selects the
command and says so. Long commands scroll inside their box, not the page.
Below 30rem the tab row spans its column and the tabs tighten, so macOS,
Linux, and Windows fit side by side at 320px without scrolling; a name is
truncated only when a longer platform list cannot fit. Each platform mark is
defined once per component as an SVG <symbol> and drawn by reference, so
the markup does not repeat the paths. Non-React sites that render the hook
classes should mirror the narrow rules on .hraness-platform-install__tabs,
.hraness-platform-install__tab, and .hraness-platform-install__tab-label.
Without JavaScript the tab row and Copy buttons hide and every platform's
commands show in order under their names. The component uses no inline
script or style, so it works under a strict content security policy.
PlatformIcon draws the Apple logo, Tux, or the Windows window in
currentColor, and a neutral terminal glyph for other ids. It is decorative
unless you pass label. PlatformBadges is a compact "Runs on" row for
heroes and footers; both are server-safe and also ship from
@hraness/design-kit/react/server. The root entry exports platformLabel,
platformMark (raw SVG paths for non-React sites), detectPlatform, and
matchDetectedPlatform. The Apple and Linux marks come from Simple Icons (CC0); see
vendor/platform-marks/UPSTREAM.md.
Import the complete stylesheet once after Tailwind, if the application uses it:
@import "tailwindcss";
@import "@hraness/design-kit/styles.css";The complete stylesheet composes the token, reset, legacy component, and extracted StyleX layers from @hraness/ui before applying design-kit presentation. It keeps base below components, then freezes UI legacy.base, legacy, and priority1 through priority7 before the design-kit legacy and priority1-through-priority8 inventory. The design-kit manifest currently maps eight raw-priority buckets to those eight serialized ranks. Rank 1 begins with generated raw-priority-0 keyframes, so its keyframes and custom-property atoms are unlayered and priority1 is reserved in the prelude; the remaining atomic output occupies the exact priority2 through priority8 blocks. Migrated declarations therefore win according to package ownership and StyleX priority without relying on import timing. Nebula Sans is the default proportional text and heading face, while explicit code and mono roles keep the system monospace stack. Package-owned atomic component presentation is authored in colocated *.stylex.ts files and compiled into deterministic dist/stylex.css with runtime injection disabled. styles.css reaches that local artifact once through components.css, so the public narrow component entry and the complete entry carry the same component recipes. Generated atomic class names are declaration hashes that may repeat across package layers; they are internal and do not identify package ownership. Use documented stable classes only when a composition exposes one. The notice retains logical block-axis inset and border declarations through canonical dashed StyleX properties. Its minimum height remains physical; the horizontal LTR and RTL compiler canary does not establish complete vertical-writing-mode parity.
Applications that compile local StyleX declarations register both @hraness/ui/stylex-manifest.json and @hraness/design-kit/stylex-manifest.json with the build tools from @hraness/ui/stylex-build. Import @hraness/design-kit/compiler-foundation.css for full design-kit compositions, or @hraness/design-kit/compiler-palettes.css for palettes, the appearance menu, and portable controls with application-owned typography. The minimal entry supplies the UI foundation and palette bridge without webfonts or marketing styles; the full entry includes it transitively. Neither route imports precompiled StyleX recipes. The finalizer unions the raw UI, design-kit, and application rules and serializes them once into components.hraness-stylex, after every package's legacy layers. Generation plans and completion records use schema 2 and bind the exact union policy; package manifests remain schema 1 and bind the minimal foundation. Start a fresh generation when upgrading the union policy. Every HTML or SSR entry links that finalized stylesheet after its foundation stylesheet. Do not combine this route with either package's styles.css or stylex.css.
Package authors use createStylexTransformCollector and serializeStylexPackageRules from @hraness/ui/stylex-build to publish externalized JavaScript, independently usable package CSS under a distinct components.* namespace, and a manifest that binds the raw rules, runtime files, standalone CSS, and compiler foundation. Final applications consume those manifests rather than concatenating independently serialized package stylesheets.
Import narrower layers when the application does not need the full presentation system:
@import "@hraness/design-kit/tokens.css";
@import "@hraness/design-kit/charts.css";
@import "@hraness/design-kit/effects.css";
@import "@hraness/design-kit/syntax-highlighting.css";site-shell.css keeps the footer at the viewport bottom on short pages and below content on long pages. Import it and add hraness-site-shell to the root containing the header, main, and footer as direct children:
<body class="hraness-site-shell">
<header>…</header>
<main>…</main>
<footer>…</footer>
</body>For React apps with a root wrapper, put the class on that wrapper instead. If the content is a grid or another wrapper around main, add hraness-site-shell__content to that direct child so it fills the available height. Keep the content’s width and maximum measure in your page styles. The shell fills the dynamic viewport with a vh fallback. Multiple footer rows stay together in normal document flow. body.plain-site and body.hraness-marketing-page include this layout by default; nested theme and marketing examples opt in with hraness-site-shell. The standalone stylesheet has no dependencies and can accompany older design-kit styles.
plain-site.css provides a compact site shell. plain-publication.css adds sourced article, citation, table, callout, and related-reading structure. reading.css carries the shared long-form scale on .hraness-prose for docs and guide surfaces outside the publication shell; the same --hraness-type-* tokens size the publication article, and data-hraness-reading-face="serif" adapts the scale to serif display faces.
typography.css (included in styles.css and compiler-foundation.css, or importable as @hraness/design-kit/typography.css) owns the heading scale for every site. Site CSS should not set a heading's size or face. A heading at level N reads three tokens:
.my-section h3 {
font-family: var(--hraness-type-h3-font);
font-size: var(--hraness-type-h3-size);
font-weight: var(--hraness-type-h3-weight);
}h1 and h2 use the display face (--hraness-type-heading-font, which follows --hraness-marketing-display-font). h3 and h4 use the text face (--hraness-type-subheading-font). The display face is used only at --hraness-type-display-min (1.75rem) or larger, and h2 never scales below it. Both marketing presets set the display face to Nebula Sans, so the floor mainly protects a product that opts in to a condensed serif, which is unreadable at card and label sizes. Choose the level by a heading's role in the page, not by the size you want: a heading that should look small is an h3 or h4, set in the text face.
paper-theme.css is a separate, zero-import CSS contract for warm neutral light
and dark surfaces, Nebula Sans font roles, and compact marketing typography.
Opt in with data-hraness-theme="paper" after the existing stylesheets. It can
be adopted from an immutable package release or as a verified CSS snapshot
without upgrading UI, React, or a StyleX compiler. Existing layouts and
appearance choices remain product-owned. See Paper theme for
installation, compatibility, snapshot verification, and preference migration.
Lantern gives opaque reading surfaces a luminous perimeter and selected controls a warm, matched fill and text color. Glass is reserved for chrome with scrolling content behind it. The selected palette, existing focus indicators and product layout remain authoritative.
Set data-hraness-material="lantern" on a themed host and apply the documented surface hooks. Complete stylesheets include the material; selective and older consumers can import a verified CSS snapshot. React primitives use the compiled lanternControlStyles through their existing controlXstyle prop. ThemeMenuButton picks up the material by itself: raised at rest, inset while pressed, with a Lantern menu.
See the material contract and examples for palette islands, portal hosts, accessibility fallbacks and the immutable installer. The gallery demonstrates light and dark workspaces with working search, selection and disclosures.
product-landscape.css draws one product-specific drawing behind public pages:
a grayscale luminance mask tinted from the palette, tiled down the page, and
kept in the margins. Set --hraness-landscape-image on :root, then mark each
page with data-hraness-landscape="page" (MarketingPage takes
landscape="page"). Over the drawing, page wrappers clear, code and tables stay
opaque with rounded edges, and small cards and callouts become frosted panels
with opaque fallbacks. styles.css and compiler-foundation.css include it.
See Product landscape for image preparation, the off switch,
contained hosts, tuning properties, and product surface hooks.
product-marketing.css is an opt-in narrative grammar for technical product
sites. It gives every Hraness product one typeface, one measured type scale,
sentence-case labels, hairline chrome, soft radii, and one accent color, so the
portfolio reads as one studio's work. The roles are a sticky site header, a main landmark that clears that header,
equal-height product card rows, an outcome-led hero with an optional product
frame, a row of pillars, an install panel, an ordered flow, fact and stat strips,
narrative sections, numbered primitives, interface and trust cards, attributed
quotes, pricing, native questions, a maker section, a closing call to action,
and an in-flow site footer. The classes own
responsive structure and semantics-facing presentation.
When a page also mounts the shared network footer from @hraness/site-footer,
render it directly after the marketing footer: the grammar joins the pair into
one band instead of two stacked bars. The product row's bottom space collapses
to a compact gap, and the network row's content follows the marketing measure
and gutter through the footer's documented --hraness-site-footer-measure
seam, so both registers share one column. Products bind the
--hraness-marketing-* roles to their own content and set one accent:
@import "@hraness/design-kit/styles.css";
.hraness-marketing-page {
--hraness-site-accent: oklch(0.55 0.21 262);
--hraness-site-accent-ink: #ffffff;
}Static sites may render the documented classes directly. React sites can use the server-safe compositions from either React entry:
import {
MarketingCallToAction,
MarketingMain,
MarketingPage,
MarketingPillars,
MarketingProofFrame,
MarketingSiteHeader,
ProductHero,
} from "@hraness/design-kit/react/server";
<MarketingPage>
<MarketingSiteHeader
action={{ href: "#install", label: "Install Relay" }}
brand="Relay"
links={[{ href: "#how", label: "How it works" }, { href: "#pricing", label: "Pricing" }]}
/>
<MarketingMain>
<ProductHero
actions={[
{ href: "#install", label: "Install Relay" },
{ href: "#how", label: "See how it works" },
]}
boundary="Free for local use on macOS and Linux · version 1.2.3"
example="Ask your agent to run the nightly job and show you the log."
eyebrow="A reference developer tool"
frame={(
<MarketingProofFrame caption="The log written by the example job." credit="Captured 5 September 2026" title="relay run job-01">
<img alt="Relay printing a run log in a terminal" src="/relay-log.png" />
</MarketingProofFrame>
)}
heading="Run a job from your terminal, your code, or your agent"
headingId="relay-title"
name="Relay"
summary="Relay runs the same job wherever you start it and writes a log you can read afterward: inputs, outputs, and how long it took."
/>
<MarketingPillars
ariaLabel="Relay in three points"
columns={3}
pillars={[
{ label: "No hosted service", summary: "Jobs run on your machine and never wait on a server." },
{ label: "A log for every run", summary: "Open it to see what went in, what came out, and when." },
{ label: "Your files stay put", summary: "Source files and credentials never leave your machine." },
]}
/>
<MarketingCallToAction
actions={[{ href: "#install", label: "Install Relay" }]}
footnote="Free for local use on macOS and Linux."
heading="Start with one job"
headingId="cta-title"
/>
</MarketingMain>
</MarketingPage>A sticky MarketingSiteHeader publishes --hraness-sticky-offset on the
page and on html, so siblings inherit it. Wrap page content in
MarketingMain (id="main-content") so skip links and hash targets use that
offset as scroll-margin. Do not add extra padding when the header stays in
flow; pass clearance="pad" only for a data-position="fixed" header. The
next sticky strip should use .hraness-sticky-below-chrome or
data-hraness-sticky instead of top: 0. When the header wraps, render
StickyOffsetSync once from @hraness/design-kit/react or call
syncStickyOffset from @hraness/design-kit/browser to replace the token
fallback with the measured border box. MarketingCardRow stretches every
direct child to the tallest item in its row. Use icon for a decorative
product mark beside the title and summary. The mark occupies a 3.5rem left
column, and the summary wraps in full. Use art for a full-width illustration;
its summary reserves two lines. A card accepts either icon or art.
MarketingCardArt / .hraness-marketing-card__art clips illustration paint
inside the card. Raw HTML uses data-layout="icon" with
.hraness-marketing-card__icon beside .hraness-marketing-card__copy.
Keep icon contents decorative and size custom mark components to 56px. Named
Hraness integrations use the registry's marks, names, URLs, and relationship
details in these icon rows. Compact recommendations use MarketingRelated.
tone="accent" on the hero or the call to action paints that role edge to
edge in the product accent. layout="split" or "split-reverse" on a
section places its heading group beside its body. Stacked section headings stay
in normal flow. Apply sticky copy only in an explicitly split layout, where its
own column contains it and the result can scroll beside it. MarketingQuoteGrid and
MarketingPillars render nothing for an empty list, so a site adds quotes
only when it has real, attributed ones.
For a policy with style-src-attr 'none', set columns to 1, 2, 3, or
4 on MarketingFacts, MarketingPillars, and MarketingStatStrip. Set
factsColumns on ProductHero for its nested facts. These finite choices use
compiled recipes and emit no inline column style. Below 48rem, facts and stats
still use two columns and pillars use one. Omitting the prop preserves the
existing item-count custom property, including its inline style and arbitrary
collection length. Use the compiled standalone stylesheet or the finalized
compiler-adopter stylesheet with the finite choices.
MarketingCardRow, MarketingPrimitives, MarketingTrustBoundary, MarketingInterfaceGrid, and MarketingRelated also accept columns from 1 to 4 as a maximum. Their cards keep a minimum reading width (16rem for card rows, 18rem for capabilities), then collapse to one track as their container narrows. Use columns={2} to keep four-item collections balanced. A MarketingRelated group may set its own columns to override the section default. Plain HTML can set --hraness-marketing-grid-columns on the grid through its stylesheet.
MarketingNotice renders one status or alert line — a redirect confirmation, a
failed action — on the content edge of the page column, with tone set to
info, success, or error. Errors use role="alert"; the other tones use
role="status". Signed-in account pages inside MarketingSiteHeader compose
MarketingMain, MarketingNotice, MarketingStatStrip, and MarketingSection
so every block shares the header's measure and gutter; they do not add
full-bleed banners or cards nested inside sections.
ProductHero.notice renders product-owned content after the boundary text in
the hero's copy group. MarketingInstallPanel.note renders after its heading,
before the separate command group. Both accept React nodes without adding a
wrapper. Omit them to preserve the existing markup. MarketingMaker.linkClassName
adds a caller class to its listed links only; it does not style biography links.
Use MarketingSectionLabel for a native paragraph with the same label recipe
as MarketingSection. Its default preserves the existing label presentation;
size="body" selects the compiled 1rem variant. Override
--hraness-marketing-example-measure in a product stylesheet to change only
the hero example's maximum inline size. When absent, it uses
--hraness-marketing-copy-measure; the summary's measure is unchanged.
Homepage copy on this grammar follows STYLE.md and
MARKETING_COPY.md, which gives each slot its job and its
length limit. The headline states what the reader can do, in sentence case with
no period. The summary says what the product is, who it is for, and the one
thing it does differently. The example is a concrete request a reader could
make. State the release status once, plainly, in boundary, and keep each other
limit beside the feature it limits. A full-sentence section heading in the
editorial preset may end with a period; other headings do not. Words such as
"bounded", "exact", "authority", "custody", "immutable", and "inspectable" stay
out of the hero and leads. Every number has a date or a source, and no quote
appears without an attributed author who agreed to it.
Import only the grammar when a site owns its reset and tokens:
@import "@hraness/design-kit/product-marketing.css";The components render complete server HTML and add no clipboard, animation, or analytics runtime. A product may enhance a command with its own accessible copy control while keeping selectable text as the fallback.
The article layer renders long-form posts with a title, a one-sentence dek, a byline, published and updated dates, a visible drafting and review note, an optional contents list, sources, and related products. React and static sites get identical markup, styled by plain-publication.css (included in styles.css). The body keeps a 68ch measure, and the contents list moves beside it on wide screens. ARTICLE_COPY.md gives the article shapes and the rules for provenance, freshness, and links.
import {
ArticleSources,
MarketingArticle,
} from "@hraness/design-kit/react/server";
import { articleProvenanceFromAdmission } from "@hraness/design-kit";
<MarketingArticle
author={{ kind: "organization", name: "Hraness" }}
dek="Relay replays a failed webhook from the stored request body."
eyebrow="Technique"
heading="Replaying webhooks without breaking signatures"
provenance={articleProvenanceFromAdmission(admission)}
published="2026-09-10"
toc={[{ href: "#approach", label: "The approach" }]}
after={<ArticleSources sources={sources} />}
>
{body}
</MarketingArticle>MarketingRelated also renders compact studio product groups. Use portfolioRelatedGroups(selectedProductIds) from @hraness/design-kit/portfolio with the heading “Other tools from our studio”; category names, category colors, product names, domains, marks, and descriptions then come from the same registry as hraness.com. Omit label and summary for this presentation. Each product is one native link with a 28px decorative mark, its name, optional domain, and one-line role. Groups accept tone (rose, indigo, amber, emerald, or neutral) and stack when there is not room for two columns. Product pages do not need the homepage's “Built on” or editorial links.
MarketingDiagram supplies an accessible, keyboard-scrollable SVG frame; the product owns its layout and facts. Use one idea per drawing, a generous viewBox, .hraness-diagram__label and __detail text, __node rectangles, and __icon/__connector strokes. Icons and connectors share the primary accent. DiagramArrowhead creates a 6-unit arrowhead with markerUnits="userSpaceOnUse" and context-stroke, so thicker lines do not inflate the arrow. Caller-owned IDs must be unique within the document. diagramMetrics from the root export supplies 20px labels, 16px details, 24px padding, 48px node gaps, and 1.5px strokes as source-space starting points. The canvas scrolls at narrow widths to preserve legible text. Keep node labels short; put implementation detail in page copy or a disclosure. Standalone generated SVGs must embed the actual Nebula Sans face and subset every glyph used by every text node, including initial capitals, punctuation, and nested tspans; inspect rendered output for fallback glyphs. Use marketing-diagram.css for equivalent raw SVG hooks.
MarketingComparison renders a compact feature matrix with named options, a highlighted option column, and rows. Each row's values can be booleans, short strings, or { status, label?, detail? }, where status is yes, no, partial, optional, or depends. Checks, crosses, and conditional marks always keep a visible word; free text is useful for exact prices. Keep qualifications in a cell's short detail, a row's note, or the table note. Supply a factual caption and reviewed sources. Static sites use the same class hooks from marketing-comparison.css.
MarketingMarquee shows the services or sources a product works with as a quiet band under the hero: each item's mark and name in the muted text color, below a label that counts them. Write the label with {count} once; the component replaces it with the number of items, so the label and the band cannot disagree. Pass only the items the count should cover. mark takes a provider-registry id, name, or alias and defaults to the item's name; names outside the registry get a two-letter monogram. Items are not links, so add action for one link beside the label, such as the full provider list.
import { MarketingMarquee } from "@hraness/design-kit/react/server";
<MarketingMarquee
action={{ href: "#providers", label: "See every provider" }}
id="providers-band"
items={[{ name: "X" }, { name: "LinkedIn" }, { mark: "facebook", name: "Facebook Pages" }]}
label="Works with {count} services"
/>On screens that allow motion, the band scrolls slowly toward the start of the line, about 3.75 seconds per item; set --hraness-marketing-marquee-duration to change the pace. Hovering pauses it, and a round control at the end of the band is a native checkbox named “Pause scrolling” by default (pauseLabel). Under reduced motion and in print, the band is one wrapped, static list without the control. Duplicate copies that keep the loop full are hidden from assistive technology, and the page never scrolls sideways. align="center" centers the label and the static list. The band sets its own measure and gutter from --hraness-marketing-measure and --hraness-marketing-gutter. Static sites call renderMarketingMarqueeHtml from the root entry and load @hraness/design-kit/marketing-marquee.css; styles.css and compiler-foundation.css include it.
MarketingAccount gives account access a full section with a heading, short summary, and content slot. MarketingAccountActions pairs a prominent primary link with an optional signIn link; both have 56px minimum touch targets. Product authentication forms and session state remain in the consuming site. Add a direct account link to the site's header so visitors can find it without scrolling. Prose links and .hraness-text-link use subtle dotted underlines; header navigation and primary buttons retain their own presentation.
Wherever a signed-out visitor needs an account, offer the same two choices with MarketingAccountActions: a primary link labeled “Create account” and the signIn link, which reads “Sign in”. This covers the account section and any page that asks a visitor to sign in before it shows private content. When one sign-in flow creates new accounts and signs in existing ones, both links may point to it. Place the actions directly in the section or page, not inside a card, panel, or other surface: Sign in is a text link, and a surrounding surface makes it look like an oversized button.
ArticleIndex lists posts, ArticleCallout marks a note, limit, or warning, and ArticleRelatedProducts wraps MarketingRelated. Static site builders import renderArticleHtml, renderArticleIndexHtml, renderArticleSourcesHtml, renderArticleCalloutHtml, and renderArticleRelatedHtml from the framework-neutral root and load @hraness/design-kit/plain-publication.css after plain-site.css. Text arguments are escaped; bodyHtml and afterHtml take HTML the site already rendered.
Each host keeps an ArticleAdmission record per article and checks the registry in a test:
import { assertArticleAdmissions } from "@hraness/design-kit";
assertArticleAdmissions(registry);A record passes when its six 0 to 2 scores total at least 9 with no zero. An indexable record also needs a review with a reviewer type, dated sources, two observations, and a refresh trigger, and every reviewed record needs reassessOn 28 to 56 days after the review. The provenance sentence says "human" only for a human editor's review.
An "Introducing" post is a column of 7 to 10 beats. Each beat makes one claim, shows one visual, and doubles as one social post, except the limits beat and beats marked social: false, which stay in the post. Keep the numbers in one facts module and build the posts from the beats:
import { assertLaunchKit, buildSocialKit, resolveLaunchBeats } from "@hraness/design-kit/launch";
import { LaunchBeats, SocialKitPanel } from "@hraness/design-kit/react";
const beats = resolveLaunchBeats(draftBeats, launchFacts);
const kit = buildSocialKit(beats, portfolioMessaging, { status: "Preview" }, canonicalUrl);
assertLaunchKit(beats, kit, { status: "Preview", publicInstall: false, canonicalUrl });
<LaunchBeats beats={beats} renderVisual={(beat) => <Visual beat={beat} />} />
<SocialKitPanel kit={kit} />Draw the visuals with @hraness/design-kit/mockups and load @hraness/design-kit/mockups.css. Build the social kit only once the post is indexable. Product tests can run the checks from @hraness/design-kit/testing, such as blogConformance for the blog and assertRoleImgWithLabel for each mockup. See ARTICLE_COPY.md for the beat rules.
Hraness product sites run the whole launch with the product-launch agent skill: mockups, the post, the social kit, the launch film, and comparison pages. It also lists the checks to run before you publish.
@hraness/design-kit/portfolio holds the public facts about Hraness products: each product's name, one-liner, canonical URL, status, and other names, plus the registered relations between products. It is a snapshot generated from one commit of the portfolio registry, recorded in portfolioProvenance, and it renders nothing.
import { ArticleRelatedProducts } from "@hraness/design-kit/react/server";
import { product, relatedFor, usesPairs } from "@hraness/design-kit/portfolio";
const textbutler = product("message-like-me");
<ArticleRelatedProducts headingId="related" items={relatedFor("message-like-me")} />;
for (const { relation, source, target } of usesPairs()) {
console.log(source.name, relation.label, target.name, relation.detail);
}relatedFor(id) returns one card per related product, and only for relations with a written detail sentence. Each item carries the product's mark (the portfolio artwork hraness.com shows, as a data:image/svg+xml URL) and its one-line role; the cards show the mark, name, and role, as on the hraness.com project index. The relation's relationship sentence stays on the item for articles that quote it. usesPairs() lists the integration relations that have one. Static sites read the same data from @hraness/design-kit/portfolio.json. Pin portfolioDigest in a test so an upgrade that changes the facts shows up in review.
import { Button, Icon, ViewportFrame } from "@hraness/ui";
import {
AppShell,
NavigationRail,
PageCanvas,
RailItem,
RailSection,
TopBar,
} from "@hraness/design-kit/react";
import { DashboardSquare01Icon } from "@hugeicons/core-free-icons";
export function Workspace() {
const rail = (
<NavigationRail>
<RailSection title="Workspace">
<RailItem
href="/"
icon={<Icon icon={DashboardSquare01Icon} />}
isActive
label="Overview"
/>
</RailSection>
</NavigationRail>
);
return (
<ViewportFrame>
<AppShell rail={rail} topBar={<TopBar title="Workspace" />}>
<PageCanvas>
<Button variant="primary">Create project</Button>
</PageCanvas>
</AppShell>
</ViewportFrame>
);
}Connect routing with RouterProvider from @hraness/ui. Design-kit rail links use that public router context and intent-prefetch contract.
AnimatedRailStage keeps the surrounding shell mounted while one keyed route stage enters and exits through AnimatePresence in wait mode. The public stageKey, stable class, data-stage-key, and caller-last className contracts remain unchanged. Its logical minimum and reduced-motion fallback are delivered through extracted StyleX classes. Reduced motion keeps content visible, removes translation and duration from the motion recipe, and forces any Motion-authored transform and transition off in CSS.
ChatMessage keeps its article, finite data-role, optional avatar, header, and action slots, and caller-last root class while its grid, logical minimum, and metadata-row presentation are delivered through extracted StyleX classes. ChatComposer remains a controlled native form composition with a multiline field and submit button. It always prevents native navigation, calls its callback only for an enabled, non-pending, nonblank value, and keeps native form attributes and inline styles caller-controlled. Its two-column layout collapses to one column at the existing compact breakpoint. Neither component exposes a public xstyle or ref seam.
TopBar, BottomBar, PageCanvas, and DockedFooter keep their native header, footer, main, or div semantics while their product-neutral layout recipes are delivered through extracted StyleX classes. Their stable classes and data attributes remain available for semantic inspection, and native className and style props remain caller-controlled. DockedFooter continues to forward its root footer ref. Its surface value remains a stable data hook; only TopBar gives glass a visual treatment. Its 90% tint and 18px blur use feature detection; unsupported filtering, reduced transparency, and forced colors use an opaque surface.
DitherSurface composes its product-neutral texture through the typed ThemedSurface seam from @hraness/ui. Its density is one of coarse, fine, or medium; the default medium texture uses 4px, while coarse and fine set the literal public --hraness-design-dither-size property to 7px and 3px. A caller xstyle recipe is applied after the shared texture, and native style remains last for deliberate per-instance overrides. Forced-colors mode removes the decorative image without changing the surface's content, native element, tone, shape, or border.
PlaybackTransport keeps one large primary command through idle, pending, and playing states. Give the toolbar exactly one of aria-label or aria-labelledby; the command changes its accessible label, glyph, busy state, and play or stop callback without changing its stable button hook. Its wrapping toolbar recipe and exact 1.5rem logical glyph and spinner dimensions are delivered through extracted StyleX classes. The existing root className, button id, keyboard-shortcut, button-ref, and trailing-control seams remain available.
Fader keeps the full React Aria single-value slider contract while defaulting to a vertical, default-density control with a hidden accessible label and output. Its default and compact dimensions, horizontal variant, label row, rails, thumb, and focus-visible state are delivered through extracted StyleX classes. The two decorative rail nodes are inert and keep the logical geometry that the former pseudo selectors provided without expanding the package layer range. Existing root and input refs, visible label and accessory, output, caller className, native slider props, and native style overrides remain available. Native styles may override the public --hraness-design-fader-* properties for a deliberate per-instance size.
RouteNotFoundPage, RouteErrorPage, and GlobalErrorDocument render one shared page for missing addresses and recoverable errors: the product's main action, a "Did you mean" link to the closest known page, up to three places to start, and a decorative glyph drawn as dots that react to the pointer. Static sites render the same markup with renderStatusPageHtml and enhance it with attachStatusPage from @hraness/design-kit/browser. STATUS_PAGES.md says what to put in each slot.
<RouteNotFoundPage
siteName="Sponge"
primaryAction={{ href: "/start", label: "Start your library" }}
next={[{ href: "/docs", label: "Docs", description: "Connect Claude, ChatGPT, or any MCP client." }]}
routes={knownPages}
agentIndexHref="/llms.txt"
/>Charts own responsive geometry, exact-value accessibility, reduced motion, and forced-color behavior. Applications own data, labels, units, and categorical colors.
import { BarListChart, SyntaxCode } from "@hraness/design-kit/react";
<BarListChart
aria-label="Requests by region"
data={[
{ id: "north", label: "North", value: 72 },
{ id: "south", label: "South", value: 48 },
]}
/>
<pre>
<SyntaxCode code={'const ready = true;'} />
</pre>SyntaxCode and highlightCode(code) choose a language for recognizable code when the language is omitted. The deterministic rules recognize JSON objects and arrays, common package and Git commands, and distinctive TypeScript, Markdown, HTML, and CSS syntax. Prose and uncertain input stay plain text. Pass a language or Markdown fence hint to keep an explicit choice. Rust, TOML, YAML, Lean, and TLA+ use explicit language hints and keep comments and multiline strings intact; unsupported hints and language="text" remain plain text. Blocks longer than 131,072 characters also remain escaped plain text.
The framework-neutral highlighter is available from @hraness/design-kit/syntax-highlighting. For sites that disallow inline styles, call highlightCode(code, "typescript", { styles: "classes" }) or pass styles="classes" to SyntaxCode. Import syntax-highlighting.css when using the highlighter alone; the complete stylesheet, compiler foundation, and narrow product-marketing.css entry include it. The default style mode remains unchanged; both modes share the same theme colors and preserve source line breaks. Markdown renderers should pass each literal code block and its fence hint to this shared highlighter. Stylesheets do not tokenize raw <pre><code> markup.
Server components can import SyntaxCode, deterministic procedural effects, and
static surfaces from @hraness/design-kit/react/server without crossing the
client boundary used by the interactive React barrel.
FoilCardSurface adds deterministic material paint behind semantic card
content. Use renderMode="static" for image capture and other motionless
surfaces. Interactive cards work on their own; a collection should be wrapped
once in FoilCardDeck, which delegates pointer and focus interaction through a
single controller and keeps geometry cached for the active descendant.
import { FoilCardDeck, FoilCardSurface } from "@hraness/design-kit/react";
<FoilCardDeck aria-label="Reference cards" className="card-grid">
{records.map((record) => (
<FoilCardSurface
intensity="standard"
key={record.id}
ornament="circuit"
preset="aurora"
renderMode="interactive"
seed={record.id}
>
<article>{record.label}</article>
</FoilCardSurface>
))}
</FoilCardDeck>The optional ornament is one of none, corners, rails, circuit,
radial, or facets. It affects edge paint only, so product content remains
legible. Set --foil-card-radius on a surface or deck descendant to match a
product-owned card radius. Fine-pointer movement activates directional
diffraction; keyboard focus gets a motionless material cue. Touch, reduced
motion, and forced-colors modes keep ordinary semantic content intact, and no
inactive card receives will-change.
Opt into Catppuccin, Gruvbox, Rosé Pine, or Tokyo Night in light or dark mode. DesignPaletteProvider and the existing ThemeMenuButton provide one appearance menu, with Catppuccin dark as the default. The shared controller supports external bootstrap scripts and strict content security policies. See palette installation, semantic roles, and sources.
Wrap browser applications with DesignThemeProvider and render
ThemeMenuButton as the final action in the product header. It exposes one
icon-only trigger and a Light, Dark, or System menu with the same presentation
across products. The first visit defaults to System and follows the device
preference. Explicit Light, Dark, and System choices persist under a versioned
Hraness-neutral key. Server-rendered document roots may use Light as a safe
concrete baseline while the blocking appearance bootstrap resolves the stored
or System preference before paint.
Show a timestamp with RelativeTime from @hraness/design-kit/react. It
renders <time> with the ISO instant in dateTime, the local date and time in
title, and relative text such as "5 minutes ago" or "in 23 hours". Pass now
to render the same text on the server and during hydration. After mount it
follows the browser clock and refreshes as often as the shown unit can change.
Set refreshInterval to a fixed number of milliseconds, or to "off" to keep
now. Framework-neutral code can call formatRelativeTime(value, { now, locale, numeric }) from the package root. Both throw on an invalid date instead
of printing "Invalid Date".
ThemeColorSync leaves adaptive media-qualified server tags in control until a
concrete Light or Dark preference resolves. It then owns one active browser
chrome color, temporarily neutralizes competing same-name tags, and restores
their exact media conditions after the final synchronized owner unmounts.
Use GlobalErrorDocument for a Next root global-error boundary. It remains
control-free because the normal product header is unavailable. Its System
default emits adaptive Light and Dark theme-color metadata plus a
light dark color-scheme before hydration, then follows the same stored
preference as the application. Products with their own canvas colors pass the
same palette once:
<GlobalErrorDocument
darkColor="#101419"
error={error}
lightColor="#f4efe7"
reset={reset}
/>An explicitly fixed theme="light" or theme="dark" emits one matching,
unqualified theme-color and a fixed color-scheme without mounting the
preference provider.
Static HTML products use the same composition without a client framework:
import { installAppearanceMenus } from "@hraness/design-kit/browser";
installAppearanceMenus({
darkThemeColor: "#09090d",
lightThemeColor: "#f7f3ea",
storageKey: "product-appearance",
});Load @hraness/design-kit/appearance-menu.css, render one progressive
[data-hraness-appearance-menu] composition as the final header action, and
bundle the installer into a small blocking local script. The installer applies
the stored or System preference before the stylesheet loads, synchronizes
browser chrome, and supplies the same icon, menu, keyboard, focus, and storage
contract as the React control. Importing the browser module has no side effects.
Nebula Sans is bundled under the SIL Open Font License and loads through tokens.css for ordinary text and headings. Explicit serif treatments remain product-owned, and code and mono roles keep the system monospace stack. Geist Mono remains available as an optional display face:
Content Security Policies must allow same-origin font assets with font-src 'self'.
Bundlers configured to inline font assets also require data: in that directive.
@import "@hraness/design-kit/fonts.css";
:root {
--font-heading: var(--font-geist-mono);
}Applications may override semantic roles in a local stylesheet after the design-kit import. Keep deliberate serif and monospace treatments explicit so they are not absorbed into the proportional default. The public package contains no restricted font assets or metric overrides.
Generated artwork can import nebulaSansSocialFonts from
@hraness/design-kit/fonts/nebula-sans/social. It returns the official Book and
Bold OTF payloads without a remote request or runtime filesystem lookup, ready
for an ImageResponse fonts option.
The Jelly surface API, stylesheet export, and browser runtime have been removed.
Replace JellySurface wrappers with the appropriate native or @hraness/ui
primitive. Apply the shared material roles to that
semantic element when it needs a surface treatment. Keep labels, refs, disabled
and pending behavior, keyboard interaction, and portal ownership on the
primitive. Remove imports of @hraness/design-kit/jelly.css and any direct
Jelly runtime integration. DesignThemeProvider continues to manage appearance
and portal themes without loading a decorative control runtime.
The EvilCharts license and adaptation provenance remain under vendor/evilcharts.
DesignSystemGallery is an executable, product-neutral reference for the package boundary. Mount it in a development route and import design-gallery.css through the complete stylesheet. The gallery exercises Chat message slots, controlled composer submission, compact responsive layout, extracted class delivery, and caller-last classes alongside the other public compositions.
Provision the pinned test browser with bun run browser:install after installing dependencies. Browser checks use that Playwright revision by default. CHROMIUM_EXECUTABLE_PATH (or CHROME_PATH) may select an explicitly provisioned Chrome for Testing executable. Invalid overrides fail; checks never fall back to an installed personal browser. Each check reports the selected executable and version.
Use Bun 1.3.14:
bun install --frozen-lockfile
bun run browser:install
bun run checkThe stable dependency pair for this release is @hraness/ui v0.5.17 with @hraness/design-kit v0.41.2. Version 0.31.0 keeps the limits beat and beats marked social: false out of the social kit and adds socialPost for social wording that stands without a caveat. Version 0.30.3 refreshes the portfolio snapshot so related-product cards use each product's own spelling, such as GhostGet and TextButler. Version 0.30.2 keeps the narrow and stacked install-tab layout when another package compiles the same atoms into a later cascade layer. Version 0.30.1 keeps every PlatformInstall tab name whole in columns down to 200px by stacking each mark above its name, using a container query on the component. Version 0.30.0 adds site-shell.css, which keeps short-page footers at the viewport bottom. Version 0.29.3 preserves readable selected install tabs in forced colors. Version 0.29.2 keeps all three PlatformInstall tabs visible at 320 to 360px and draws each platform mark once per component. Version 0.29.1 keeps marketing action labels readable in forced colors. Version 0.29.0 adds PlatformInstall, one install block for every CLI site: a tab per operating system with its mark, the install command with a Copy button that announces the result, optional alternative commands such as npm or Homebrew, and a note for platforms without a native build. After hydration it selects the visitor's operating system; without JavaScript every command shows. It also adds PlatformIcon (Apple, Tux, and Windows marks in currentColor) and a PlatformBadges "Runs on" row, plus platformLabel, platformMark, detectPlatform, and matchDetectedPlatform at the package root. See Install commands for each platform. No existing export changes. Version 0.28.0 adds the launch kit. @hraness/design-kit/mockups draws labelled illustrations of chat, feeds, inboxes, articles, terminals, browsers, desktop windows, menu bars, and phones in server-safe plain React with no StyleX or React Aria, styled by @hraness/design-kit/mockups.css; each renders as one image with a text description and no headings. @hraness/design-kit/mockups/client adds ModeShowcase, FitToWidth, and StepThrough. @hraness/design-kit/launch, with no React, holds the LaunchBeat, LaunchFacts, and SocialKit shapes with resolveLaunchBeats, buildSocialKit, and assertLaunchKit. @hraness/design-kit/testing gives product repositories blogConformance, renderMatrix, assertNoHeadings, assertRoleImgWithLabel, and assertFakeHandles. The React entries add ArticleFigure, ArticleVideo with articleVideoJsonLd, ArticleTable, ArticleBarChart, ComparisonTable, LaunchBeats, and SocialKitPanel, and MarketingProofFrame takes chrome (window, browser with url, or terminal). ARTICLE_COPY.md now writes "Introducing a product" as beats; the admission rules are unchanged. No existing export changes. Version 0.27.0 adds explicit Rust, TOML, YAML, Lean, and TLA+ code highlighting, including nested comments and multiline strings. The existing highlighter API and stylesheet remain the shared entry points; unsupported languages still render as escaped text. Version 0.26.0 adds MarketingNotice and the raw .hraness-marketing-notice class: one status or alert line (info, success, or error) that sits on the content edge of the page column instead of spanning the window. Errors are announced as alerts and other tones as status. Signed-in account pages that render inside MarketingSiteHeader should compose MarketingMain, MarketingNotice, MarketingStatStrip, and MarketingSection so every block shares the header's measure and gutter, rather than hand-rolled full-bleed banners and nested cards. Version 0.25.0 moves bordered foil surfaces off the spectrum: .hraness-foil and [data-emphasis="primary"] actions keep their flat surface fill under one theme-aware monochrome edge — black on light, white on dark, overridable through the new --hraness-foil-edge token — while the metallic spectrum stays on wordmarks and marks. The foil material contract advances to version 3: surfaceImage and surfaceBackgroundClip are replaced by surfaceFill, edge, edgeDark, and edgeColor. Reinstall any vendored marketing snapshot from the 0.25.0 commit. Version 0.24.0 joins a marketing footer directly followed by the shared @hraness/site-footer network footer into one band: the product row's bottom space collapses to a compact gap and the network row's content follows the marketing measure and gutter through --hraness-site-footer-measure, so product links and the organization row share one column instead of reading as two stacked bars. Mount the network footer directly after MarketingSiteFooter; no prop or markup change is needed inside either landmark. Version 0.23.0 moves marketing pages to the Quiet direction described in DESIGN.md. ProductHero no longer renders a backdrop, HeroBackdrop renders nothing, and attachHeroLight is an inert disposer; their names and props stay so existing code compiles and are marked deprecated. The cells, weave, contour, and mesh patterns render as none, and marketing fields and Lantern walls paint the flat palette background. Both marketing presets use Nebula Sans for display headings (editorial at weight 550); Instrument Serif stays vendored for explicit opt-in only. Hero eyebrows are plain labels, the accent band loses its grid, and cards and header chrome use hairline edges instead of lifted shadows. On phones MarketingSiteHeader keeps the brand, primary action, and appearance menu on one row and moves the links to a second row that scrolls sideways, with 44px targets. To migrate, delete backdrop artwork, HeroBackdrop, attachHeroLight, pattern values, and product CSS that recreated textures or serif headings, then reinstall any vendored marketing snapshot from the 0.23.0 commit. Version 0.22.2 restores the canonical roughday mark — the rain-cloud artwork pinned by the portfolio registry — after the mark slot briefly carried a stray illustration. Version 0.22.1 completes the icon library with the roughday inline chip family (65 admitted assets, no pending members). Version 0.22.0 adds the ./icons surface: 62 vetted single-ink assets across ten sets — a shared illustration family, product illustration families, and the product marks, each admitted through a measured gate (single ink, bounded aspect, coverage, stroke, paths, bytes) and per-set family coherence, distributed as a typed registry module (hranessIcons, hranessIcon, hranessIconMarkup, hranessIconsForSet) and as canonical ./icons/<set>/<slug>.svg files for image consumers. See ICONS.md. Version 0.21.1 accepts status-page routes straight from a sitemap: long titles are shortened for the "Did you mean" line and unusable entries are dropped instead of failing the render. Version 0.21.0 replaces the 404 and route-error pages with one shared status page: the product's main action, a "Did you mean" link to the closest known page, at most three next links with one-line descriptions, an optional line for AI agents, and a glyph drawn as dots that gather, move away from the pointer, and settle. RouteNotFoundPage, RouteErrorPage, and GlobalErrorDocument render it; renderStatusPageHtml and attachStatusPage bring the same page to static sites; status-page.css ships in styles.css and compiler-foundation.css. The default copy changes to "We can’t find that page". See STATUS_PAGES.md. Version 0.20.0 gives ThemeMenuButton the Lantern material: under data-hraness-material="lantern" its trigger is raised at rest and inset while pressed, and its menu uses the material plane, lift and warm selection, with no new props. Sites can delete their local Lantern CSS for the appearance menu. It also adds formatRelativeTime at the package root and a hydration-safe RelativeTime component in @hraness/design-kit/react. Version 0.19.0 redraws related-product cards as the hraness.com project cards: each shows the product's mark, its name, and its one-line role at reading size, and no longer renders the relationship sentence (the prop stays accepted and is deprecated). MarketingRelatedProduct and the static ArticleRelatedLink take a mark, and every @hraness/design-kit/portfolio product and relatedFor() item now carries its portfolio mark as an inert data:image/svg+xml URL. Related-tier and proof headings move to the new --hraness-marketing-h3-size token, so the editorial display face never renders below 1.75rem. typography.css gains per-level heading tokens (--hraness-type-h1-font through --hraness-type-h4-font, the matching -weight tokens, --hraness-type-subheading-font, and --hraness-type-display-min). h3 and h4 in .hraness-prose and publication articles now use the text face, and the h2 floor rises to 1.75rem. Version 0.18.3 adds the Sponge and Wordcell runtime relations to Oh to the @hraness/design-kit/portfolio snapshot, so related-product cards on all three sites can link them. Version 0.18.2 refreshes the @hraness/design-kit/portfolio snapshot from the registry: the public ghostget.com entry, updated Gobstopper and xcb naming and descriptions, and the current sibling-product relations for related-product cards. Version 0.18.1 adds the Perplexity mark and a dependency-free @hraness/design-kit/provider-marks leaf entry (providerMark, providerMarks, providerMarkFallback, providerMarkGlyphDataUri, providerMarkArtDataUri, providerMarkOnAccent, providerMarkMonogram) so packages such as @hraness/ui can consume the registry without pulling the marketing surface. Version 0.18.0 adds the shared provider-mark registry: providerMarks, providerMark() alias resolution, ProviderMark/ProviderMarkChip accent-tinted tile components on both React entry points, vendored agent and vendor artwork from LobeHub icons 1.95.1 plus documented Crush and Aider marks, monogram fallbacks for uncovered names, and data-URI helpers for CSS-mask consumers. See vendor/provider-marks/UPSTREAM.md for asset provenance. Version 0.17.2 carries the canonical portfolio messaging record per product (names, category, tagline, short, meta, medium, long, and hero copy) in the /portfolio snapshot. Version 0.17.1 lets Next.js apps that compile dependencies with Babel build the article layer: its link check no longer uses a Unicode property escape that Babel cannot rewrite, and it rejects the same control characters and whitespace as before. Version 0.17.0 adds the article layer: server-safe MarketingArticle, byline, provenance, sources, callout, related-product, and index components, a matching static HTML renderer for sites without React, long-form article styles in plain-publication.css, and the ArticleAdmission rubric with assertArticleAdmissions(). It also adds @hraness/design-kit/portfolio, a snapshot of public product names, one-liners, links, and relations for related-product cards and "How X uses Y" posts. See ARTICLE_COPY.md. Version 0.16.4 keeps native appearance-menu labels usable inside keyboard-focusable page landmarks, including mouse and touch selection. Version 0.16.3 keeps decorative hero backgrounds hidden in forced colors and reduced transparency when other packages load their styles afterward. Version 0.16.2 preserves palette hues in translucent headers, material surfaces, and decorative highlights. Version 0.16.1 keeps plain reading pages and nested compiled surfaces in their selected palette, including system appearance without JavaScript. Version 0.16.0 adds a shared bounded hero light, product-owned backdrop slots, five Lantern patterns, palette-derived soft surfaces, and responsive typography across marketing and reading layouts. Static sites can use the palette bridge for system appearance before JavaScript. See HERO_FIELDS.md, LANTERN_MATERIAL.md, and MARKETING_PRESET.md. Version 0.15.0 lets MarketingRelated present labeled tiers of sibling products: a groups collection renders each tier under its own heading with an accessible card-row label, while the flat items shape stays available for a single group. Version 0.14.0 adds MarketingRelated, a collection section that presents sibling products as linked cards, each framed by its relationship to the featured product. Version 0.13.0 publishes --hraness-sticky-offset from sticky marketing chrome, gives MarketingMain and the next sticky sibling a clearance contract, stretches marketing card rows to the tallest item with a reserved two-line meta block, and clips per-card art wells so a logo surface cannot paint through the gutter. Version 0.12.0 replaces pointer-driven gradient rotation with a steady material and a moving light: the spectrum and its 115deg direction stay fixed while --hraness-foil-x/--hraness-foil-y highlights travel across each lockup, and marketing footers can opt into the same icon-plus-wordmark foil as the site header with brandMark. Version 0.11.1 preserves visible metallic marks when another package repeats a generic hidden atom in a later CSS layer. React consumers load components.css or styles.css; the raw marketing entry supports authored HTML hooks. Version 0.11 adds server-rendered FoilMark artwork and metallic text with subtle rainbow reflections, including a shared header mark seam and original-artwork fallbacks. Version 0.10.1 adds conservative server syntax defaults, includes their styles in the narrow marketing entry, and pins the icon dependency to preserve fresh Linux installs. Version 0.10 adds the shared foil contract: .hraness-foil surfaces and .hraness-foil-text wordmarks render a pointer-following metallic spectrum from the --hraness-foil-* custom properties, applied by default to marketing header brands and primary actions. The attachFoil browser export drives the bounded x/y/angle inputs with damped easing, reduced-motion and forced-color fallbacks, and no style injection; the same spectrum feeds @hraness/site-footer signup controls. Dark appearances use a deeper palette so the sheen stays visible. Version 0.8 removes Jelly's optional API, stylesheet, vendor runtime and theme-provider side effect. Migrate direct Jelly surfaces to native shared primitives before upgrading. Lantern now uses softer directional depth and shaded faces; the marketing preset adds individually shaded static cells, theme-aware terminal colors and window chrome. Code blocks retain source lines and follow the active palette. Paper preferences, semantic palettes, and compiler identity stay stable. Compiler adopters must regenerate their finalized stylesheet with the new package manifest.
The complete check runs linting, typechecking, production builds, an installed-package smoke test, deterministic examples, property tests, server rendering, vendor-integrity checks, and headless Chromium regressions. The browser gate verifies responsive shell ownership, extracted AnimatedRailStage, Fader, layout-surface, and playback-transport delivery, reduced-motion stage fallback, Fader keyboard and focus behavior, forced-color behavior, keyboard-operable appearance, browser-chrome synchronization across opposing device and saved preferences, global-error static metadata and runtime lifecycle, accessible title and copy, deterministic procedural layers, viewport containment, and the absence of the excluded canvas effect. Provision the pinned Chromium with bun run browser:install. An explicit CHROMIUM_EXECUTABLE_PATH must resolve to a provisioned Chrome for Testing; the browser gate rejects installed auto-updating Chrome.
Global CSS remains the boundary for tokens, resets, document grammar, cross-component layout, accessibility media rules, and audited vendor fallbacks. A new overlapping compiled style family from another package requires a layer-order compatibility gate against that package's released artifact before adoption.
Read CONTRIBUTING.md before opening a pull request. Report suspected vulnerabilities as described in SECURITY.md.
MIT. Vendored Nebula Sans, Geist Mono, and other upstream artifacts retain their own included license and provenance files.
Import @hraness/design-kit/product-marketing-preset.css after your existing marketing styles, then opt in with <MarketingPage preset="editorial">. The editorial preset sets one large Nebula Sans heading at weight 550 on the flat palette background, with hairline rules and a thin header whose links move to their own scrolling row on phones. preset="minimal" uses a more compact sans hierarchy. Hero backdrops, patterns, and textures are retired; see Hero fields. Product accents and copy remain product-owned.
Writing for the marketing components says what each slot is for and which copy patterns to avoid.
Marketing preset contract documents the native HTML hooks, shared tokens, paint-only header hook, and immutable CSS/font/asset snapshots for consumers that retain an older component release. The preset leaves application UI outside its explicit scope untouched.
Mockups use inline-size container queries. Give their ancestor a width or let it
stretch in its grid or flex layout; a shrink-wrapped figure cannot infer its
width from size-contained children. For a figure in a grid with
justify-items: start, set justify-self: stretch, inline-size: 100%, and
min-inline-size: 0 on the figure. Keep its maximum width in the product layout.
MarketingPillars accepts presentation="benefits" for a generous open grid with optional decorative icon, a short label, and one-sentence summary. Use explicit columns for six-item grids. Keep statistical evidence in the numerical components; benefits do not need oversized numbers. StepThrough uses a descriptive left selector on wide screens and a top tab strip on compact screens, with Back and Next above the preview, plain muted step numbers, and a screen-reader-only progress announcement. Optional hints remain visible beside their labels or above the compact preview. Its render functions stay mounted; use pure mockups and keep inactive panels inert.
Icon cards keep a square mark beside the full title and summary while the copy has at least 10rem of reading width. When the card is narrower, including at larger text sizes, the copy wraps below the mark and takes the available width.
ProductHero layout="split" places copy beside the frame from 64rem upwards, with proof and facts across both columns. It stays stacked on smaller screens or when no frame is present. Use align="start" for left-aligned copy.
ProductHero accepts an install slot directly below its summary, before secondary actions, and omits an empty product name. Install commands in PlatformInstall use the shared shell syntax highlighter and a restrained primary border and shadow. Copying still uses the original command string.
For a sequence that mixes terminals and reports, use StepThrough fit="fill".
Each frame fills the tallest complete slide at the current width; selecting a
step leaves that height unchanged. This mode wraps content without scaling it,
so minWidth applies only to the default natural layout. Set
TerminalFrame density="presentation" inside that walkthrough to fit complete terminal lines at one shared size between
1rem and 4rem in the available width and height, without adding line wraps beyond
the readable baseline. Keep the full lines stable
while animating their reveal. Text zoom can increase the natural stage height.
An explicit nested FitToWidth keeps a desktop graphic at its declared minimum
width and scales the whole graphic on smaller screens. Filled showcases reserve
that scaled natural height alongside phones and readable terminals. Their fill
rules and terminal type fitting stop at the graphic boundary, including any
terminal drawn inside it.