commit 468e1ea559751c85b8563bdd7218ded975e31630
parent 5f287409597064ca7d4f74f838f5eaafb2fc3255
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 00:16:00 -0400
common: the mark's tile gets a 1px ring on dark, in the palette's own dim
The operator (2026-09-28): "Add a thin outline to the tile on dark." On the
dark base the tile has no edge: a site's ink tile is the ink page itself
(#0c0a08 on #0c0a08) and the parent's slate (#151b20) is 1.14:1 on it.
- BrandMark draws a 1px ring OUTSIDE the tile, only under `.dark` (tokens.css's
@custom-variant): `dark:shadow-[0_0_0_1px_var(--mark-ring)]`, with
`--mark-ring` set from `palette.dim` on the svg — #6b5d47 for a site mark,
#586977 for the parent — so each mark rings in its own dim and the ring
reads as part of it (3.09:1 and 3.48:1 on the ink page, the dims' own floor).
- `rounded-[21.875%]` is the ground's rx (112 of 512), so the ring follows the
tile's corner; `overflow-visible` drops the <svg>'s default clip, which with
a radius laid a second anti-aliased edge over the ground's own corner (70–84
px differed from main at 2x; with it, 2 px by <= 2 levels).
- A box-shadow, so no layout moves; light and sepia render pixel-identical to
main. Every BrandMark use draws the tile, so all five get it (export header
and footer credit, hub, homepage, editor sidebar); the icon files (markSvg)
are untouched.
- BrandMark.test.ts: the corner equals MARK_GROUND_RX / MARK_VIEWBOX, the ring
is the dark variant only, each palette sets its own dim, a caller's classes
and style still apply, and markSvg carries no ring.
Chosen over a 60% ring (1.82:1 on ink, too faint at 1px) and a ring 1px
inside the edge (eats a pixel of the tile, so it looks smaller than on light).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 79 insertions(+), 3 deletions(-)
diff --git a/common/components/BrandMark.test.ts b/common/components/BrandMark.test.ts
@@ -0,0 +1,57 @@
+import { test } from "node:test";
+import assert from "node:assert/strict";
+import * as React from "react";
+import { renderToStaticMarkup } from "react-dom/server";
+import { BRAND_MARK_RING_CLASS, BrandMark } from "./BrandMark";
+import { ICON_PALETTES, MARK_GROUND_RX, MARK_VIEWBOX, childIconPalette, markSvg } from "../lib/brand";
+
+// The ring on dark (release 10, slice MR): a 1px box-shadow ring outside the
+// tile in the palette's own dim, drawn only under `.dark`, following the
+// ground's corner. The browser half — the computed box-shadow per base — is
+// held by the export, hub and homepage brand.spec e2e.
+
+// common's tsconfig has `jsx: "preserve"` (Next compiles it), so tsx falls
+// back to the classic transform — `React.createElement` on a free `React`.
+(globalThis as { React?: typeof React }).React = React;
+const { createElement } = React;
+const render = (el: Parameters<typeof renderToStaticMarkup>[0]) => renderToStaticMarkup(el);
+
+test("the ring's corner is the ground's: MARK_GROUND_RX of MARK_VIEWBOX", () => {
+ const pct = (MARK_GROUND_RX / MARK_VIEWBOX) * 100; // 21.875
+ assert.equal(pct, 21.875);
+ assert.ok(BRAND_MARK_RING_CLASS.split(" ").includes(`rounded-[${pct}%]`));
+ // The ring is the dark variant only, 1px, spread with no blur or offset,
+ // in the palette's ring colour; light and sepia get no shadow class at all.
+ const shadows = BRAND_MARK_RING_CLASS.split(" ").filter((c) => c.includes("shadow"));
+ assert.deepEqual(shadows, ["dark:shadow-[0_0_0_1px_var(--mark-ring)]"]);
+ // The svg must not clip the ground a second time at that corner.
+ assert.ok(BRAND_MARK_RING_CLASS.split(" ").includes("overflow-visible"));
+});
+
+test("each palette rings in its own dim, set as --mark-ring on the svg", () => {
+ const parent = render(createElement(BrandMark, { palette: ICON_PALETTES.archilyzer }));
+ assert.match(parent, /style="--mark-ring:#586977"/);
+ const child = render(
+ createElement(BrandMark, {
+ palette: { ...ICON_PALETTES.child, lit: "var(--brand-mark, var(--brand))" },
+ }),
+ );
+ assert.match(child, /style="--mark-ring:#6b5d47"/);
+});
+
+test("the caller's classes follow the ring's, and its style can still override the ring", () => {
+ const html = render(
+ createElement(BrandMark, {
+ palette: childIconPalette("#e3b15c"),
+ className: "size-7 shrink-0",
+ style: { "--mark-ring": "#123456" } as Record<string, string>,
+ }),
+ );
+ assert.ok(html.includes(`class="${BRAND_MARK_RING_CLASS} size-7 shrink-0"`), html);
+ assert.match(html, /style="--mark-ring:#123456"/);
+});
+
+test("the ring is in-page only: the icon files carry no ring", () => {
+ const svg = markSvg(ICON_PALETTES.archilyzer);
+ assert.doesNotMatch(svg, /stroke|shadow|mark-ring/);
+});
diff --git a/common/components/BrandMark.tsx b/common/components/BrandMark.tsx
@@ -6,12 +6,31 @@ import { MARK, MARK_VIEWBOX, type MarkTone } from "../lib/brand";
// this is not lib/brand.ts's hex-only IconPalette.
export type BrandMarkPalette = Readonly<Record<MarkTone, string>>;
+// THE RING ON DARK. On the dark base the tile has no edge: a site's ink tile
+// is the ink page itself (#0c0a08 on #0c0a08), and the parent's slate barely
+// clears it. So on dark — `.dark` on <html>, tokens.css's `@custom-variant
+// dark` — the tile gets a 1px ring OUTSIDE it, in the palette's own dim
+// (`--mark-ring`, set from `palette.dim` below), so the ring reads as part of
+// the mark. Light and sepia are untouched.
+// - A box-shadow, so it takes no layout: the mark's size and the header's
+// alignment are the same on every base.
+// - `rounded-[21.875%]` is the ground's corner (MARK_GROUND_RX / MARK_VIEWBOX,
+// 112 of 512; BrandMark.test.ts holds the two equal), so the ring follows
+// the tile's own curve.
+// - `overflow-visible`: an <svg> clips its content by default, and with a
+// border-radius that clip would be a second anti-aliased edge laid over
+// the ground's own rounded corner.
+// Complete literal class strings — Tailwind v4 only sees whole literals.
+export const BRAND_MARK_RING_CLASS =
+ "rounded-[21.875%] overflow-visible dark:shadow-[0_0_0_1px_var(--mark-ring)]";
+
// The Found-line mark, inline — drawn from the same MARK shape list as
// markSvg (the icon files), so the two cannot drift. Decorative: the text
// beside it names the link. Colours go through `style`, not a `fill="var()"`
// attribute, because a presentation attribute does not resolve custom
// properties everywhere a style declaration does. Size it with `className`
-// (e.g. `size-7`) or `size`.
+// (e.g. `size-7`) or `size`. Every use draws the tile, so every use rings on
+// dark; the icon files (markSvg) never do.
export function BrandMark({
palette,
size,
@@ -30,8 +49,8 @@ export function BrandMark({
height={size}
aria-hidden="true"
focusable="false"
- className={className}
- style={style}
+ className={className ? `${BRAND_MARK_RING_CLASS} ${className}` : BRAND_MARK_RING_CLASS}
+ style={{ "--mark-ring": palette.dim, ...style } as CSSProperties}
data-brand-mark=""
>
{MARK.map((s) =>