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 @@ -45,7 +45,7 @@
- Keep root exports framework-neutral. React runtime belongs behind `@hraness/design-kit/react`; standards-only DOM and browser runtime code belongs behind `@hraness/design-kit/browser`. Do not register custom elements or mutate browser globals on module import.
- Author package-owned atomic component presentation in colocated `*.stylex.ts` files. Compile through the public `@hraness/ui/stylex-build` collector with its pinned StyleX 0.19.0 contract, CommonJS module roots, property specificity, and runtime injection disabled. Serialize the independently usable `dist/stylex.css` under the package-owned `components.hraness-design-kit` namespace, and bind its raw rules and artifacts in the canonical package manifest.
- Keep the precompiled and compiler-adopter routes disjoint. `components.css` imports `dist/stylex.css` exactly once, while `styles.css` reaches it only through `components.css` and imports the UI legacy and standalone StyleX artifacts exactly once before the local component entry. `compiler-palettes.css` is the manifest-bound minimal foundation: UI compiler foundation plus the semantic palette bridge, without webfonts, marketing styles, or standalone recipes. The full `compiler-foundation.css` imports it once and adds design-kit global and legacy CSS. Neither compiler route imports either package's `styles.css` or `stylex.css`. Generated atomic class names are declaration hashes that may legitimately repeat across package layers, so never infer package ownership from a generated name.
- Keep every handwritten component rule inside `components.hraness-design-kit.legacy`. Declare the complete standalone StyleX priority1-through-priority8 inventory. The pinned serializer maps raw priorities `{0,0.1,0.5,1,31,131,201,231,241,331}`, `{1000,1200}`, `{2000,2040,2130,2200}`, `{3000,3040,3045,3092,3130,3200,3330}`, `{4000,4130,4200}`, `{6000}`, `{7000}`, and `{8000,8040}` to ranks 1 through 8. Because rank 1 begins with raw-priority-0 keyframes, its keyframes, defineVars (0.1), createTheme (0.5), and custom-property atoms remain unlayered while `priority1` stays reserved in the prelude; all later output belongs in the exact `priority2` through `priority8` blocks. The complete standalone stylesheet keeps `base` below `components` and freezes the exact UI `legacy.base`, legacy, and priority1-through-priority7 order before the design-kit legacy and priority1-through-priority8 order. Final compiler-adopter applications load every registered package foundation before one finalized stylesheet that unions raw rules into `components.hraness-stylex` after all package legacy namespaces. Bind schema-2 generation and completion records to the exact union policy, reserve that namespace against foundation CSS, and keep immutable package manifests on schema 1. Package gates must derive rank placement from manifest priorities and structurally reject bare component rules, mixed standalone/compiler routes, missing foundations, unaccounted unlayered rules, and overlapping standalone namespaces.
- Keep every handwritten component rule inside `components.hraness-design-kit.legacy`. Declare the complete standalone StyleX priority1-through-priority8 inventory. The pinned serializer maps raw priorities `{0,0.1,0.5,1,31,131,201,231,241,331}`, `{1000,1200}`, `{2000,2040,2130,2200}`, `{3000,3040,3045,3092,3130,3200,3330}`, `{4000,4130,4200}`, `{6000}`, `{7000}`, and `{8000,8040,8200}` to ranks 1 through 8. Because rank 1 begins with raw-priority-0 keyframes, its keyframes, defineVars (0.1), createTheme (0.5), and custom-property atoms remain unlayered while `priority1` stays reserved in the prelude; all later output belongs in the exact `priority2` through `priority8` blocks. The complete standalone stylesheet keeps `base` below `components` and freezes the exact UI `legacy.base`, legacy, and priority1-through-priority7 order before the design-kit legacy and priority1-through-priority8 order. Final compiler-adopter applications load every registered package foundation before one finalized stylesheet that unions raw rules into `components.hraness-stylex` after all package legacy namespaces. Bind schema-2 generation and completion records to the exact union policy, reserve that namespace against foundation CSS, and keep immutable package manifests on schema 1. Package gates must derive rank placement from manifest priorities and structurally reject bare component rules, mixed standalone/compiler routes, missing foundations, unaccounted unlayered rules, and overlapping standalone namespaces.
- Keep tokens, resets, document grammar, cross-component layout, forced-color/media contracts, and audited vendor fallbacks in the existing global CSS boundary. Keep the manifest-bound compiler foundation semantically aligned with that boundary, and verify both standalone and finalized cross-package ordering against immutable UI artifacts before changing either package's component layers.
- Keep persistent appearance selection to one icon-menu control at the final/rightmost product-header action. Do not place appearance controls in footers, navigation lists, route-state action rows, or nested gallery content. Fixed-theme, authentication-only, global-error, and non-HTML surfaces may omit the control.
- Render product display names exactly as `messaging.names.name` in visible wordmarks, accessible labels, related cards, social cards, and metadata. Keep domains, commands, packages, paths, anchors, and audience keys separate. Never recase a display name in source or with CSS; preserve intentionally lowercase names and authored acronyms. Follow `MARKETING_COPY.md` and require the canonical visible `brandName` in `inspectMarketingHeader` checks.
Expand Down
7 changes: 6 additions & 1 deletion dist/chunk-9sjaav5t.js → dist/chunk-w76f3ah1.js
Original file line number Diff line number Diff line change
Expand Up @@ -2676,6 +2676,7 @@ var marketingStyles = {
kGmCso: "x1qughib",
kOIVth: "x8fetqu",
kF3gjK: "xgepmj6",
kJVvJu: "x18wjirm",
kMwMTN: "xtylnni",
kkrTdU: "x1ypdohk",
kLh5Sq: "x1ksoetq",
Expand All @@ -2688,12 +2689,16 @@ var marketingStyles = {
kNmtQP: "x1h4a8v0",
kF3crb: "x17thtq2",
kJ3DBm: "x1ioofie",
kLigFv: "x1bz48vb",
kLZV2q: "x78hkw1 x4oqjru",
kVDYEw: "xjocvvi",
k7kefo: "x15fj410",
kLigFv: "xba5a1s",
$$css: true
},
question__answer: {
k2kXS: "xjq529q",
kF3gjK: "xs0puwk",
kJVvJu: "x18wjirm",
kMwMTN: "xs87ocq",
kN5DiO: "x1dbl2gt",
$$css: true
Expand Down
2 changes: 1 addition & 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-9sjaav5t.js";
} from "../chunk-w76f3ah1.js";
import {
SyntaxCode
} from "../chunk-6ts4955n.js";
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-9sjaav5t.js";
} from "../chunk-w76f3ah1.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.

