Bits By Me uses the blog uses the hugo static site generator. Publishing articles is easy; working with Jupyter notebooks that become embedded GitHub gists is slightly more involved.
The easiest way to start is to use a GitHub Codespace or run locally as a devcontainer in VS Code (see devcontainer.json for details).
If you must use locally then you need to install:
Required:
- Go - I use
asdfsoasdf install golang latestworks for me. - Hugo -
brew install hugo - Task -
go install github.com/go-task/task/v3/cmd/task@latest&&asdf reshim. - D2 -
curl -fsSL https://d2lang.com/install.sh | sh -s --(required if using D2 diagrams) - Chrome or Chromium - Required for resume PDF generation. Usually already installed on macOS/Linux. On Ubuntu:
sudo apt-get install chromium-browser
Write a new blog post by providing a TITLE and calling the task blog:
TITLE="some-factoid" task blog
...write write write...
git add .
git commit -m "my cool post"
git push origin masterToday I Learned posts work exactly the same way - provide a TITLE:
TITLE="something-i-learned" task til
# OR
TITLE="something-i-learned" task today-i-learnedEach post is put into a folder like blog/1971/01/01/my-post-title/index.md or til/1971/01/01/my-til-title/index.md. This gives you a spot to drop post-specific content next to the post itself. For example, if you want an image for a post you can drop it in that folder and reference it in the post thus:
Recipes work similarly - provide a TITLE:
TITLE="chocolate-chip-cookies" task recipeRecipe pages include:
- Kitchen-optimized layout (large text, mobile-friendly)
- Schema.org Recipe structured data for SEO
- Unit conversion reference at
/recipes/units/ - Print-friendly layout
- Color-coded taxonomy tags (cuisines, categories, diets)
Recipe markdown uses nested shortcodes to extract ingredients and instructions for schema.org:
{{< recipe >}}
### Ingredients
{{< ingredients >}}
- 2 cups flour
- 1 cup sugar
{{< /ingredients >}}
### Instructions
{{< instructions >}}
**Prep:** Mix dry ingredients...
**Bake:** Pour into pan and bake at 350°F...
{{< /instructions >}}
{{< /recipe >}}The blog itself is hosting on GitHub Pages. The DNS configuration is in Squarespace Domains accessible using my personal Google account.
Start the dev server (recommended):
task # Renders D2 diagrams + starts Hugo dev server
# OR
go run blog.go --serve # Same thing without TaskVisit http://localhost:1313 to preview. The server watches for changes and automatically:
- Re-renders D2 diagrams when .d2 files change
- Rebuilds Hugo when markdown/templates change
- Reloads your browser
Build for production:
task build # Renders D2 + builds Hugo + generates resume PDF
# OR manually:
go run blog.go && hugo --gc --minify && go run blog.go # D2 → Hugo → PDFThe build process:
- Renders D2 diagrams to SVG
- Builds Hugo site to
public/ - Generates
public/resume.pdffrompublic/resume/index.html
Look at the draft Tips post to see examples of how you can use various elements like diagrams, code, and tweets.
The blog supports D2 diagrams - a modern declarative diagram language. D2 files in your page bundles are automatically rendered to SVG images during the build process.
Install D2:
# Install D2 (required for rendering diagrams)
curl -fsSL https://d2lang.com/install.sh | sh -s --
# Or via Go:
go install oss.terrastruct.com/d2@latest- Create a D2 file in your page bundle directory:
# Example: content/blog/2025-10-17/my-post/diagram.d2
x -> y -> z: Simple flow
a -> b: {
style.stroke: "#0d32b2"
style.fill: "#c2d2e0"
}- Reference it in your post using the
d2shortcode:
{{</* d2 src="diagram.d2" */>}}- Optional: Add a caption:
{{</* d2 src="diagram.d2" */>}}
This diagram shows the data flow through our system.
{{</* /d2 */>}}You can set D2 rendering options for all diagrams in config.toml:
[params.d2]
theme = "6" # D2 theme: number (0-8) or name like "cool-classics"
layout = "elk" # Layout engine: "dagre", "elk", etc.
sketch = true # Enable hand-drawn style globallyAvailable themes: Run d2 --list-themes to see all available themes.
Available layouts: Run d2 --list-layouts to see all available layout engines.
Note: These settings apply to all D2 diagrams site-wide. They're used during the render step before Hugo builds the site.
The shortcode is simple - just reference your D2 file:
{{</* d2 src="diagram.d2" */>}}With a caption:
{{</* d2 src="diagram.d2" */>}}
This diagram shows the system architecture.
{{</* /d2 */>}}The only parameter is:
src(required): Path to the .d2 file relative to your page bundle
Development mode (task):
- Renders all D2 files once at startup
- Starts Hugo dev server with the
blog.gotool - Watches for changes to
.d2files and automatically re-renders them - Hugo detects the SVG changes and reloads the page
Build mode (task build):
- Renders all D2 files before building
- Generates static site in
public/directory
Generated files:
- SVG files generated from
.d2files are not tracked in git (see.gitignore) - Only commit your
.d2source files - Generated SVGs are recreated during dev/build
# Render all D2 diagrams
task render-d2
# Or use the Go tool directly
go run blog.go
# Watch D2 files only (no Hugo server)
go run blog.go --watch
# Verbose output
go run blog.go --verboseDiagrams not appearing:
- Ensure D2 is installed:
d2 --version - Check that the
.svgfile was generated next to your.d2file - Verify the
srcpath in your shortcode matches the actual file name - Try manually running:
task render-d2
Following this advice it's a good idea to compress PNG images before adding to Git. Typical results: 480KB → 70KB (85% reduction).
Install the compression tools once:
# macOS
brew install pngquant oxipng
# Linux (Ubuntu/Debian)
sudo apt install pngquant
cargo install oxipngThe squish script recursively finds and compresses all PNG images in a directory:
# Preview what will be compressed (dry-run, safe)
./squish content/blog/2021/05/11
# Actually compress images (creates -fs8.png versions)
./squish content/blog/2021/05/11 --now
# Replace original images with compressed versions (destructive!)
./squish content/blog/2021/05/11 --now --cleanFeatures:
- Recursively finds all PNG files in a directory
- Handles filenames with spaces
- Shows before/after sizes and compression ratios
- Safe by default (dry-run mode)
- Intermediate
-fs8.pngfiles are git-ignored
Workflow:
- Add images to your post directory
- Run
./squish <path>to preview - Run
./squish <path> --nowto compress - Review the
-fs8.pngfiles - Run
./squish <path> --now --cleanto replace originals - Commit the compressed images