Skip to content

Add Omarchy-style navigation and animated hero announcement - #203

Open
HANCORE-linux wants to merge 3 commits into
omacom:masterfrom
HANCORE-linux:hancore/site-bar-announcement
Open

HANCORE-linux wants to merge 3 commits into
omacom:masterfrom
HANCORE-linux:hancore/site-bar-announcement

Conversation

@HANCORE-linux

@HANCORE-linux HANCORE-linux commented Sep 7, 2026

Copy link
Copy Markdown

Summary

  • Replace the layered, hero-blended header with an opaque, theme-aware Omarchy-style bar. Preserve navigation, search, theme switching, RSS, GitHub, installation, and mobile music controls.
  • Preserve Astro's persistent header and route-aware active links; remove the obsolete duplicate navigation layer from the homepage and 404 hero.
  • Move the Foundation announcement above the primary hero actions. Match their 40px single-line height, keep clean mobile wrapping, and use a neutral background with a full accent fill on hover. Omit extra badges and remove the trailing link icon.
  • Scramble the complete announcement, including the current $15.5 million amount, using the registry PR #10 glyph pool. Resolve the text over approximately 1.6 seconds and repeat every six seconds while visible.

Motion and accessibility

  • Keep the complete authored message available to screen readers and without JavaScript.
  • Pause for hover, focus, offscreen content, hidden tabs, and reduced motion.
  • Retain a separate keyboard-accessible pause/resume action, revealed only on focus.
  • Repeat indefinitely only where a fine pointer provides hover/pause feedback; touch-only devices play once and remain readable.
  • Preserve the current language switcher and translate the two new pause actions across all 29 non-English catalogues.
  • Use a text-colored focus outline for current-page links, clearing 3:1 in all 22 themes while retaining the external reviewer's mobile Install focus fix.
  • Keep the article destination and current translated banner copy unchanged.

Verification

  • npm run lint
  • npm run typecheck
  • npm test — 31 Node tests and 26 Python tests passed
  • npm run check:translations — no pending UI messages
  • English, Danish, and Arabic production builds
  • npm run parity against the complete working-tree export
  • Production-browser checks: persistent Astro navigation, SSR/client active links, responsive header/menu, language and music controls, search/theme dialogs, 404 rendering, accessible/no-JS announcement, full accent hover, 40px height, five viewport widths, measured 1.6-second scramble / six-second desktop repeat interval, one-shot touch behavior, and 44 focused current-link checks across 22 themes (minimum 4.93:1)

The unchanged upstream site already emits AbortError: Transition was skipped during client navigation. The same warnings were reproduced on both builds; this PR introduces no additional navigation errors and leaves that upstream behavior untouched.

Base

PR #198 has merged. This branch is rebased onto master at 83dbfa5748527737c37e468b1ee6eef5817e982a, retaining the current localization, workstation, branding, content, download, and navigation updates.

@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed by Claude Opus 5 at high effort and by Codex at xhigh reasoning as an independent second reviewer. Two verdicts, kept apart: whether the site's chrome should look like this is a design decision for the maintainer and nothing below tries to make it; what follows is what the rewrite does mechanically, and one defect that has been fixed on this branch.

Nothing was lost in the -675. Both trees were built on a disposable VM and compared file for file. 2647 HTML pages on each side and an identical file set — the only difference is the hashed _astro bundle names. / carries the same 244 links (188 unique) before and after, and so does /404; no route or link that worked on master 404s now. The <head> of /, /404/, /news/, /themes/ and /manual/ is tag-for-tag identical once hashed asset names are normalised, so no meta tag, OG tag, structured data or analytics went with the deletions. All four navigation entries, search, the theme picker, RSS, GitHub, Install and the mobile music control survive, and the announcement still lands on /news/2026/08/omacom-foundation-launches-with-8-million/.

