Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
62 changes: 62 additions & 0 deletions .github/workflows/hugo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Deploy Hugo site to Pages

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

defaults:
run:
shell: bash

env:
HUGO_VERSION: 0.166.0

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Install Hugo
run: |
wget -q -O "${{ runner.temp }}/hugo.deb" \
"https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb"
sudo dpkg -i "${{ runner.temp }}/hugo.deb"
- name: Setup Pages
id: pages
uses: actions/configure-pages@v5
- name: Build
env:
HUGO_ENVIRONMENT: production
TZ: Europe/Amsterdam
run: hugo --gc --minify --baseURL "${{ steps.pages.outputs.base_url }}/"
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./public

deploy:
if: github.event_name != 'pull_request'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public/
resources/_gen/
.hugo_build.lock
.claude/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "themes/PaperMod"]
path = themes/PaperMod
url = https://github.com/adityatelange/hugo-PaperMod
50 changes: 50 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# minhtt159.github.io (hnimn.art)

Personal blog. Hugo + PaperMod, deployed by GitHub Actions on merge to `main`. Commands are in `README.md`.

## Content

- Frontmatter: `title`, `date` (in the past, or Hugo hides it), `draft: true` until Minh flips it, `description`, `summary`, `tags`. Archetype: `archetypes/posts.md`.
- ASCII punctuation in prose: `-` not dashes, `->` not arrows, `...` not the ellipsis character, straight quotes. Goldmark renders typographic quotes; the source stays greppable.
- Work material is genericised: no customer, product, internal system, registry, or supplier names. Numbers are real and rounded. Sources and the disclosure rule live in `../resume/docs/blog-inspirations.md`.
- Every claim with a number traces to a verified source (`../resume/docs/facts.md` or a commit). Recalled numbers do not go in.
- One name per thing across a post. Timeless phrasing: no "currently", "soon", "not yet".

## Colour scheme

Catppuccin Latte (light) and Mocha (dark), `assets/css/extended/theme.css`. Roles follow the upstream style guide: base page, mantle secondary panes, surface0 surface elements, text body, subtext0 labels, blue links, green/yellow/red for good/caution/bad, tints at 15%. Change a colour there and only there; nothing else in the repo carries a hex value.

## Fonts

No third-party font requests. Body text uses PaperMod's system stack. Code and SVG mono text use `--font-mono` from `theme.css`: JetBrains Mono, self-hosted under `static/fonts/` (regular and bold woff2, OFL licence file next to them, `local()` first so an installed copy is used). Adding a face means the same recipe: woff2 in `static/fonts/`, licence alongside, `@font-face` with `font-display: swap` in `theme.css`, two weights at most. Never load from fonts.googleapis.com.

## Diagrams

Inline SVG in the markdown, one per post unless the post cannot be understood without a second.

- Open with `<svg class="dg" viewBox="..." xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="<id>-t <id>-d">`, then `<title id="<id>-t">` and `<desc id="<id>-d">` that describe the mechanism in one or two sentences. `<id>` is unique per post; marker ids carry the same prefix.
- No `<style>`, no `fill=`, `stroke=`, or hex anywhere in the SVG. Colour comes only from the `svg.dg` classes in `theme.css`:
- boxes: `box` (neutral), `box-a` blue primary flow, `box-b` green good/counted, `box-c` red bad/excluded, `box-d` yellow caution, `box-e` mauve external
- lines: `ln`, `ln-a` .. `ln-e`; arrowhead paths inside `<marker>`: `ar`, `ar-a` .. `ar-e`
- text: `t` heading, `s` subtitle/muted, `m` monospace
- `zone` dashed outline for a region or a caption strip
A colour encodes one meaning per diagram; two or three accents at most; neutral `box` for anything structural.
- No blank lines between `<svg` and `</svg>`. Goldmark closes the raw HTML block at the first blank line and renders the rest as text.
- Text inside the SVG is ASCII; `>` is written `&gt;`.
- Done when `hugo` builds, the diagram reads correctly with the theme toggle in both states, and `grep -c 'fill=\|stroke=\|#[0-9a-fA-F]\{3,6\}' <post>` returns 0 inside the SVG.

