Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
eab002b
docs: add theme refresh design spec
nofutur3 Sep 2, 2026
0c4a5e0
docs: add theme refresh implementation plan
nofutur3 Sep 2, 2026
ba077ab
chore: swap typeface packages for @fontsource/inter and @fontsource/j…
nofutur3 Sep 2, 2026
2c18f58
style: rework color and typography tokens for the theme refresh
nofutur3 Sep 2, 2026
c2927f2
fix: remove fixed-position footer and duplicate CSS overriding the si…
nofutur3 Sep 2, 2026
df9ace4
fix: make sidebar/content grid responsive and add primary nav links
nofutur3 Sep 2, 2026
fc0e7ee
feat: add a categories list to the sidebar
nofutur3 Sep 2, 2026
7839bf5
style: restyle post meta line and prev/next nav, constrain prose width
nofutur3 Sep 2, 2026
86c6637
style: add a Matrix-inspired dark theme for code blocks
nofutur3 Sep 2, 2026
911a7de
fix: restore pinned footer per user request
nofutur3 Sep 2, 2026
0fe2a59
refactor: unify homepage post list with PostsList/PostsListCard, drop…
nofutur3 Sep 2, 2026
3ef1c46
fix: center footer text per user request
nofutur3 Sep 2, 2026
efdc3ad
feat: add prompt-wordmark logo and matching favicon
nofutur3 Sep 2, 2026
e490d3b
fix: shrink brand wordmark so it fits the sidebar column
nofutur3 Sep 2, 2026
2021bba
fix: align brand cursor block to text baseline, not descender line
nofutur3 Sep 2, 2026
bef20a2
fix: center brand cursor via flexbox instead of baseline alignment
nofutur3 Sep 2, 2026
110ac46
fix: render brand cursor as a text glyph instead of a separate box
nofutur3 Sep 2, 2026
0a7de76
refactor: move categories into its own sidebar component
nofutur3 Sep 4, 2026
8c950e2
fix: restore commented-out avatar/summary block in Bio
nofutur3 Sep 4, 2026
c26200f
style: merge categories heading into the primary nav list
nofutur3 Sep 4, 2026
1c0ef78
style: prefix each category link with the same > marker
nofutur3 Sep 4, 2026
f3b43e8
chore: comment out Bio in the sidebar for now
nofutur3 Sep 4, 2026
defa8cb
fix: stop double-slashing post card links
nofutur3 Sep 4, 2026
ac88477
fix: always show the logo instead of a duplicate home link
nofutur3 Sep 4, 2026
a7b8568
refactor: simplify category/tag archive pages
nofutur3 Sep 4, 2026
06c9264
refactor: reduce category/tag pages to a plain title list
nofutur3 Sep 4, 2026
1e7ee5e
feat: replace post-card border with a git-diffstat-style divider
nofutur3 Sep 4, 2026
403b74a
fix: center the post divider
nofutur3 Sep 4, 2026
ee15e44
style: replace the post-page <hr> with the // EOF marker
nofutur3 Sep 4, 2026
452bd43
feat: implement the Support component (was a literal "Implement" stub)
nofutur3 Sep 4, 2026
2381cab
feat: redesign support links as pixel-notched banners with a copy modal
nofutur3 Sep 4, 2026
af164ae
feat: add a lead-in line to the support section
nofutur3 Sep 4, 2026
d85a837
docs: write the Adminer post (was empty, just frontmatter)
nofutur3 Sep 4, 2026
f291365
fix: correct a bad git command and rephrase awkward sentences
nofutur3 Sep 4, 2026
3df74bb
style: fix inconsistent core.excludesFile capitalization
nofutur3 Sep 4, 2026
1f6e97a
fix: make link color win over Bootstrap's default
nofutur3 Sep 4, 2026
ef09224
feat: use frontmatter.description only for SEO, show real excerpts on…
nofutur3 Sep 4, 2026
e1b1fe7
content: add an excerpt marker to the auto-push post
nofutur3 Sep 4, 2026
f3dddb0
fix: write real content for the sqlsrv post, fix wrong description
nofutur3 Sep 4, 2026
bd067e2
docs: expand the Doctrine Collections post
nofutur3 Sep 4, 2026
1952d23
fix: show full text on homepage for posts without an excerpt marker
nofutur3 Sep 4, 2026
7ad051b
style: give full-text (no excerpt marker) posts a distinct box treatment
nofutur3 Sep 4, 2026
503e1b9
img updated
nofutur3 Sep 4, 2026
e020713
feat: add Umami analytics script to every page
nofutur3 Oct 7, 2026
45adba8
chore: remove Google Analytics plugin
nofutur3 Oct 7, 2026
9a18b81
style: apply Prettier to support and base layout
nofutur3 Oct 7, 2026
506ee67
chore: bump Docker image to Node 24 and npm 11
nofutur3 Oct 7, 2026
2fce38b
fix: build and deploy on Node 24 to match local Docker
nofutur3 Oct 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,206 changes: 1,206 additions & 0 deletions .claude/work/plans/2026-09-02-theme-refresh.md

