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
13 changes: 10 additions & 3 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stylex-manifest.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hraness/design-kit",
"version": "0.23.0",
"version": "0.24.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
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.23.0"
"version": "0.24.0"
}
],
"contract": "hraness.portfolio-inventory/v1",
Expand Down
4 changes: 2 additions & 2 deletions scripts/check-stylex-artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1608,8 +1608,8 @@ assert.deepEqual(
);
assert.deepEqual(
manifest.package,
{ name: "@hraness/design-kit", version: "0.23.0" },
"StyleX manifest must describe design-kit v0.23.0",
{ name: "@hraness/design-kit", version: "0.24.0" },
"StyleX manifest must describe design-kit v0.24.0",
);
assert.equal(manifest.compilerSha256, compilerSha256);
assert.equal(manifest.compiler.transform.propertyValidationMode, "throw");
Expand Down
6 changes: 3 additions & 3 deletions scripts/package-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,7 @@ function requireDesignKitManifest(
assert.equal(manifest.kind, "hraness-stylex-package-manifest");
assert.deepEqual(
manifest.package,
{ name: "@hraness/design-kit", version: "0.23.0" },
{ name: "@hraness/design-kit", version: "0.24.0" },
`${label} package identity changed`,
);
assert.equal(manifest.schemaVersion, STYLEX_PACKAGE_MANIFEST_SCHEMA_VERSION);
Expand Down Expand Up @@ -901,7 +901,7 @@ if (!immutableUiRelease.test(uiDevelopmentSpecifier)
}
if (uiDevelopmentSpecifier !== "github:hraness/ui#v0.5.17") {
throw new Error(
"Design-kit v0.23.0 must build and publish against the immutable @hraness/ui v0.5.17 release.",
"Design-kit v0.24.0 must build and publish against the immutable @hraness/ui v0.5.17 release.",
);
}
if (process.argv.includes("--publication")) {
Expand All @@ -919,7 +919,7 @@ const uiPeerRange = stringField(
"package.json peerDependencies",
);
if (uiPeerRange !== ">=0.5.16 <0.6.0") {
throw new Error("Design-kit v0.23.0 must declare the exact @hraness/ui v0.5 peer range.");
throw new Error("Design-kit v0.24.0 must declare the exact @hraness/ui v0.5 peer range.");
}
if (stringField(rootDependencies, "@stylexjs/stylex", "package.json dependencies") !== "0.19.0") {
throw new Error("The StyleX authoring/runtime dependency must be pinned to 0.19.0.");
Expand Down
2 changes: 1 addition & 1 deletion scripts/product-marketing-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async function legacyStylesheetHash(): Promise<string> {
const syntaxImport = '@import "./syntax-highlighting.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, "077ca62bb226cb9d03c4bff07e52f2b65998b99b3850cc45c06cc120d54b0a57", "The independent static CSS grammar changed");
assert.equal(grammarSha256, "fa72c62662f244b3830675b9fd54db18bc14051c50ed1d960a615cebe8614a1e", "The independent static CSS grammar changed");
return createHash("sha256").update(source).digest("hex");
}

Expand Down
20 changes: 20 additions & 0 deletions src/product-marketing.css
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,26 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
.hraness-marketing-footer__nav a[aria-current="page"] {
color: var(--hraness-marketing-ink);
}

/* Network footer composition
*
* A marketing footer directly followed by the shared network footer
* (`.hraness-site-footer` from `@hraness/site-footer`) reads as one band
* instead of two stacked bars: the product row's bottom space collapses to a
* compact gap, and the network footer's content inherits the marketing
* measure and gutter through its documented `--hraness-site-footer-measure`
* seam so both registers share one column at every width. The band keeps two
* edges: this landmark's top rule above the product row and the network
* footer's own hairline above the organization row.
*/
.hraness-marketing-footer:has(+ .hraness-site-footer) {
--hraness-marketing-footer-space: 2rem 1.25rem;
}

.hraness-marketing-footer + .hraness-site-footer {
--hraness-site-footer-measure: calc(min(100%, var(--hraness-marketing-measure, 72rem)) - 2 * var(--hraness-marketing-gutter, clamp(1.25rem, 4vw, 3rem)));
}

/* Shared foil contract
*
* One pointer-tracked metallic chrome recipe shared by brand wordmarks,
Expand Down
16 changes: 16 additions & 0 deletions src/product-marketing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,19 @@ test("a ruled section directly after a framed component drops its redundant sepa
/\.hraness-marketing-install,[\s\S]*?\.hraness-marketing-proof-frame[\s\S]*?\+ :is\([\s\S]*?\.hraness-marketing-questions,[\s\S]*?\.hraness-marketing-cta[\s\S]*?\) \{\s*border-block-start: 0;/u,
);
});

test("a marketing footer directly before the network footer joins one band", () => {
// The composition keeps two landmarks and two edges while collapsing the
// double spacing and aligning the network row to the marketing column.
expect(css).toContain(".hraness-marketing-footer:has(+ .hraness-site-footer)");
expect(css).toMatch(
/\.hraness-marketing-footer:has\(\+ \.hraness-site-footer\)\s*\{\s*--hraness-marketing-footer-space:\s*2rem 1\.25rem;/u,
);
expect(css).toContain(".hraness-marketing-footer + .hraness-site-footer");
expect(css).toMatch(
/\.hraness-marketing-footer \+ \.hraness-site-footer\s*\{\s*--hraness-site-footer-measure:\s*calc\(min\(100%, var\(--hraness-marketing-measure, 72rem\)\) - 2 \* var\(--hraness-marketing-gutter/u,
);
// The seam is one shared column, not a second boxed band: the network
// footer keeps its own hairline and the product row keeps its top rule.
expect(css).toMatch(/\.hraness-marketing-footer\s*\{[^}]*border-block-start: var\(--hraness-marketing-rule\)/u);
});
2 changes: 1 addition & 1 deletion src/public-boundary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ test("the package exposes compositions without a second primitive barrel", async
];

expect(packageJson.dependencies["@hraness/ui"]).toBeUndefined();
expect(packageJson.version).toBe("0.23.0");
expect(packageJson.version).toBe("0.24.0");
expect(packageJson.peerDependencies["@hraness/ui"]).toBe(">=0.5.16 <0.6.0");
expect(packageJson.peerDependenciesMeta["@hraness/ui"]).toEqual({ optional: true });
expect(packageJson.devDependencies["@hraness/ui"]).toBe("github:hraness/ui#v0.5.17");
Expand Down
2 changes: 1 addition & 1 deletion src/react/product-marketing-stylex.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ test("the reviewed static grammar and 30-token foundation stay separate from own
const syntaxImport = '@import "./syntax-highlighting.css";\n\n';
expect(legacy).toStartWith(syntaxImport);
expect(createHash("sha256").update(legacy.slice(syntaxImport.length)).digest("hex"))
.toBe("077ca62bb226cb9d03c4bff07e52f2b65998b99b3850cc45c06cc120d54b0a57");
.toBe("fa72c62662f244b3830675b9fd54db18bc14051c50ed1d960a615cebe8614a1e");
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));
Expand Down
7 changes: 4 additions & 3 deletions src/react/product-marketing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,9 @@ export function MarketingSiteHeader({
/**
* In-flow site footer. The brand lockup is always the product mark followed by
* its name; optional children carry a product note and optional links a quiet
* footer navigation. It rests at the end of document flow, above any fixed
* shared footer a product mounts after it.
* footer navigation. It rests at the end of document flow; when the shared
* network footer follows it directly, the marketing grammar joins the pair
* into one band.
*/
export function MarketingSiteFooter({
ariaLabel = "Site",
Expand All @@ -375,7 +376,7 @@ export function MarketingSiteFooter({
linksLabel = "Footer navigation",
name,
}: Readonly<{
/** Landmark label; keep it distinct from a fixed network footer on the same page. */
/** Landmark label; keep it distinct from the network footer on the same page. */
ariaLabel?: string;
/** The product mark, conventionally one inline SVG; rendered before `name`. */
brand: ReactNode;
Expand Down
Loading