Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
- Keep `PlaybackTransport` as one stable large primary command with an exclusive accessible name, idle-only disablement, pending busy state, play-or-stop callback routing, logical 1.5rem glyph sizing, and its existing caller, id, shortcut, ref, and trailing-control seams. Do not add a public `xstyle` prop without a concrete consumer contract.
- Keep `Fader` as one React Aria single-value slider with its vertical, default-density, hidden-label, and hidden-output defaults. Preserve the full native slider prop surface, label accessory, root and input refs, stable hooks, exact public sizing properties, logical rail geometry, physical thumb cross-axis fallback, state-driven focus ring, caller-last class, and native style seams. Keep decorative rail nodes inert. Do not add a public `xstyle` prop without a concrete consumer contract.
- Keep decorative effects pointer-transparent, reduced-motion aware, forced-color safe, deterministic where seeded, and removable without losing meaningful content.
- Keep marketing surfaces on the Quiet direction in `DESIGN.md`. Hero backdrops, the hero light, material patterns, background textures, gradients behind text, and blur outside a sticky header are retired: keep their public names compiling but inert, and do not reintroduce them as shared defaults.
- Keep marketing surfaces on the Quiet direction in `DESIGN.md`. Hero backdrops, the hero light, material patterns, background textures, gradients behind text, and blur outside a sticky header or a product landscape are retired: keep their public names compiling but inert, and do not reintroduce them as shared defaults. The opt-in product landscape in `src/product-landscape.css` and `LANDSCAPE.md` is the one sanctioned page drawing (owner decision 2026-10-05): one product-specific luminance mask tiled behind public pages, with opaque rounded reading surfaces and frosted small cards over it. Keep it inert without a host and an image, and keep its forced-color, print, reduced-transparency, and phone contracts.
- Fix marketing visual defects in shared semantic roles and compositions, then update every affected consumer and vendored snapshot through immutable releases. Pair filled controls with their matching foreground; verify text contrast of at least 4.5:1 across palettes, light/dark, selection, hover, focus, and forced colors. Never hardcode white text on an accent fill.
- Keep showcase hints and figure captions optional. Remove redundant explanatory subtitles, generic illustration disclaimers, and repeated alt text from visible marketing copy. Keep accurate accessible descriptions and useful limitations without manufacturing filler for empty slots.
- Keep synchronous typography and layout measurements independent of consumer motion rules. Disable transitions and animations on the entire owned measurement clone before insertion and for its complete lifetime, including reduced-motion resets with `!important`; preserve live presentation styles and remove the clone in cleanup.
Expand Down
5 changes: 3 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ Public product pages follow the Quiet direction, taken from the structure of alg
- Explain the product's central benefit immediately after the hero, with a terminal or code block, a static screenshot with `alt` text, or compact real output. A mockup from `@hraness/design-kit/mockups`, or a `ModeShowcase` with a few of its states, can demonstrate the result. It uses invented names on example domains and an accurate accessible description. Add visible captions only when they contribute useful context.
- Ruled sections: each has an eyebrow, a heading, one short paragraph, and one concrete element such as steps, code, a table, or a small figure. Prefer rows and lists to walls of equal cards. Keep a bounded reading width.
- A flat palette background with content on opaque surfaces and hairline edges. Mono only for code, commands, and small labels.
- Optionally, one product landscape: a detailed, product-specific drawing tinted from the palette, kept in the margins, and tiled behind the marketing, documentation, and blog pages. Over it, reading surfaces stay opaque with rounded edges and small cards and callouts become frosted panels. See [Product landscape](LANDSCAPE.md).

Retired, and no longer produced by any shared default: hero backdrops and pointer-driven light (`ProductHero` `backdrop`, `HeroBackdrop`, `attachHeroLight`, `data-hraness-hero-item`), background textures and patterns (grain, cells, weave, contour, mesh, grid tiling, gradients behind text), serif display headings, glass or blur on cards and content, heavy shadows and glowing edges, and large interactive app mockups on marketing pages. The retired component names stay exported and do nothing, so products can remove them on their own schedule. See [Hero fields](HERO_FIELDS.md) and [Marketing preset](MARKETING_PRESET.md).
Retired, and no longer produced by any shared default: hero backdrops and pointer-driven light (`ProductHero` `backdrop`, `HeroBackdrop`, `attachHeroLight`, `data-hraness-hero-item`), background textures and patterns (grain, cells, weave, contour, mesh, grid tiling, gradients behind text), serif display headings, glass or blur on cards and content outside a product landscape, heavy shadows and glowing edges, and large interactive app mockups on marketing pages. The retired component names stay exported and do nothing, so products can remove them on their own schedule. See [Hero fields](HERO_FIELDS.md) and [Marketing preset](MARKETING_PRESET.md).

