<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://csswitch.github.io/jekyll-dashboard-theme/feed.xml" rel="self" type="application/atom+xml" /><link href="https://csswitch.github.io/jekyll-dashboard-theme/" rel="alternate" type="text/html" /><updated>2026-05-12T20:27:52+00:00</updated><id>https://csswitch.github.io/jekyll-dashboard-theme/feed.xml</id><title type="html">Dev Dashboard</title><subtitle>Your developer portfolio and blog — OS desktop UI aesthetic</subtitle><author><name>Your Name</name><email>you@example.com</email></author><entry><title type="html">Building OS-Style UI with CSS: Window Chrome, Sidebars &amp;amp; Widgets</title><link href="https://csswitch.github.io/jekyll-dashboard-theme/2024/01/22/building-os-style-ui-with-css/" rel="alternate" type="text/html" title="Building OS-Style UI with CSS: Window Chrome, Sidebars &amp;amp; Widgets" /><published>2024-01-22T09:00:00+00:00</published><updated>2024-01-22T09:00:00+00:00</updated><id>https://csswitch.github.io/jekyll-dashboard-theme/2024/01/22/building-os-style-ui-with-css</id><content type="html" xml:base="https://csswitch.github.io/jekyll-dashboard-theme/2024/01/22/building-os-style-ui-with-css/"><![CDATA[<p>The most distinctive thing about Dashboard Theme is that it looks like an operating system, not a website. This post breaks down the CSS techniques that make that work — all pure CSS, no JS for layout, no external component libraries.</p>

<h2 id="the-palette-github-dark">The Palette: GitHub Dark</h2>

<p>The entire color system is built on GitHub’s dark interface palette. The key insight is that GitHub uses <em>four</em> background layers, not one:</p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$bg-desktop</span><span class="p">:</span>  <span class="mh">#0d1117</span><span class="p">;</span>  <span class="c1">// outermost — the "desktop"</span>
<span class="nv">$bg-sidebar</span><span class="p">:</span>  <span class="mh">#161b22</span><span class="p">;</span>  <span class="c1">// sidebar panel</span>
<span class="nv">$bg-panel</span><span class="p">:</span>    <span class="mh">#1c2128</span><span class="p">;</span>  <span class="c1">// card surfaces</span>
<span class="nv">$bg-titlebar</span><span class="p">:</span> <span class="mh">#21262d</span><span class="p">;</span>  <span class="c1">// titlebar / elevated chrome</span>
</code></pre></div></div>

<p>Each layer is ~6–8 hex steps lighter. This subtle progression creates genuine depth without needing <code class="language-plaintext highlighter-rouge">box-shadow</code> for every panel.</p>

<h2 id="window-chrome-css-pseudo-elements">Window Chrome: CSS Pseudo-elements</h2>