## Raster images

Only when the pixels are the point (a screenshot of a UI). Redraw charts and topologies as SVG.

- Path `static/img/<post-slug>/<name>.png`, referenced as `/img/<post-slug>/<name>.png`, under 300 KB, alt text that states what the reader should notice.
- Take screenshots in the dark scheme where the tool offers one, and crop to the panel that carries the claim.
- Nothing in a screenshot may show a customer, hostname, account id, or internal URL. Check before committing.

## Before opening a PR

1. `hugo --minify -D` builds with no `ERROR`.
2. `grep -rnP '[^\x00-\x7F]' content` returns nothing.
3. Disclosure grep over `content/` for the names in `blog-inspirations.md` returns nothing.
4. Preview both theme states: home, the post, the diagram.
5. Cold-read: a fresh subagent with only the post text, briefed as a senior SRE, lists open questions, undefined terms, and arithmetic mismatches. Fix, then hand to Minh.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
# minhtt159.github.io
# hnimn.art

Personal blog. Hugo + [PaperMod](https://github.com/adityatelange/hugo-PaperMod), deployed to GitHub Pages by `.github/workflows/hugo.yml` on every merge to `main`.

```bash
git submodule update --init # once, pulls the theme
hugo server -D # preview with drafts at http://localhost:1313
hugo new posts/<slug>.md # new post from archetypes/posts.md, starts as draft: true
```

Flip `draft: false` to publish. Every post: ASCII punctuation only, one inline SVG max, no customer, internal system or supplier names.
8 changes: 8 additions & 0 deletions archetypes/posts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "{{ replace .File.ContentBaseName "-" " " | title }}"
date: {{ .Date }}
draft: true
description: ""
summary: ""
tags: []
---
164 changes: 164 additions & 0 deletions assets/css/extended/chroma.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/* Chroma: catppuccin-latte, and catppuccin-mocha under [data-theme="dark"]. Generated with hugo gen chromastyles; block background comes from --code-block-bg. */

/* Other */ .chroma .x { color:#4c4f69 }
/* Error */ .chroma .err { color:#d20f39 }
/* CodeLine */ .chroma .cl { color:#4c4f69 }
/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
/* LineHighlight */ .chroma .hl { background-color:#bcc0cc }
/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8c8fa1 }
/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8c8fa1 }
/* Line */ .chroma .line { display:flex; }
/* Keyword */ .chroma .k { color:#8839ef }
/* KeywordConstant */ .chroma .kc { color:#fe640b }
/* KeywordDeclaration */ .chroma .kd { color:#d20f39 }
/* KeywordNamespace */ .chroma .kn { color:#179299 }
/* KeywordPseudo */ .chroma .kp { color:#8839ef }
/* KeywordReserved */ .chroma .kr { color:#8839ef }
/* KeywordType */ .chroma .kt { color:#d20f39 }
/* NameAttribute */ .chroma .na { color:#1e66f5 }
/* NameClass */ .chroma .nc { color:#df8e1d }
/* NameConstant */ .chroma .no { color:#df8e1d }
/* NameDecorator */ .chroma .nd { color:#1e66f5;font-weight:bold }
/* NameEntity */ .chroma .ni { color:#179299 }
/* NameException */ .chroma .ne { color:#fe640b }
/* NameLabel */ .chroma .nl { color:#04a5e5 }
/* NameNamespace */ .chroma .nn { color:#fe640b }
/* NameOther */ .chroma .nx { color:#4c4f69 }
/* NameProperty */ .chroma .py { color:#fe640b }
/* NameTag */ .chroma .nt { color:#8839ef }
/* NameBuiltin */ .chroma .nb { color:#04a5e5 }
/* NameBuiltinPseudo */ .chroma .bp { color:#04a5e5 }
/* NameVariable */ .chroma .nv { color:#dc8a78 }
/* NameVariableClass */ .chroma .vc { color:#dc8a78 }
/* NameVariableGlobal */ .chroma .vg { color:#dc8a78 }
/* NameVariableInstance */ .chroma .vi { color:#dc8a78 }
/* NameVariableMagic */ .chroma .vm { color:#dc8a78 }
/* NameFunction */ .chroma .nf { color:#1e66f5 }
/* NameFunctionMagic */ .chroma .fm { color:#1e66f5 }
/* LiteralDate */ .chroma .ld { color:#4c4f69 }
/* LiteralString */ .chroma .s { color:#40a02b }
/* LiteralStringAffix */ .chroma .sa { color:#d20f39 }
/* LiteralStringBacktick */ .chroma .sb { color:#40a02b }
/* LiteralStringChar */ .chroma .sc { color:#40a02b }
/* LiteralStringDelimiter */ .chroma .dl { color:#1e66f5 }
/* LiteralStringDoc */ .chroma .sd { color:#9ca0b0 }
/* LiteralStringDouble */ .chroma .s2 { color:#40a02b }
/* LiteralStringEscape */ .chroma .se { color:#1e66f5 }
/* LiteralStringHeredoc */ .chroma .sh { color:#9ca0b0 }
/* LiteralStringInterpol */ .chroma .si { color:#40a02b }
/* LiteralStringOther */ .chroma .sx { color:#40a02b }
/* LiteralStringRegex */ .chroma .sr { color:#179299 }
/* LiteralStringSingle */ .chroma .s1 { color:#40a02b }
/* LiteralStringSymbol */ .chroma .ss { color:#40a02b }
/* LiteralNumber */ .chroma .m { color:#fe640b }
/* LiteralNumberBin */ .chroma .mb { color:#fe640b }
/* LiteralNumberFloat */ .chroma .mf { color:#fe640b }
/* LiteralNumberHex */ .chroma .mh { color:#fe640b }
/* LiteralNumberInteger */ .chroma .mi { color:#fe640b }
/* LiteralNumberIntegerLong */ .chroma .il { color:#fe640b }
/* LiteralNumberOct */ .chroma .mo { color:#fe640b }
/* Operator */ .chroma .o { color:#04a5e5;font-weight:bold }
/* OperatorWord */ .chroma .ow { color:#04a5e5;font-weight:bold }
/* OperatorReserved */ .chroma .or { color:#04a5e5;font-weight:bold }
/* Comment */ .chroma .c { color:#9ca0b0;font-style:italic }
/* CommentHashbang */ .chroma .ch { color:#acb0be;font-style:italic }
/* CommentMultiline */ .chroma .cm { color:#9ca0b0;font-style:italic }
/* CommentSingle */ .chroma .c1 { color:#9ca0b0;font-style:italic }
/* CommentSpecial */ .chroma .cs { color:#9ca0b0;font-style:italic }
/* CommentPreproc */ .chroma .cp { color:#9ca0b0;font-style:italic }
/* CommentPreprocFile */ .chroma .cpf { color:#9ca0b0;font-weight:bold;font-style:italic }
/* GenericDeleted */ .chroma .gd { color:#d20f39;background-color:#ccd0da }
/* GenericEmph */ .chroma .ge { font-style:italic }
/* GenericError */ .chroma .gr { color:#d20f39 }
/* GenericHeading */ .chroma .gh { color:#fe640b;font-weight:bold }
/* GenericInserted */ .chroma .gi { color:#40a02b;background-color:#ccd0da }
/* GenericOutput */ .chroma .go { color:#4c4f69 }
/* GenericPrompt */ .chroma .gp { color:#4c4f69 }
/* GenericStrong */ .chroma .gs { font-weight:bold }
/* GenericSubheading */ .chroma .gu { color:#fe640b;font-weight:bold }
/* GenericTraceback */ .chroma .gt { color:#d20f39 }
/* GenericUnderline */ .chroma .gl { text-decoration:underline }


/* Other */ [data-theme="dark"] .chroma .x { color:#cdd6f4 }
/* Error */ [data-theme="dark"] .chroma .err { color:#f38ba8 }
/* CodeLine */ [data-theme="dark"] .chroma .cl { color:#cdd6f4 }
/* LineLink */ [data-theme="dark"] .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
/* LineTableTD */ [data-theme="dark"] .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
/* LineTable */ [data-theme="dark"] .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
/* LineHighlight */ [data-theme="dark"] .chroma .hl { background-color:#45475a }
/* LineNumbersTable */ [data-theme="dark"] .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c }
/* LineNumbers */ [data-theme="dark"] .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c }
/* Line */ [data-theme="dark"] .chroma .line { display:flex; }
/* Keyword */ [data-theme="dark"] .chroma .k { color:#cba6f7 }
/* KeywordConstant */ [data-theme="dark"] .chroma .kc { color:#fab387 }
/* KeywordDeclaration */ [data-theme="dark"] .chroma .kd { color:#f38ba8 }
/* KeywordNamespace */ [data-theme="dark"] .chroma .kn { color:#94e2d5 }
/* KeywordPseudo */ [data-theme="dark"] .chroma .kp { color:#cba6f7 }
/* KeywordReserved */ [data-theme="dark"] .chroma .kr { color:#cba6f7 }
/* KeywordType */ [data-theme="dark"] .chroma .kt { color:#f38ba8 }
/* NameAttribute */ [data-theme="dark"] .chroma .na { color:#89b4fa }
/* NameClass */ [data-theme="dark"] .chroma .nc { color:#f9e2af }
/* NameConstant */ [data-theme="dark"] .chroma .no { color:#f9e2af }
/* NameDecorator */ [data-theme="dark"] .chroma .nd { color:#89b4fa;font-weight:bold }
/* NameEntity */ [data-theme="dark"] .chroma .ni { color:#94e2d5 }
/* NameException */ [data-theme="dark"] .chroma .ne { color:#fab387 }
/* NameLabel */ [data-theme="dark"] .chroma .nl { color:#89dceb }
/* NameNamespace */ [data-theme="dark"] .chroma .nn { color:#fab387 }
/* NameOther */ [data-theme="dark"] .chroma .nx { color:#cdd6f4 }
/* NameProperty */ [data-theme="dark"] .chroma .py { color:#fab387 }
/* NameTag */ [data-theme="dark"] .chroma .nt { color:#cba6f7 }
/* NameBuiltin */ [data-theme="dark"] .chroma .nb { color:#89dceb }
/* NameBuiltinPseudo */ [data-theme="dark"] .chroma .bp { color:#89dceb }
/* NameVariable */ [data-theme="dark"] .chroma .nv { color:#f5e0dc }
/* NameVariableClass */ [data-theme="dark"] .chroma .vc { color:#f5e0dc }
/* NameVariableGlobal */ [data-theme="dark"] .chroma .vg { color:#f5e0dc }
/* NameVariableInstance */ [data-theme="dark"] .chroma .vi { color:#f5e0dc }
/* NameVariableMagic */ [data-theme="dark"] .chroma .vm { color:#f5e0dc }
/* NameFunction */ [data-theme="dark"] .chroma .nf { color:#89b4fa }
/* NameFunctionMagic */ [data-theme="dark"] .chroma .fm { color:#89b4fa }
/* LiteralDate */ [data-theme="dark"] .chroma .ld { color:#cdd6f4 }
/* LiteralString */ [data-theme="dark"] .chroma .s { color:#a6e3a1 }
/* LiteralStringAffix */ [data-theme="dark"] .chroma .sa { color:#f38ba8 }
/* LiteralStringBacktick */ [data-theme="dark"] .chroma .sb { color:#a6e3a1 }
/* LiteralStringChar */ [data-theme="dark"] .chroma .sc { color:#a6e3a1 }
/* LiteralStringDelimiter */ [data-theme="dark"] .chroma .dl { color:#89b4fa }
/* LiteralStringDoc */ [data-theme="dark"] .chroma .sd { color:#6c7086 }
/* LiteralStringDouble */ [data-theme="dark"] .chroma .s2 { color:#a6e3a1 }
/* LiteralStringEscape */ [data-theme="dark"] .chroma .se { color:#89b4fa }
/* LiteralStringHeredoc */ [data-theme="dark"] .chroma .sh { color:#6c7086 }
/* LiteralStringInterpol */ [data-theme="dark"] .chroma .si { color:#a6e3a1 }
/* LiteralStringOther */ [data-theme="dark"] .chroma .sx { color:#a6e3a1 }
/* LiteralStringRegex */ [data-theme="dark"] .chroma .sr { color:#94e2d5 }
/* LiteralStringSingle */ [data-theme="dark"] .chroma .s1 { color:#a6e3a1 }
/* LiteralStringSymbol */ [data-theme="dark"] .chroma .ss { color:#a6e3a1 }
/* LiteralNumber */ [data-theme="dark"] .chroma .m { color:#fab387 }
/* LiteralNumberBin */ [data-theme="dark"] .chroma .mb { color:#fab387 }
/* LiteralNumberFloat */ [data-theme="dark"] .chroma .mf { color:#fab387 }
/* LiteralNumberHex */ [data-theme="dark"] .chroma .mh { color:#fab387 }
/* LiteralNumberInteger */ [data-theme="dark"] .chroma .mi { color:#fab387 }
/* LiteralNumberIntegerLong */ [data-theme="dark"] .chroma .il { color:#fab387 }
/* LiteralNumberOct */ [data-theme="dark"] .chroma .mo { color:#fab387 }
/* Operator */ [data-theme="dark"] .chroma .o { color:#89dceb;font-weight:bold }
/* OperatorWord */ [data-theme="dark"] .chroma .ow { color:#89dceb;font-weight:bold }
/* OperatorReserved */ [data-theme="dark"] .chroma .or { color:#89dceb;font-weight:bold }
/* Comment */ [data-theme="dark"] .chroma .c { color:#6c7086;font-style:italic }
/* CommentHashbang */ [data-theme="dark"] .chroma .ch { color:#585b70;font-style:italic }
/* CommentMultiline */ [data-theme="dark"] .chroma .cm { color:#6c7086;font-style:italic }
/* CommentSingle */ [data-theme="dark"] .chroma .c1 { color:#6c7086;font-style:italic }
/* CommentSpecial */ [data-theme="dark"] .chroma .cs { color:#6c7086;font-style:italic }
/* CommentPreproc */ [data-theme="dark"] .chroma .cp { color:#6c7086;font-style:italic }
/* CommentPreprocFile */ [data-theme="dark"] .chroma .cpf { color:#6c7086;font-weight:bold;font-style:italic }
/* GenericDeleted */ [data-theme="dark"] .chroma .gd { color:#f38ba8;background-color:#313244 }
/* GenericEmph */ [data-theme="dark"] .chroma .ge { font-style:italic }
/* GenericError */ [data-theme="dark"] .chroma .gr { color:#f38ba8 }
/* GenericHeading */ [data-theme="dark"] .chroma .gh { color:#fab387;font-weight:bold }
/* GenericInserted */ [data-theme="dark"] .chroma .gi { color:#a6e3a1;background-color:#313244 }
/* GenericOutput */ [data-theme="dark"] .chroma .go { color:#cdd6f4 }
/* GenericPrompt */ [data-theme="dark"] .chroma .gp { color:#cdd6f4 }
/* GenericStrong */ [data-theme="dark"] .chroma .gs { font-weight:bold }
/* GenericSubheading */ [data-theme="dark"] .chroma .gu { color:#fab387;font-weight:bold }
/* GenericTraceback */ [data-theme="dark"] .chroma .gt { color:#f38ba8 }
/* GenericUnderline */ [data-theme="dark"] .chroma .gl { text-decoration:underline }
Loading
Loading