Large diffs are not rendered by default.

116 changes: 116 additions & 0 deletions .claude/work/specs/2026-09-02-theme-refresh-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Theme refresh: minimalist-professional with cyberpunk accents

## Goal

Replace the stock `gatsby-starter-blog` visual theme with a more professional,
readable design that carries a light cyberpunk/Matrix accent, without going
full dark-mode. Also fix real layout bugs found along the way (broken
responsive grid, `position: fixed` footer, unstyled prev/next nav,
inconsistent post-list markup between the homepage and category/tag pages).

## Non-goals

- No dark mode / theme toggle (light-first only, per decision).
- No "recent posts" sidebar list — Categories list only, to keep the sidebar
minimal and avoid two overlapping "browse" widgets.
- No content/IA changes beyond what's needed to render nav + categories in
the sidebar (no new pages, no changed URLs).

## 1. Design tokens (colors & type)

Rework the CSS custom properties in `src/style.css`:

- Background: near-white (`#fafafa`). Body text: near-black (`#161a16`).
- Accent (Matrix green), two shades:
- `--color-accent`: a phosphor green tuned for AA contrast on white
(~`#0a8f4f`) — used for links, hover states, small UI accents.
- `--color-accent-bright`: a brighter neon green (~`#00ff9c`) — used only on
dark surfaces (code blocks, small badges) where it won't fail contrast.
- Body font: **Inter**, self-hosted via `@fontsource/inter`, replacing
`typeface-merriweather`. Line-height tuned to ~1.65 for prose.
- Monospace accent font: **JetBrains Mono**, self-hosted via
`@fontsource/jetbrains-mono`, replacing `typeface-montserrat`. Used for:
h1–h3 and the site title, nav labels, post meta (date/category/tags), and
code blocks.
- Code blocks (Prism): replace `prismjs/themes/prism.css` with a custom
dark theme — near-black background, green/white token colors. This is
where the Matrix reference gets to be literal, since a terminal-styled
code block fits its content.
- Keep the existing `--spacing-*` scale in `src/style.css`; retune heading/
paragraph/list margins for Inter instead of Merriweather (sans generally
wants slightly different rhythm than serif).

## 2. Layout & sidebar (`src/layouts/base.js`, `src/components/bio.js`, `src/components/footer.js`)