One behaviour did change, and it is the maintainer's call. Deleting useNavSurface means the bar no longer takes the colour of the section beneath it. Measured on nord: the new bar is rgb(46, 52, 64) at every scroll position, while #watch, #hardware, #agents, #developers and #windows are all rgb(34, 39, 48) — a lighter band with a hard edge across the top of every bg-deep section. On master the bar painted that section's own colour at 90% behind a 12px backdrop blur and was invisible against it. That is the design being proposed rather than a bug, but the PR body does not say it, so it is named here.

The animation checks out. Under emulated prefers-reduced-motion: reduce the visual span never leaves phase="idle", plays stays at 0, and the text does not change across 4.8s — the setting is honoured, and changing it mid-scramble restores the message immediately. Cumulative layout shift measured with a layout-shift PerformanceObserver is 0 at 320px, 390px, 768px and 1280px: the announcement box and the CTA below it hold the same geometry from first paint through every scramble, because each cell is a fixed 1ch inline-block that a wider fallback glyph overflows rather than widens. With JavaScript disabled the full message is still in the server-rendered HTML, in both the sr-only copy and the aria-hidden visual copy, so it is readable without scripts and read once by a screen reader. Hover, focus, an offscreen hero and a hidden tab each stop it, and every timer, interval, observer and listener is torn down on unmount — after a client-side navigation to /news/ nothing is left running.

Keyboard and contrast. The tab order runs skip link, home, News, Manual, Plugins, Themes, search, theme, RSS, GitHub, Install, and every stop has a visible 2px ring. The pause control is real: it is tab stop 14, :focus-visible lifts it out of sr-only at 166x32 with its own ring, and pressing it holds the message still. Worth knowing that pressing Resume appears to do nothing until focus leaves the announcement, because focus-within is itself one of the pause conditions — correct, but surprising for the person who just pressed it. All 22 themes pass AA for the 12px .site-bar__page label; the lowest is Rosé Pine at 6.07:1.

Fixed and pushed as 1ce7533: .site-bar :is(a, button):focus-visible set outline-offset: -2px on every link and button in the header, which reached the mobile menu's Install button. That one carries the brand fill, so the brand ring was drawn inside a brand fill — 1.00:1 in every theme, no visible focus indicator at all on the primary call to action. Confirmed by measuring the computed styles in a real browser (outline-color and background-color both rgb(129, 161, 193) on nord) and by screenshot before and after. The rule now names the bar's own flat controls, so they keep the inset ring the compact row needs, and Install, RSS, GitHub and the music control keep the ring the shared button already ships, outside their box.

Still open, reported rather than fixed because the remedy is a design choice: a focused current-page nav link draws the same inset brand ring over the aria-current fill (color-mix(in srgb, var(--color-text) 18%, transparent)), which is under the 3:1 WCAG 2.4.11 needs on four themes — Rosé Pine 2.41:1, Miasma 2.57:1, Nord 2.89:1, Osaka Jade 2.95:1. Changing the active fill, the ring colour or the offset are all reasonable answers and each affects the look under review.

Three loose ends the deletions left, none with any effect on screen: src/styles.css:2238 still selects [data-nav-past-hero], which nothing sets any more; groundOf at src/lib/theme.ts:142 has no callers left; and data-ground at src/astro/pages/HomePage.tsx:487 is no longer read by anything. Separately, npm run check fails on src/styles.css — that is prettier objecting to the .brand__preview gradient, which is unformatted on master too and is not this PR's doing.

No supply-chain or privacy change. package.json and the lockfile are untouched, no font, script or asset from a new origin appears anywhere in the diff, and the only external URLs are the plugins.omarchy.org and github.com/omacom/omarchy destinations that were already there.