<p>The window card header uses <code class="language-plaintext highlighter-rouge">::before</code> and <code class="language-plaintext highlighter-rouge">::after</code> on the <code class="language-plaintext highlighter-rouge">&lt;pre&gt;</code> element for the traffic-light dots:</p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">pre</span> <span class="p">{</span>
  <span class="nl">border-top</span><span class="p">:</span> <span class="si">#{</span><span class="nv">$titlebar-h</span><span class="si">}</span> <span class="nb">solid</span> <span class="nv">$bg-titlebar</span><span class="p">;</span>
  <span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span>

  <span class="k">&amp;</span><span class="nd">::before</span> <span class="p">{</span>
    <span class="nl">content</span><span class="p">:</span> <span class="s1">'◉  ◉  ◉'</span><span class="p">;</span>
    <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
    <span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
    <span class="nl">height</span><span class="p">:</span> <span class="nv">$titlebar-h</span><span class="p">;</span>
    <span class="nl">color</span><span class="p">:</span> <span class="nv">$text-dim</span><span class="p">;</span>
    <span class="nl">letter-spacing</span><span class="p">:</span> <span class="m">0</span><span class="mi">.3em</span><span class="p">;</span>
    <span class="nl">font-size</span><span class="p">:</span> <span class="m">0</span><span class="mi">.5rem</span><span class="p">;</span>
    <span class="nl">line-height</span><span class="p">:</span> <span class="nv">$titlebar-h</span><span class="p">;</span>
    <span class="nl">padding</span><span class="p">:</span> <span class="m">0</span> <span class="m">12px</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The trick is <code class="language-plaintext highlighter-rouge">border-top: 36px solid $bg-titlebar</code> — that creates the titlebar <em>space</em> without a separate element. The <code class="language-plaintext highlighter-rouge">::before</code> pseudo-element then sits absolutely positioned inside that space.</p>

<h2 id="sidebar-layout">Sidebar Layout</h2>

<p>The workspace is a two-column flex container:</p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.dash-workspace</span> <span class="p">{</span>
  <span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
  <span class="nl">flex</span><span class="p">:</span> <span class="m">1</span><span class="p">;</span>
  <span class="nl">overflow</span><span class="p">:</span> <span class="nb">hidden</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.dash-sidebar</span> <span class="p">{</span>
  <span class="nl">width</span><span class="p">:</span> <span class="nv">$sidebar-width</span><span class="p">;</span>   <span class="c1">// 240px</span>
  <span class="nl">flex-shrink</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.dash-content</span> <span class="p">{</span>
  <span class="nl">flex</span><span class="p">:</span> <span class="m">1</span><span class="p">;</span>
  <span class="nl">overflow-y</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The active sidebar link uses an <code class="language-plaintext highlighter-rouge">::after</code> right-border indicator — the same pattern VS Code uses:</p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">.</span><span class="na">dash-sidebar__link</span><span class="err">.</span><span class="na">active</span><span class="p">:</span><span class="o">:</span><span class="n">after</span> <span class="p">{</span>
  <span class="nl">content</span><span class="p">:</span> <span class="s1">''</span><span class="p">;</span>
  <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
  <span class="nl">right</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
  <span class="nl">width</span><span class="p">:</span> <span class="m">3px</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nv">$blue</span><span class="p">;</span>
  <span class="nl">border-radius</span><span class="p">:</span> <span class="m">2px</span> <span class="m">0</span> <span class="m">0</span> <span class="m">2px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="stat-widgets-css-grid-auto-fill">Stat Widgets: CSS Grid Auto-fill</h2>

<p>The home-page widget row uses <code class="language-plaintext highlighter-rouge">auto-fill</code> with a min-width constraint so it reflows without media queries:</p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.widget-row</span> <span class="p">{</span>
  <span class="nl">display</span><span class="p">:</span> <span class="n">grid</span><span class="p">;</span>
  <span class="na">grid-template-columns</span><span class="p">:</span> <span class="nf">repeat</span><span class="p">(</span><span class="n">auto-fill</span><span class="o">,</span> <span class="nf">minmax</span><span class="p">(</span><span class="m">180px</span><span class="o">,</span> <span class="m">1fr</span><span class="p">));</span>
  <span class="na">gap</span><span class="p">:</span> <span class="m">1rem</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Four widgets → four columns on wide screens, two columns on tablet, one column on mobile. No breakpoints written.</p>

<h2 id="reading-progress-bar">Reading Progress Bar</h2>

<p>A thin 2px <code class="language-plaintext highlighter-rouge">fixed</code> bar at <code class="language-plaintext highlighter-rouge">top: 40px</code> (just below the topbar). JavaScript updates <code class="language-plaintext highlighter-rouge">width</code> as a percentage:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">scroll</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">scrollTop</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">scrollY</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">docH</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">scrollHeight</span> <span class="o">-</span> <span class="nb">window</span><span class="p">.</span><span class="nx">innerHeight</span><span class="p">;</span>
  <span class="nx">bar</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">docH</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="p">(</span><span class="nx">scrollTop</span> <span class="o">/</span> <span class="nx">docH</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">%</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">0%</span><span class="dl">'</span><span class="p">;</span>
<span class="p">},</span> <span class="p">{</span> <span class="na">passive</span><span class="p">:</span> <span class="kc">true</span> <span class="p">});</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">passive: true</code> option prevents the browser from waiting for <code class="language-plaintext highlighter-rouge">preventDefault()</code> before scrolling — important for smooth 60fps on mobile.</p>

<h2 id="mobile-sidebar-drawer">Mobile: Sidebar Drawer</h2>

<p>On mobile, the sidebar switches from a persistent panel to a drawer via a single class toggle:</p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@media</span> <span class="p">(</span><span class="n">max-width</span><span class="o">:</span> <span class="m">768px</span><span class="p">)</span> <span class="p">{</span>
  <span class="nc">.dash-sidebar</span> <span class="p">{</span>
    <span class="nl">display</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
    <span class="nl">position</span><span class="p">:</span> <span class="nb">fixed</span><span class="p">;</span>
    <span class="nl">top</span><span class="p">:</span> <span class="m">40px</span><span class="p">;</span> <span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
    <span class="nl">height</span><span class="p">:</span> <span class="nf">calc</span><span class="p">(</span><span class="m">100vh</span> <span class="o">-</span> <span class="m">40px</span><span class="p">);</span>
    <span class="nl">z-index</span><span class="p">:</span> <span class="m">150</span><span class="p">;</span>

    <span class="k">&amp;</span><span class="nc">.open</span> <span class="p">{</span> <span class="nl">display</span><span class="p">:</span> <span class="nb">block</span><span class="p">;</span> <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>JavaScript toggles <code class="language-plaintext highlighter-rouge">.open</code> on the hamburger button click. An outside-click handler removes it. No <code class="language-plaintext highlighter-rouge">transform: translateX()</code> animation needed — the instant show/hide matches OS behavior better than a slide.</p>

<h2 id="takeaway">Takeaway</h2>

<p>The whole Dashboard aesthetic comes down to three decisions:</p>

<ol>
  <li><strong>Multiple background layers</strong> (not one dark color)</li>
  <li><strong>Border-top titlebar</strong> + <code class="language-plaintext highlighter-rouge">::before</code> for window chrome (no extra elements)</li>
  <li><strong>Flex + overflow:hidden</strong> on the workspace (OS layout in two lines of CSS)</li>
</ol>

<p>Everything else — widgets, tag pills, status dots — follows from those foundations.</p>]]></content><author><name>Your Name</name><email>you@example.com</email></author><category term="css" /><category term="ui" /><category term="jekyll" /><category term="design-system" /><category term="dark-mode" /><summary type="html"><![CDATA[How Dashboard Theme recreates OS desktop UI patterns — window chrome, sidebar layouts, and stat widgets — using pure CSS and Sass.]]></summary></entry><entry><title type="html">Welcome to Dashboard Theme</title><link href="https://csswitch.github.io/jekyll-dashboard-theme/2024/01/15/welcome-to-dashboard-theme/" rel="alternate" type="text/html" title="Welcome to Dashboard Theme" /><published>2024-01-15T09:00:00+00:00</published><updated>2024-01-15T09:00:00+00:00</updated><id>https://csswitch.github.io/jekyll-dashboard-theme/2024/01/15/welcome-to-dashboard-theme</id><content type="html" xml:base="https://csswitch.github.io/jekyll-dashboard-theme/2024/01/15/welcome-to-dashboard-theme/"><![CDATA[<p>Dashboard Theme is a Jekyll theme that turns your GitHub Pages site into something that feels less like a blog and more like a developer’s workspace. Every post lives inside a window-chrome card. A persistent sidebar keeps navigation within reach. Stat widgets on the home page give you a glanceable overview of your content.</p>

<h2 id="design-philosophy">Design Philosophy</h2>

<p>Most Jekyll themes are websites that happen to be built with Jekyll. Dashboard Theme is a <em>developer’s UI</em> that happens to be a blog.</p>

<p>The design draws from:</p>

<ul>
  <li><strong>GitHub’s dark interface</strong> — <code class="language-plaintext highlighter-rouge">#0d1117</code> backgrounds, <code class="language-plaintext highlighter-rouge">#388bfd</code> blue accents</li>
  <li><strong>VS Code / Electron apps</strong> — sidebar-left layout with section headers and active indicators</li>
  <li><strong>macOS window chrome</strong> — traffic-light dots, title bars, window frames around content</li>
</ul>

<h2 id="installing-the-theme">Installing the Theme</h2>

<p>Clone the repository and run Jekyll locally:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/csswitch/jekyll-dashboard-theme.git
<span class="nb">cd </span>jekyll-dashboard-theme
bundle <span class="nb">install
</span>bundle <span class="nb">exec </span>jekyll serve
</code></pre></div></div>

<p>Visit <code class="language-plaintext highlighter-rouge">http://localhost:4000</code> — you’ll have the full Dashboard UI running locally.</p>

<h2 id="configuration">Configuration</h2>

<p>Open <code class="language-plaintext highlighter-rouge">_config.yml</code> and update the core settings:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Your</span><span class="nv"> </span><span class="s">Site</span><span class="nv"> </span><span class="s">Name"</span>
<span class="na">description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Your</span><span class="nv"> </span><span class="s">site</span><span class="nv"> </span><span class="s">description"</span>
<span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://yourusername.github.io"</span>
<span class="na">baseurl</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">dashboard:</code> block controls theme-specific behavior:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">dashboard</span><span class="pi">:</span>
  <span class="na">os</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Your</span><span class="nv"> </span><span class="s">OS"</span>          <span class="c1"># shown in taskbar</span>
  <span class="na">sidebar</span><span class="pi">:</span> <span class="no">true</span>          <span class="c1"># toggle sidebar</span>
  <span class="na">widgets</span><span class="pi">:</span> <span class="no">true</span>          <span class="c1"># home stat widgets</span>
  <span class="na">window_chrome</span><span class="pi">:</span> <span class="no">true</span>    <span class="c1"># post window frames</span>
  <span class="na">taskbar</span><span class="pi">:</span> <span class="no">true</span>          <span class="c1"># bottom bar</span>
</code></pre></div></div>

<h2 id="writing-posts">Writing Posts</h2>

<p>Posts follow standard Jekyll conventions. Add front matter with <code class="language-plaintext highlighter-rouge">layout: post</code>:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">layout</span><span class="pi">:</span> <span class="s">post</span>
<span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">My</span><span class="nv"> </span><span class="s">Post</span><span class="nv"> </span><span class="s">Title"</span>
<span class="na">date</span><span class="pi">:</span> <span class="s">2024-01-15</span>
<span class="na">tags</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">jekyll</span><span class="pi">,</span> <span class="nv">tutorial</span><span class="pi">]</span>
<span class="nn">---</span>

Your content here.
</code></pre></div></div>

<p>Tags automatically appear in both the Tags page cloud and the post header. The sidebar recent-posts panel updates to show your 5 most recent entries.</p>

<h2 id="whats-next">What’s Next</h2>

<p>Explore the Archive page (<code class="language-plaintext highlighter-rouge">/archive/</code>) for a year-grouped view of all posts, or head to Tags (<code class="language-plaintext highlighter-rouge">/tags/</code>) for the full tag cloud. The About page is ready to customize with your own bio and links.</p>]]></content><author><name>Your Name</name><email>you@example.com</email></author><category term="jekyll" /><category term="theme" /><category term="dashboard" /><category term="getting-started" /><summary type="html"><![CDATA[Dashboard Theme brings the aesthetic of a desktop OS to your GitHub Pages blog.]]></summary></entry></feed>