Blur remains allowed only on a sticky header with real content scrolling behind it, and that header turns opaque without backdrop support, with reduced transparency, and in forced colors.
Blur remains allowed on a sticky header with real content scrolling behind it, and on small cards and callouts over a product landscape. Both turn opaque without backdrop support, with reduced transparency, and in forced colors.

## Launch posts

Expand Down
4 changes: 3 additions & 1 deletion HERO_FIELDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ and their types are marked deprecated. Remove them when convenient.

A product that still passes decorative artwork should delete it rather than
recreate it in product CSS. Floating or blurred elements behind the heading make
it harder to read, especially on phones.
it harder to read, especially on phones. The one page-level drawing the kit
supports is a [product landscape](LANDSCAPE.md), which sits in the margins
behind the whole page rather than behind the hero copy.

`ProductHero` stays available from the server entry and no longer crosses a
client boundary. The `@hraness/design-kit/react/hero-backdrop` export remains a
Expand Down
100 changes: 100 additions & 0 deletions LANDSCAPE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Product landscape

A product landscape is one detailed drawing of the product's world, drawn
behind its marketing, documentation, and blog pages. It is the one sanctioned
exception to the Quiet direction's flat page background. The drawing stays in
the margins, takes its colour from the page's own text and background, and
tiles down the page, so it never competes with the copy and never runs out on
a long page.

## Supply the drawing

Give each product its own drawing. Prepare it as a grayscale luminance mask:
the drawing in light lines on black, with a quiet centre column where the copy
sits and both ends fading to black so the tiles meet without a seam. Export
WebP at three widths and name each file by its SHA-256 digest:

| Image | Width | Use |
| --- | --- | --- |
| Wide | 1920px | Desktop, 1x |
| Wide 2x | 2880px | Desktop, high-density screens |
| Narrow | 1170px | Phones up to 48rem |

Do not put text, logos, or colour in the drawing. The kit tints it for every
palette and appearance.

## Turn it on

Import the stylesheet once. `styles.css` and `compiler-foundation.css` already
include it.

```css
@import "@hraness/design-kit/product-landscape.css";

:root {
--hraness-landscape-image: image-set(
url("/landscape/landscape-<wide-digest>.webp") 1x,
url("/landscape/landscape-<wide-2x-digest>.webp") 2x
);
--hraness-landscape-image-narrow: url("/landscape/landscape-<narrow-digest>.webp");
}
```

Use root-relative or absolute URLs: custom properties resolve relative URLs
differently across browsers.

Then mark every public page that should show it. `data-hraness-landscape="page"`
on `<body>` or on any element inside the page draws the drawing behind the whole
document, so a marketing page, a documentation shell, and a blog layout can
each opt in from their own markup:

```tsx
<MarketingPage landscape="page">…</MarketingPage>
```

`MarketingPage` emits the attribute from its `landscape` prop; any other
element takes the attribute directly.

Put `data-hraness-landscape="off"` anywhere on an application route that
shares the layout. Use `data-hraness-landscape="contained"` to draw it behind one
element instead, such as a section below a full-screen application.

| Property | Default | Meaning |
| --- | --- | --- |
| `--hraness-landscape-image` | none (draws nothing) | Wide mask, usually an `image-set()` |
| `--hraness-landscape-image-narrow` | the wide image | Mask at 48rem and below |
| `--hraness-landscape-opacity` | `0.5` | Layer opacity on wide screens |
| `--hraness-landscape-opacity-narrow` | `0.36` | Layer opacity on phones, where copy spans the drawing |
| `--hraness-landscape-strength` | `30%` | Share of the text colour mixed into the background for the ink |
| `--hraness-landscape-ink` | the mix above | Replace the ink entirely |
| `--hraness-landscape-radius` | `0.75rem` | Radius for solid and glass surfaces |

Forced colours and print never draw the layer.

## Keep panels over the drawing soft

A hard-edged slab over a detailed drawing reads as a mistake. Under a
landscape, the kit adjusts its own surfaces:

- Page wrappers that repaint the page colour (`.hraness-marketing-page`, a
nested `.plain-site`, `.plain-footer`) become transparent, so the identical
body colour and the drawing show through.
- Reading surfaces stay opaque and get rounded edges: article code blocks and
tables.
- Small cards and callouts (marketing cards, quotes, plans, trust items,
interfaces, primitives, article callouts, and social kit items) become
frosted panels using the sticky header's material: 82% surface tint with
`blur(14px) saturate(1.4)`. Without backdrop support, under reduced
transparency, and in forced colours they keep their opaque surface.

Product-owned elements opt in with one attribute:

