diff options
35 files changed, 388 insertions, 75 deletions
diff --git a/blog/css-only-code-blocks/index.html b/blog/css-only-code-blocks/index.html new file mode 100644 index 0000000..76888b2 --- /dev/null +++ b/blog/css-only-code-blocks/index.html @@ -0,0 +1,267 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Code listings with nice line wrapping and line numbers from plain CSS | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> + + <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin /> + <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin /> + <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin /> + <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin /> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="search"> + <div id="search"></div> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/@jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Code listings with nice line wrapping and line numbers from plain CSS</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/css-only-code-blocks/">Code listings with nice line wrapping and line numbers from plain CSS</a></li> +</ul> + <strong>2025-07-23</strong> + </header> + <main data-pagefind-body> + <div class="document"> + + +<p>Code listings in web pages are often a bit of a pain to use. Often, they don't wrap on small screens. Also, copy-pasting +code from a code listing often copies the line numbers along with the code. Finally, many implementations use +heavyweight HTML and/or javascript, making them slow to render (looking at you, gitlab).</p> +<p>For this blog, I wrote an implementation that renders HTML code listings entirely without JavaScript, renders line +numbers using plain CSS such that they don't get selected with the code, and that works with the browser to wrap in a +natural way while still supporting the little line continuation arrows that are used to show that a line was soft +wrapped in text editors.</p> +<p>This blog is rendered as a static site using <a class="reference external" href="https://gohugo.io/">Hugo</a> from a pile of <a class="reference external" href="https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html">RestructuredText</a> documents. RestructuredText renders +code listings using <a class="reference external" href="https://pygments.org/">Pygments</a> by default. Pygments hard-bakes the line numbers into the generated HTML, so I am using a +<a class="reference external" href="https://en.wikipedia.org/wiki/Monkey_patch">monkey-patched</a> hook that changes the line number rendering to just a bunch of empty <tt class="docutils literal"><span></tt> elements. The resulting +HTML for a code block then looks like this:</p> +<pre class="code html literal-block"> +<span class="lineno"></span><span class="line"><span class="p"><</span><span class="nt">pre</span> <span class="na">class</span><span class="o">=</span><span class="s">"code [language] literal-block"</span><span class="p">></span></span> +<span class="lineno"></span><span class="line"> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"lineno"</span><span class="p">></</span><span class="nt">span</span><span class="p">></span></span> +<span class="lineno"></span><span class="line"> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"line"</span><span class="p">></span></span> +<span class="lineno"></span><span class="line"> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"[syntax highlight token]"</span><span class="p">></span>The <span class="p"></</span><span class="nt">span</span><span class="p">><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"[other syntax highlight token]"</span><span class="p">></span>code!<span class="p"><</span><span class="nt">span</span><span class="p">></span></span> +<span class="lineno"></span><span class="line"> <span class="p"></</span><span class="nt">span</span><span class="p">></span></span> +<span class="lineno"></span><span class="line"> <span class="cm"><!-- ... repeat once for each source line. --></span></span> +<span class="lineno"></span><span class="line"><span class="p"></</span><span class="nt">pre</span><span class="p">></span> +</span></pre> +<p>You can find the (rather short) source of the <tt class="docutils literal">rst2html</tt> wrapper <a class="reference external" href="rst2html_wrapper">below</a>.</p> +<div class="section" id="the-css"> +<h2>The CSS</h2> +<p>This modified HTML structure of the code listing gets accompanied by some CSS to make it flow nicely. Here is a listing +of the complete CSS controlling the listing. The only bit that isn't included here is the actual syntax styling rules +for the pygments tokens.</p> +<pre class="code css literal-block"> +<span class="lineno"></span><span class="line"><span class="c">/*****************************************************/</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* Code block formatting / syntax highlighting rules */</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/*****************************************************/</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">font-family</span><span class="p">:</span><span class="w"> </span><span class="s2">"Fira Code"</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">13</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">text-align</span><span class="p">:</span><span class="w"> </span><span class="kc">left</span><span class="p">;</span><span class="w"> </span><span class="c">/* Override default content "justify" alignment */</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">white-space</span><span class="p">:</span><span class="w"> </span><span class="kc">pre-wrap</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">word-wrap</span><span class="p">:</span><span class="w"> </span><span class="kc">break-word</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">overflow-x</span><span class="p">:</span><span class="w"> </span><span class="kc">auto</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="k">grid</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">start</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">grid-template-columns</span><span class="p">:</span><span class="w"> </span><span class="n">min-content</span><span class="w"> </span><span class="mi">1</span><span class="n">fr</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="p">.</span><span class="nc">line</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding-left</span><span class="p">:</span><span class="w"> </span><span class="nb">calc</span><span class="p">(</span><span class="mi">2</span><span class="kt">em</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">5</span><span class="kt">px</span><span class="p">);</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">text-indent</span><span class="p">:</span><span class="w"> </span><span class="mi">-2</span><span class="kt">em</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding-top</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">min-width</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="kt">em</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* Make individual syntax tokens wrap anywhere */</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="p">.</span><span class="nc">line</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="nt">span</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">overflow-wrap</span><span class="p">:</span><span class="w"> </span><span class="n">anywhere</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">white-space</span><span class="p">:</span><span class="w"> </span><span class="kc">pre-wrap</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* We render line numbers in CSS! */</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="p">.</span><span class="nc">lineno</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">counter-increment</span><span class="p">:</span><span class="w"> </span><span class="n">lineno</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">word-break</span><span class="p">:</span><span class="w"> </span><span class="n">keep-all</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding-left</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding-right</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">overflow</span><span class="p">:</span><span class="w"> </span><span class="kc">clip</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">relative</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">text-align</span><span class="p">:</span><span class="w"> </span><span class="kc">right</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--c-text-muted</span><span class="p">);</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">border-right</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">px</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--c-fg-highlight</span><span class="p">);</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">align-self</span><span class="p">:</span><span class="w"> </span><span class="kc">stretch</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* We also handle line continuation markers in CSS. */</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="p">.</span><span class="nc">lineno</span><span class="p">::</span><span class="nd">after</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">absolute</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">right</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">content</span><span class="p">:</span><span class="w"> </span><span class="s2">"\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳"</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">white-space</span><span class="p">:</span><span class="w"> </span><span class="kc">pre</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--c-text-muted</span><span class="p">);</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* Insert the actual line number */</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="p">.</span><span class="nc">lineno</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">content</span><span class="p">:</span><span class="w"> </span><span class="nb">counter</span><span class="p">(</span><span class="n">lineno</span><span class="p">);</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">counter-reset</span><span class="p">:</span><span class="w"> </span><span class="n">lineno</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="nt">footer</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">margin-top</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">align-self</span><span class="p">:</span><span class="w"> </span><span class="kc">center</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">margin-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">text-align</span><span class="p">:</span><span class="w"> </span><span class="kc">center</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="kt">px</span><span class="w"> </span><span class="mi">25</span><span class="kt">px</span><span class="w"> </span><span class="mi">5</span><span class="kt">px</span><span class="w"> </span><span class="mi">25</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="p">.</span><span class="nc">hll</span><span class="w"> </span><span class="p">{}</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* Following are about 50 lines that define the styling of each kind of pygments syntax highlight token. These lines</span></span> +<span class="lineno"></span><span class="line"><span class="c"> all look like the following: */</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="p">.</span><span class="nc">c</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--c-text</span><span class="p">);</span><span class="w"> </span><span class="k">font-weight</span><span class="p">:</span><span class="w"> </span><span class="mi">400</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="c">/* Comment */</span> +</span></pre> +<p>This CSS does a few things:</p> +<blockquote> +<ol class="arabic simple"> +<li>It renders the <tt class="docutils literal"><pre></tt> code listing element using a two-column CSS <tt class="docutils literal">display: grid</tt> layout. The left column is +used for the line numbers, and the right column is used for the code lines.</li> +<li>It numbers the lines using a <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/CSS/counter">CSS Counter</a>. CSS counters are meant for things like numbering headings and such, but +they are a perfect fit for our purpose.</li> +<li>It inserts the counter value as the line number into the <tt class="docutils literal"><span <span class="pre">class="lineno"></span></tt> element's <tt class="docutils literal">::before</tt> +pseudo-element. A side effect of using the <tt class="docutils literal">::before</tt> pseudo-element is that without doing anything extra, the +line numbers will remain outside of the normal text selection so they will neither be highlighted when selecting +listing content, nor will they be copied when copy/pasting the listing content.</li> +<li>It inserts a string of <tt class="docutils literal"><span class="pre">"\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳"</span></tt> into the line number span's +<tt class="docutils literal">::after</tt> pseudo-element. This string evaluates to a sequence of unicode arrows separated by line breaks, and +starting with an empty line. The <tt class="docutils literal">::after</tt> pseudo-element is positioned using <tt class="docutils literal">position: absolute</tt>, and the +parent <tt class="docutils literal"><span <span class="pre">class="lineno"></span></tt> has <tt class="docutils literal">position: relative</tt> set. This way, the arrow pseudo-element gets placed on +top of the lineno span without affecting the layout at all. By setting <tt class="docutils literal">overflow: clip</tt> on the parent <tt class="docutils literal"><span +<span class="pre">class="lineno"></span></tt>, the arrow pseudo-element gets cut off vertically wherever the parent lineno element naturally +ends.</li> +</ol> +</blockquote> +<p>The line number span is inserted into the parent <tt class="docutils literal"><pre></tt> element's CSS grid using <tt class="docutils literal"><span class="pre">align-self:</span> stretch</tt>, which +causes it to vertically stretch to fill the available space. Since the line number span only contains the line number, +its minimum height is a single line. As a result, it will stretch higher only when the corresponding code line in the +right grid column stretches vertically because of line wrapping. When that happens, part of the arrow pseudo-element +starts showing through from behind the <tt class="docutils literal">overflow: clip</tt> of the line number span, and one arrow gets rendered for each +wrapped listing line.</p> +<p>When the page is too narrow, we don't want the code listing's lines to wrapp into a column of single characters. To +prevent that, we simply set a <tt class="docutils literal"><span class="pre">min-width</span></tt> on the <tt class="docutils literal"><span <span class="pre">class="line"></span></tt> in the right column, and set <tt class="docutils literal"><span class="pre">overflow-x:</span> +auto</tt> on the listing <tt class="docutils literal"><pre></tt>. This results in a horizontal scroll bar appearing whenever the listing gets too narrow.</p> +<p>You can try out the line wrapping by resizing this page!</p> +</div> +<div class="section" id="rst2html-wrapper"> +<h2>rst2html wrapper</h2> +<p>Here is the python <tt class="docutils literal">rst2html</tt> wrapper that monkey-patches code rendering. I made hugo invoke this while building the +page by simply overriding the <tt class="docutils literal">PATH</tt> environment variable.</p> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># Based on https://gist.github.com/mastbaum/2655700 for the basic plugin scaffolding</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">re</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">docutils.core</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">docutils.transforms</span><span class="w"> </span><span class="kn">import</span> <span class="n">Transform</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">docutils.nodes</span><span class="w"> </span><span class="kn">import</span> <span class="n">TextElement</span><span class="p">,</span> <span class="n">Inline</span><span class="p">,</span> <span class="n">Text</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">docutils.parsers.rst</span><span class="w"> </span><span class="kn">import</span> <span class="n">Directive</span><span class="p">,</span> <span class="n">directives</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">docutils.writers.html4css1</span><span class="w"> </span><span class="kn">import</span> <span class="n">Writer</span><span class="p">,</span> <span class="n">HTMLTranslator</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">class</span><span class="w"> </span><span class="nc">UnfuckedHTMLTranslator</span><span class="p">(</span><span class="n">HTMLTranslator</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">in_literal_block</span> <span class="o">=</span> <span class="kc">False</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="nf">visit_literal_block</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># Insert an empty "lineno" span before each line. We insert the line numbers using pure CSS in a ::before</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># pseudo-element. This has the added advantage that the line numbers don't get included in text selection.</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># These line number spans are also used to show line continuation markers when a line is wrapped.</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">in_literal_block</span> <span class="o">=</span> <span class="kc">True</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">starttag</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="s1">'pre'</span><span class="p">,</span> <span class="n">CLASS</span><span class="o">=</span><span class="s1">'literal-block'</span><span class="p">))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">'<span class="lineno"></span><span class="line">'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="nf">depart_literal_block</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">in_literal_block</span> <span class="o">=</span> <span class="kc">False</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">'</span><span class="se">\n</span><span class="s1"></span></pre></span><span class="se">\n</span><span class="s1">'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="nf">visit_Text</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">in_literal_block</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">for</span> <span class="n">match</span> <span class="ow">in</span> <span class="n">re</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="s1">'([^</span><span class="se">\n</span><span class="s1">]*)(</span><span class="se">\n</span><span class="s1">|$)'</span><span class="p">,</span> <span class="n">node</span><span class="o">.</span><span class="n">astext</span><span class="p">()):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">text</span><span class="p">,</span> <span class="n">end</span> <span class="o">=</span> <span class="n">match</span><span class="o">.</span><span class="n">groups</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="n">text</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">visit_Text</span><span class="p">(</span><span class="n">Text</span><span class="p">(</span><span class="n">text</span><span class="p">))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="n">end</span> <span class="o">==</span> <span class="s1">'</span><span class="se">\n</span><span class="s1">'</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">Inline</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">depart_inline</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s1">'</span></span><span class="se">\n</span><span class="s1"><span class="lineno"></span><span class="line">'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">Inline</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">visit_inline</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">else</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">visit_Text</span><span class="p">(</span><span class="n">node</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">html_writer</span> <span class="o">=</span> <span class="n">Writer</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">html_writer</span><span class="o">.</span><span class="n">translator_class</span> <span class="o">=</span> <span class="n">UnfuckedHTMLTranslator</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">docutils</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">publish_cmdline</span><span class="p">(</span><span class="n">writer</span><span class="o">=</span><span class="n">html_writer</span><span class="p">)</span> +</span></pre> +</div> +</div> + </main><footer> + Copyright © 2025 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script> + <script> + window.addEventListener('DOMContentLoaded', (event) => { + new PagefindUI({element: "#search", showSubResults: true}); + }); + </script> + <script type="speculationrules"> + { + "prerender": [ + { + "source": "document", + "where": { + "and": [ + {"href_matches": "/*"} + ] + }, + "eagerness": "moderate" + } + ] + } + </script> + </body> +</html> diff --git a/blog/index.html b/blog/index.html index 97f5921..3c2bb69 100644 --- a/blog/index.html +++ b/blog/index.html @@ -40,6 +40,18 @@ </header> <main class="cards"> + <div class="card"><h3><a href="/blog/css-only-code-blocks/">Code listings with nice line wrapping and line numbers from plain CSS</a></h3><strong>2025-07-23</strong> + + <div class="summary"> + <div class="document"> + + +<p>Code listings in web pages are often a bit of a pain to use. Usually, they don't wrap on small screens. Also, copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.</p> +</div> + <a href="http://jaseg.de/blog/css-only-code-blocks/">Read more</a> + </div> +</div> + <div class="card"><h3><a href="/blog/jupyterlab-notebook-file-oneliner/">Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</a></h3><strong>2025-06-29</strong> <div class="summary"> diff --git a/blog/index.xml b/blog/index.xml index 285b3f8..5ce7d35 100644 --- a/blog/index.xml +++ b/blog/index.xml @@ -7,9 +7,16 @@ <generator>Hugo</generator> <language>en-us</language> <copyright>Jan Sebastian Götte</copyright> - <lastBuildDate>Sun, 29 Jun 2025 23:42:00 +0100</lastBuildDate> + <lastBuildDate>Wed, 23 Jul 2025 23:42:00 +0100</lastBuildDate> <atom:link href="http://jaseg.de/blog/index.xml" rel="self" type="application/rss+xml" /> <item> + <title>Code listings with nice line wrapping and line numbers from plain CSS</title> + <link>http://jaseg.de/blog/css-only-code-blocks/</link> + <pubDate>Wed, 23 Jul 2025 23:42:00 +0100</pubDate> + <guid>http://jaseg.de/blog/css-only-code-blocks/</guid> + <description><div class="document">


