Skip to content

Theme refresh + Umami analytics - #12

Merged
nofutur3 merged 48 commits into
mainfrom
theme-refresh
Oct 7, 2026
Merged

nofutur3 merged 48 commits into
mainfrom
theme-refresh

Conversation

@nofutur3

@nofutur3 nofutur3 commented Oct 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • Theme refresh: new color/typography tokens (Inter + JetBrains Mono), prompt-style wordmark logo and favicon, responsive sidebar with primary nav and categories, git-diffstat-style post cards, Matrix-style code block theme, simplified category/tag archive pages
  • Support component implemented (pixel-notched banners with copy modal)
  • Homepage cards show real excerpts; full-text posts (no excerpt marker) get a distinct box treatment
  • Content fixes: Adminer and sqlsrv posts written, Doctrine Collections post expanded, assorted typo/command fixes
  • Umami analytics added via gatsby-ssr.js; Google Analytics plugin removed (Universal Analytics stopped collecting data in 2024)

Test plan

  • gatsby build succeeds in Docker
  • Built public/index.html contains the Umami script and no Google Analytics tag
  • Note: prettier --check flags pre-existing formatting in src/components/support.tsx and src/layouts/base.js

🤖 Generated with Claude Code

nofutur3 and others added 30 commits September 2, 2026 14:20
…debar

- Trim global.scss to only Bootstrap imports (remove old fixed footer and .bio { display: block } rules)
- Change footer.js to use site-footer class and remove Bootstrap utility classes
- Make .global-wrapper a flex column layout with min-height 100vh
- Add .global-wrapper > .row flex rule to push footer to bottom
- Add .site-footer styling with border-top, centered text, and hover states

Fixes footer overlapping on short pages and sidebar layout being flattened by CSS cascade.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_$(uuidgen | head -c 8)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
… Bootstrap Card styling

- Rewrite PostsListCard to render plain <article className="post-card"> instead of react-bootstrap Card
- Update index.js to use PostsList component and query edges { node { ... } } instead of nodes
- Add category to GraphQL query fields (was missing from homepage)
- Replace .post-list-item CSS rules with .post-card styling that matches category/tag pages
- Homepage now uses identical markup and styling as category/tag pages

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
The pinned footer restore in the previous commit dropped the centering
that used to come from Bootstrap's text-center utility class on the old
JSX. Add it back directly in the CSS.
Replaces the stock Gatsby favicon/manifest icon with a generated mark
(">" chevron + cursor bar) matching the new theme, and swaps the plain
site-title header for a "$ happyphpdev_" wordmark styled the same way.
.main-heading was sized for a short plain-text title (fontSize-7); the
13-character "$ happyphpdev_" wordmark overflowed the narrow col-md-3
sidebar at that size. Also drop the now-unused .brand flex wrapper.
vertical-align (text-bottom, then baseline) kept looking off by a few
pixels depending on the font's descender metrics. Switch to flexbox
cross-axis centering on the .brand wrapper instead, which centers the
cursor against the line box rather than a glyph baseline/descender line
-- much less sensitive to font-specific metric quirks.

Also: blink the cursor on hover (steps(1) 1s infinite), disabled under
prefers-reduced-motion, per user request.
The cursor was a distinct inline-block flex item, so browsers could
independently decide it didn't fit the remaining line width and wrap it
onto its own line -- confirmed happening in Firefox but not Chrome at the
sidebar's width, a classic atomic-inline-box wrapping edge case.