| Attribute | Use for |
| --- | --- |
| `data-hraness-landscape-surface="clear"` | A full-width wrapper or band painted in the page colour |
| `data-hraness-landscape-surface="solid"` | Code, tables, documentation sidebars, forms: opaque, rounded |
| `data-hraness-landscape-surface="glass"` | Small cards, callouts, and badges: frosted, rounded |

Set `--hraness-landscape-solid` or `--hraness-landscape-glass` on an element to
choose its base colour. Leave a deliberately coloured full-width band as it
is; it simply covers the drawing.
33 changes: 31 additions & 2 deletions README.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions dist/chunk-s96ppgkp.js → dist/chunk-9sjaav5t.js
Original file line number Diff line number Diff line change
Expand Up @@ -3921,14 +3921,18 @@ function MarketingPage({
children,
className,
id,
landscape,
preset,
pattern
}) {
if (preset !== undefined && preset !== "editorial" && preset !== "minimal")
throw new RangeError("Unknown marketing preset.");
if (landscape !== undefined && landscape !== "page" && landscape !== "contained" && landscape !== "off")
throw new RangeError("Unknown landscape host.");
assertMarketingPattern(pattern);
return /* @__PURE__ */ jsx3("div", {
className: marketingClassName("hraness-marketing-page", className),
"data-hraness-landscape": landscape,
"data-hraness-marketing": "page",
"data-hraness-marketing-preset": preset,
"data-hraness-pattern": pattern,
Expand Down
34 changes: 33 additions & 1 deletion dist/react/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ import {
marketingProofFrameAddress,
proceduralBackdropVariants,
proceduralRecipeVersion
} from "../chunk-s96ppgkp.js";
} from "../chunk-9sjaav5t.js";
import {
SyntaxCode
} from "../chunk-6ts4955n.js";
Expand Down Expand Up @@ -7922,6 +7922,38 @@ Run the job, then show me its log.`,
]
})
]
}),
/* @__PURE__ */ jsxs15("div", {
className: "design-gallery__landscape",
"data-hraness-landscape": "contained",
style: {
"--hraness-landscape-image": `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 300'%3E%3Crect width='400' height='300'/%3E%3Cg fill='none' stroke='white' stroke-width='1.5'%3E%3Cpath d='M0 240 L40 190 L70 215 L110 150 L150 205 L170 185'/%3E%3Cpath d='M230 185 L260 150 L300 200 L340 140 L370 180 L400 160'/%3E%3Cpath d='M20 270 C60 250 100 262 150 250'/%3E%3Cpath d='M250 252 C300 262 340 248 390 262'/%3E%3Ccircle cx='340' cy='60' r='16'/%3E%3C/g%3E%3C/svg%3E")`
},
children: [
/* @__PURE__ */ jsxs15("div", {
className: "design-gallery__landscape-copy",
children: [
/* @__PURE__ */ jsx17("h3", {
children: "Product landscape"
}),
/* @__PURE__ */ jsx17("p", {
children: "One product drawing, tinted from the palette and tiled down the page. Code and tables stay solid with rounded edges; small cards and callouts frost over it."
})
]
}),
/* @__PURE__ */ jsx17("pre", {
className: "design-gallery__landscape-code",
"data-hraness-landscape-surface": "solid",
children: /* @__PURE__ */ jsx17("code", {
children: "relay run job-01"
})
}),
/* @__PURE__ */ jsx17("p", {
className: "design-gallery__landscape-note",
"data-hraness-landscape-surface": "glass",
children: "The log is written next to the job."
})
]
})
]
}),
Expand Down
2 changes: 1 addition & 1 deletion dist/react/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import {
marketingProofFrameAddress,
proceduralBackdropVariants,
proceduralRecipeVersion
} from "../chunk-s96ppgkp.js";
} from "../chunk-9sjaav5t.js";
import {
SyntaxCode
} from "../chunk-6ts4955n.js";
Expand Down
2 changes: 1 addition & 1 deletion dist/stylex-manifest.json

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hraness/design-kit",
"version": "0.40.3",
"version": "0.41.0",
"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",
Expand Down Expand Up @@ -129,7 +129,8 @@
},
"./marketing-comparison.css": "./src/marketing-comparison.css",
"./marketing-diagram.css": "./src/marketing-diagram.css",
"./marketing-marquee.css": "./src/marketing-marquee.css"
"./marketing-marquee.css": "./src/marketing-marquee.css",
"./product-landscape.css": "./src/product-landscape.css"
},
"main": "./dist/index.js",
"types": "./src/index.ts",
Expand Down Expand Up @@ -314,7 +315,9 @@
"src/marketing-diagram.css",
"src/marketing-marquee.ts",
"src/marketing-marquee.css",
"src/react/marketing-marquee.tsx"
"src/react/marketing-marquee.tsx",
"src/product-landscape.css",
"LANDSCAPE.md"
],
"scripts": {
"build": "bun run ./scripts/build.ts",
Expand All @@ -328,7 +331,7 @@
"browser:install": "bun ./node_modules/playwright-core/cli.js install chromium",
"test:browser": "bun run test:browser:marketing && bun run test:browser:rest",
"test:browser:marketing": "bun run ./scripts/product-marketing-browser.ts",
"test:browser:rest": "bun run ./scripts/marketing-header-browser.ts && bun run ./scripts/site-shell-browser.ts && bun run ./scripts/palette-browser.ts && bun run ./scripts/gallery-browser.ts && bun run ./scripts/global-error-browser.ts && bun run ./scripts/security-delivery-browser.ts && bun run ./scripts/paper-theme-browser.ts && bun run ./scripts/product-marketing-preset-browser.ts && bun run ./scripts/lantern-material-browser.ts && bun run ./scripts/foil-browser.ts && bun run ./scripts/hero-light-browser.ts && bun run ./scripts/status-page-browser.ts && bun run ./scripts/marketing-marquee-browser.ts && bun run ./scripts/platform-install-browser.ts && bun run ./scripts/mockups-browser.ts && bun run ./scripts/agent-setup-browser.ts",
"test:browser:rest": "bun run ./scripts/marketing-header-browser.ts && bun run ./scripts/site-shell-browser.ts && bun run ./scripts/palette-browser.ts && bun run ./scripts/gallery-browser.ts && bun run ./scripts/global-error-browser.ts && bun run ./scripts/security-delivery-browser.ts && bun run ./scripts/paper-theme-browser.ts && bun run ./scripts/product-marketing-preset-browser.ts && bun run ./scripts/lantern-material-browser.ts && bun run ./scripts/foil-browser.ts && bun run ./scripts/hero-light-browser.ts && bun run ./scripts/status-page-browser.ts && bun run ./scripts/marketing-marquee-browser.ts && bun run ./scripts/product-landscape-browser.ts && bun run ./scripts/platform-install-browser.ts && bun run ./scripts/mockups-browser.ts && bun run ./scripts/agent-setup-browser.ts",
"test:package": "bun run ./scripts/package-smoke.ts && bun run ./scripts/mockups-import.ts",
"kb:refresh": "bunx --bun --package https://github.com/hraness/wordcell/releases/download/v0.24.0/hraness-wordcell-0.24.0.tgz wordcell refresh --root kb",
"kb:check:lane": "bunx --bun --package https://github.com/hraness/wordcell/releases/download/v0.24.0/hraness-wordcell-0.24.0.tgz wordcell check --root kb --no-catalog",
Expand Down
2 changes: 1 addition & 1 deletion portfolio-inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"name": "@hraness/design-kit",
"path": ".",
"visibility": "public",
"version": "0.40.3"
"version": "0.41.0"
}
],
"contract": "hraness.portfolio-inventory/v1",
Expand Down
1 change: 1 addition & 0 deletions scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const COMPILER_STYLESHEET_PATHS = [
"src/fonts.css",
"src/lantern-material.css",
"src/marketing-marquee.css",
"src/product-landscape.css",
"src/mockups.css",
"src/palette-bridge.css",
"src/palette-system.css",
Expand Down
3 changes: 3 additions & 0 deletions scripts/check-stylex-artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const COMPILER_STYLESHEET_PATHS = [
"src/fonts.css",
"src/lantern-material.css",
"src/marketing-marquee.css",
"src/product-landscape.css",
"src/mockups.css",
"src/palette-bridge.css",
"src/palette-system.css",
Expand Down Expand Up @@ -468,6 +469,7 @@ function requireAggregateContract(source: string): void {
'@import "./status-page.css";',
'@import "./product-marketing.css";',
'@import "./product-marketing-foundation.css";',
'@import "./product-landscape.css";',
'@import "./design-gallery.css";',
'@import "./mockups.css";',
'@import "./palette-bridge.css";',
Expand Down Expand Up @@ -500,6 +502,7 @@ function requireCompilerFoundationContract(source: string): void {
'@import "./status-page.css";',
'@import "./marketing-marquee.css";',
'@import "./product-marketing-foundation.css";',
'@import "./product-landscape.css";',
'@import "./design-gallery.css";',
'@import "./mockups.css";',
'@import "./lantern-material.css";',
Expand Down
1 change: 1 addition & 0 deletions scripts/package-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ const compilerStylesheetPaths = [
"src/fonts.css",
"src/lantern-material.css",
"src/marketing-marquee.css",
"src/product-landscape.css",
"src/mockups.css",
"src/palette-bridge.css",
"src/palette-system.css",
Expand Down
Loading
Loading