From 512884fca53a0e842f5971a18f52d7aeb8cb845b Mon Sep 17 00:00:00 2001
From: 0thernet
Date: Mon, 5 Oct 2026 00:44:57 -0400
Subject: [PATCH 1/3] Pin interface-card links to the card foot; release
v0.41.1
- Add an optional `link` slot to `MarketingInterfaceGrid` entries so the
shared component owns the docs/reference link: pinned to the foot of the
equal-height card, end-aligned, and set in muted ink with a quiet
underline.
- Lay interface cards out as a flex column so `margin-block-start: auto`
anchors the action row regardless of example height, and set card code
at 0.78rem.
- Slim proof-frame chrome padding and let `chrome="terminal"` and
`chrome="window"` render without a `title`, drawing only the window
lights so consumers can drop the generic "Terminal" label; browser
chrome still requires a `url` or `title`.
- Mirror the descendant rules across `product-marketing.css` and
`product-marketing-foundation.css`, register the new atoms in the
StyleX recipes, and cover the slot and titleless chrome in tests and
the gallery fixture.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---
README.md | 8 +++-
dist/chunk-w76f3ah1.js | 42 +++++++++++++++---
dist/stylex.css | 20 +++++++--
gallery/product-marketing-fixture.tsx | 4 +-
package.json | 2 +-
portfolio-inventory.json | 2 +-
scripts/product-marketing-browser.ts | 2 +-
src/product-marketing-foundation.css | 6 ++-
src/product-marketing.css | 48 +++++++++++++++++++--
src/react/design-gallery.tsx | 4 +-
src/react/product-marketing-stylex.test.tsx | 2 +-
src/react/product-marketing.stylex.ts | 27 ++++++++++--
src/react/product-marketing.test.tsx | 46 ++++++++++++++++++++
src/react/product-marketing.tsx | 20 +++++++--
14 files changed, 200 insertions(+), 33 deletions(-)
diff --git a/README.md b/README.md
index f66728d..2eec813 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Pin a GitHub release tag:
```json
{
"dependencies": {
- "@hraness/design-kit": "github:hraness/design-kit#v0.41.0",
+ "@hraness/design-kit": "github:hraness/design-kit#v0.41.1",
"@hraness/ui": "github:hraness/ui#v0.5.17"
}
}
@@ -72,6 +72,10 @@ header titled “My workspace” above “Saved work”. `TopBar` renders a sema
## Release notes and focused examples
+### Quieter interface cards
+
+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.
+
### Product landscapes
Version 0.41.0 adds `product-landscape.css`: one product-specific drawing behind
@@ -982,7 +986,7 @@ bun run browser:install
bun run check
```
-The stable dependency pair for this release is `@hraness/ui` `v0.5.17` with `@hraness/design-kit` `v0.41.0`. 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](#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](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](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//.svg` files for image consumers. See [ICONS.md](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](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](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](HERO_FIELDS.md), [LANTERN_MATERIAL.md](LANTERN_MATERIAL.md), and [MARKETING_PRESET.md](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 stable dependency pair for this release is `@hraness/ui` `v0.5.17` with `@hraness/design-kit` `v0.41.1`. 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](#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](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](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//.svg` files for image consumers. See [ICONS.md](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](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](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](HERO_FIELDS.md), [LANTERN_MATERIAL.md](LANTERN_MATERIAL.md), and [MARKETING_PRESET.md](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.
diff --git a/dist/chunk-w76f3ah1.js b/dist/chunk-w76f3ah1.js
index 7360af3..49402be 100644
--- a/dist/chunk-w76f3ah1.js
+++ b/dist/chunk-w76f3ah1.js
@@ -1269,7 +1269,7 @@ var marketingStyles = {
k1xSpc: "x78zum5",
kkeX5w: "x6s0dn4",
kOIVth: "x8233eu",
- kmVPX3: "x86o7ao",
+ kmVPX3: "xrqs5gp",
kb5WsR: "x1drtmal xhobzj1",
kTJQHc: "xs301gt xwaqzdf",
ke4D0g: "xothggy x1j9yjdw",
@@ -2214,10 +2214,10 @@ var marketingStyles = {
$$css: true
},
interface: {
- k1xSpc: "xrvj5dj",
+ k1xSpc: "x78zum5",
+ kvQiKF: "xdt5ytf",
kdYMnH: "xesnm00",
kVQ08L: "x1ljpu7r",
- kNk6WL: "x10ukxgv",
kOIVth: "x13z6uf9",
kmVPX3: "x1nn0urv",
k99D8V: "x17p5ghk x18z9243",
@@ -2285,6 +2285,20 @@ var marketingStyles = {
kN5DiO: "x1evy7pa",
$$css: true
},
+ interface__action: {
+ kAiAap: "x1n5yk0f",
+ kLh5Sq: "x1qzg9v8",
+ kMCLAl: "xp4054r",
+ $$css: true
+ },
+ interface__link: {
+ kMwMTN: "xs87ocq x16tyrwk",
+ kXaGww: "xujl8zx",
+ kRHfhz: "x4k6xgu x9ojkr9 x1e7jyuc",
+ kKoZWP: "xyi4chj",
+ k1PBYE: "x5hzr90",
+ $$css: true
+ },
trust_item__label: {
kogj98: "x1ghz6dp",
kLh5Sq: "x1ksoetq",
@@ -3659,6 +3673,12 @@ var recipes = {
"hraness-marketing-interface__summary": {
default: marketingStyles.interface__summary
},
+ "hraness-marketing-interface__action": {
+ default: marketingStyles.interface__action
+ },
+ "hraness-marketing-interface__link": {
+ default: marketingStyles.interface__link
+ },
"hraness-marketing-trust-item__label": {
default: marketingStyles.trust_item__label
},
@@ -4475,8 +4495,8 @@ function MarketingProofFrame({
throw new RangeError('Proof frame url needs chrome="browser".');
const bar = chrome ?? (title === undefined ? undefined : "window");
const label = bar === "browser" ? url === undefined ? title : marketingProofFrameAddress(url) : title;
- if (bar !== undefined && (label === undefined || label.trim() === "")) {
- throw new RangeError(`Proof frame chrome=${bar} needs ${bar === "browser" ? "a url or title" : "a title"}.`);
+ if (bar === "browser" && (label === undefined || label.trim() === "")) {
+ throw new RangeError("Proof frame chrome=browser needs a url or title.");
}
return /* @__PURE__ */ jsxs3("figure", {
className: marketingClassName("hraness-marketing-proof-frame", className),
@@ -4504,7 +4524,7 @@ function MarketingProofFrame({
bar === "browser" ? /* @__PURE__ */ jsx3("span", {
className: marketingClassName("hraness-marketing-proof-frame__address"),
children: label
- }) : /* @__PURE__ */ jsx3("span", {
+ }) : label === undefined || label.trim() === "" ? null : /* @__PURE__ */ jsx3("span", {
className: marketingClassName("hraness-marketing-proof-frame__title", undefined, bar === "terminal" ? "terminal" : "default"),
children: label
})
@@ -4882,7 +4902,15 @@ function MarketingInterfaceGrid({
className: marketingClassName("hraness-marketing-interface__summary"),
children: entry.summary
}),
- entry.example
+ entry.example,
+ entry.link === undefined ? null : /* @__PURE__ */ jsx3("p", {
+ className: marketingClassName("hraness-marketing-interface__action"),
+ children: /* @__PURE__ */ jsx3("a", {
+ className: marketingClassName("hraness-marketing-interface__link"),
+ href: entry.link.href,
+ children: entry.link.label
+ })
+ })
]
}, entry.label))
})
diff --git a/dist/stylex.css b/dist/stylex.css
index 71685a1..8cf2871 100644
--- a/dist/stylex.css
+++ b/dist/stylex.css
@@ -85,6 +85,10 @@
padding: .5rem 1rem 4rem;
}
+ .xrqs5gp {
+ padding: .5rem 1rem;
+ }
+
.x1i4hug3 {
padding: .65rem .8rem;
}
@@ -101,10 +105,6 @@
padding: .75rem .95rem;
}
- .x86o7ao {
- padding: .8rem 1rem;
- }
-
.x1717udv {
padding: 0;
}
@@ -2943,6 +2943,10 @@
margin-block-start: 1rem;
}
+ .x1n5yk0f {
+ margin-block-start: auto;
+ }
+
.xvc5jky {
margin-inline-start: auto;
}
@@ -3390,6 +3394,10 @@
text-align: center;
}
+ .xp4054r {
+ text-align: end;
+ }
+
.x1yc453h {
text-align: start;
}
@@ -3428,6 +3436,10 @@
text-transform: uppercase;
}
+ .x5hzr90 {
+ text-underline-offset: .18em;
+ }
+
.xdsgf93 {
text-underline-offset: .22em;
}
diff --git a/gallery/product-marketing-fixture.tsx b/gallery/product-marketing-fixture.tsx
index d45118c..6894539 100644
--- a/gallery/product-marketing-fixture.tsx
+++ b/gallery/product-marketing-fixture.tsx
@@ -153,8 +153,8 @@ export function ProductMarketingFixture({ api }: Readonly<{ api: typeof Marketin
That link expired.
Snapshot todayonly>} stats={facts} />
Consumer paragraph.
},
- { label: "SDK", summary: "For typed code.", example: relay.run()
},
+ { label: "CLI", summary: "For terminal users.", example: Consumer paragraph.
, link: { href: "#interfaces", label: "Read the CLI guide" } },
+ { label: "SDK", summary: "For typed code.", example: relay.run()
, link: { href: "#interfaces", label: "Read the SDK guide" } },
]} />
, href: "#fixture", title: "Grok 4.7", meta: "First observed 21 September 2026." },
diff --git a/package.json b/package.json
index e5e2e47..b20d30f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@hraness/design-kit",
- "version": "0.41.0",
+ "version": "0.41.1",
"description": "React components and CSS for Hraness apps and product sites: application shells, marketing sections, charts, themes, effects, and syntax highlighting. Built on @hraness/ui.",
"license": "MIT",
"type": "module",
diff --git a/portfolio-inventory.json b/portfolio-inventory.json
index 504ae89..b5da6f6 100644
--- a/portfolio-inventory.json
+++ b/portfolio-inventory.json
@@ -6,7 +6,7 @@
"name": "@hraness/design-kit",
"path": ".",
"visibility": "public",
- "version": "0.41.0"
+ "version": "0.41.1"
}
],
"contract": "hraness.portfolio-inventory/v1",
diff --git a/scripts/product-marketing-browser.ts b/scripts/product-marketing-browser.ts
index 08de1a7..fbdff45 100644
--- a/scripts/product-marketing-browser.ts
+++ b/scripts/product-marketing-browser.ts
@@ -32,7 +32,7 @@ async function legacyStylesheetHash(): Promise {
const syntaxImport = '@import "./syntax-highlighting.css";\n@import "./site-shell.css";\n\n';
assert(source.startsWith(syntaxImport), "The marketing entry lost its exact syntax import");
const grammarSha256 = createHash("sha256").update(source.slice(syntaxImport.length)).digest("hex");
- assert.equal(grammarSha256, "d73aadc873410e05dd148143c693baa6e263053bb4caa80db3859842e1f48dab", "The independent static CSS grammar changed");
+ assert.equal(grammarSha256, "bcb20eb716d0320acc277accc73bc48f36ec74a5f8566e43b11130dbe0e1d154", "The independent static CSS grammar changed");
return createHash("sha256").update(source).digest("hex");
}
diff --git a/src/product-marketing-foundation.css b/src/product-marketing-foundation.css
index e1b14a5..30651ba 100644
--- a/src/product-marketing-foundation.css
+++ b/src/product-marketing-foundation.css
@@ -304,10 +304,14 @@
line-height: 1.5;
}
-.hraness-marketing-interface pre {
+.hraness-marketing-interface > :where(pre) {
margin-block-start: 0.5rem;
}
+.hraness-marketing-interface :where(pre) {
+ font-size: 0.78rem;
+}
+
.hraness-marketing-question__answer > :first-child {
margin-block-start: 0;
}
diff --git a/src/product-marketing.css b/src/product-marketing.css
index b9f5448..709ab21 100644
--- a/src/product-marketing.css
+++ b/src/product-marketing.css
@@ -1081,7 +1081,7 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
display: flex;
align-items: center;
gap: 0.75rem;
- padding: 0.8rem 1rem;
+ padding: 0.5rem 1rem;
background-image: var(--hraness-marketing-frame-chrome, none);
box-shadow: var(--hraness-marketing-chrome-shadow);
border-block-end: var(--hraness-marketing-chrome-rule);
@@ -1641,10 +1641,8 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
.hraness-marketing-interface,
.hraness-marketing-trust-item {
- display: grid;
min-inline-size: 0;
min-block-size: 100%;
- align-content: start;
gap: 0.5rem;
padding: clamp(1.25rem, 2.5vw, 1.75rem);
border: var(--hraness-marketing-surface-rule);
@@ -1653,6 +1651,17 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
box-shadow: var(--hraness-marketing-surface-shadow);
}
+/* A vertical column lets the optional action pin to the foot of the card. */
+.hraness-marketing-interface {
+ display: flex;
+ flex-direction: column;
+}
+
+.hraness-marketing-trust-item {
+ display: grid;
+ align-content: start;
+}
+
.hraness-marketing-interface :where(.hraness-marketing-interface__heading, p),
.hraness-marketing-trust-item :where(dt, dd) {
margin: 0;
@@ -1672,10 +1681,41 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
line-height: 1.5;
}
-.hraness-marketing-interface pre {
+/* The card's reference link sits at the foot, quiet and end-aligned. */
+.hraness-marketing-interface .hraness-marketing-interface__action {
+ margin-block-start: auto;
+ font-size: 0.85rem;
+ text-align: end;
+}
+
+.hraness-marketing-interface__link {
+ color: var(--hraness-marketing-muted);
+ text-decoration-line: underline;
+ text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
+ text-decoration-thickness: 1px;
+ text-underline-offset: 0.18em;
+}
+
+.hraness-marketing-interface__link:hover,
+.hraness-marketing-interface__link:focus-visible {
+ color: var(--hraness-marketing-ink);
+ text-decoration-color: currentColor;
+}
+
+@media (forced-colors: active) {
+ .hraness-marketing-interface__link {
+ color: LinkText;
+ }
+}
+
+.hraness-marketing-interface > :where(pre) {
margin-block-start: 0.5rem;
}
+.hraness-marketing-interface :where(pre) {
+ font-size: 0.78rem;
+}
+
/* Equal-height product card rows. Direct children stretch to the tallest
* item. Meta occupies a reserved two-line block so one long blurb does not
* stagger the row. Titles wrap without clamping. Each card is a containing
diff --git a/src/react/design-gallery.tsx b/src/react/design-gallery.tsx
index 46707e0..39d6358 100644
--- a/src/react/design-gallery.tsx
+++ b/src/react/design-gallery.tsx
@@ -512,8 +512,8 @@ export function DesignSystemGallery({
Consumer-owned content can include links and inline code.
},
- { label: "SDK", summary: "Use typed application code." },
+ { label: "CLI", summary: "Run a named job.", example: , link: { href: "#gallery-install", label: "Read the CLI guide" } },
+ { label: "SDK", summary: "Use typed application code.", link: { href: "#gallery-install", label: "Read the SDK guide" } },
]} />
, href: "#marketing", title: "Grok 4.7", meta: "First observed 21 September 2026." },
diff --git a/src/react/product-marketing-stylex.test.tsx b/src/react/product-marketing-stylex.test.tsx
index 609f03f..1620beb 100644
--- a/src/react/product-marketing-stylex.test.tsx
+++ b/src/react/product-marketing-stylex.test.tsx
@@ -215,7 +215,7 @@ test("the reviewed static grammar and 30-token foundation stay separate from own
const syntaxImport = '@import "./syntax-highlighting.css";\n@import "./site-shell.css";\n\n';
expect(legacy).toStartWith(syntaxImport);
expect(createHash("sha256").update(legacy.slice(syntaxImport.length)).digest("hex"))
- .toBe("d73aadc873410e05dd148143c693baa6e263053bb4caa80db3859842e1f48dab");
+ .toBe("bcb20eb716d0320acc277accc73bc48f36ec74a5f8566e43b11130dbe0e1d154");
const tokenNames = (text: string) => [...new Set([...(text.match(/:where\([\s\S]*?\)\s*\{([^}]*)\}/u)?.[1] ?? "").matchAll(/(--hraness-marketing-[a-z-]+):/gu)].map((match) => match[1]))].sort();
expect(tokenNames(foundation)).toHaveLength(30);
expect(tokenNames(foundation)).toEqual(tokenNames(legacy));
diff --git a/src/react/product-marketing.stylex.ts b/src/react/product-marketing.stylex.ts
index adfc1ae..0490915 100644
--- a/src/react/product-marketing.stylex.ts
+++ b/src/react/product-marketing.stylex.ts
@@ -1195,7 +1195,7 @@ export const marketingStyles = stylex.create({
"display": "flex",
"align-items": "center",
"gap": "0.75rem",
- "padding": "0.8rem 1rem",
+ "padding": "0.5rem 1rem",
"background-image": { default: "var(--hraness-marketing-frame-chrome, none)", "@media (forced-colors: active)": "none" },
"box-shadow": { default: "var(--hraness-marketing-chrome-shadow)", "@media (forced-colors: active)": "none" },
"border-block-end": { "default": "var(--hraness-marketing-chrome-rule)", "@media (forced-colors: active)": "1px solid CanvasText" },
@@ -2216,10 +2216,10 @@ export const marketingStyles = stylex.create({
"grid-template-columns": "repeat(auto-fit, minmax(min(100%, var(--_hraness-marketing-grid-track, 16rem)), 1fr))"
},
"interface": {
- "display": "grid",
+ "display": "flex",
+ "flex-direction": "column",
"min-inline-size": "0",
"min-block-size": "100%",
- "align-content": "start",
"gap": "0.5rem",
"padding": "clamp(1.25rem, 2.5vw, 1.75rem)",
"border-top": {
@@ -2395,6 +2395,25 @@ export const marketingStyles = stylex.create({
"font-size": "0.95rem",
"line-height": "1.5"
},
+ "interface__action": {
+ "margin-block-start": "auto",
+ "font-size": "0.85rem",
+ "text-align": "end"
+ },
+ "interface__link": {
+ "color": {
+ "default": "var(--hraness-marketing-muted)",
+ ":hover": "var(--hraness-marketing-ink)"
+ },
+ "text-decoration-line": "underline",
+ "text-decoration-color": {
+ "default": "color-mix(in srgb, currentColor 40%, transparent)",
+ ":hover": "currentColor",
+ ":focus-visible": "currentColor"
+ },
+ "text-decoration-thickness": "1px",
+ "text-underline-offset": "0.18em"
+ },
"trust_item__label": {
"margin": "0",
"font-size": "1.05rem",
@@ -4483,6 +4502,8 @@ const recipes = {
"hraness-marketing-trust-item": { "default": marketingStyles.trust_item },
"hraness-marketing-interface__heading": { "default": marketingStyles.interface__heading },
"hraness-marketing-interface__summary": { "default": marketingStyles.interface__summary },
+ "hraness-marketing-interface__action": { "default": marketingStyles.interface__action },
+ "hraness-marketing-interface__link": { "default": marketingStyles.interface__link },
"hraness-marketing-trust-item__label": { "default": marketingStyles.trust_item__label },
"hraness-marketing-trust-item__detail": { "default": marketingStyles.trust_item__detail },
"hraness-marketing-card-row": { "default": marketingStyles.card_row },
diff --git a/src/react/product-marketing.test.tsx b/src/react/product-marketing.test.tsx
index d1f0ff6..1f18543 100644
--- a/src/react/product-marketing.test.tsx
+++ b/src/react/product-marketing.test.tsx
@@ -235,6 +235,52 @@ test("the marketing compositions preserve headings, native disclosure, and produ
expect(html).toMatch(marketingMarkupPattern('CLI
'));
});
+test("an interface card's reference link renders as its last child", () => {
+ const html = renderToStaticMarkup(
+ relay run,
+ label: "CLI",
+ link: { href: "https://example.com/cli", label: "Read the CLI guide" },
+ summary: "For terminals and scripts.",
+ },
+ { label: "SDK", summary: "For typed application code." },
+ ]}
+ label="Interfaces"
+ />,
+ );
+ const { document } = parseHTML(html);
+ const cards = [...document.querySelectorAll("article.hraness-marketing-interface")];
+ expect(cards).toHaveLength(2);
+ const action = cards[0]?.querySelector(":scope > p.hraness-marketing-interface__action:last-child");
+ expect(action).not.toBeNull();
+ const link = action?.querySelector("a.hraness-marketing-interface__link");
+ expect(link?.getAttribute("href")).toBe("https://example.com/cli");
+ expect(link?.textContent).toBe("Read the CLI guide");
+ expect(cards[1]?.querySelector(".hraness-marketing-interface__action")).toBeNull();
+ expect(html).not.toMatch(/onClick|