Using the U+2588 full-block character as plain text content instead means
it's part of the same run as "happyphpdev": it wraps exactly like any
other character (predictably, identically across browsers) and never
detaches from the word.
Extract the category list out of Bio (which was only fetching it via a
second query alongside author data) into a dedicated Categories
component, and render it in the sidebar right under the primary nav
(home/rss) instead of after the bio block, per request.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Was left disabled in a pre-existing uncommitted change; user confirmed
it should render.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
"// categories" was a separate <p> in its own block, visually disconnected
from the "> home" / "> rss" menu above it (different prefix width, extra
margin between blocks). Categories now returns an <li> rendered directly
inside the same <ul> as home/rss, and a shared .menu-marker fixed-width
(3ch) prefix wrapper makes "home", "rss", and "categories" all start at
the same column regardless of the ">" vs "//" prefix length. Vertical
rhythm between all three now comes from the single shared
.sidebar-nav li margin-bottom rule instead of a separate heading margin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Reuses the shared .menu-marker fixed-width prefix so individual category
links line up with the rest of the menu, since they're links too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Kept but disabled per request while its design/usage is reconsidered.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
fields.slug already comes with leading/trailing slashes from Gatsby's
createFilePath (e.g. "/2026/08/mikrotik-backup/"), so wrapping it again as
`/${fields.slug}/` produced "//2026/08/mikrotik-backup//" -- a leading //
in an href is parsed as a protocol-relative URL, so browsers were
resolving these links to a bogus host named "2026" instead of the post.

Pre-existing bug (not introduced by this refresh), previously only hit
category/tag pages since the homepage had its own separate, correct
markup; became visible everywhere once the homepage was unified onto
this shared component.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Non-root pages showed both a "\xe2\x86\x90 home" text link in the header and a
"> home" item in the nav menu right below it -- two links to the same
place. Show the "\$ happyphpdev_" wordmark everywhere instead (it already
links home), and drop the now-redundant header-link-home link/CSS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Drop the leftover react-bootstrap Container and empty
.category-container/.tag-container wrappers (zero CSS, inconsistent
spacing vs. the rest of the theme) and the plain "Category: x" / "Tag: x"
heading in favor of a styled archive-header matching the site's other
eyebrow+title patterns.

Also stop repeating the category on every card on a category page -- it's
already the page you're on. New showCategory prop (default true) on
PostsList/PostsListCard, set to false only from category.js.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Replace the full PostsList (meta line, excerpt, "read more" link) with a
new minimal PostTitleList component on category/tag archive pages, per
request -- these are browsing pages, not the homepage, so just the titles
as a clickable list is enough. Trims category.js/tag.js's GraphQL queries
down to only fields.slug + frontmatter.title (dropping excerpt,
timeToRead -- which was already dead, never rendered -- and date).

Also reverts the showCategory prop added to PostsList/PostsListCard
earlier today: it was only ever passed false from category.js, which no
longer renders PostsList at all, so the prop was dead weight.

