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
6 changes: 4 additions & 2 deletions app/llms-full.txt/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ describe("homepage mirror", () => {
expect(body).toContain(`${figure.label}: ${figure.value}`);
});

it("mirrors the stack chips in order", () => {
expect(lines).toContain(`Stack: ${STACK.join(" · ")}`);
it("mirrors the stack strip in order", () => {
expect(lines).toContain(
`Stack: ${STACK.map((s) => `${s.name} (${s.role})`).join(" · ")}`,
);
});

// The header prose says "the three legal documents" in words, the way
Expand Down
4 changes: 2 additions & 2 deletions app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
HERO_LEAD,
METHOD_STEPS,
SITE_DESCRIPTION,
STACK,
stackLine,
statusBlock,
} from "@/lib/copy";
import { FAQ_ITEMS } from "@/lib/faq";
Expand Down Expand Up @@ -215,7 +215,7 @@ ${compareTable}

### How it works

Stack: ${STACK.join(" · ")}
Stack: ${stackLine()}

${methodSteps}

Expand Down
39 changes: 39 additions & 0 deletions components/site/Method.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, expect, it } from "vitest";
import { Method } from "./Method";
import { STACK } from "@/lib/copy";
import {
asElement,
attrs,
childElements,
findAll,
findOne,
textOf,
} from "@/test-utils/react-tree";

const section = asElement(Method());
const list = findOne(section, "ul");

describe("Method stack strip", () => {
// The page and the llms-full.txt mirror both read STACK; this is what keeps
// the rendered side from dropping a role or reordering without a failure.
it("renders one cell per STACK item, name over role, in order", () => {
const cells = childElements(list);
expect(cells.map((c) => c.type)).toEqual(STACK.map(() => "li"));
cells.forEach((cell, i) => {
const [name, role] = childElements(cell);
expect(textOf(name)).toBe(STACK[i].name);
expect(textOf(role)).toBe(STACK[i].role);
});
});

it("hides the square marker and names the list by its eyebrow", () => {
for (const cell of childElements(list)) {
const [, role] = childElements(cell);
const [marker] = childElements(role);
expect(attrs(marker)["aria-hidden"]).toBe(true);
}
const id = attrs(list)["aria-labelledby"];
const eyebrow = findAll(section, "span").find((s) => attrs(s).id === id);
expect(eyebrow && textOf(eyebrow)).toBe("stack");
});
});
47 changes: 32 additions & 15 deletions components/site/Method.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Fragment } from "react";
import { METHOD_STEPS, STACK } from "@/lib/copy";
import { Frame } from "@/components/ui/Frame";
import { MethodRow } from "@/components/ui/MethodRow";
Expand All @@ -25,21 +24,39 @@ export function Method() {
</div>

<div data-reveal className="pt-12">
<span className="meta mb-3 block opacity-60">stack</span>
<div className="hug flex flex-wrap items-baseline gap-x-5 gap-y-2 text-h3 font-semibold tracking-[-0.03em]">
{/* The `·` is a separator between chips, not part of one, so it is
aria-hidden and rendered only between items. */}
{STACK.map((name, i) => (
<Fragment key={name}>
{i > 0 && (
<span aria-hidden className="opacity-30">
·
</span>
)}
<span>{name}</span>
</Fragment>
<span id="method-stack" className="meta mb-3 block opacity-60">
stack
</span>
{/* Hairlines come from the list's top/left edge plus each cell's
right/bottom edge, so the grid stays single-ruled at any column
count — and closed while STACK.length divides evenly by it. In one
row of six the longest name (currently "BLAKE3") overflows
--text-h3, so names size off the frame's container width instead.
`.meta` and `.hug` sit outside any cascade layer, so a utility
beside either can't override it; the role label therefore spells
`.meta` out at 12px rather than adding a size to it. */}
<ul
aria-labelledby="method-stack"
className="grid grid-cols-2 border-t border-l border-current/20 @xl:grid-cols-3 @6xl:grid-cols-6"
>
{STACK.map(({ name, role }) => (
<li
key={name}
className="flex flex-col gap-3 border-r border-b border-current/20 px-5 py-6 @xl:px-6 @xl:py-7 @6xl:px-5"
>
<span className="hug text-h3 leading-none font-semibold @6xl:text-[3.4cqi]">
{name}
</span>
<span className="flex items-start gap-2.5 text-[12px] leading-[1.2] font-medium tracking-[0.22em] uppercase opacity-60">
<span
aria-hidden
className="mt-[0.33em] size-1.5 shrink-0 bg-current"
/>
{role}
</span>
</li>
))}
</div>
</ul>
</div>
</Frame>
);
Expand Down
19 changes: 18 additions & 1 deletion lib/copy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ const ALL_STRINGS: readonly [string, string][] = [
[`METHOD_STEPS.${s.n}.word`, s.word],
[`METHOD_STEPS.${s.n}.body`, s.body],
]),
...STACK.map((s, i): [string, string] => [`STACK[${i}]`, s]),
...STACK.flatMap((s): [string, string][] => [
[`STACK.${s.name}.name`, s.name],
[`STACK.${s.name}.role`, s.role],
]),
["DEMO_CAPTIONS.terminal", DEMO_CAPTIONS.terminal],
["DEMO_CAPTIONS.fleet", DEMO_CAPTIONS.fleet],
...CONTACT_HEADLINE.map((s, i): [string, string] => [
Expand All @@ -77,6 +80,20 @@ describe("copy strings", () => {
);
});

describe("STACK", () => {
// components/site/Method.tsx lays the strip out 2 / 3 / 6 across, and its
// hairlines only close when the count divides evenly by the column count.
it("has exactly six items", () => {
expect(STACK).toHaveLength(6);
});

// `name` is the React key in Method.tsx and the label in ALL_STRINGS above.
it("names each item once", () => {
const names = STACK.map((s) => s.name);
expect(new Set(names).size).toBe(names.length);
});
});

describe("COMPARE_ROWS", () => {
// Hardcoded, not `COMPARE_ROWS.length`: the section header says "seven
// axes" in prose (components/site/Compare.tsx), so the count is a published
Expand Down
25 changes: 21 additions & 4 deletions lib/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,27 @@ export const METHOD_STEPS: readonly MethodStep[] = [
},
];

/** The stack chips under the method steps, rendered with an aria-hidden `·`
* between each. Order is the render order. This is the section's only stack
* summary — a figure strip beside it used to restate QUIC, iroh and USDC. */
export const STACK = ["Rust", "BLAKE3", "QUIC", "iroh", "USDC", "EVM"] as const;
export type StackItem = {
name: string;
/** What the piece does in the protocol — the small label under the name. */
role: string;
};

/** The stack under the method steps, in render order. Rendered by `Method`;
* serialised by `stackLine()`. This is the section's only stack summary: put
* per-piece detail in `role`, not in a separate strip beside it. */
export const STACK: readonly StackItem[] = [
{ name: "Rust", role: "node & client" },
{ name: "BLAKE3", role: "verification" },
{ name: "QUIC", role: "transport" },
{ name: "iroh", role: "connectivity" },
{ name: "USDC", role: "payment" },
{ name: "EVM", role: "settlement" },
];

/** The stack as one line of text, for the machine mirror. */
export const stackLine = (): string =>
STACK.map(({ name, role }) => `${name} (${role})`).join(" · ");

/** The target price, as the page and both machine surfaces quote it. */
export const TARGET_RATE = "$0.01/GB";
Expand Down
Loading