26 changes: 24 additions & 2 deletions dist/stylex.css
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,10 @@
padding-inline: var(--hraness-marketing-gutter);
}

.x18wjirm {
padding-inline: var(--hraness-marketing-question-inline-inset, 1.25rem);
}

.xfmotut {
padding-inline: var(--layout-chrome-inset);
}
Expand Down Expand Up @@ -4943,8 +4947,26 @@
transform: translate3d(0, -4vh, 0);
}

.x1bz48vb.x1bz48vb:where(.x1mw0dh9[open] *):after {
transform: rotate(45deg);
.x78hkw1:after {
transition-duration: .24s;
}

.xjocvvi:after {
transition-property: transform;
}

.x15fj410:after {
transition-timing-function: cubic-bezier(.16, 1, .3, 1);
}

.xba5a1s.xba5a1s:where(.x1mw0dh9[open] *):after {
transform: rotate(135deg);
}

@media (prefers-reduced-motion: reduce) {
.x4oqjru.x4oqjru:after {
transition-duration: 0s;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion scripts/check-stylex-artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1712,7 +1712,7 @@ assert.deepEqual(
[4000, 4130, 4200],
[6000],
[7000],
[8000, 8040],
[8000, 8040, 8200],
],
"Design-kit raw StyleX priorities no longer map to the reviewed eight-rank inventory",
);
Expand Down
2 changes: 1 addition & 1 deletion scripts/gallery-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2172,7 +2172,7 @@ try {
[4000, 4130, 4200],
[6000],
[7000],
[8000, 8040],
[8000, 8040, 8200],
]),
"Gallery design-kit manifest no longer matches its reviewed eight-rank inventory.",
);
Expand Down
4 changes: 2 additions & 2 deletions scripts/product-marketing-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async function legacyStylesheetHash(): Promise<string> {
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, "627c8cd90a4332c4f1b31d3f9242b086448dab2580ddfadc96c1fbe6ea6abfbf", "The independent static CSS grammar changed");
assert.equal(grammarSha256, "d73aadc873410e05dd148143c693baa6e263053bb4caa80db3859842e1f48dab", "The independent static CSS grammar changed");
return createHash("sha256").update(source).digest("hex");
}

Expand Down Expand Up @@ -1011,7 +1011,7 @@ try {
await settle(page);
assert.equal(await page.locator("details").first().getAttribute("open"), "");
const matrix = await summary.evaluate((node) => getComputedStyle(node, "::after").transform);
assert.match(matrix, /^matrix\(0\.70710\d*, 0\.70710\d*, -0\.70710\d*, 0\.70710\d*, 0, 0\)$/u);
assert.match(matrix, /^matrix\(-0\.70710\d*, 0\.70710\d*, -0\.70710\d*, -0\.70710\d*, 0, 0\)$/u);
const state: Observation[] = [...await snapshot(page, ".hraness-marketing-question__summary, .hraness-marketing-question__answer, .hraness-marketing-question__answer [data-marketing-oracle]")];
await page.keyboard.press("Space");
await settle(page);
Expand Down
11 changes: 10 additions & 1 deletion src/product-marketing.css
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,7 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
justify-content: space-between;
gap: 1rem;
padding-block: 1rem;
padding-inline: var(--hraness-marketing-question-inline-inset, 1.25rem);
color: var(--hraness-marketing-ink);
cursor: pointer;
font-size: 1.05rem;
Expand All @@ -2027,10 +2028,11 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
font-size: 1.25rem;
font-weight: 400;
line-height: 1;
transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
}

.hraness-marketing-question[open] summary::after {
transform: rotate(45deg);
transform: rotate(135deg);
}

