Having fun with my GitHub Profile
A little story about limitations of READMEs on GitHub and how to circumvent them.
genuinely enjoy playing around with ideas that are completely useless on one hand, but also present very interesting problems to solve on the other hand. So recently I decided to play around with my GitHub user profile and the content it should show.
GitHub has pioneered a really open feature: Allowing users to completely customize their public profiles on their platform only with Markdown by utilizing a naming convention to create special repos (<username>/<username>). As one can imagine, this limits people to Markdown features*1. However, developers love solving tricky challenges. And this has led to some really cool and creative inventions, like websites, which generate an SVG that you can embed in your profile Readme.
To name some examples I really enjoy: Astro Badges, Nuxters, GitHub Profile Stats and so many awesome more that this post could go on forever. Many users also handcraft their own creations, and so I decided to join them as well and share my learnings with the community here.
The idea
Since I was deep diving into Bento grids the other day, my vision of a fitting user profile was a grid of different stats put together in some aligning way.
I wanted some static stats, like my name with my profile picture blurred in the background and my Astro contributions. But to make it more challenging, I also wanted some dynamic elements: My GitHub contribution heatmap being eaten by a 2D snake (which is from a project I discovered recently) and a widget of a recent song I played on Spotify*2.
The limitation
When I wrote "limited to Markdown", I was lying a little bit. You can also put HTML elements into your Markdown files and they render fine. For security reasons however, GitHub strictly refuses to render stuff like <iframe>s, which to my knowledge is necessary if you want some dynamically embedded stuff (without relying on GitHub actions).
Another potential solution I played around with were generating images of the dynamically generated content on a schedule in a script called by a GitHub action. But then I would not be able to have continuous animations on the profile.
One info worth mentioning is that GitHub has also built in security measures to not execute fetches from the Markdown. Even when you embed an SVG image, which uses foreignObject, fetches are still blocked. To work around this, we base64-encode the prefetched images.
There were some other ideas along my little journey, but a very helpful StackOverflow entry demonstrated a workaround to get more HTML possibilities out of GFM. This hint together with base64-encoded data URLs allowed me to open Pandora's box 🥳
The implementation
The final setup works like this:
- Every 30 minutes, a GitHub action triggers, which executes a build script.
- This script fetches two APIs for the two dynamic elements: Spotify and snk/svg-only*3
- It then base64-encodes the resulting SVG files and creates data URLs.
- Those huge URLs can then be included in a bento grid HTML template with prefilled styles and desired layout.
- This generated HTML is then wrapped in a
foreignObjecttag in an SVG, which is committed to the repository. - The Readme then just has to include the SVG with Markdown image syntax and since all data is stored within the SVG (no external fetches anymore), it renders on my profile.
It's not beautiful internally, but its presence shines. Feel free to check out the final result out on my GitHub.
- Not entirely limited to CommonMark, but still limited to GitHub flavored Markdown ↩
- At first, I wanted to make it real-time, but I was too lazy to set up webhooks and I feared that GitHub would ban me if I spammed actions every time the song changes (which happens around every 3 minutes on average when listening to normal music). ↩
- Strictly speaking, there is no Snake API call. Arthur Brongniart provided a fully functional GitHub action, which generates the animated SVG Snake visualization of your GitHub contribution graph. This is saved in the dist folder, so it can be read from inside the script. ↩
Did you enjoy this article?
Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.