My GitHub profile README was fine. It worked. It had a little HCL block with my name, a wall of links, and a “Buy Me a Coffee” badge that nobody ever clicked.

But “fine” is the enemy of fun. So I rewrote the whole thing.

What was wrong with the old one

The old README was mostly hardcoded. Every time I added a project, wrote a blog post, or changed a link, I had to go edit the README manually. The social links were plain URLs dumped into an HCL block. The BlueSky section truncated post text. There were no images. The styling was… default GitHub markdown.

It wasn’t bad. It was just static, and static is boring when your whole job is automation.

The new approach

Everything is now generated by a single bash script that runs on a schedule via GitHub Actions. The content comes from three places:

  1. YAML files in my profile repo for social links, projects, and hand-picked blog posts
  2. The GitHub API to pull my latest site blog posts from Jekyll’s _posts/ directory
  3. The BlueSky API to show my latest posts with actual images

No manual README edits. Push a change to the YAML, and the README updates itself on the next run.

Dracula everything

I’m a Dracula theme person. My terminal, my IDE, my browser, all Dracula. So naturally the README had to match.

Every badge uses the Dracula palette:

  • YouTube → #ff5555 (red)
  • BlueSky → #8be9fd (cyan)
  • LinkedIn → #bd93f9 (purple)
  • Email → #f1fa8c (yellow)
  • Nerds Who Fish → #50fa7b (green)

The tech stack badges (Kubernetes, OpenTofu, Python, Node.js, Go, GitOps) all follow the same scheme. Dark background, Dracula accent colors. It actually looks like one person designed it.

The base64 SVG trick

This was the most fun part. Shields.io has built-in logos for YouTube, LinkedIn, and the usual suspects. But it doesn’t have a logo for “Nerds Who Fish” or my GPG key.

The solution: fetch the SVG from Iconify’s API, base64-encode it, and embed it directly in the shields.io URL.

iconify_to_base64_logo(){
  local icon_path=$(echo "$1" | sed 's/:/\//')
  local b64=$(curl -sS "https://api.iconify.design/${icon_path}.svg" \
    | base64 | tr -d '\n')
  echo "data:image/svg%2bxml;base64,${b64}"
}

That function takes an Iconify icon identifier like mdi:fish and returns a data URI that shields.io accepts as a custom logo. The %2b is a URL-encoded + sign because shields.io is picky about that.

The result is a badge that looks exactly like the native ones, but with whatever icon you want. You’d never know the fish badge isn’t a built-in shields.io logo.

Dynamic social badges from YAML

All the social links live in a links.yml file in my profile repo. The script reads the “Where You Can Find Me” category and generates a shields.io badge for each one:

logo=$(shield_logo_for "$title")
color=$(shield_color_for "$title")

if [ -z "$logo" ]; then
  icon=$(echo "$LINKS" | yq -r ".buttons[$i].items[$j].icon")
  logo=$(iconify_to_base64_logo "$icon")
fi

If the title maps to a known shields.io logo (YouTube, BlueSky, etc.), it uses that. Otherwise, it falls back to fetching the icon from Iconify and encoding it. Same visual result either way. I spent way too long making sure you can’t tell the difference.

I also skip the GitHub badge entirely. You’re already on GitHub if you’re reading the README.

Live BlueSky posts with images

The old BlueSky section was text-only and truncated. Now it renders up to three posts side-by-side in an HTML table, with actual thumbnail images from the BlueSky CDN:

thumb=$(echo "$posts" | jq -r \
  ".feed[$i].post.embed.images[0].thumb // empty" 2>/dev/null)

if [ -n "$thumb" ]; then
  echo "<a href=\"${post_url}\"><img src=\"${thumb}\" width=\"150\" /></a>"
fi

The posts also handle reposts (prefixed with 🔄), fall back to image alt text when there’s no body, and HTML-escape everything to avoid injection from whatever nonsense I post.

Blog posts from Jekyll

The README now pulls the five most recent posts from my Jekyll site’s _posts/ directory via the GitHub API. It parses the YAML frontmatter to get the title, description, and icon, then renders them in a table alongside the hand-picked posts from the YAML file.

content=$(curl -sS "https://raw.githubusercontent.com/.../main/_posts/${file}")
frontmatter=$(echo "$content" | sed -n '/^---$/,/^---$/p' | sed '1d;$d')

title=$(echo "$frontmatter" | yq -r '.title')
desc=$(echo "$frontmatter" | yq -r '.description // ""')
icon=$(echo "$frontmatter" | yq -r '.icon // ""')

This means whenever I publish a new blog post on my site, it shows up in my GitHub profile README without touching the profile repo. One less thing to forget. The YAML posts (like the Spacelift blog articles on Medium) still appear below the site posts.

Iconify for everything

Every table row gets a little icon in its own centered column. The icons come from Iconify’s public API with a white color parameter to match the Dracula foreground:

iconify_img(){
  local icon_path=$(echo "$1" | sed 's/:/\//')
  echo "<img src=\"https://api.iconify.design/${icon_path}.svg?color=%23f8f8f2\" width=\"20\" height=\"20\" />"
}

Iconify has icons for basically everything: fish, Kubernetes, OpenTofu, rockets, clipboards. The API is free and doesn’t require authentication, which made it an easy choice.

What I removed

  • Buy Me a Coffee because nobody clicked it, and it added visual noise
  • GitHub badge since it’s redundant when you’re already on the platform
  • Collapsible sections because I originally had <details> tags to hide sections. Turns out scrolling is fine. The collapsed sections just made things harder to scan.
  • Text truncation so BlueSky posts now show the full text. If I wrote it, I want people to read it.

The final structure

The README flows from the logo and social badges down through the tech stack, an HCL intro block, live BlueSky posts, projects, blog posts, and work links at the bottom. All generated by a 293-line bash script.

Was it worth it?

Absolutely not, if you’re measuring by “impact on career” or “impressions on hiring managers.”

Absolutely yes, if you’re measuring by “did I have fun and learn something.” I got to play with the Iconify API, figure out shields.io’s undocumented base64 logo support, parse YAML frontmatter with sed and yq in a bash script, and build something that actually looks like me.

That’s the whole point of a profile README. It should feel personal. Mine has Dracula colors, fish icons, and a live feed of whatever I’m posting on BlueSky. That’s about as personal as it gets.

If you want to see the script, it’s right here. Steal whatever’s useful.