Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

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:
Acommon/components/BrandMark.test.ts | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommon/components/BrandMark.tsx | 25++++++++++++++++++++++---
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) =>