- **Fix responsive grid bug**: sidebar/content columns are currently
`col-2`/`col-10` with no breakpoint prefix, so they never stack and get
crushed on narrow viewports. Change to `col-12 col-md-3` /
`col-12 col-md-9`.
- **Fix footer bug**: `footer { position: fixed; }` in
`src/styles/global.scss` currently overlaps page content on short pages.
Replace with a standard sticky-footer flex layout (`body` as flex column
with `min-height: 100vh`, main content area `flex: 1 0 auto`) so the
footer sits at the bottom in normal flow. Restyle footer text as small
monospace, muted color, top border instead of a solid background block.
- **Sidebar additions**:
- Nav links: Home, RSS feed link — terminal-prompt styled (e.g. `> home`),
monospace, small text.
- Categories list: query `allMarkdownRemark` grouped by
`fields.category` (Gatsby's `group` aggregation) in `bio.js`'s existing
`useStaticQuery`, render links to the existing `/category/<name>/`
pages generated by `gatsby-plugin-categories`. This is the only new
data dependency — no schema changes needed.
- Keep avatar + name + summary as-is content-wise, restyle spacing/
typography to match new tokens.

## 3. Article page polish (`src/templates/blog-post.js`, `src/style.css`)

- Post header: title uses the mono heading font; meta line becomes a single
terminal-style line, e.g. `// 2026-09-02 · php` (monospace, muted color),
replacing the current bare `<p>{date}</p>`.
- Prev/next nav: move the current inline `style={}` object in
`blog-post.js` into CSS classes, style as two terminal-arrow links
(`← previous`, `next →`) with hover accent color.
- Retune paragraph/heading/list spacing for the new sans body font so
vertical rhythm is consistent (this is the direct fix for "vertical
rhythm of texts is broken").
- Blockquote keeps its left-border accent, recolored to the new green.

## 4. Post list unification (`src/pages/index.js`, `src/components/post-list.js`, `src/components/post-list-card.js`)

- `src/pages/index.js` currently hand-rolls its own `<ol>`/`<article>`
markup instead of using the existing `PostsList`/`PostsListCard`
components (those are only wired up on category/tag pages today).
Update `index.js` to use `PostsList`, so homepage/category/tag pages
render posts identically.
- Restyle `PostsListCard`: drop the `react-bootstrap` `Card` wrapper (its
default chrome looks generic and clashes with the new minimal design) in
favor of a plain `<article>` block: title in mono heading font, a
monospace meta line (date · category), excerpt in body font, green
accent border-left and hover state, "Read more" as a text link (not a
Bootstrap button).
- `index.js`'s current GraphQL query for posts must be checked against
what `PostsList`/`PostsListCard` expect (`fields.slug`, `frontmatter`,
`excerpt`) — align field selection if needed, no schema changes.

## 5. Dependencies (`package.json`, `gatsby-browser.js`)

- Remove: `typeface-montserrat`, `typeface-merriweather`.
- Add: `@fontsource/inter`, `@fontsource/jetbrains-mono`.
- Update `gatsby-browser.js` imports accordingly; swap the Prism theme
import for the new custom dark theme (added under `src/styles/`).

## Verification

No test suite exists for this project (`npm test` is a stub per
`CLAUDE.md`). Verification is manual:

- Run `npm run develop` (or the existing Docker `app` service).
- Check in-browser: homepage, a single post, a category page, a tag page —
at both desktop and mobile widths.
- Confirm: footer no longer overlaps short pages, sidebar stacks correctly
on mobile, categories list links resolve, code blocks render with the
new dark theme, prev/next nav is styled, homepage and category pages
render posts with identical markup/styling.
- Run `npm run build` to confirm the production build succeeds (font
package swap, GraphQL query changes).
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
node-version-file: .nvmrc
cache: 'npm'
- run: npm ci
- run: make lint
Expand All @@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
node-version-file: .nvmrc
cache: 'npm'
- run: npm ci
- run: make test
44 changes: 31 additions & 13 deletions .github/workflows/deploy-to-firebase.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,66 @@ on:
push:
branches:
- main
pull_request:
repository_dispatch:
types: [deploy]

permissions:
contents: read
deployments: write

jobs:
build:
ci:
name: CI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Set Node.js
uses: actions/setup-node@master
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version-file: .nvmrc
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint
run: make lint

- name: Test
run: make test

- name: Build
run: npm run build

- name: Archive Production Artifact
uses: actions/upload-artifact@master
- name: Upload Build Artifact
uses: actions/upload-artifact@v4
with:
name: public
path: public
retention-days: 7

deploy:
name: deploy
needs: build
name: Deploy
runs-on: ubuntu-latest
needs: ci
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Download Artifact
uses: actions/download-artifact@master
- uses: actions/checkout@v4

- name: Download Build Artifact
uses: actions/download-artifact@v4
with:
name: public
path: public

- name: Deploy to Firebase
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_HAPPYPHPDEV_COM }}'
channelId: live
projectId: happyphpdev-com
env:
PARSER_PLATFORM: 5
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
25 changes: 24 additions & 1 deletion content/blog/2021/06/adminer/index.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
---
title: Adminer
date: "2021-06-02"
description: "Hello World"
description: "What Adminer is, why it beats phpMyAdmin, and how to run it safely in Docker Compose."
category: "tools"
tags:
- database
- docker
---

