Foundations
| Property | Value |
|---|---|
| Name size / weight | 24px / 800 |
| Suffix size / weight | 19px / 700 |
| Letter spacing | 0.10em |
| Suffix : name ratio | 0.79em (19/24), the footer's own ratio, now the universal default — replaces an old 0.6em base that predated it |
| AMOLEO colour | #1c1c2e (light) / #ffffff (dark) — --brand-amoleo, never --text-strong |
The wordmark — the canonical spec
AMOLEO · PETS, Amoleo · Connections, Amoleo · Rissbrook — every product
uses the same construction: the family name, a middle dot, the product name.
It shows up in three places (a site's own header, the family footer's
lockup, and a pre-auth hero) that used to each carry their own copy of these
rules. They drifted the same way the brand colours did, for the same reason
— three copies, one written down. This doc is the one copy; family-footer.md
and family-header.md reference it rather than restate it.
This governs the wordmark wherever it appears on a page, not only in
those three named spots. The Website's own product listing puts a genuine
.brand-wordmark (Amoleo · Pets) atop each product's blurb — that's a
fourth instance, not a special case, and §7 covers what's actually shipped
there against what this doc says. §8 covers a related but distinct
construct — "Visit · Pets" — that borrows the wordmark's typography
without being one.
Split 4 August 2026, in the same session that grew the footer wordmark and
then the header to match it — the pattern of "one spec, values duplicated
into every context that uses it" already existed for colours
(tools/tokens/*.tokens.json) and the footer lockup itself; the wordmark hadn't been
factored out the same way yet.
The literal base rule (§1–§5) now lives in
tools/family-css/components/wordmark.css,
compiled into family-css/base.css — see
docs/family-css-system.md. This doc stays the
record of why each line is what it is; that file is the one copy of the
literal CSS, so a fix here doesn't also need retyping into five repos by
hand.
0. The recipe — apply this to any site
Everything below this point is the reasoning, kept because it's what stops the next fix from re-breaking what this one repaired. This section is the short version: implement (or audit) a wordmark anywhere in the family by doing these five things, in this order, on any site.
1. Markup — three classes, always.
<span class="brand-wordmark">
Amoleo<span class="brand-dot" aria-hidden="true">·</span><span class="brand-suffix">Pets</span>
</span>
2. Size — pick the base or a documented offset from it, never a fourth
number. 24px name / 19px bold suffix is the base (§2). The header is
+4px/+3px over it; the pre-auth hero is +12px/+3px. If a context
needs its own size, do the maths, then write the result as an explicit
absolute value — never leave two elements each computing the same em
independently.
3. The dot's size copies the suffix's, not the name's.
.brand-wordmark .brand-suffix { font-size: 19px; font-weight: 700; }
.brand-wordmark .brand-dot { font-size: 19px; } /* identical value, not a ratio */
This single line is most of the alignment fix. Two elements that share a
font-size share font metrics, and align-items: center on the parent then
aligns their visual centres as a consequence of that, not as something you
separately tune.
4. Alignment on the parent — center, not baseline.
.brand-wordmark { display: flex; align-items: center; }
.brand-wordmark .brand-suffix { /* no position, no top offset */ }
Baseline alignment sounds more correct for "mixed-size text on one line" and is worse here in practice — it sinks the dot and suffix visibly below the name once you can see them side by side. Center is what actually looks right; don't relitigate this per site.
5. Links — decorate the specific span that should show a line, never the shared ancestor.
.wordmark-wrap a { text-decoration: none; }
.wordmark-wrap a:hover .brand-suffix { text-decoration: underline; }
/* .brand-dot: no rule, in any state — never decorated, so nothing to leak */
Never write a:hover { text-decoration: underline } and then try to
text-decoration: none a child out of it. It looks like it works and
doesn't reliably: the excluded element can still show a stray fragment of
the ancestor's line, and the line renders in the ancestor's colour rather
than the specific piece's own — which matters here because the point is
each product name underlining in its own accent, not one shared link
colour. Give every piece that should show a line its own rule, on itself.
Verify with the same check every time, at the size the context actually renders at, not a guess: get the bounding boxes of the last letter of the name and the first letter of the suffix, and compare their vertical centres.
function charRect(el, i) {
const t = [...el.childNodes].find(n => n.nodeType === 3 && n.textContent.trim());
const r = document.createRange();
r.setStart(t, i); r.setEnd(t, i + 1);
return r.getBoundingClientRect();
}
A difference under ~1px is the floor of what CSS text rendering can guarantee and isn't worth chasing further. Anything larger means step 3 or step 2 wasn't followed — usually a font-size computed independently on two elements that were supposed to match exactly.
1. The markup
<span class="brand-wordmark">
Amoleo<span class="brand-dot" aria-hidden="true">·</span><span class="brand-suffix">Pets</span>
</span>
Three classes, always: .brand-wordmark (the whole mark), .brand-dot (the
separator, aria-hidden — a screen reader must not read "middot"),
.brand-suffix (the product name). The footer's lockup wraps several of
these in a .family-mark; that's family-footer.md's concern, not this
doc's — the wordmark itself is the same three classes wherever it appears.
2. Base size — the reference every context scales from
24px name / 19px bold suffix. This is the floor: the smallest either
part is ever allowed to be, and where the footer sits, because a footer
wordmark colour has nowhere left to hide if it needs to be smaller still.
19px bold is what clears WCAG's large-text contrast bar (18.66px) — the
reason every product's accent could drop its separate dark-theme value (see
family-footer.md §2). Going smaller than this reopens that problem.
Other contexts don't restate this number — they express themselves as an
offset over it (family-header.md's "+4px name / +3px suffix" for the
app-shell header, "+12px name / +3px suffix" for the pre-auth hero). If this
base ever changes, every context that's defined as an offset changes with
it; a context that copied the absolute number instead would silently stop
tracking it. This is the same reasoning as --brand-* tokens living in one
JSON file rather than four CSS files.
3. Weight and tracking
- Name:
font-weight: 800,text-transform: uppercase,letter-spacing: 0.10em. - Suffix:
font-weight: 700— bold is load-bearing, not decorative: it's half of what earns the large-text WCAG exemption at 19px (the other half is the size itself). - The suffix and dot are both rendered in caps too —
text-transform: uppercasesits on.brand-wordmarkitself (§1), not on a separate.brand-namechild, and neither.brand-dotnor.brand-suffixoverrides it back tonone. So the shipped mark isAMOLEO · PETSend to end, notAMOLEO · Pets— confirmed in every repo's markup, e.g. Amoleo-Pets'<span class="brand-suffix">PETS</span>. Worth stating outright because this section used to go silent on the suffix's case right after covering its weight, which reads as "keeps its own case" by omission — it doesn't. - The suffix is always smaller than the name, at every size the wordmark appears at. This is what makes the product name read as hanging off the family name rather than as two unrelated words — tried flattened once (in Connections, on the footer), reverted. The two parts changing size together, staying in that same proportion, is the point of scaling by offset rather than picking sizes independently per context.
4. Colour
- The name takes
--brand-amoleo— never--text-strongor any other per-site ink. This is the one part of the wordmark that has to render identically on every site for the family to read as one mark; seefamily-footer.md§2 for the incident that made this a written rule rather than an assumption. - The suffix takes the product's own accent (
--brand-pets,--brand-connections, etc.) — one fixed value in every theme, no separate dark variant, per the same section.
5. Alignment
display: flex; align-items: center on .brand-wordmark is the base
mechanism, and gets most of the way there on its own — but as of 6 August
2026 it is not the whole story; see "Cross-browser correction" below.
An earlier version of this mark used top: 0.15em to make the suffix
visually "hang" below the name's baseline; corrected 4 August 2026, because
it doesn't survive scrutiny at the sizes the wordmark now renders at.
Baseline alignment (align-items: baseline) was tried as the theoretically
"more correct" fix and made things worse — it reads fine for the
dot-and-suffix pair on their own, but against the much larger, bolder name
it visibly sinks the smaller text below where centering had it looking
right. center is the one that actually looks right; trust the visual
result over the more principled-sounding option.
Cross-browser correction, found 6 August 2026. align-items: center
does not land the dot+suffix pair's visual centre on the name's own centre
identically across browsers when the two sides differ this much in
font-size and weight (the name at 24px+/800, the pair at 0.79em/700+400).
Firefox centres it correctly with no help. Chrome and Edge render the pair
sitting a small, consistent amount below true centre — about 1.2px in the
footer lockup's 19px suffix, about 1.5px in the inline mention's smaller
compound-link size (§9) — found by measuring the actual rendered gap in
Chrome. docs/family-css-system.md's extraction briefly reintroduced
vertical-align: center, the value a manual fix on the Website had
settled on the night before — plausible on its face, but genuinely inert:
center isn't a valid vertical-align keyword in any browser, so the
declaration is dropped and every browser's computed value stays
baseline, meaning it changed nothing, in Chrome or Firefox, before or
after. Confirmed by reading the computed style directly, and by
A/B-measuring the exact pre-extraction deployment against the
post-extraction one — identical rendered geometry in both.
Not a clean formula. (name size − suffix size) / 2 is a reasonable
guess at why a gap like this exists at all, and it lands exactly right
for the inline mention ((14.45px − 11.42px) / 2 ≈ 1.5px, matching the
measured value precisely) — but it overshoots for the footer
((24px − 19px) / 2 = 2.5px against a measured optimum of 1.2px; feeding
the formula's own value back in leaves a −1.3px residual error the other
direction). The underlying browser behaviour doesn't scale linearly with
the size delta, so treat the formula as a decent starting guess to measure
from, never as the value to ship directly.
The real fix is position: relative; top: <value>; on both
.brand-suffix and .brand-dot, tuned per context because the
product-listing heading context (§7, 26px name/21px suffix) needed no
correction at all (already sub-0.1px) — a fixed offset at the base rule
would have overcorrected there.
Correction, 6 August 2026: dot and suffix don't always move as a unit —
this section previously said they do, and that was wrong. Every value
above was measured with getBoundingClientRect()/Range on each
element's own line box (ascent+descent from the font's metrics), not
its ink (the glyph's actual painted pixels) — and a · character's
ink sits at a different position relative to its own baseline than a
capital letter's does, even at the identical font-size and even though
their line boxes centre identically. Line-box centring can't see that
gap, so a single value tuned by that method reads as "correct" against
whichever glyph the check happened to weight more (usually the suffix's
full word, since it's what draws the eye) and leaves the other one
visibly off. Found wiring Rissbrook's header in: the app-shell's 28px/22px
pairing (below) shipped a single -0.6px for both since 6 Aug 2026 —
right for the dot, about 1.1px wrong for the suffix, invisible in the
line-box measurement that produced it and only caught once Rissbrook's
own header made the suffix's "too high" obvious enough to flag by eye.
Corrected to -0.5px (dot) and +0.5px (suffix) — opposite signs, not
just different magnitudes. Re-derive with
CanvasRenderingContext2D.measureText()'s actualBoundingBoxAscent/
actualBoundingBoxDescent (the real per-glyph ink extents) when tuning
a context from scratch, and confirm at high CSS transform: scale() zoom
against the live page — the visual check the box-based numbers were never
run against — rather than trust getBoundingClientRect() alone.
Second correction, same day: "a zoomed visual check" isn't enough if the
check is one glance, not a before/after comparison. This section
originally said the ink-based model's discrepancy in the 36px/22px
pre-auth hero pairing was a false alarm — a single 6x-zoom screenshot at
the existing -0.7px value was read as "already correct" and left
alone. It wasn't: caught for real only once Rissbrook's own header made
"RISSBROOK" sitting visibly high next to "AMOLEO" obvious on an ordinary,
unzoomed page — the same fault the model had correctly flagged, dismissed
because the check that was supposed to confirm or deny it never actually
compared the flagged value against a corrected one. Corrected to 0.5px
(dot) and 1.5px (suffix) — 0px residual on both by the ink model,
confirmed this time with an actual before/after screenshot pair, not a
single look. The two glyphs land on the same sign here, unlike the
28px/22px pairing above, which needed opposite signs — another way the
"not a clean formula" finding below holds. Treat a visual check as
"compare candidate values against each other," never "does this one value
look plausible" — a plausible-looking wrong value is exactly what a
single glance can't catch. Unit depends on whether
the context's
own size is fixed or relative: the footer lockup's suffix is always a
literal 19px (the wordmark's documented floor, never scaled anywhere in
the family), so its correction is the literal -1.2px that was measured
directly. The inline mention (§9) is explicitly meant to render at
whatever size its surrounding prose is — the hero, a footer copyright
line, a cookie-banner mention, all different sizes — so a frozen px value
measured at just one of those (the bio's 17px paragraph) would be wrong
anywhere else it's used; its correction is expressed as -0.132em
(relative to .brand-suffix's own font-size) instead, the exact ratio
that produced the measured -1.5px at that specific size. em scales in
the right direction as the surrounding size changes, though — per the
paragraph above — not with perfect linearity, so it's an improvement over
a frozen px, not a guarantee. See
tools/family-css/components/{footer-lockup,wordmark}.css for the current
values. Re-measure rather than assume a value still holds if a
context's font-size or weights ever change, or if the inline mention is
ever used at a size meaningfully different from the ones already checked —
these are empirical, not computed from the sizes.
Two more measured values, found wiring Amoleo-Connections in (6 Aug
2026) and now shipped in tools/family-css/components/header.css
itself: the app-shell's 28px/22px pairing needs -0.5px on the dot and
+0.5px on the suffix (originally shipped as one shared -0.6px,
corrected the same day wiring Rissbrook in — see the correction above);
the pre-auth hero's 36px/22px pairing needs 0.5px on the dot and 1.5px
on the suffix (originally shipped as one shared -0.7px, corrected the
same day too — see the second correction above) — tools/header.json's
wordmark.dotTopCorrectionPx/suffixTopCorrectionPx and
wordmarkPreAuthHero.dotTopCorrectionPx/suffixTopCorrectionPx record
both. Neither was visible from Website alone: Website's own
wordmark in both of these spots is the bare word "Amoleo" with no product
suffix (it has no product to hang off it — family-header.md §1.1), so
there was nothing there to misalign. The correction only bites once a
repo renders a real "Amoleo · Product" pairing at these sizes, which
Connections was the first to do. Shipped as a container-scoped rule
(.family-header-bar .brand-wordmark .brand-suffix, not .family-header- bar-brand .brand-suffix) specifically so it reaches a repo whose wordmark
is a reusable component rather than a class on the element itself — see
docs/family-css-system.md.
A fourth, smaller pairing exists too: 22px/17px, the Post-Auth Header's
own overflow-shrink. .family-header-postauth's 28px/22px wordmark
drops to this size specifically when a burger needs the room beside it
(family-header.md §1.2) — -0.2px, found by sweeping -1px to +1px
in steps and reading the actual offset at each rather than reasoning about
the sign, after guessing wrong once (a more positive top made the
measured residual worse, not better — a reminder that "which direction is
top even pushing this" is exactly the kind of thing worth checking
empirically rather than assuming, same spirit as the formula warning
above). Not recorded in tools/header.json as a base size/delta pairing
the way the other three are, since it's a compression applied on top of
the app-shell size rather than a size in its own right.
A third trigger for re-measuring, alongside a font-size/weight change:
nesting the mark inside another flex or flex-wrap container shifts the
correction, even at a size already measured elsewhere. Connections'
compact footer (docs/family-footer.md's compact variant) puts
.family-mark — the footer lockup's own 24px/19px pairing, already
correct at -1.2px in the plain footer — one layer deeper, as a flex item
inside .site-footer.compact's own flex-wrap row. That extra layer
rounds sub-pixels differently: measured a real 0.81px residual there
(6 Aug 2026), on top of the -1.2px that already accounted for everything
family-footer.md's plain layout does. The fix is an additional,
context-scoped correction (.site-footer.compact .family-mark .brand- suffix { top: -2px; }, not a change to the shared -1.2px) — the two
numbers aren't meant to reconcile into one formula, any more than the
footer's and the header's do. If a future repo nests the mark inside its
own wrapping flex container (a card, a compact variant of its own), treat
that nesting itself as a reason to re-measure, not just a font-size change.
A fourth failure mode, found wiring Amoleo-Pets in (7 Aug 2026): a
correction can be duplicated across two separate selectors that were
never actually kept in sync. .family-header-postauth's own 28px/22px
pairing is, by definition, the identical size to .family-header-bar's —
the comment directly above both rules in tools/family-css/components/ header.css has said so since 6 Aug 2026, the day .family-header-bar's
own dot/suffix got the opposite-sign -0.5px/+0.5px fix described
above. .family-header-postauth didn't get it — it kept the old, wrong,
single -0.6px for a full day, silently, because the two are deliberately
separate rules (they're never nested inside each other, and a repo may
adopt one without the other — family-css-system.md's own reasoning for
keeping them apart), and nothing re-checks a second rule when a
first one gets corrected. Caught only because Pets adopted
.family-header-postauth for real and a zoomed screenshot of "AMOLEO ·
PETS" showed the dot sitting visibly high — the exact same fault, in a
different repo's screenshot, a full day after the fix that should have
covered it. The shared-CSS system's actual guarantee is narrower than it
sounds: one correction, loaded from one file, reaches every repo that
loads that selector — it does nothing to keep two textually-separate
selectors holding the same value in sync with each other, even inside
the same file, even when a comment already says they must match. That
still takes a human noticing, or a check written to notice for them.
When a correction changes for one context, grep the same file for
every other selector whose comment claims the same size — top: -0.6px
would have found this one immediately — rather than trusting the
claim without re-reading the code under it.
Where a context fixes an explicit pixel size (the footer, Rissbrook,
Connections), the dot/suffix size is written as a literal value on
.family-mark .brand-suffix/.brand-dot rather than left to the base
rule's em ratio — see the next paragraph for why. check-wordmark.mjs
looks for the family-mark suffix/dot sizes; it does not check the top
correction value, which is expected to vary by context.
The dot is sized to match the suffix, not the name. .brand-dot used
to inherit the wordmark's full size (the name's), which is what actually
broke the centering — two differently-sized siblings, box-centred
independently, don't share a visual centre just because the CSS says
center. Sizing the dot to the suffix's own value (0.79em at the base —
see §2, this replaced an old 0.6em that predated the ratio the footer
actually ships) means dot and suffix share identical font metrics, so
centring them lands their visual centres together as a direct consequence,
not a coincidence of one particular ratio.
Once a context fixes an explicit pixel size, give the dot the identical
explicit value too — never two separate em computations. 26px × 0.79em
computed independently for the dot and the suffix can round to a different
sub-pixel value for each (this actually happened — about a 0.7px gap,
visible once magnified), even though the formula is the same for both. The
footer avoids this by writing 19px on both rather than 0.79em on both;
any other context that fixes its own size (the header's 28px, a future
hero) should do the same rather than trust two independent roundings to
agree.
6. The wordmark as a link — three behaviours, not one
The wordmark links to somewhere in three different situations, and they don't all share a decoration rule. Two of the three used to be treated as one ("self-link") until 5 August 2026, when the footer instance picked up the cross-link's hover-underline convention instead of keeping the header's zero-decoration one — see below.
Self-link, header (logo behaviour). A header wordmark links to the
current site's own root, acting as a logo rather than a nav link — no
text-decoration in any state, no hover underline, no hover/focus
background. The link is still identifiable (it's the logo, the one
convention every site's visitors already share) and keyboard users still
get the standard :focus-visible ring; only the hover/decoration styling
is dropped. On the page the link would point to, the wordmark renders as
plain text instead — there's nowhere to send you. See family-header.md
§1.
Self-link, footer (the family-name in the lockup) — underline on hover, same as a cross-link, not zero decoration. The footer's "Amoleo" family-name links to amoleo.com from every product site, the same as the header logo does, but it doesn't get the header's exemption: it sits in the lockup beside four cross-links that do underline on hover, and a link that behaves differently from its immediate neighbours for no visible reason reads as inconsistent — the same WCAG 2.1 SC 1.4.1 (Use of Color) territory the cross-link fix below is already in, just applied to one more link in the same row rather than a new rule of its own. So, since 5 August 2026, the family-name gets the cross-link's own hover treatment:
.family-mark a.family-name:hover { text-decoration: underline; }
Set directly on the <a> itself, not a child span — unlike the cross-link
fix below, family-name wraps no separate suffix element to decorate
instead, and there's no dot or sibling here for a line to leak onto, so
the anchor is a safe target the way it wasn't for the cross-link case.
color: inherit (already on .family-mark a.family-name, §4) means the
underline defaults to that same inherited colour (currentColor —
--brand-amoleo) with no separate colour rule needed, same reasoning the
cross-link fix relies on. At rest it's undecorated, same as everywhere
else in the lockup; only the hover state changed. On the page the link
would point to (amoleo.com itself), the wordmark still renders as plain
text, not a link — nothing for this rule to apply to, same as the header
case. check-wordmark.mjs looks for this rule; check-lockup.mjs already
confirms the family-name only links out where it should (§4 there).
Cross-link (a product's name in the footer lockup). This genuinely
navigates to a different site, so it follows the ordinary link rule
(family-header.md §4): underline on hover. Two things make this different
from an ordinary text link, both corrected 4 August 2026:
The underline goes on .brand-suffix directly, never on the <a>.
.family-mark a { text-decoration: none; }
.family-mark a:hover .brand-suffix { text-decoration: underline; }
The first version of this rule set text-decoration: underline on the
<a> itself and excluded the dot with text-decoration: none, which
looked right and wasn't, for two compounding reasons: the dot has no size
override the way .brand-suffix does (§2 — it renders at the wordmark's
full base size, not the smaller suffix size), so a line sized to the <a>
sits at a different vertical position than one sized to the suffix; and
some browsers still paint a stray fragment of the ancestor's line under the
dot's own margin gap even with the child's decoration explicitly excluded —
exclusion suppresses the line over the dot's content, not reliably over
the whitespace around it. Underlining the suffix directly sidesteps both:
there's no ancestor line to leak, and nothing to exclude.
The underline's colour follows the product name's own colour, not a
generic accent. This falls out of the fix above rather than needing its
own rule: a text-decoration set directly on .brand-suffix defaults to
that element's own color (the product's accent), where a decoration set
on the ancestor <a> would have defaulted to its colour — which, absent
an explicit override, was the page's generic link colour, not the
product's. The dot's own colour was never the issue; the ancestor <a>'s
was.
Only the product name itself shows the hover line, in its own colour. Before the 4 August fix, hovering underlined the dot too (as visual noise once the type got large enough to notice) and the whole line rendered in the page's generic link colour rather than the product's — two related symptoms of decorating the wrong element.
7. A fourth instance — the Website's product listing
Each product card on the Website has a real .brand-wordmark.product-mark
heading (Amoleo · Pets, Amoleo · Connections, ...) above its blurb. This
is a genuine wordmark, not a variant — §1 through §6 apply to it exactly as
they do to the header or the footer. Auditing what's actually shipped there
against what this doc says, as of 4 August 2026:
- Size stays 26px, deliberately not reconciled to 24/28/36. It predates
the base/offset system and doesn't match any of the three established
sizes — but forcing it onto one wasn't part of this fix and remains open;
what is fixed is that whatever size it uses, the dot and suffix are
pinned to identical explicit pixel values rather than left to two
separate
emroundings (§5) —21pxeach, not0.79emeach of a 26px parent. That was the actual bug (the alignment fix below wouldn't hold without it), not the round number itself. - Alignment fixed 4 August 2026.
align-items: center(§5, not baseline), dot sized to match the suffix rather than the name, and both given the same explicit21pxrather than independently-computedemvalues, which is what a ~0.7px misalignment traced back to. - The colour is deliberately not the family token.
.product.petsuses--brand-pets-ink(#bd4b2f/#d9603f), not--brand-pets(#c45032until 6 Aug 2026) — true terracotta managed 4.33:1/4.11:1 at this heading's small rendered suffix size, both short of 4.5:1 AA for normal text.--brand-pets-inkis the same hue nudged until it clears. This is a legitimate, documented exception (tools/brand-policy.json'sextras.--brand-pets-inkentry,sites: ["Amoleo-Website"]) — not a drift to correct, because the wordmark colour rule (§4) is about which token carries the product's identity, and terracotta at any exact value is still Pets' identity.--brand-petsitself moved to#D9603Fon 6 August 2026 (docs/family-footer.md§1–2) — identical to--brand-pets-ink's own dark value, so on[data-theme="dark"]the exception is now moot in practice even though the tokens stay separate; the light-mode gap (#bd4b2fvs#D9603F) still stands, and#D9603Fitself is only 3.69:1 on white, still short of body-text AA there. Worth revisiting next time this heading is touched, not urgent on its own.
8. A related, distinct construct — the inline mention
"Visit · Pets", not "Visit Pets" — the Website's product cards, and
family-footer.md §1's own body-copy guidance, both use this shorter form
outside the footer and outside a true wordmark. It borrows from the
wordmark's typography without being one, and has always been documented
that way (Website's own styles.css comments on .link-mark predate this
doc and say the same thing independently) — worth keeping distinct rather
than merging into §1–§6, because merging it would mean forcing display: flex into a sentence, which was tried and broke inline text flow onto
three separate lines in a narrow column.
What it borrows: uppercase, 0.10em letter-spacing, and the product's
own accent colour on the name — same visual family as the wordmark, so
Visit · Pets still reads as carrying the family's mark.
What it doesn't: .brand-wordmark's display: flex, and critically not
the suffix's 0.6em-of-a-large-name sizing, because there's no large family
name here to hang off. 0.9em of the surrounding sentence size instead —
chosen for a specific optical reason, not proportion: capital letters in a
sentence read larger than the lowercase beside them, because every letter
reaches cap-height where lowercase mostly sits at x-height. 0.9em brings
the caps back down to roughly the height of the ascenders on either side,
rather than looming over them at 1em. The weight, though, does carry
over: the product name is font-weight: 700, the same as .brand-suffix
everywhere else it appears (§3) — only the lead-in word ("Visit") stays at
.link-mark's 600, because it isn't part of the mark.
What still applies, in a form specific to this construct. An inline
mention has a third part the true wordmark's cross-link rule doesn't — a
lead-in word ("Visit") that isn't part of the mark at all, sitting in the
same <a> as the dot and the name. That gives it three independent hover
treatments, and — corrected 4 August 2026 — none of them are set on the
<a> itself, only ever on the specific span that should show it:
- The lead-in word (
.link-visit) underlines in the page's ordinary link colour on hover — it's just a sentence, not part of the mark. - The separator dot (
.link-dot) is never mentioned in a decoration rule at all — not underlined, and not "excluded" either, because there's nothing decorating its ancestor to exclude it from. - The product name (
.link-name) underlines in its own colour on hover, matching the wordmark's cross-link rule (§6).
An earlier version of this set text-decoration: underline on the <a>
(for "Visit") and excluded the dot from it — the same mistake §6 describes,
and it produced the same symptom: a sliver of the ancestor's line peeking
out from under the dot despite the exclusion, plus a second sliver visible
behind the name's own more specific colour where the two decorating boxes
didn't quite align. Decorating three independent spans directly, and never
the shared ancestor, removes the possibility of a leak rather than trying
to contain one.
.product-link a:hover .link-visit { text-decoration: underline; } /* "Visit" — generic */
.product-link a:hover .link-name { text-decoration: underline; } /* the name — its own colour, 700 weight */
/* .link-dot: no rule at all — never decorated, so nothing to leak */
Implemented on the Website 4 August 2026 (.link-visit wraps "Visit",
.link-name wraps the product name, inside each .link-mark). Only the
Website has this pattern — it's the one repo whose page lists more than one
product. It isn't expected to appear in Pets, Connections, or Rissbrook,
each of which only ever talks about itself.
9. A fifth instance — inline in running prose
Every context above fixes one explicit size, because each renders at exactly
one place, once: the footer's 19px, the header's +4px offset, the product
listing's 26px. The wrinkle this section covers is a context that doesn't
get that luxury — the plain word "Amoleo" (or "Amoleo · Rissbrook") turning
up inside ordinary sentences, and not just one sentence: a hero headline at
clamp(34px, 6.2vw, 60px), a bio paragraph at 17px, a footer copyright line
smaller still, a cookie-consent banner in between. One absolute pixel value
can't serve all of those the way it serves a single fixed heading — the
wordmark has to sit inside whatever size the surrounding prose already is,
in every place that prose exists, not replace it with a number of its own.
This is still a genuine wordmark, unlike §8. The inline mention
("Visit · Pets") deliberately sheds .brand-wordmark's flex layout and
size ratio because there's no large family name to hang off. Here there
is — "Amoleo" is still the family name, still wants its dot-and-suffix
child for a compound mention like "Amoleo · Rissbrook" — so the fix keeps
all three classes (§1) and only changes how the size and layout are
expressed: relatively, off the surrounding text, rather than as an
absolute value the way §2 and §7 do it.
.brand-wordmark.inline-mark {
display: inline-flex; /* not the base rule's flex — that opens a block
and breaks the sentence onto its own line */
vertical-align: baseline;
font-size: 0.85em; /* relative to the surrounding text's own
computed size, not an absolute px */
}
0.79em (§2) on .brand-dot/.brand-suffix then compounds automatically
off this element's own 0.85em, exactly as it does everywhere else the
wordmark appears — nothing extra to write for a compound mention.
0.85em, not §8's 0.9em — a fresh tuning pass, not a borrowed number.
The optical reasoning is the same one §8 gives (uppercase, 800 weight and
0.10em tracking all read "louder" than lowercase prose at equal size, so
sizing down compensates) but the two constructs aren't scaled from the same
starting shape — §8 flattens to a single string with no flex box or
dot/suffix pair, this keeps the full assembly — so the number that looks
right for one isn't assumed to be the number that looks right for the
other. Check by eye at each size this actually renders at, same as §0's
verification step for any other context.
No manual alignment offset, unlike §5's saga. vertical-align: baseline
on the inline-flex box is enough on its own here. §5's centering problem was
specifically about a small dot-and-suffix pair losing their shared visual
centre against a much larger name; that only bites when one piece of the
mark is sized independently of another. Here the whole assembly — name,
dot, suffix — scales down together as one 0.85em unit, so there's no
independently-sized sibling pair left to misalign.
A compound mention's suffix accent still needs --wordmark-accent set
somewhere — there's no .family-mark or .product.rissbrook ancestor to
carry it in running prose, so it goes on the inline mark itself:
.inline-mark.rissbrook { --wordmark-accent: var(--brand-rissbrook); }
Implemented on the Website 5 August 2026, and not treated as a Website-only trick the way §8's multi-product listing is — this one is expected to recur anywhere a site's own copy mentions "Amoleo" (or, on the Website specifically, another product by name) inside a sentence rather than a heading. Covers, on the Website: the hero headline, the Accounts blurb, two plain mentions and one compound "Amoleo · Rissbrook" mention in Luke's bio, the footer copyright line, and the cookie-consent banner text.
10. What to check afterwards
- Every wordmark, wherever it appears — header, footer, hero, or a page's
own body content like the Website's product cards: three classes present,
dot
aria-hidden. - Suffix is always smaller than the name, always bold, always the product's
own fixed accent colour (or a documented,
sites-scoped exception like--brand-pets-ink— §7). - A
topoffset on.brand-suffix/.brand-dotis normal, not a smell — every context measured so far except the product-listing heading (§7) has needed one. What to check is narrower: the value is a real, measured number (§5's table below), applied to both spans together, never invented or copied from a different size pairing without re-measuring. - A header self-link wordmark shows no decoration in any state, including hover and focus background; the standard focus ring still shows for keyboard users.
- The footer's family-name self-link is the one exception, since 5
August 2026: it underlines on hover, in its own colour (
--brand-amoleo, viacolor: inherit) — same as the cross-links beside it, not the header's zero-decoration-ever rule. At rest it's still undecorated; only the hover state differs from the header logo. Don't "fix" it back to zero decoration in every state; the two are deliberately different now. - A cross-link product name underlines on hover in its own colour, not a
generic link colour — because the underline is set directly on the
name (
.brand-suffixor its inline-mention equivalent), never on the<a>. The separator dot never shows a line, and there's no exclusion rule to check for, because the dot was never in the decorating element's scope to begin with. Applies equally to a true wordmark's cross-link and to an inline mention's (§8). - An inline mention (§8) never uses
display: flexor the wordmark's own size/weight numbers — it has its own, smaller and lighter, tuned for sitting inside a sentence rather than hanging off "Amoleo".
Generated from docs/family-wordmark.md + tools/wordmark.json. Edit there and re-run npm run build in docs-site-src/ — never hand-edit a page.