<p>Code listings in web pages are often a bit of a pain to use. Usually, they don't wrap on small screens. Also, copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.</p>
</div></description> + </item> + <item> <title>Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</title> <link>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</link> <pubDate>Sun, 29 Jun 2025 23:42:00 +0100</pubDate> @@ -96,6 +96,19 @@ pages. If you want to learn more about me, head over to the about page.</p> <h2>Blog</h2> + <div class="card"><h3><a href="/blog/css-only-code-blocks/">Code listings with nice line wrapping and line numbers from plain CSS</a></h3><strong>2025-07-23</strong> + + <div class="summary"> + <div class="document"> + + +<p>Code listings in web pages are often a bit of a pain to use. Usually, they don't wrap on small screens. Also, copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.</p> +</div> + <a href="http://jaseg.de/blog/css-only-code-blocks/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/jupyterlab-notebook-file-oneliner/">Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</a></h3><strong>2025-06-29</strong> <div class="summary"> @@ -220,19 +233,6 @@ set up your UART to talk to an external hardware USB to serial converter is a ma </div> - <div class="card"><h3><a href="/blog/multichannel-led-driver/">32-Channel LED tape driver</a></h3><strong>2018-05-02</strong> - - <div class="summary"> - <div class="document"> - - -<p>Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. For this installation, I made a 32-channel LED driver that achieves high dynamic range on all 32 channels using a cheap microcontroller by using Binary Code Modulation.</p> -</div> - <a href="http://jaseg.de/blog/multichannel-led-driver/">Read more</a> - </div> -</div> - - <div class="pagination-links"> @@ -7,9 +7,16 @@ <generator>Hugo</generator> <language>en-us</language> <copyright>Jan Sebastian Götte</copyright> - <lastBuildDate>Sun, 29 Jun 2025 23:42:00 +0100</lastBuildDate> + <lastBuildDate>Wed, 23 Jul 2025 23:42:00 +0100</lastBuildDate> <atom:link href="http://jaseg.de/index.xml" rel="self" type="application/rss+xml" /> <item> + <title>Code listings with nice line wrapping and line numbers from plain CSS</title> + <link>http://jaseg.de/blog/css-only-code-blocks/</link> + <pubDate>Wed, 23 Jul 2025 23:42:00 +0100</pubDate> + <guid>http://jaseg.de/blog/css-only-code-blocks/</guid> + <description><div class="document">