No conflict with the theme gallery PRs (#187, #188, #189, #190, #191, #193, #196, #197, #200). Those touch themes/index.html and assets/themes/*.webp; this touches only src/. There is no file overlap, scripts/port_content.py still reads themes/index.html into src/data/themes.json untouched, and /themes/ renders its gallery after this change. Merge order between them is free and none of the nine needs re-checking on account of this.

On a worker VM built from the worktree: npm run lint, npm run typecheck, npm test (21 passing) and npm run build all pass after the pushed fix, and every measurement above was taken in headless Chromium against that build.

Codex agreed with the conclusions already reached on the deletions, the animation lifecycle and the no-JS path; its independence is not currently guaranteed, so that agreement is worth less than it looks. The focus-ring defect is the exception and stands on its own: it is a mechanism this review had not reasoned about, and it is credited on the commit.

Waiting on the maintainer, for the design.

HANCORE-linux and others added 3 commits September 8, 2026 10:11
The bar's controls are no taller than its row, so their ring is drawn 2px
inside the box. Applying that to every a and button in the header reached the
mobile menu's Install too, which carries the brand fill: a brand outline drawn
inside a brand fill is the colour of the fill, so the ring measured 1.00:1 in
every theme and the primary call to action had no visible focus at all.
Naming the bar's own controls leaves Install, RSS, GitHub and the music
control with the ring the shared button already ships, outside their box.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.com>
@HANCORE-linux
HANCORE-linux force-pushed the hancore/site-bar-announcement branch from 1ce7533 to 571a46b Compare September 8, 2026 08:56
@omarchybot

Copy link
Copy Markdown
Collaborator

Re-reviewed at 571a46b by Claude Opus 5, with a second opinion from Codex at xhigh reasoning as an independent reviewer. The branch's own work is three commits against the merge-base 83dbfa5, so that is the delta that was read; the raw compare is large only because master moved underneath. Two things to say up front about what this rests on: the site was not built this run — code execution was withheld as a policy matter on the machine that runs this triage, not because anything failed — so every contrast figure below is computed from the theme tokens in src/styles.css rather than measured on a rendered page, and CI is what I am relying on for the suite. gh pr checks 203 at this exact head reports check success (lint, typecheck, test, build, parity all green) and deploy skipped, which is correct on a pull_request event.

The open finding is closed

571a46b points the current-page ring at --color-text. The ring is drawn at outline-offset: -2px, so what it sits on is the [aria-current] fill — color-mix(in srgb, var(--color-text) 18%, transparent) over the bar's --color-bg — which composites to 0.18·text + 0.82·bg. Against that:

theme brand ring (before) text ring (now)
rose-pine 2.41:1 5.11:1
miasma 2.57:1 5.87:1
nord 2.89:1 5.76:1
osaka-jade 2.94:1 8.65:1

All 22 themes were computed, not only those four, because a shared token moves every theme at once and this is exactly the change where a dark-theme fix costs a light one. Nothing was traded: the floor across all 22 is everforest at 4.91:1, which is the same ratio the label text already had on that fill — the ring now reuses the ink that was already proven to read there, which is the neat part of the fix. The ordinary non-current ring is untouched, still 3.14:1 or better everywhere with rose-pine the floor. The cascade works out too: (0,4,0) for the new rule against (0,3,0) for the shorthand above it, so outline-color overrides while the 2px width and the inset offset survive. Your own rendered minimum of 4.93:1 and this computed 4.91:1 agree to within 8-bit rounding, which is the two methods checking each other.

Two other things that were verified rather than assumed

The mobile Install fix survived the rebase intact. Because the branch was rebased, the commit we pushed is no longer an ancestor of this head — it came back as dcd9384 with an identical git patch-id, so the scoped selector is byte-for-byte what went in. Worth confirming out loud: a CSS file across a rebase is precisely where that gets lost, and a 1.00:1 ring is a worse outcome than no ring at all.

The named-class scoping leaves nothing unringed. Every focusable element inside .site-bar was enumerated — home, the four desktop nav links, search, theme, the language trigger, RSS, GitHub, desktop Install, the mobile theme and menu toggles, and every .site-bar__menu-link all carry one of the six named classes; the menu's filled Install/RSS/GitHub buttons take the shared ring from ui/button.tsx; MusicMenuControl ships its own at MusicControl.tsx:80; the locale list is portalled out of the bar and rings its own items. The scrim is tabIndex={-1}. There is also a base-layer :focus-visible backstop at styles.css:949, so an over-narrow bar selector could only ever cost the inset geometry, never the indicator — which is also why the original defect was an override rather than an omission.

Three small things, none blocking

1. rose-pine, hovered and keyboard-focused at once. A non-current bar control that is both hovered and keyboard-focused draws the brand ring against the 8% hover fill at 2.797:1, under the same 3:1 the fix above just cleared. rose-pine only; the next lowest is miasma at 3.26:1. Reachable by tabbing to a control and then moving the mouse over it. Current-page links are safe — the later [aria-current] background wins the cascade over :hover at equal specificity. Not pushed, because every remedy (extending the outline-color override to :hover, dropping the hover fill while focused, lightening the mix) is a visible change inside a look that is still the maintainer's to approve.

2. SiteHeader.tsx:254. aria-label={t('Close navigation menu')} became a literal string in this commit. There is no audible difference today, since t falls back to its key and no catalogue has that key. What it does change is that the string is now permanently invisible to the tooling: the source collector only queues literal arguments to t(...), so this label can never enter the --pending-site queue translate-site.py drains, and every non-English screen reader will keep announcing the scrim in English. Not pushed — you changed that line deliberately, so reversing it is your call rather than something a reviewer should quietly undo. If it was not deliberate, restoring t(...) is the whole fix. (SiteHeader.tsx:241 has the same gap and predates this commit.)

3. The highlighted amount now reaches 27 more locales. The new fallback in announcement-scramble.ts is correct: running it over all 30 catalogues bolds the amount — 15.5 or 15,5 — in every single one, including Sinhala's trailing form, with no locale where an earlier unrelated number gets caught instead. The consequence is that <strong> now renders in 30 of 30 locales where it rendered in 3, and .hero-announcement__text strong is var(--color-brand) at 14px/700, which is not WCAG "large" text, so 4.5:1 applies rather than 3:1. Against the announcement panel over the hero's field background that is rose-pine 3.00:1, miasma 4.01:1, catppuccin-latte 4.16:1. This was already true for English, so it is not something the commit got wrong so much as something it widened. The hero canvas sits behind the panel, so the real backdrop varies and a rendered check is the arbiter here — flagging it rather than claiming it.

Still open from the previous pass and unchanged: the dead [data-nav-past-hero] rule at styles.css:2261, groundOf in lib/theme.ts, and data-ground in HomePage.tsx, all left without producers or callers by the rewrite. No screen effect.

What was checked, and by whom

Reduced motion, layout shift and the no-JS path were all re-verified at this head rather than carried forward from the last review, since the animation changed: canPlay() still rejects while the reduced-motion query matches and the new canSchedule() is strictly narrower than it; every substituted glyph is still a fixed 1ch inline-block in a monospace face with ligatures off, so advance widths and wrap points do not move; and both the sr-only and aria-hidden copies are still server-rendered. The canHover lifecycle was traced through sync, schedule, play, restore and the ticker — at most one timeout and one ticker, everything torn down on cleanup, touch-only plays once and stops, no leak or stuck state found.

Codex at xhigh confirmed the anchors it was given against 571a46b, computed the same 22-theme table to three decimals, and reached the same verdict on the fix, the selector scoping, the regex across all 30 catalogues and the canHover lifecycle. That is agreement with conclusions already written down, and its independence is not currently guaranteed — it runs read-only on the same machine and can read this session's own working notes — so it is worth what agreement is worth. Two things it contributed that stand on their own and were verified before being repeated here: the base-layer focus backstop that changes what an over-narrow selector could actually cost, and the collector mechanism that makes the literal aria-label undiscoverable rather than merely untranslated. It also flagged that with JavaScript off the "Pause news animation" button is still rendered and focusable with no animation to pause — pre-existing on this branch rather than new in this commit.

Nothing was pushed this run. The accessibility objection that was left open is genuinely closed; what remains is the maintainer's, and it has not moved: whether the chrome should be this bar at all, and whether an indefinitely repeating hero animation belongs on the front page. Worth their attention alongside it: #244 is a competing fix for the duplicate hero navigation this rewrite removes outright, and #266 styles a header wordmark this PR deletes, so those two need a merge order rather than a rebase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants