<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Igor Braz</title>
    <description>The latest articles on DEV Community by Igor Braz (@igorcbraz).</description>
    <link>https://dev.to/igorcbraz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1897691%2F310e6d4c-8d50-4da1-b3dc-39d7822962b2.jpg</url>
      <title>DEV Community: Igor Braz</title>
      <link>https://dev.to/igorcbraz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/igorcbraz"/>
    <language>en</language>
    <item>
      <title>GitAscii vs github-readme-stats: Moving from Query Strings to Edge Rendering 🚀</title>
      <dc:creator>Igor Braz</dc:creator>
      <pubDate>Tue, 11 Aug 2026 18:58:10 +0000</pubDate>
      <link>https://dev.to/igorcbraz/gitascii-vs-github-readme-stats-moving-from-query-strings-to-edge-rendering-5d41</link>
      <guid>https://dev.to/igorcbraz/gitascii-vs-github-readme-stats-moving-from-query-strings-to-edge-rendering-5d41</guid>
      <description>&lt;p&gt;The classic &lt;code&gt;github-readme-stats&lt;/code&gt; has been the undisputed king of profile customization for years. We have all seen the familiar stats cards pinned to thousands of developer portfolios. But relying on query parameters appended directly to an image tag introduces structural bottlenecks when you want true creative freedom and optimal load performance.&lt;/p&gt;

&lt;p&gt;In this deep dive, we will analyze the limitations of query-string-based configurations, compare the request lifecycle of legacy card layouts against modern edge-rendered unified canvases, and look at how decoupling style from content yields superior render times and layout flexibility.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/assets%2Fgitascii-vs-stats.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/assets%2Fgitascii-vs-stats.jpg" alt="GitAscii vs github-readme-stats" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Limitations of Query-String Architectures
&lt;/h3&gt;

&lt;p&gt;Under the legacy approach of &lt;code&gt;github-readme-stats&lt;/code&gt;, every visual tweak—such as colors, layout types, icons, and hidden sections—requires editing the URL parameters directly within the markdown content. A typical setup looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Traditional query-string approach --&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;My GitHub Stats&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github-readme-stats.vercel.app/api?username=anuraghazra&amp;amp;show_icons=true&amp;amp;theme=radical&amp;amp;hide=contribs,prs&amp;amp;custom_title=My%20Stats&amp;amp;title_color=fff&amp;amp;icon_color=f00&amp;amp;text_color=000&amp;amp;bg_color=fff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this appears convenient for single cards, it introduces major architectural issues when scaling up a developer profile:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;URI Length Limits (RFC 7230 &amp;amp; RFC 3986)&lt;/strong&gt;: Standard web servers, proxies, and CDNs enforce limits on URI length (often capped at 2,048 or 8,192 characters). If you want an editorial dashboard with precise typography, custom spacing, multiple layout sections, and color configurations, your URL quickly balloons to unmanageable sizes, risking truncated requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tight Coupling (Content vs. Presentation)&lt;/strong&gt;: Changing the style (e.g., swapping a theme from light to dark or aligning elements) forces you to rewrite the code in your repository's &lt;code&gt;README.md&lt;/code&gt;. Your content delivery layer is directly coupled to your layout presentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waterfall Latency Bottlenecks&lt;/strong&gt;: When you stack multiple cards (e.g., stats, top languages, and typing SVGs), the browser must make multiple independent connections to backend servers. Each connection triggers a separate TLS handshake and request-response loop, producing a noticeable page loading waterfall.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;br&gt;
Stacking 3 to 5 independent server-rendered SVG badges on a single README page can increase the page's cumulative layout shift (CLS) and aggregate load times, especially under constrained mobile networks where concurrent TCP connections are limited.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  The GitAscii Paradigm Shift
&lt;/h3&gt;

&lt;p&gt;With &lt;strong&gt;GitAscii&lt;/strong&gt;, we decoupled the user layout configuration from the content URL. Instead of packing visual parameters into query strings, we store a structured JSON design schema in a database and expose a clean, static, visual editor.&lt;/p&gt;

&lt;p&gt;The markdown in your GitHub README remains simple, clean, and never needs to change when you edit your layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Decoupled GitAscii Canvas --&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;![GitAscii Profile Canvas&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://gitascii.com/api/render/igorcbraz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;](https://gitascii.com/edit/igorcbraz)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How the Architectural Lifecycles Compare
&lt;/h4&gt;

&lt;p&gt;To see why this improves latency and maintainability, let us compare the request lifecycles of both architectures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    autonumber
    actor User as Developer Browser
    participant Camo as GitHub Camo Proxy
    participant Stats as Legacy Stats Server
    participant Edge as GitAscii Edge (Next.js Runtime)
    participant DB as GitAscii DB / Cache
    participant GH as GitHub GraphQL API

    rect rgb(240, 240, 240)
        note right of Stats: Legacy Request Cycle (Multiple Cards)
        User-&amp;gt;&amp;gt;Camo: Request README Images
        Camo-&amp;gt;&amp;gt;Stats: GET /api?username=user&amp;amp;theme=radical&amp;amp;... (Synchronous Engine)
        Stats-&amp;gt;&amp;gt;GH: Fetch user statistics (REST/GraphQL)
        GH--&amp;gt;&amp;gt;Stats: Returns raw stats
        Stats--&amp;gt;&amp;gt;Camo: Returns rendered SVG Card
        Camo--&amp;gt;&amp;gt;User: Delivers Card Image
    end

    rect rgb(230, 245, 230)
        note right of Edge: GitAscii Request Cycle (Decoupled Canvas)
        User-&amp;gt;&amp;gt;Camo: Request Unified Canvas Image
        Camo-&amp;gt;&amp;gt;Edge: GET /api/render/user
        par Fetch Layout Configuration &amp;amp; GitHub Stats
            Edge-&amp;gt;&amp;gt;DB: Query JSON design configuration schema
            Edge-&amp;gt;&amp;gt;GH: Query user statistics in parallel via GraphQL
        end
        DB--&amp;gt;&amp;gt;Edge: Config schema (layout, styles, ASCII art)
        GH--&amp;gt;&amp;gt;Edge: Raw stats payload
        Edge-&amp;gt;&amp;gt;Edge: Assemble responsive unified SVG canvas
        Edge--&amp;gt;&amp;gt;Camo: Returns optimized single SVG (Cache-Control headers)
        Camo--&amp;gt;&amp;gt;User: Delivers responsive unified Canvas
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Architecture Comparison Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Legacy Stats Cards (e.g. github-readme-stats)&lt;/th&gt;
&lt;th&gt;GitAscii Unified Canvas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Config Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Embed in query strings (&lt;code&gt;?theme=dark&amp;amp;bg_color=...&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Stored as serialized JSON config in DB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Markdown Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (bloated URLs, multiple image tags)&lt;/td&gt;
&lt;td&gt;Minimal (a single link containing username)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Design Process&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual text editing of URLs&lt;/td&gt;
&lt;td&gt;Interactive, drag-and-drop Visual Builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Request Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple HTTP calls (waterfall loading)&lt;/td&gt;
&lt;td&gt;Single HTTP call (unified responsive SVG)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Fetching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Synchronous REST / GraphQL per badge&lt;/td&gt;
&lt;td&gt;Parallel GraphQL queries executed at Edge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Heavy Processing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Done entirely server-side on request&lt;/td&gt;
&lt;td&gt;Offloaded to Client-side (ASCII conversion)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Behind the Scenes: Parallel Edge Fetching
&lt;/h3&gt;

&lt;p&gt;To keep response times under &lt;strong&gt;80ms&lt;/strong&gt;, GitAscii uses the Vercel Edge Runtime to execute parallel fetching. Instead of waiting sequentially for database configurations and GitHub API responses, we execute them concurrently using &lt;code&gt;Promise.all&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example from GitAscii rendering handler on Next.js Edge Runtime&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;edge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RenderRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RenderRequest&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Fetch layout configuration and GitHub statistics concurrently&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;configResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;githubStats&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nf"&gt;fetchLayoutConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;fetchGitHubGraphQLStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;configResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Profile not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Hydrate the pre-processed client-side design layout with live data&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderedSvg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;assembleUnifiedSvg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;githubStats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Return the payload with aggressive, safe caching strategies&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderedSvg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/svg+xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Instruct GitHub Camo to cache, but revalidate in the background&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public, s-maxage=1800, stale-while-revalidate=3600&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Content-Type-Options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nosniff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Edge rendering failed for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal Server Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!TIP]&lt;br&gt;
By using &lt;code&gt;stale-while-revalidate=3600&lt;/code&gt;, GitHub's Camo proxy instantly serves the cached version to the reader while triggering a background fetch to update the stats, completely eliminating user-facing latency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Sticking to traditional stats cards limits your profile to rigid box templates. By decoupling presentation state from HTTP URLs and leveraging Edge servers to assemble unified, adaptive SVGs on the fly, GitAscii provides a modern alternative. It gives you professional, editorial-grade design layouts for your developer portfolio without polluting your Markdown files with query string soup.&lt;/p&gt;

</description>
      <category>github</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Solving Dark/Light Themes in SVGs on GitHub Without DOM Access (Bypassing Camo)</title>
      <dc:creator>Igor Braz</dc:creator>
      <pubDate>Tue, 11 Aug 2026 18:57:46 +0000</pubDate>
      <link>https://dev.to/igorcbraz/solving-darklight-themes-in-svgs-on-github-without-dom-access-bypassing-camo-155d</link>
      <guid>https://dev.to/igorcbraz/solving-darklight-themes-in-svgs-on-github-without-dom-access-bypassing-camo-155d</guid>
      <description>&lt;p&gt;The most frustrating aspect of GitHub's image proxy, Camo, is not its caching behavior, but the absolute sandboxing of client-side scripts. This constraint creates a classic frontend challenge: how do you deliver a dynamic banner or layout inside a &lt;code&gt;README.md&lt;/code&gt; that automatically adjusts to the reader's dark or light theme when you have zero access to the DOM or JavaScript?&lt;/p&gt;

&lt;p&gt;Many dynamic profile engines try to solve this by appending theme parameters to the query string (e.g., &lt;code&gt;?theme=dark&lt;/code&gt;). The obvious downside is that this is static: if the reader changes their operating system or browser theme, the image fails to adapt and breaks the layout's aesthetic.&lt;/p&gt;

&lt;p&gt;In this deep dive, we look at how GitAscii uses &lt;strong&gt;CSS-in-SVG&lt;/strong&gt; to bypass these constraints, using client-side media queries embedded directly inside vector assets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/assets%2Fdynamic-svg-themes.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/assets%2Fdynamic-svg-themes.jpg" alt="Dynamic SVG themes comparison"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Sandbox Constraints of GitHub Camo
&lt;/h3&gt;

&lt;p&gt;When you embed an image link in a markdown file, GitHub's markdown parser processes it and redirects it through the Camo proxy. In the browser, this image is rendered inside a standard HTML &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://camo.githubusercontent.com/.../img.svg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Dynamic Canvas"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the W3C specifications for SVG integration, when an SVG file is loaded via an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag (or as a CSS &lt;code&gt;background-image&lt;/code&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript is fully disabled&lt;/strong&gt;: Any &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags inside the SVG are ignored and blocked by the browser's security sandbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External resources are blocked&lt;/strong&gt;: The browser will not fetch external stylesheets (&lt;code&gt;&amp;lt;link rel="stylesheet"&amp;gt;&lt;/code&gt;) or external web fonts declared within the SVG.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOM Isolation&lt;/strong&gt;: The SVG is rendered in its own separate document context. It cannot query the parent document (the GitHub repository page) or inspect class attributes like &lt;code&gt;.theme-dark&lt;/code&gt; on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; elements.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Bypassing the Sandbox with Embedded Media Queries
&lt;/h3&gt;

&lt;p&gt;Although external resources and scripts are blocked, &lt;strong&gt;the browser's layout engine still evaluates internal style sheets&lt;/strong&gt; declared within the SVG itself. Because the SVG is parsed as an XML document, rules written inside &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks are processed in the user's browser context.&lt;/p&gt;

&lt;p&gt;This means we can embed the standard CSS media query &lt;code&gt;@media (prefers-color-scheme: dark)&lt;/code&gt; inside the SVG payload returned by our Edge server. When the reader's browser renders the SVG, it evaluates the media query against the user's system preferences (dark or light mode) and applies the correct styling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"800"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"220"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 800 220"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    /* -------------------------------------------------------------
       Default styles (Light Mode configuration)
       ------------------------------------------------------------- */
    .canvas-bg {
      fill: #ffffff;
      stroke: #e1e4e8;
      stroke-width: 1px;
      transition: fill 0.3s ease, stroke 0.3s ease;
    }
    .text-title {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
      font-weight: 600;
      font-size: 16px;
      fill: #24292e;
    }
    .text-body {
      font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
      font-size: 14px;
      fill: #586069;
    }
    .accent-bar {
      fill: #0366d6; /* GitHub Classic Light Blue */
    }

    /* -------------------------------------------------------------
       Media Query override (Executed by the reader's browser)
       ------------------------------------------------------------- */
    @media (prefers-color-scheme: dark) {
      .canvas-bg {
        fill: #0d1117; /* GitHub Dark canvas background */
        stroke: #30363d;
      }
      .text-title {
        fill: #c9d1d9; /* GitHub Dark text color */
      }
      .text-body {
        fill: #8b949e;
      }
      .accent-bar {
        fill: #c5ff4a; /* GitAscii Lime Accent */
      }
    }
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Vector Layout Markup --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"canvas-bg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;rx=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"accent-bar"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;rx=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-title"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"45"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;GitAscii Layout Engine&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-body"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"90"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&amp;gt; Status: Active&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-body"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&amp;gt; Theme Mode: Auto-Detecting System Colors&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-body"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"150"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&amp;gt; Cache Resolution: Edge-Triggered&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
Even though the image file is fetched from GitHub's Camo CDN cache, the browser performs CSS rules evaluation locally. This guarantees that if a user switches their OS theme from light to dark, the SVG shifts color schemes instantly, without triggering a network request or requiring a page reload.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Request and Evaluation Architecture
&lt;/h3&gt;

&lt;p&gt;The diagram below details the boundary between server caching and client evaluation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  SERVER-SIDE (Edge &amp;amp; CDN)                    │       CLIENT-SIDE (Browser)
                                                              │
┌──────────────┐         ┌─────────────┐       ┌────────────┐ │ ┌────────────────────────────────┐
│ GitAscii DB  ├────────&amp;gt;│ Vercel Edge ├──────&amp;gt;│ GitHub CDN │ │ │ Reader Browser                  │
│ Layout Config│         │ XML Compile │       │ (Camo Cache) │ │ │                                │
└──────────────┘         └─────────────┘       └─────┬──────┘ │ │ 1. Load image: &amp;lt;img src="camo"&amp;gt; │
                                                     │        │ │ 2. Parse XML markup            │
                                                     └───────────&amp;gt; 3. Read internal &amp;lt;style&amp;gt;      │
                                                              │ │ 4. Evaluate system theme       │
                                                              │ │ 5. Render SVG colors dynamically│
                                                              │ └────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Considerations for Dynamic Themes
&lt;/h3&gt;

&lt;p&gt;When using CSS-in-SVG for GitHub profiles, keep the following guidelines in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Avoid External Font References&lt;/strong&gt;: Font files imported via &lt;code&gt;@import url(...)&lt;/code&gt; inside the SVG &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag will be blocked. Stick to system font stacks (e.g., &lt;code&gt;-apple-system&lt;/code&gt;, &lt;code&gt;monospace&lt;/code&gt;, or standard fonts like &lt;code&gt;Courier New&lt;/code&gt;) to ensure clean rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set XML Namespace Attributes&lt;/strong&gt;: Always include &lt;code&gt;xmlns="http://www.w3.org/2000/svg"&lt;/code&gt; in your root &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; tag. Without this, browsers might fail to parse the XML hierarchy, which prevents style sheets from applying.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Transition Effects Sparingly&lt;/strong&gt;: While CSS transition rules (like &lt;code&gt;transition: fill 0.3s ease&lt;/code&gt;) work in modern browsers, they can consume CPU cycles if there are too many animated SVG nodes. Focus transitions on background and main text layers.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;[!TIP]&lt;br&gt;
If you need to debug SVG styling issues, open the raw SVG file directly in your browser. Inspect it using your developer tools to view the applied CSS classes and toggle dark/light mode emulation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Open web standards like XML, CSS, and media queries provide robust workarounds for security sandboxes and CDN caching limits. By designing edge-rendered assets that contain their own responsive stylesheets, you can bypass GitHub Camo's strict scripting limitations and deliver theme-aware components that load instantly.&lt;/p&gt;

</description>
      <category>css</category>
      <category>svg</category>
      <category>github</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Top 10 Hacks to Make Your GitHub README Actually Interesting in 2026 🔥</title>
      <dc:creator>Igor Braz</dc:creator>
      <pubDate>Thu, 06 Aug 2026 20:33:42 +0000</pubDate>
      <link>https://dev.to/igorcbraz/top-10-hacks-to-make-your-github-readme-actually-interesting-in-2026-3i3h</link>
      <guid>https://dev.to/igorcbraz/top-10-hacks-to-make-your-github-readme-actually-interesting-in-2026-3i3h</guid>
      <description>&lt;h1&gt;
  
  
  Top 10 Hacks to Make Your GitHub README Actually Interesting in 2026 🔥
&lt;/h1&gt;

&lt;p&gt;A recruiter, hiring manager, or open-source maintainer spends an average of 6 seconds looking at your GitHub profile before deciding whether to explore further. Seeing a wall of default unformatted text is the fastest way to lose their attention.&lt;/p&gt;

&lt;p&gt;Your GitHub profile README is the landing page of your engineering career. In this guide, we will look at 10 advanced hacks to turn your profile from a static resume into an interactive, high-performance portfolio.&lt;/p&gt;




&lt;h2&gt;
  
  
  Static vs. Dynamic README Elements
&lt;/h2&gt;

&lt;p&gt;Before jumping into the list, let's understand the tradeoff between static layouts and dynamic widgets:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature Type&lt;/th&gt;
&lt;th&gt;Implementation Method&lt;/th&gt;
&lt;th&gt;Caching Impact&lt;/th&gt;
&lt;th&gt;Best Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Static Markdown&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct README.md text&lt;/td&gt;
&lt;td&gt;None (cached indefinitely)&lt;/td&gt;
&lt;td&gt;Biography, contact info, core skill lists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic SVGs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hosted Serverless APIs&lt;/td&gt;
&lt;td&gt;Heavy (Camo cache controls apply)&lt;/td&gt;
&lt;td&gt;Real-time coding hours, Spotify status, live stats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Action-Generated&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub Actions scheduled writes&lt;/td&gt;
&lt;td&gt;Rebuilds on push&lt;/td&gt;
&lt;td&gt;Blog feeds, daily statistics, interactive games&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  1. Leverage Dynamic Typing SVGs
&lt;/h3&gt;

&lt;p&gt;Adding an animated typing banner is the easiest way to give your profile a modern terminal aesthetic. Using &lt;code&gt;readme-typing-svg&lt;/code&gt;, you can list your specialties or key projects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;![Typing SVG&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://readme-typing-svg.demolab.com?font=Fira+Code&amp;amp;weight=600&amp;amp;size=22&amp;amp;pause=1000&amp;amp;color=F85B44&amp;amp;width=435&amp;amp;lines=Senior+Backend+Engineer;System+Architect;Open+Source+Maintainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;](https://git.io/typing-svg)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!TIP]&lt;br&gt;
Use standard web-safe fonts or specify system monospaced choices like &lt;code&gt;Fira Code&lt;/code&gt; or &lt;code&gt;JetBrains Mono&lt;/code&gt; to prevent layout shifts when the SVG renders inside the browser.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2. Ditch Standard Markdown Tables for GitAscii
&lt;/h3&gt;

&lt;p&gt;Stop writing complex nested HTML tables to align your stack icons. It creates bloated, unreadable Markdown code and frequently breaks on mobile viewports. Instead, use &lt;strong&gt;GitAscii&lt;/strong&gt;, a visual workspace built for GitHub profiles.&lt;/p&gt;

&lt;p&gt;GitAscii lets you arrange terminal-style layout widgets, GitHub statistics, and custom ASCII art in a visual editor. The tool compiles your design into a single SVG URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;![My GitAscii Profile&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://gitascii.com/api/render/yourusername&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;](https://gitascii.com/edit/yourusername)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it is superior:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Client-Side Overhead&lt;/strong&gt;: The visitor only downloads a single highly-optimized SVG.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pixel-Perfect Grids&lt;/strong&gt;: Eliminates Markdown styling limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-updating&lt;/strong&gt;: The backend updates statistics automatically when requested by the GitHub proxy.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Embed WakaTime Coding Metrics
&lt;/h3&gt;

&lt;p&gt;Instead of showing only commit counts (which can be easily inflated), show the language metrics and time you spend actively typing inside your IDE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;WakaTime Stats&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github-readme-stats.vercel.app/api/wakatime?username=yourusername&amp;amp;layout=compact&amp;amp;langs_count=8&amp;amp;theme=radical&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;br&gt;
WakaTime lists your active editing time. Ensure you review WakaTime's privacy dashboard settings to prevent leaking internal project names or proprietary repositories.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  4. Interactive Chess Games and Mini-Games
&lt;/h3&gt;

&lt;p&gt;You can run an active chess game right on your profile. The board is rendered as a dynamic SVG, and users play against each other by clicking a move link.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  A   B   C   D   E   F   G   H
8 [♜][♞][♝][♛][♚][♝][♞][♜] 8
7 [♟][♟][♟][♟][ ][♟][♟][♟] 7
6 [ ][ ][ ][ ][ ][ ][ ][ ] 6
5 [ ][ ][ ][ ][♟][ ][ ][ ] 5
4 [ ][ ][ ][ ][ ][ ][ ][ ] 4
3 [ ][ ][ ][ ][ ][ ][ ][ ] 3
2 [♙][♙][♙][♙][♙][♙][♙][♙] 2
1 [♖][♘][♗][♕][♔][♗][♘][♖] 1
  A   B   C   D   E   F   G   H
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a user clicks a move, it directs them to open a pre-filled GitHub Issue in your repository. A GitHub Action workflow triggered by the &lt;code&gt;issues&lt;/code&gt; event parses the move, updates the game state file, and commits the change, triggering the SVG board update.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Automated Blog Post Feeds
&lt;/h3&gt;

&lt;p&gt;If you write articles on Dev.to, Medium, or Hashnode, do not update your profile links manually. Setup a scheduled GitHub Action to pull the RSS feed and inject the list into your README.&lt;/p&gt;

&lt;p&gt;Create the following workflow file in your profile repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/blog-posts.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update Blog Posts&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Runs every 6 hours&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*/6&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Allows manual trigger&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;update-readme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fetch and Inject Blog Posts&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gautamkrishnar/blog-post-workflow@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;feed_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://dev.to/feed/yourusername'&lt;/span&gt;
          &lt;span class="na"&gt;max_post_count&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside your &lt;code&gt;README.md&lt;/code&gt;, place these comment tags where you want the feed to appear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- BLOG-POST-LIST:START --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- BLOG-POST-LIST:END --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6. Use HTML Details Tags for Accordions
&lt;/h3&gt;

&lt;p&gt;Keep your profile clean by moving secondary details—like certifications, system config configurations, or dotfiles—into collapsible panels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;details&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;🛠️ View My Terminal &amp;amp; Hardware Setup&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;### Development Environment&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**OS**&lt;/span&gt;: macOS Sequoia / Arch Linux
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Terminal**&lt;/span&gt;: Alacritty + Tmux
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Editor**&lt;/span&gt;: Neovim (configured using Lua)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Shell**&lt;/span&gt;: Zsh with Starship prompt

&lt;span class="nt"&gt;&amp;lt;/details&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7. Dynamically Updated Spotify Widget
&lt;/h3&gt;

&lt;p&gt;Show recruiters your coding soundtrack by embedding a Spotify widget. By setting up a serverless function that integrates with the Spotify Web API, you can generate an SVG showing your active playback state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;![Spotify Status&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://novatfy.vercel.app/api/spotify?username=yourusername&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;](https://open.spotify.com/user/yourusername)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The serverless function handles the OAuth refresh token flow in the background, retrieves the track metadata and album art, converts the image to base64, and renders the dynamic progress bar as an SVG wrapper.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Add GitHub Profile Trophy Cards
&lt;/h3&gt;

&lt;p&gt;Add gamified trophies to your profile based on your open-source accomplishments. It displays awards for stars, total commits, pull request merges, and account age.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;![GitHub Trophies&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github-profile-trophy.vercel.app/?username=yourusername&amp;amp;theme=onedark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;](https://github.com/ryo-ma/github-profile-trophy)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
The trophies are grouped by tiers: &lt;code&gt;C&lt;/code&gt; (bronze), &lt;code&gt;B&lt;/code&gt; (silver), &lt;code&gt;A&lt;/code&gt; (gold), and &lt;code&gt;S&lt;/code&gt; (secret/platinum) based on your contribution statistics.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  9. Adapt SVG Assets to Light and Dark Modes
&lt;/h3&gt;

&lt;p&gt;To prevent custom architecture diagrams or flowcharts from being unreadable in dark mode, embed media queries inside your SVG source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.canvas-bg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0d1117&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.text-title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.grid-lines&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#30363d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.canvas-bg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.text-title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#24292f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.grid-lines&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d0d7de&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that the image matches the reader's operating system or GitHub interface theme without needing duplicate files.&lt;/p&gt;




&lt;h3&gt;
  
  
  10. Center Your Assets with HTML Divs
&lt;/h3&gt;

&lt;p&gt;Markdown aligns elements to the left by default. To make your profile look balanced across wide monitors and mobile viewports, wrap your header badges and main statistics in centered HTML &lt;code&gt;div&lt;/code&gt; blocks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;align=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://readme-typing-svg.demolab.com?font=Fira+Code&amp;amp;lines=Hello+World"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Header"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://linkedin.com/in/yourprofile"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&amp;amp;logo=linkedin"&lt;/span&gt;
        &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"LinkedIn"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A well-crafted profile README shows your attention to detail and engineering quality. By combining dynamic assets (like &lt;strong&gt;GitAscii&lt;/strong&gt; or automated blog workflows) with responsive design guidelines, you can build a portfolio that stands out.&lt;/p&gt;

</description>
      <category>github</category>
      <category>productivity</category>
      <category>career</category>
      <category>devtools</category>
    </item>
    <item>
      <title>GitAscii: A Visual Editor for GitHub Profile READMEs</title>
      <dc:creator>Igor Braz</dc:creator>
      <pubDate>Sat, 01 Aug 2026 03:42:11 +0000</pubDate>
      <link>https://dev.to/igorcbraz/gitascii-a-visual-editor-for-github-profile-readmes-d4o</link>
      <guid>https://dev.to/igorcbraz/gitascii-a-visual-editor-for-github-profile-readmes-d4o</guid>
      <description>&lt;h1&gt;
  
  
  GitAscii: A Visual Editor for GitHub Profile READMEs
&lt;/h1&gt;

&lt;p&gt;Your GitHub profile is your digital business card in the open-source community. However, maintaining a profile README that is visually striking, informative, and dynamically updated often turns into a tedious chore. Developers are forced to choose between manually editing Markdown files, relying on multiple mismatched third-party statistics generators, or dealing with broken alignments on mobile devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitAscii&lt;/strong&gt; solves this problem by introducing a unified, visual drag-and-drop workspace. Instead of writing nested HTML tables or configuring multiple external repositories, you construct your profile visually. The editor generates a configuration schema, which is compiled on-demand into a single, highly optimized, theme-responsive SVG.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Problem: Why Profile READMEs Are Fragile
&lt;/h2&gt;

&lt;p&gt;Building a custom profile README presents several technical challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Camo Proxying&lt;/strong&gt;: GitHub routes all README images through &lt;code&gt;camo.githubusercontent.com&lt;/code&gt; to prevent mixed-content warnings and trace user IP addresses. This proxy caches images aggressively, meaning any dynamic stats must declare strict caching policies or risk serving stale data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive Alignment&lt;/strong&gt;: Markdown doesn't natively support grid layouts or advanced flexbox styling. Creating a multi-column layout with status badges, coding stats, and bio sections requires writing raw HTML &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; structures that frequently break on narrow screens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme Synchronization&lt;/strong&gt;: GitHub supports light and dark themes. Most external badge generators return flat images with hardcoded background colors, rendering them illegible when a user toggles their GitHub interface theme.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance Overhead&lt;/strong&gt;: Keeping metrics up-to-date (like recent blog posts, current repository stars, or Spotify listening state) requires background cron jobs or GitHub Actions that push commits to your profile repository, cluttering your Git history.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The GitAscii Architecture
&lt;/h2&gt;

&lt;p&gt;GitAscii combines a browser-based layout engine with an Edge-hosted rendering pipeline to bypass these limitations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    A[Visual Builder React UI] --&amp;gt;|Saves Configuration| B[(Database / URL State)]
    C[GitHub Camo Proxy] --&amp;gt;|Requests SVG /api/render/:user| D[Next.js Edge Route]
    D --&amp;gt;|Fetches Config &amp;amp; GitHub API| E[Data Aggregator]
    E --&amp;gt;|Renders Tailwind-like HTML| F[Satori Engine]
    F --&amp;gt;|Outputs Dynamic SVG| C
    C --&amp;gt;|Renders to| G[Client Browser]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. The Visual Editor (Client-Side)
&lt;/h3&gt;

&lt;p&gt;Built using &lt;strong&gt;Next.js&lt;/strong&gt;, &lt;strong&gt;React&lt;/strong&gt;, and &lt;strong&gt;Tailwind CSS&lt;/strong&gt;, the editor enables you to arrange layout elements (widgets) on a grid canvas. The layout is stored as a JSON schema, defining positions, dimensions, widget types, and user configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Edge Rendering &amp;amp; Caching Pipeline
&lt;/h3&gt;

&lt;p&gt;Instead of rendering HTML to client-side browsers and making users download hundreds of kilobytes of JavaScript, GitAscii serves a single &lt;code&gt;&amp;lt;img src="..."&amp;gt;&lt;/code&gt; URL. When GitHub Camo requests this URL, our serverless Edge handler fetches the user's layout schema, aggregates live statistics from the GitHub API, and compiles the components using &lt;a href="https://github.com/vercel/satori" rel="noopener noreferrer"&gt;Satori&lt;/a&gt;. Satori converts HTML/CSS elements styled with Tailwind inline-styles into standard, clean SVG paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dynamic Caching &amp;amp; Bypassing Camo Latency
&lt;/h3&gt;

&lt;p&gt;To balance fast load times and fresh stats, GitAscii uses a tailored cache invalidation strategy. We leverage the &lt;code&gt;Cache-Control&lt;/code&gt; header to control how the GitHub Camo proxy and the user's browser handle the asset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=600
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;max-age=0&lt;/code&gt;&lt;/strong&gt;: Instructs the user's browser not to cache the image, ensuring that returning to the page triggers a re-fetch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;s-maxage=300&lt;/code&gt;&lt;/strong&gt;: Tells GitHub Camo to cache the SVG for exactly 5 minutes (300 seconds).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stale-while-revalidate=600&lt;/code&gt;&lt;/strong&gt;: Allows Camo to serve the stale cached SVG immediately while fetching the fresh version in the background, minimizing TTFB (Time to First Byte) latency for the visitor.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Technical Deep Dive: Browser-Side ASCII Art Conversion
&lt;/h2&gt;

&lt;p&gt;One of GitAscii’s unique widgets is the client-side ASCII art converter. Users can upload an image, and GitAscii converts it into a terminal-style monochrome or colored text representation.&lt;/p&gt;

&lt;p&gt;To achieve this without server-side processing overhead, we perform canvas manipulation directly in the browser using the following JavaScript logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Converts a source image file into an ASCII string based on pixel luminance.
 *
 * @param imageEl - The loaded HTMLImageElement.
 * @param cols - Number of columns in the target ASCII art (controls horizontal resolution).
 * @param rows - Number of rows in the target ASCII art (controls vertical resolution).
 * @returns The converted ASCII text.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;convertImageToAscii&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageEl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLImageElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;

  &lt;span class="c1"&gt;// Set visual dimensions to match target resolution&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;

  &lt;span class="c1"&gt;// Draw image scaled down to columns * rows&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Retrieve raw RGBA pixel data&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imgData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getImageData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imgData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;

  &lt;span class="c1"&gt;// ASCII character ramp ordered from densest (darkest) to sparsest (lightest)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charRamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@#S%?*+;:+,. &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rampLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;charRamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;asciiResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="c1"&gt;// If pixel is fully transparent, treat it as whitespace&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;asciiResult&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Calculate relative luminance using standard Rec. 709 weights&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;luminance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2126&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.7152&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.0722&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;

      &lt;span class="c1"&gt;// Map luminance (0-255) to a character index in the ramp&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;luminance&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rampLength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="nx"&gt;asciiResult&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;charRamp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;charIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Append newline at the end of each row&lt;/span&gt;
    &lt;span class="nx"&gt;asciiResult&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;asciiResult&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
Since standard text characters are taller than they are wide, we recommend correcting the aspect ratio by reducing the vertical row count (&lt;code&gt;rows&lt;/code&gt;) relative to &lt;code&gt;cols&lt;/code&gt; by a factor of roughly &lt;code&gt;0.55&lt;/code&gt; before rendering to prevent the ASCII art from stretching vertically.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Achieving Theme Responsiveness in Generated SVGs
&lt;/h2&gt;

&lt;p&gt;To adapt to GitHub's light and dark themes seamlessly, GitAscii inserts a dynamic CSS stylesheet block inside the generated SVG file. By utilizing the standard system media queries within the SVG definition itself, we target GitHub's system theme variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 800 400"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    /* Default dark theme values */
    .bg { fill: #0d1117; }
    .text-primary { fill: #c9d1d9; font-family: 'Fira Code', monospace; }
    .text-accent { fill: #58a6ff; font-weight: bold; }

    /* Override rules when light mode is preferred */
    @media (prefers-color-scheme: light) {
      .bg { fill: #ffffff; }
      .text-primary { fill: #24292f; }
      .text-accent { fill: #0969da; }
    }
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Background Layer --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;rx=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Dynamic Content --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"60"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-accent"&lt;/span&gt; &lt;span class="na"&gt;font-size=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;GitAscii Dashboard&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-primary"&lt;/span&gt; &lt;span class="na"&gt;font-size=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&amp;gt; Initializing widgets...&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When GitHub’s container renders this inline or via an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, the user’s client browser processes the &lt;code&gt;@media&lt;/code&gt; query and instantly shifts colors without requesting a new asset from the server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Join the Project
&lt;/h2&gt;

&lt;p&gt;GitAscii is completely open-source and built for developers who want absolute control over their profiles without writing thousands of lines of boilerplate Markdown.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!TIP]&lt;br&gt;
You can host GitAscii yourself! The application is packaged to run within Vercel Serverless Functions, Netlify, or standard Docker containers. Check out our repository instructions on self-hosting config variables to spin up a private instance with custom GitHub OAuth scopes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/Igorcbraz/GitAscii" rel="noopener noreferrer"&gt;https://github.com/Igorcbraz/GitAscii&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual Editor&lt;/strong&gt;: &lt;a href="https://gitascii.com" rel="noopener noreferrer"&gt;https://gitascii.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