.hraness-marketing-question summary:focus-visible {
Expand All @@ -2042,6 +2044,7 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
.hraness-marketing-question__answer {
max-inline-size: var(--hraness-marketing-prose-measure);
padding-block: 0 1.5rem;
padding-inline: var(--hraness-marketing-question-inline-inset, 1.25rem);
color: var(--hraness-marketing-muted);
line-height: 1.6;
}
Expand Down Expand Up @@ -2259,6 +2262,12 @@ html:has(:is(.hraness-marketing-header, .hraness-marketing-header-surface):not([
}
}

@media (prefers-reduced-motion: reduce) {
.hraness-marketing-question summary::after {
transition: none;
}
}

@media (forced-colors: active) {
.hraness-marketing-plan[data-emphasis="primary"] {
border-color: CanvasText;
Expand Down
13 changes: 12 additions & 1 deletion src/product-marketing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,21 @@ test("the marketing grammar keeps compact, coarse-pointer, and forced-color cont
expect(css).toContain("min-block-size: 3rem");
expect(css).toContain("background: Canvas;");
expect(css).toContain("color: CanvasText;");
expect(css).not.toContain("transition:");
// The only motion the grammar owns is the question glyph's + to × turn.
for (const rule of css.matchAll(/[^{}@]+\{[^{}]*\btransition:[^;]+;[^{}]*\}/gu)) {
expect(rule[0]).toContain("hraness-marketing-question summary::after");
}
expect(css).not.toContain("animation:");
});

test("question rows inset their content and turn the marker with a restrained motion", () => {
expect(css).toMatch(/\.hraness-marketing-question summary \{[^}]*padding-inline: var\(--hraness-marketing-question-inline-inset, 1\.25rem\)/u);
expect(css).toMatch(/\.hraness-marketing-question__answer \{[^}]*padding-inline: var\(--hraness-marketing-question-inline-inset, 1\.25rem\)/u);
expect(css).toMatch(/\.hraness-marketing-question summary::after \{[^}]*transition: transform 240ms cubic-bezier\(0\.16, 1, 0\.3, 1\)/u);
expect(css).toMatch(/\.hraness-marketing-question\[open\] summary::after \{\s*transform: rotate\(135deg\);/u);
expect(css).toMatch(/@media \(prefers-reduced-motion: reduce\) \{\s*\.hraness-marketing-question summary::after \{\s*transition: none;/u);
});

test("the hero eyebrow is a plain muted label, not a badge", () => {
const eyebrow = css.match(/\.hraness-marketing-hero__eyebrow \{([^}]*)\}/u)?.[1] ?? "";
for (const declaration of ["padding: 0;", "border: 0;", "border-radius: 0;", "background: none;", "color: var(--hraness-marketing-muted);"]) expect(eyebrow).toContain(declaration);
Expand Down
4 changes: 2 additions & 2 deletions src/react/product-marketing-stylex.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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("627c8cd90a4332c4f1b31d3f9242b086448dab2580ddfadc96c1fbe6ea6abfbf");
.toBe("d73aadc873410e05dd148143c693baa6e263053bb4caa80db3859842e1f48dab");
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 Expand Up @@ -301,7 +301,7 @@ test("the public collector compiles native logical edges, backgrounds, media, an
expect(css).toContain("forced-colors");
expect(css).toContain("pointer: coarse");
expect(css).toContain("[open]");
expect(css).toContain("rotate(45deg)");
expect(css).toContain("rotate(135deg)");
expect(a.code).not.toContain("inject(");
const recipeRules = (recipe: stylex.CompiledStyles) => {
const tokens = new Set(stylex.props(recipe).className?.split(" ") ?? []);
Expand Down
10 changes: 9 additions & 1 deletion src/react/product-marketing.stylex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3026,6 +3026,7 @@ export const marketingStyles = stylex.create({
"justify-content": "space-between",
"gap": "1rem",
"padding-block": "1rem",
"padding-inline": "var(--hraness-marketing-question-inline-inset, 1.25rem)",
"color": "var(--hraness-marketing-ink)",
"cursor": "pointer",
"font-size": "1.05rem",
Expand All @@ -3041,15 +3042,22 @@ export const marketingStyles = stylex.create({
"font-size": "1.25rem",
"font-weight": "400",
"line-height": "1",
"transition-duration": {
"default": "240ms",
"@media (prefers-reduced-motion: reduce)": "0ms"
},
"transition-property": "transform",
"transition-timing-function": "cubic-bezier(0.16, 1, 0.3, 1)",
"transform": {
"default": null,
[stylex.when.ancestor("[open]", questionMarker)]: "rotate(45deg)"
[stylex.when.ancestor("[open]", questionMarker)]: "rotate(135deg)"
}
}
},
"question__answer": {
"max-inline-size": "var(--hraness-marketing-prose-measure)",
"padding-block": "0 1.5rem",
"padding-inline": "var(--hraness-marketing-question-inline-inset, 1.25rem)",
"color": "var(--hraness-marketing-muted)",
"line-height": "1.6"
},
Expand Down
Loading