<p>Code listings in web pages are often a bit of a pain to use. Usually, they don't wrap on small screens. Also, copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.</p>
</div></description> + </item> + <item> <title>Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</title> <link>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</link> <pubDate>Sun, 29 Jun 2025 23:42:00 +0100</pubDate> diff --git a/pagefind/fragment/unknown_26a6593.pf_fragment b/pagefind/fragment/unknown_117d485.pf_fragment Binary files differindex a356c24..c751b98 100644 --- a/pagefind/fragment/unknown_26a6593.pf_fragment +++ b/pagefind/fragment/unknown_117d485.pf_fragment diff --git a/pagefind/fragment/unknown_1ced448.pf_fragment b/pagefind/fragment/unknown_1ced448.pf_fragment Binary files differdeleted file mode 100644 index e86b6db..0000000 --- a/pagefind/fragment/unknown_1ced448.pf_fragment +++ /dev/null diff --git a/pagefind/fragment/unknown_1dc17c1.pf_fragment b/pagefind/fragment/unknown_1dc17c1.pf_fragment Binary files differnew file mode 100644 index 0000000..e10c683 --- /dev/null +++ b/pagefind/fragment/unknown_1dc17c1.pf_fragment diff --git a/pagefind/fragment/unknown_7aa9d62.pf_fragment b/pagefind/fragment/unknown_3470936.pf_fragment Binary files differindex e87ac51..84d52fa 100644 --- a/pagefind/fragment/unknown_7aa9d62.pf_fragment +++ b/pagefind/fragment/unknown_3470936.pf_fragment diff --git a/pagefind/fragment/unknown_60c6413.pf_fragment b/pagefind/fragment/unknown_60c6413.pf_fragment Binary files differnew file mode 100644 index 0000000..cdeb2f0 --- /dev/null +++ b/pagefind/fragment/unknown_60c6413.pf_fragment diff --git a/pagefind/fragment/unknown_67ef208.pf_fragment b/pagefind/fragment/unknown_67ef208.pf_fragment Binary files differnew file mode 100644 index 0000000..a4f085a --- /dev/null +++ b/pagefind/fragment/unknown_67ef208.pf_fragment diff --git a/pagefind/fragment/unknown_684b5ee.pf_fragment b/pagefind/fragment/unknown_684b5ee.pf_fragment Binary files differdeleted file mode 100644 index 278d844..0000000 --- a/pagefind/fragment/unknown_684b5ee.pf_fragment +++ /dev/null diff --git a/pagefind/fragment/unknown_6aca334.pf_fragment b/pagefind/fragment/unknown_6aca334.pf_fragment Binary files differdeleted file mode 100644 index 5945f47..0000000 --- a/pagefind/fragment/unknown_6aca334.pf_fragment +++ /dev/null diff --git a/pagefind/fragment/unknown_6b283ed.pf_fragment b/pagefind/fragment/unknown_6b283ed.pf_fragment Binary files differnew file mode 100644 index 0000000..2edd8ca --- /dev/null +++ b/pagefind/fragment/unknown_6b283ed.pf_fragment diff --git a/pagefind/fragment/unknown_77ac5be.pf_fragment b/pagefind/fragment/unknown_77ac5be.pf_fragment Binary files differnew file mode 100644 index 0000000..68e26b7 --- /dev/null +++ b/pagefind/fragment/unknown_77ac5be.pf_fragment diff --git a/pagefind/fragment/unknown_8a619f9.pf_fragment b/pagefind/fragment/unknown_8a619f9.pf_fragment Binary files differnew file mode 100644 index 0000000..83c8365 --- /dev/null +++ b/pagefind/fragment/unknown_8a619f9.pf_fragment diff --git a/pagefind/fragment/unknown_62c6a43.pf_fragment b/pagefind/fragment/unknown_903d5c1.pf_fragment Binary files differindex 587431b..aaa5a7a 100644 --- a/pagefind/fragment/unknown_62c6a43.pf_fragment +++ b/pagefind/fragment/unknown_903d5c1.pf_fragment diff --git a/pagefind/fragment/unknown_a118647.pf_fragment b/pagefind/fragment/unknown_a118647.pf_fragment Binary files differdeleted file mode 100644 index c51f0b3..0000000 --- a/pagefind/fragment/unknown_a118647.pf_fragment +++ /dev/null diff --git a/pagefind/fragment/unknown_abc2923.pf_fragment b/pagefind/fragment/unknown_abc2923.pf_fragment Binary files differdeleted file mode 100644 index c6d3d46..0000000 --- a/pagefind/fragment/unknown_abc2923.pf_fragment +++ /dev/null diff --git a/pagefind/fragment/unknown_bb28932.pf_fragment b/pagefind/fragment/unknown_bb28932.pf_fragment Binary files differnew file mode 100644 index 0000000..3cb19a3 --- /dev/null +++ b/pagefind/fragment/unknown_bb28932.pf_fragment diff --git a/pagefind/fragment/unknown_c2c6ec9.pf_fragment b/pagefind/fragment/unknown_c2c6ec9.pf_fragment Binary files differdeleted file mode 100644 index 08c09f8..0000000 --- a/pagefind/fragment/unknown_c2c6ec9.pf_fragment +++ /dev/null diff --git a/pagefind/fragment/unknown_c3ee675.pf_fragment b/pagefind/fragment/unknown_c3ee675.pf_fragment Binary files differnew file mode 100644 index 0000000..f41e1e5 --- /dev/null +++ b/pagefind/fragment/unknown_c3ee675.pf_fragment diff --git a/pagefind/fragment/unknown_83ecd38.pf_fragment b/pagefind/fragment/unknown_f17ff3b.pf_fragment Binary files differindex e7f2639..3ed8fbd 100644 --- a/pagefind/fragment/unknown_83ecd38.pf_fragment +++ b/pagefind/fragment/unknown_f17ff3b.pf_fragment diff --git a/pagefind/fragment/unknown_fd69f69.pf_fragment b/pagefind/fragment/unknown_fd69f69.pf_fragment Binary files differdeleted file mode 100644 index 8e999f6..0000000 --- a/pagefind/fragment/unknown_fd69f69.pf_fragment +++ /dev/null diff --git a/pagefind/index/unknown_1a2698a.pf_index b/pagefind/index/unknown_1a2698a.pf_index Binary files differdeleted file mode 100644 index aa0b2b8..0000000 --- a/pagefind/index/unknown_1a2698a.pf_index +++ /dev/null diff --git a/pagefind/index/unknown_3133a12.pf_index b/pagefind/index/unknown_3133a12.pf_index Binary files differnew file mode 100644 index 0000000..e2381c7 --- /dev/null +++ b/pagefind/index/unknown_3133a12.pf_index diff --git a/pagefind/index/unknown_3fb8321.pf_index b/pagefind/index/unknown_3fb8321.pf_index Binary files differdeleted file mode 100644 index 15da04a..0000000 --- a/pagefind/index/unknown_3fb8321.pf_index +++ /dev/null diff --git a/pagefind/index/unknown_57b4e5c.pf_index b/pagefind/index/unknown_57b4e5c.pf_index Binary files differnew file mode 100644 index 0000000..461f5f6 --- /dev/null +++ b/pagefind/index/unknown_57b4e5c.pf_index diff --git a/pagefind/index/unknown_631b97d.pf_index b/pagefind/index/unknown_631b97d.pf_index Binary files differdeleted file mode 100644 index a2556ed..0000000 --- a/pagefind/index/unknown_631b97d.pf_index +++ /dev/null diff --git a/pagefind/index/unknown_94b0ced.pf_index b/pagefind/index/unknown_94b0ced.pf_index Binary files differnew file mode 100644 index 0000000..0cbaf1f --- /dev/null +++ b/pagefind/index/unknown_94b0ced.pf_index diff --git a/pagefind/pagefind-entry.json b/pagefind/pagefind-entry.json index b83100a..695aeba 100644 --- a/pagefind/pagefind-entry.json +++ b/pagefind/pagefind-entry.json @@ -1 +1 @@ -{"version":"1.0.4","languages":{"unknown":{"hash":"unknown_1722432a8a5b089","wasm":null,"page_count":21}}}
\ No newline at end of file +{"version":"1.0.4","languages":{"unknown":{"hash":"unknown_a6e7cd42f4ee53b","wasm":null,"page_count":22}}}
\ No newline at end of file diff --git a/pagefind/pagefind.unknown_1722432a8a5b089.pf_meta b/pagefind/pagefind.unknown_1722432a8a5b089.pf_meta Binary files differdeleted file mode 100644 index d441ab0..0000000 --- a/pagefind/pagefind.unknown_1722432a8a5b089.pf_meta +++ /dev/null diff --git a/pagefind/pagefind.unknown_a6e7cd42f4ee53b.pf_meta b/pagefind/pagefind.unknown_a6e7cd42f4ee53b.pf_meta Binary files differnew file mode 100644 index 0000000..f785482 --- /dev/null +++ b/pagefind/pagefind.unknown_a6e7cd42f4ee53b.pf_meta diff --git a/sitemap.xml b/sitemap.xml index 7cfe3cf..c0965ab 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -3,12 +3,15 @@ xmlns:xhtml="http://www.w3.org/1999/xhtml"> <url> <loc>http://jaseg.de/blog/</loc> - <lastmod>2025-06-29T23:42:00+01:00</lastmod> + <lastmod>2025-07-23T23:42:00+01:00</lastmod> </url><url> - <loc>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</loc> - <lastmod>2025-06-29T23:42:00+01:00</lastmod> + <loc>http://jaseg.de/blog/css-only-code-blocks/</loc> + <lastmod>2025-07-23T23:42:00+01:00</lastmod> </url><url> <loc>http://jaseg.de/</loc> + <lastmod>2025-07-23T23:42:00+01:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</loc> <lastmod>2025-06-29T23:42:00+01:00</lastmod> </url><url> <loc>http://jaseg.de/blog/8seg/</loc> @@ -267,6 +267,14 @@ h3 { font-weight: 700; } +/* Prevent long literals from breaking the page layout's width */ +span.pre { + white-space: pre-wrap; + word-wrap: break-word; + overflow-x: auto; + overflow-wrap: anywhere; +} + body > header { z-index: 1; margin-top: 100px; @@ -471,9 +479,14 @@ img:hover { filter: none; } +/*****************************************************/ +/* Code block formatting / syntax highlighting rules */ +/*****************************************************/ + .code { font-family: "Fira Code"; font-size: 13px; + text-align: left; /* Override default content "justify" alignment */ white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; @@ -495,6 +508,7 @@ img:hover { white-space: pre-wrap; } +/* We render line numbers in CSS! */ .code > .lineno { counter-increment: lineno; word-break: keep-all; @@ -509,6 +523,7 @@ img:hover { align-self: stretch; } +/* We also handle line continuation markers in CSS. */ .code > .lineno::after { position: absolute; right: 5px; @@ -517,6 +532,7 @@ img:hover { color: var(--c-text-muted); } +/* Insert the actual line number */ .code > .lineno::before { content: counter(lineno); } @@ -533,62 +549,63 @@ footer { padding: 5px 25px 5px 25px; } -body .hll {} -body .c { color: var(--c-text); font-weight: 400 } /* Comment */ -body .n { color: var(--c-text); font-weight: 400 } /* Name */ -body .o { color: var(--c-text); font-weight: 400 } /* Operator */ -body .cm { color: var(--c-text); font-weight: 400 } /* Comment.Multiline */ -body .cp { color: var(--c-text); font-weight: 400 } /* Comment.Preproc */ -body .c1 { color: var(--c-text); font-weight: 400 } /* Comment.Single */ -body .cs { color: var(--c-text); font-weight: 400 } /* Comment.Special */ -body .nd { color: var(--c-text); font-weight: 400 } /* Name.Decorator */ -body .nn { color: var(--c-text); font-weight: 400 } /* Name.Namespace */ -body .vc { color: var(--c-text); font-weight: 400 } /* Name.Variable.Class */ -body .vg { color: var(--c-text); font-weight: 400 } /* Name.Variable.Global */ -body .vi { color: var(--c-text); font-weight: 400 } /* Name.Variable.Instance */ -body .err { color: var(--c-text-highlight); font-weight: 500 } /* Error */ -body .k { color: var(--c-text-highlight); font-weight: 500 } /* Keyword */ -body .l { color: var(--c-text-highlight); font-weight: 500 } /* Literal */ -body .kc { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Constant */ -body .kd { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Declaration */ -body .kn { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Namespace */ -body .kp { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Pseudo */ -body .kr { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Reserved */ -body .kt { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Type */ -body .na { color: var(--c-text-highlight); font-weight: 500 } /* Name.Attribute */ -body .nb { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin */ -body .nc { color: var(--c-text-highlight); font-weight: 500 } /* Name.Class */ -body .no { color: var(--c-text-highlight); font-weight: 500 } /* Name.Constant */ -body .ni { color: var(--c-text-highlight); font-weight: 500 } /* Name.Entity */ -body .ne { color: var(--c-text-highlight); font-weight: 500 } /* Name.Exception */ -body .nf { color: var(--c-text-highlight); font-weight: 500 } /* Name.Function */ -body .nl { color: var(--c-text-highlight); font-weight: 500 } /* Name.Label */ -body .nx { color: var(--c-text-highlight); font-weight: 500 } /* Name.Other */ -body .py { color: var(--c-text-highlight); font-weight: 500 } /* Name.Property */ -body .nt { color: var(--c-text-highlight); font-weight: 500 } /* Name.Tag */ -body .nv { color: var(--c-text-highlight); font-weight: 500 } /* Name.Variable */ -body .ow { color: var(--c-text-highlight); font-weight: 500 } /* Operator.Word */ -body .bp { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin.Pseudo */ -body .ld { color: var(--c-text); font-weight: 600 } /* Literal.Date */ -body .m { color: var(--c-text); font-weight: 600 } /* Literal.Number */ -body .s { color: var(--c-text); font-weight: 600 } /* Literal.String */ -body .mb { color: var(--c-text); font-weight: 600 } /* Literal.Number.Bin */ -body .mf { color: var(--c-text); font-weight: 600 } /* Literal.Number.Float */ -body .mh { color: var(--c-text); font-weight: 600 } /* Literal.Number.Hex */ -body .mi { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer */ -body .mo { color: var(--c-text); font-weight: 600 } /* Literal.Number.Oct */ -body .sb { color: var(--c-text); font-weight: 600 } /* Literal.String.Backtick */ -body .sc { color: var(--c-text); font-weight: 600 } /* Literal.String.Char */ -body .sd { color: var(--c-text); font-weight: 600 } /* Literal.String.Doc */ -body .s2 { color: var(--c-text); font-weight: 600 } /* Literal.String.Double */ -body .se { color: var(--c-text); font-weight: 600 } /* Literal.String.Escape */ -body .sh { color: var(--c-text); font-weight: 600 } /* Literal.String.Heredoc */ -body .si { color: var(--c-text); font-weight: 600 } /* Literal.String.Interpol */ -body .sx { color: var(--c-text); font-weight: 600 } /* Literal.String.Other */ -body .sr { color: var(--c-text); font-weight: 600 } /* Literal.String.Regex */ -body .s1 { color: var(--c-text); font-weight: 600 } /* Literal.String.Single */ -body .ss { color: var(--c-text); font-weight: 600 } /* Literal.String.Symbol */ -body .il { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer.Long */ +/* Token styling rules for syntax highlighting */ +.code .hll {} +.code .c { color: var(--c-text); font-weight: 400 } /* Comment */ +.code .n { color: var(--c-text); font-weight: 400 } /* Name */ +.code .o { color: var(--c-text); font-weight: 400 } /* Operator */ +.code .cm { color: var(--c-text); font-weight: 400 } /* Comment.Multiline */ +.code .cp { color: var(--c-text); font-weight: 400 } /* Comment.Preproc */ +.code .c1 { color: var(--c-text); font-weight: 400 } /* Comment.Single */ +.code .cs { color: var(--c-text); font-weight: 400 } /* Comment.Special */ +.code .nd { color: var(--c-text); font-weight: 400 } /* Name.Decorator */ +.code .nn { color: var(--c-text); font-weight: 400 } /* Name.Namespace */ +.code .vc { color: var(--c-text); font-weight: 400 } /* Name.Variable.Class */ +.code .vg { color: var(--c-text); font-weight: 400 } /* Name.Variable.Global */ +.code .vi { color: var(--c-text); font-weight: 400 } /* Name.Variable.Instance */ +.code .err { color: var(--c-text-highlight); font-weight: 500 } /* Error */ +.code .k { color: var(--c-text-highlight); font-weight: 500 } /* Keyword */ +.code .l { color: var(--c-text-highlight); font-weight: 500 } /* Literal */ +.code .kc { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Constant */ +.code .kd { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Declaration */ +.code .kn { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Namespace */ +.code .kp { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Pseudo */ +.code .kr { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Reserved */ +.code .kt { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Type */ +.code .na { color: var(--c-text-highlight); font-weight: 500 } /* Name.Attribute */ +.code .nb { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin */ +.code .nc { color: var(--c-text-highlight); font-weight: 500 } /* Name.Class */ +.code .no { color: var(--c-text-highlight); font-weight: 500 } /* Name.Constant */ +.code .ni { color: var(--c-text-highlight); font-weight: 500 } /* Name.Entity */ +.code .ne { color: var(--c-text-highlight); font-weight: 500 } /* Name.Exception */ +.code .nf { color: var(--c-text-highlight); font-weight: 500 } /* Name.Function */ +.code .nl { color: var(--c-text-highlight); font-weight: 500 } /* Name.Label */ +.code .nx { color: var(--c-text-highlight); font-weight: 500 } /* Name.Other */ +.code .py { color: var(--c-text-highlight); font-weight: 500 } /* Name.Property */ +.code .nt { color: var(--c-text-highlight); font-weight: 500 } /* Name.Tag */ +.code .nv { color: var(--c-text-highlight); font-weight: 500 } /* Name.Variable */ +.code .ow { color: var(--c-text-highlight); font-weight: 500 } /* Operator.Word */ +.code .bp { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin.Pseudo */ +.code .ld { color: var(--c-text); font-weight: 600 } /* Literal.Date */ +.code .m { color: var(--c-text); font-weight: 600 } /* Literal.Number */ +.code .s { color: var(--c-text); font-weight: 600 } /* Literal.String */ +.code .mb { color: var(--c-text); font-weight: 600 } /* Literal.Number.Bin */ +.code .mf { color: var(--c-text); font-weight: 600 } /* Literal.Number.Float */ +.code .mh { color: var(--c-text); font-weight: 600 } /* Literal.Number.Hex */ +.code .mi { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer */ +.code .mo { color: var(--c-text); font-weight: 600 } /* Literal.Number.Oct */ +.code .sb { color: var(--c-text); font-weight: 600 } /* Literal.String.Backtick */ +.code .sc { color: var(--c-text); font-weight: 600 } /* Literal.String.Char */ +.code .sd { color: var(--c-text); font-weight: 600 } /* Literal.String.Doc */ +.code .s2 { color: var(--c-text); font-weight: 600 } /* Literal.String.Double */ +.code .se { color: var(--c-text); font-weight: 600 } /* Literal.String.Escape */ +.code .sh { color: var(--c-text); font-weight: 600 } /* Literal.String.Heredoc */ +.code .si { color: var(--c-text); font-weight: 600 } /* Literal.String.Interpol */ +.code .sx { color: var(--c-text); font-weight: 600 } /* Literal.String.Other */ +.code .sr { color: var(--c-text); font-weight: 600 } /* Literal.String.Regex */ +.code .s1 { color: var(--c-text); font-weight: 600 } /* Literal.String.Single */ +.code .ss { color: var(--c-text); font-weight: 600 } /* Literal.String.Symbol */ +.code .il { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer.Long */ @media (max-width: 40em) { nav > div { |