Verified against a real `gatsby build` output (dev mode's page shell
can't show this), since it's a GraphQL query shape change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
PostsList now interleaves a "+1 post" fake diffstat (flanked by short
lines) between cards instead of giving each card its own border-bottom.
Only renders between cards, not after the last one -- verified via a real
gatsby build (7 cards, 6 dividers) since dev mode's page shell can't show
this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Missed carrying justify-content: center over from the mockup -- both
flex lines cap at max-width: 3rem, so without it the leftover space had
nowhere to go and the whole row sat left-aligned by default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Same treatment as the homepage divider concepts -- extends the site's
existing // comment language instead of a plain rule. Verified against a
real gatsby build (no <hr> left in the article body, "// EOF" text
present).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Adds a Revolut link and a BTC on-chain address with a QR code (new
qrcode.react dependency, React 19 compatible). Verified against a real
gatsby build (dev mode's page shell can't show a client-rendered SVG QR
code either).

Deliberately left out for now, with TODOs in the source:
- Lightning: the value provided was a BOLT11 invoice, not a Lightning
  Address -- invoices are single-use and expire (usually within an hour),
  so embedding one statically would silently stop working for every
  visitor after that. Needs an actual name@walletprovider.com address.
- A coffee-tip link (Ko-fi vs Buy Me a Coffee) -- platform not decided yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
nofutur3 and others added 18 commits September 4, 2026 14:50
Replace the plain link + inline QR with small 8-bit-style badge buttons
(clip-path corner notches, dark terminal surface, bright green glyph) --
Revolut is a plain outbound link, Bitcoin opens a modal with the QR code
and address. Clicking either the QR or the address copies it via
navigator.clipboard, with brief "copied to clipboard" feedback.

Verified against a real gatsby build: SSR renders the two banners with
the modal correctly absent (React state starts closed, no hydration
mismatch) since dev mode's page shell can't show this either.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
"Enjoyed the post? A tip's always welcome." above the banner buttons, per
request.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Covers what Adminer is, why it's preferable to phpMyAdmin, how to run it
in Docker Compose for local dev, and a warning against exposing it on
production. Notes from the site owner.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
"git remotes" isn't a real command -- it's "git remote -v". Also fixed
several grammar rough spots (title, description, intro sentence,
"GIT" -> "Git" capitalization) and clarified the Bitbucket screenshot's
lead-in sentence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Rest of the post is solid already, this is the only nit -- both spellings
work (git config keys are case-insensitive) but the docs convention is
excludesFile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Bootstrap's own "a { color: ...; text-decoration: underline; }" rule has
the same specificity as ours and was loading later (global.scss, which
imports Bootstrap, comes after style.css in gatsby-browser.js), so every
link on the site -- including markdown body links -- rendered in
Bootstrap's default blue with a permanent underline instead of the site's
green/no-underline treatment.

Tried reordering the stylesheet imports first, but that flips the winner
for every selector Bootstrap and the theme both define, not just links,
and visibly changed fonts/other elements. Reverted that and used a
targeted specificity bump instead ("body a" beats a bare "a" regardless
of source order) so only the link rule is affected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
… cards

The homepage post cards were showing frontmatter.description (meant for
SEO meta tags) instead of actual article content. Now they always use
Gatsby's excerpt field, and authors can mark exactly where an excerpt
should end with an "<!-- more -->" separator in the markdown body
(configured via gatsby-transformer-remark's excerpt_separator option --
note the snake_case key name, "excerptSeparator" silently does nothing).
Posts without the marker fall back to the default prune-length excerpt,
same as before.

description remains untouched for its SEO/meta-description purpose
(Seo component, blog-post.js).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Without one, the homepage card's excerpt bled straight into the raw
error-message code block instead of stopping after the intro sentence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
This post was just a bare Dockerfile with no explanation, and its
frontmatter description was a copy-paste leftover from the Doctrine
Collections post (wrong topic entirely, would have shown up as a
misleading SEO meta description). Added an intro paragraph, a closing
note on the --allow-untrusted flags, and an excerpt marker so the
homepage card doesn't lead with a raw code block.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Adds why the generic key/value types matter and, more importantly, that
this is pure PHPDoc: PHP ignores it at runtime, it only helps when an IDE
or a static analysis tool (PHPStan, Psalm) is actually reading it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Only posts that explicitly opt in with "<!-- more -->" get truncated with
a "Read more" link. Posts without the marker (short announcements, etc.)
now show their complete content on the card and skip the "Read more" link
entirely, instead of silently falling back to a 140-char prune -- which
was never the intent.

Determined by checking rawMarkdownBody for the separator string, since
Gatsby's excerpt field doesn't expose whether it actually found one.
Verified against a real gatsby build: only the two posts carrying the
marker show "Read more", the rest render their full HTML.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
post-card--note: inset panel (surface background, bordered, green left
accent) so short announcement-style posts visually read as a different
kind of content from regular article cards, not just another list item.
Verified against a real gatsby build (5 note-styled, 2 regular, matching
the 5 posts without a marker vs 2 with one).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXxyPkq7xMErj3CmKamPKk
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Universal Analytics stopped collecting data in 2024; Umami replaces it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The deploy workflow ran on Node 18, which can't require() ES modules
used by gatsby-transformer-remark's deps, so every main deploy failed.
Pin Node via .nvmrc for both workflows, run make lint/test instead of
missing npm scripts, and build on PRs (deploy stays main-only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nofutur3
nofutur3 merged commit 14334cc into main Oct 7, 2026
6 checks passed
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.

1 participant