[Adminer](https://www.adminer.org/) is a database management tool, similar to phpMyAdmin, but it ships as a single PHP file. No installation, no dependencies, no folder full of assets. You just drop it next to your project (or run its Docker image) and open it in the browser.

A few reasons I reach for it over phpMyAdmin:

- It talks to more than MySQL/MariaDB out of the box: PostgreSQL, SQLite, MS SQL, Oracle, and a couple more.
- The UI is lighter and loads faster, since there's a lot less markup and JavaScript behind it.
- Being one file makes it trivial to drop into any project without touching the rest of your setup.

In practice I add it as a service in my `docker-compose.yml` during local development:

```yaml
services:
adminer:
image: adminer
restart: always
ports:
- 8080:8080
```

Point your browser at `localhost:8080`, log in with your database container's service name as the host, and you're browsing your data without installing any client.

**One warning though:** never leave Adminer running on a production server. It's a full database admin panel reachable straight from a browser, and exposing that publicly is a real security risk. Keep it in your local or dev compose file only, and if you genuinely need it somewhere shared, put it behind a VPN or an IP allowlist, never open to the internet.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions content/blog/2021/06/git-rename-master-to-main/index.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
---
title: How to rename master to main at Git
title: How to rename master to main in Git
date: "2021-06-01"
description: "Do you want to have your main branch named to main instead of deprecated master? Not problem at all. Just follow these steps."
description: "Do you want to rename your main branch from the deprecated master to main? No problem at all, just follow these steps."
category: "tips"
tags:
- git
---

Do you want to have your main branch named to <strong>main</strong> instead of deprecated <strong>master</strong>? Not problem at all. You should check how you named your remote repo(s), it's usually called <strong>origin</strong>. You can check it:
Do you want to rename your main branch from the deprecated <strong>master</strong> to <strong>main</strong>? No problem at all. First check what your remote repository is called, it's usually <strong>origin</strong>:

```bash
git remotes
git remote -v
```

And now you are ready to go:
Expand All @@ -29,11 +29,11 @@ Do you see this error message?
! [remote rejected] master (deletion of the current branch prohibited)
```

Alright, you have to set the main branch in your remote GIT repository.
You'll need to change the default branch in your remote Git host's settings first (Bitbucket in this example):

![Bitbucket setup](./bitbucket-setup.png)

And don't forget to set the default branch in your local GIT repository:
And don't forget to set the default branch in your local Git repository:

```bash
git config --global init.defaultBranch main
Expand Down
2 changes: 1 addition & 1 deletion content/blog/2022/05/global-gitignore/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ Thumbs.db
Once you have your .gitignore file, you can tell Git to use it globally:

```bash
git config --global core.excludesfile ~/.gitignore
git config --global core.excludesFile ~/.gitignore
```

11 changes: 9 additions & 2 deletions content/blog/2022/06/annotate-doctrine-collections/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ tags:
- annotations
---

When working with collections in PHP entities, it's recommended to use the Doctrine\Common\Collections\Collection interface as a type. This will allow you to use either the ArrayCollection or PersistentCollection implementation, depending on your needs. To properly document your code with PHPDoc, you can use the following syntax:
When working with collections in PHP entities, it's recommended to use the `Doctrine\Common\Collections\Collection` interface as a type. This lets you use either the `ArrayCollection` or `PersistentCollection` implementation, depending on your needs, without changing any code that consumes the property.

The problem is that plain PHP has no generics: `Collection` alone doesn't tell you, or your IDE, what's actually inside it. That's where PHPDoc comes in:

```php
/** @var Collection<int, Article> $articles */
private Collection $articles;
private Collection $articles;
```

The first type parameter is the key type Doctrine uses internally (`int` for a plain array-like collection), and the second is the entity class actually stored inside, `Article` here. Annotate it this way and static analysis tools like PHPStan or Psalm, and IDEs like PhpStorm, understand that iterating over `$articles` gives you `Article` instances, with proper autocompletion and type checking.

Keep in mind this annotation has no effect at runtime. PHP itself ignores it completely. It only helps if something is actually reading it, your IDE or a static analysis tool in your CI pipeline.
2 changes: 2 additions & 0 deletions content/blog/2022/07/auto-push/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ tags:

Do you often get this message when you try to push your changes to the remote repository?

<!-- more -->

```bash
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
Expand Down
10 changes: 9 additions & 1 deletion content/blog/2022/10/sqlsrv-docker-alpine/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: How to add sqlsrv extension for docker (alpine)
date: "2022-10-27"
description: "Did you ever wonder how to annotate Doctrine Collections with PHPDoc? Check out the syntax!"
description: "A working Dockerfile for installing the sqlsrv and pdo_sqlsrv PHP extensions on Alpine."
category: "php"
tags:
- php
Expand All @@ -10,6 +10,12 @@ tags:
- sqlsrv
---

Getting the `sqlsrv` and `pdo_sqlsrv` PHP extensions working on an Alpine-based image is annoying: there's no prebuilt package for musl libc, so you have to install Microsoft's ODBC driver first and then compile the extensions yourself with `pecl`.

<!-- more -->

Here's a working Dockerfile for PHP 8.1 on Alpine:

```Dockerfile
FROM php:8.1-alpine

Expand All @@ -24,3 +30,5 @@ RUN wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8
rm msodbcsql17_17.5.1.1-1_amd64.apk && \
rm mssql-tools_17.5.1.1-1_amd64.apk
```

The `--allow-untrusted` flags are needed because Microsoft's `.apk` packages aren't signed with an Alpine-trusted key. The package URLs above pin a specific driver version, so if Microsoft moves them, check their official ODBC driver documentation for the current download links.
4 changes: 2 additions & 2 deletions docker/node/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:18-alpine
FROM node:24-alpine

MAINTAINER Jakub Vyvazil <jakub@vyvazil.cz>

Expand All @@ -17,7 +17,7 @@ RUN apk add --no-cache libsodium \
gcc make g++

# update npm
RUN npm install -g npm@10.8.2
RUN npm install -g npm@11.0.0

COPY docker/node/docker-entrypoint.sh /usr/local/bin/

Expand Down
10 changes: 7 additions & 3 deletions gatsby-browser.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// custom typefaces
import "typeface-montserrat"
import "typeface-merriweather"
import "@fontsource/inter/400.css"
import "@fontsource/inter/500.css"
import "@fontsource/inter/700.css"
import "@fontsource/jetbrains-mono/400.css"
import "@fontsource/jetbrains-mono/500.css"
import "@fontsource/jetbrains-mono/700.css"

// custom CSS styles
import "./src/style.css"

// Highlighting for code blocks
import "prismjs/themes/prism.css"
import "./src/styles/prism-theme.css"

import "./src/styles/global.scss"
Loading
Loading