Add code listing CSS hack article
This commit is contained in:
parent
338de75fb4
commit
f5d03ed1cf
3 changed files with 286 additions and 57 deletions
217
content/blog/css-only-code-blocks/index.rst
Normal file
217
content/blog/css-only-code-blocks/index.rst
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
---
|
||||||
|
title: "Code listings with nice line wrapping and line numbers from plain CSS"
|
||||||
|
date: 2025-07-23T23:42:00+01:00
|
||||||
|
summary: >
|
||||||
|
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.
|
||||||
|
---
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
This blog is rendered as a static site using Hugo_ from a pile of RestructuredText_ documents. RestructuredText renders
|
||||||
|
code listings using Pygments_ by default. Pygments hard-bakes the line numbers into the generated HTML, so I am using a
|
||||||
|
`monkey-patched`_ hook that changes the line number rendering to just a bunch of empty ``<span>`` elements. The resulting
|
||||||
|
HTML for a code block then looks like this:
|
||||||
|
|
||||||
|
.. code:: html
|
||||||
|
|
||||||
|
<pre class="code [language] literal-block">
|
||||||
|
<span class="lineno"></span>
|
||||||
|
<span class="line">
|
||||||
|
<span class="[syntax highlight token]">The </span><span class="[other syntax highlight token]">code!<span>
|
||||||
|
</span>
|
||||||
|
<!-- ... repeat once for each source line. -->
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
You can find the (rather short) source of the ``rst2html`` wrapper `below <rst2html_wrapper>`_.
|
||||||
|
|
||||||
|
The CSS
|
||||||
|
-------
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. code:: css
|
||||||
|
|
||||||
|
/*****************************************************/
|
||||||
|
/* 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;
|
||||||
|
display: grid;
|
||||||
|
align-items: start;
|
||||||
|
grid-template-columns: min-content 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code > .line {
|
||||||
|
padding-left: calc(2em + 5px);
|
||||||
|
text-indent: -2em;
|
||||||
|
padding-top: 2px;
|
||||||
|
min-width: 15em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make individual syntax tokens wrap anywhere */
|
||||||
|
.code > .line > span {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We render line numbers in CSS! */
|
||||||
|
.code > .lineno {
|
||||||
|
counter-increment: lineno;
|
||||||
|
word-break: keep-all;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 5px;
|
||||||
|
overflow: clip;
|
||||||
|
position: relative;
|
||||||
|
text-align: right;
|
||||||
|
color: var(--c-text-muted);
|
||||||
|
border-right: 1px solid var(--c-fg-highlight);
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We also handle line continuation markers in CSS. */
|
||||||
|
.code > .lineno::after {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
content: "\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳";
|
||||||
|
white-space: pre;
|
||||||
|
color: var(--c-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert the actual line number */
|
||||||
|
.code > .lineno::before {
|
||||||
|
content: counter(lineno);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code::before {
|
||||||
|
counter-reset: lineno;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 30px;
|
||||||
|
align-self: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px 25px 5px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code .hll {}
|
||||||
|
/* Following are about 50 lines that define the styling of each kind of pygments syntax highlight token. These lines
|
||||||
|
all look like the following: */
|
||||||
|
.code .c { color: var(--c-text); font-weight: 400 } /* Comment */
|
||||||
|
|
||||||
|
This CSS does a few things:
|
||||||
|
|
||||||
|
1. It renders the ``<pre>`` code listing element using a two-column CSS ``display: grid`` layout. The left column is
|
||||||
|
used for the line numbers, and the right column is used for the code lines.
|
||||||
|
2. It numbers the lines using a `CSS Counter`_. CSS counters are meant for things like numbering headings and such, but
|
||||||
|
they are a perfect fit for our purpose.
|
||||||
|
3. It inserts the counter value as the line number into the ``<span class="lineno">`` element's ``::before``
|
||||||
|
pseudo-element. A side effect of using the ``::before`` 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.
|
||||||
|
4. It inserts a string of ``"\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳"`` into the line number span's
|
||||||
|
``::after`` pseudo-element. This string evaluates to a sequence of unicode arrows separated by line breaks, and
|
||||||
|
starting with an empty line. The ``::after`` pseudo-element is positioned using ``position: absolute``, and the
|
||||||
|
parent ``<span class="lineno">`` has ``position: relative`` set. This way, the arrow pseudo-element gets placed on
|
||||||
|
top of the lineno span without affecting the layout at all. By setting ``overflow: clip`` on the parent ``<span
|
||||||
|
class="lineno">``, the arrow pseudo-element gets cut off vertically wherever the parent lineno element naturally
|
||||||
|
ends.
|
||||||
|
|
||||||
|
The line number span is inserted into the parent ``<pre>`` element's CSS grid using ``align-self: stretch``, 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 ``overflow: clip`` of the line number span, and one arrow gets rendered for each
|
||||||
|
wrapped listing line.
|
||||||
|
|
||||||
|
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 ``min-width`` on the ``<span class="line">`` in the right column, and set ``overflow-x:
|
||||||
|
auto`` on the listing ``<pre>``. This results in a horizontal scroll bar appearing whenever the listing gets too narrow.
|
||||||
|
|
||||||
|
You can try out the line wrapping by resizing this page!
|
||||||
|
|
||||||
|
rst2html wrapper
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Here is the python ``rst2html`` wrapper that monkey-patches code rendering. I made hugo invoke this while building the
|
||||||
|
page by simply overriding the ``PATH`` environment variable.
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Based on https://gist.github.com/mastbaum/2655700 for the basic plugin scaffolding
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
import docutils.core
|
||||||
|
from docutils.transforms import Transform
|
||||||
|
from docutils.nodes import TextElement, Inline, Text
|
||||||
|
from docutils.parsers.rst import Directive, directives
|
||||||
|
from docutils.writers.html4css1 import Writer, HTMLTranslator
|
||||||
|
|
||||||
|
|
||||||
|
class UnfuckedHTMLTranslator(HTMLTranslator):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.in_literal_block = False
|
||||||
|
|
||||||
|
def visit_literal_block(self, node):
|
||||||
|
# Insert an empty "lineno" span before each line. We insert the line numbers using pure CSS in a ::before
|
||||||
|
# pseudo-element. This has the added advantage that the line numbers don't get included in text selection.
|
||||||
|
# These line number spans are also used to show line continuation markers when a line is wrapped.
|
||||||
|
self.in_literal_block = True
|
||||||
|
self.body.append(self.starttag(node, 'pre', CLASS='literal-block'))
|
||||||
|
self.body.append('<span class="lineno"></span><span class="line">')
|
||||||
|
|
||||||
|
def depart_literal_block(self, node):
|
||||||
|
self.in_literal_block = False
|
||||||
|
self.body.append('\n</span></pre>\n')
|
||||||
|
|
||||||
|
def visit_Text(self, node):
|
||||||
|
if self.in_literal_block:
|
||||||
|
for match in re.finditer('([^\n]*)(\n|$)', node.astext()):
|
||||||
|
text, end = match.groups()
|
||||||
|
|
||||||
|
if text:
|
||||||
|
super().visit_Text(Text(text))
|
||||||
|
|
||||||
|
if end == '\n':
|
||||||
|
if isinstance(node.parent, Inline):
|
||||||
|
self.depart_inline(node.parent)
|
||||||
|
self.body.append(f'</span>\n<span class="lineno"></span><span class="line">')
|
||||||
|
if isinstance(node.parent, Inline):
|
||||||
|
self.visit_inline(node.parent)
|
||||||
|
|
||||||
|
else:
|
||||||
|
super().visit_Text(node)
|
||||||
|
|
||||||
|
|
||||||
|
html_writer = Writer()
|
||||||
|
html_writer.translator_class = UnfuckedHTMLTranslator
|
||||||
|
docutils.core.publish_cmdline(writer=html_writer)
|
||||||
|
|
||||||
|
.. _Hugo: https://gohugo.io/
|
||||||
|
.. _RestructuredText: https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html
|
||||||
|
.. _Pygments: https://pygments.org/
|
||||||
|
.. _`monkey-patched`: https://en.wikipedia.org/wiki/Monkey_patch
|
||||||
|
.. _`CSS Counter`: https://developer.mozilla.org/en-US/docs/Web/CSS/counter
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# https://gist.github.com/mastbaum/2655700
|
# Based on https://gist.github.com/mastbaum/2655700 for the basic plugin scaffolding
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
|
@ -17,6 +17,9 @@ class UnfuckedHTMLTranslator(HTMLTranslator):
|
||||||
self.in_literal_block = False
|
self.in_literal_block = False
|
||||||
|
|
||||||
def visit_literal_block(self, node):
|
def visit_literal_block(self, node):
|
||||||
|
# Insert an empty "lineno" span before each line. We insert the line numbers using pure CSS in a ::before
|
||||||
|
# pseudo-element. This has the added advantage that the line numbers don't get included in text selection.
|
||||||
|
# These line number spans are also used to show line continuation markers when a line is wrapped.
|
||||||
self.in_literal_block = True
|
self.in_literal_block = True
|
||||||
self.body.append(self.starttag(node, 'pre', CLASS='literal-block'))
|
self.body.append(self.starttag(node, 'pre', CLASS='literal-block'))
|
||||||
self.body.append('<span class="lineno"></span><span class="line">')
|
self.body.append('<span class="lineno"></span><span class="line">')
|
||||||
|
|
|
||||||
|
|
@ -471,9 +471,14 @@ img:hover {
|
||||||
filter: none;
|
filter: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************/
|
||||||
|
/* Code block formatting / syntax highlighting rules */
|
||||||
|
/*****************************************************/
|
||||||
|
|
||||||
.code {
|
.code {
|
||||||
font-family: "Fira Code";
|
font-family: "Fira Code";
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
text-align: left; /* Override default content "justify" alignment */
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|
@ -495,6 +500,7 @@ img:hover {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We render line numbers in CSS! */
|
||||||
.code > .lineno {
|
.code > .lineno {
|
||||||
counter-increment: lineno;
|
counter-increment: lineno;
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
|
|
@ -509,6 +515,7 @@ img:hover {
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We also handle line continuation markers in CSS. */
|
||||||
.code > .lineno::after {
|
.code > .lineno::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
|
|
@ -517,6 +524,7 @@ img:hover {
|
||||||
color: var(--c-text-muted);
|
color: var(--c-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Insert the actual line number */
|
||||||
.code > .lineno::before {
|
.code > .lineno::before {
|
||||||
content: counter(lineno);
|
content: counter(lineno);
|
||||||
}
|
}
|
||||||
|
|
@ -533,62 +541,63 @@ footer {
|
||||||
padding: 5px 25px 5px 25px;
|
padding: 5px 25px 5px 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body .hll {}
|
/* Token styling rules for syntax highlighting */
|
||||||
body .c { color: var(--c-text); font-weight: 400 } /* Comment */
|
.code .hll {}
|
||||||
body .n { color: var(--c-text); font-weight: 400 } /* Name */
|
.code .c { color: var(--c-text); font-weight: 400 } /* Comment */
|
||||||
body .o { color: var(--c-text); font-weight: 400 } /* Operator */
|
.code .n { color: var(--c-text); font-weight: 400 } /* Name */
|
||||||
body .cm { color: var(--c-text); font-weight: 400 } /* Comment.Multiline */
|
.code .o { color: var(--c-text); font-weight: 400 } /* Operator */
|
||||||
body .cp { color: var(--c-text); font-weight: 400 } /* Comment.Preproc */
|
.code .cm { color: var(--c-text); font-weight: 400 } /* Comment.Multiline */
|
||||||
body .c1 { color: var(--c-text); font-weight: 400 } /* Comment.Single */
|
.code .cp { color: var(--c-text); font-weight: 400 } /* Comment.Preproc */
|
||||||
body .cs { color: var(--c-text); font-weight: 400 } /* Comment.Special */
|
.code .c1 { color: var(--c-text); font-weight: 400 } /* Comment.Single */
|
||||||
body .nd { color: var(--c-text); font-weight: 400 } /* Name.Decorator */
|
.code .cs { color: var(--c-text); font-weight: 400 } /* Comment.Special */
|
||||||
body .nn { color: var(--c-text); font-weight: 400 } /* Name.Namespace */
|
.code .nd { color: var(--c-text); font-weight: 400 } /* Name.Decorator */
|
||||||
body .vc { color: var(--c-text); font-weight: 400 } /* Name.Variable.Class */
|
.code .nn { color: var(--c-text); font-weight: 400 } /* Name.Namespace */
|
||||||
body .vg { color: var(--c-text); font-weight: 400 } /* Name.Variable.Global */
|
.code .vc { color: var(--c-text); font-weight: 400 } /* Name.Variable.Class */
|
||||||
body .vi { color: var(--c-text); font-weight: 400 } /* Name.Variable.Instance */
|
.code .vg { color: var(--c-text); font-weight: 400 } /* Name.Variable.Global */
|
||||||
body .err { color: var(--c-text-highlight); font-weight: 500 } /* Error */
|
.code .vi { color: var(--c-text); font-weight: 400 } /* Name.Variable.Instance */
|
||||||
body .k { color: var(--c-text-highlight); font-weight: 500 } /* Keyword */
|
.code .err { color: var(--c-text-highlight); font-weight: 500 } /* Error */
|
||||||
body .l { color: var(--c-text-highlight); font-weight: 500 } /* Literal */
|
.code .k { color: var(--c-text-highlight); font-weight: 500 } /* Keyword */
|
||||||
body .kc { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Constant */
|
.code .l { color: var(--c-text-highlight); font-weight: 500 } /* Literal */
|
||||||
body .kd { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Declaration */
|
.code .kc { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Constant */
|
||||||
body .kn { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Namespace */
|
.code .kd { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Declaration */
|
||||||
body .kp { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Pseudo */
|
.code .kn { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Namespace */
|
||||||
body .kr { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Reserved */
|
.code .kp { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Pseudo */
|
||||||
body .kt { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Type */
|
.code .kr { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Reserved */
|
||||||
body .na { color: var(--c-text-highlight); font-weight: 500 } /* Name.Attribute */
|
.code .kt { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Type */
|
||||||
body .nb { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin */
|
.code .na { color: var(--c-text-highlight); font-weight: 500 } /* Name.Attribute */
|
||||||
body .nc { color: var(--c-text-highlight); font-weight: 500 } /* Name.Class */
|
.code .nb { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin */
|
||||||
body .no { color: var(--c-text-highlight); font-weight: 500 } /* Name.Constant */
|
.code .nc { color: var(--c-text-highlight); font-weight: 500 } /* Name.Class */
|
||||||
body .ni { color: var(--c-text-highlight); font-weight: 500 } /* Name.Entity */
|
.code .no { color: var(--c-text-highlight); font-weight: 500 } /* Name.Constant */
|
||||||
body .ne { color: var(--c-text-highlight); font-weight: 500 } /* Name.Exception */
|
.code .ni { color: var(--c-text-highlight); font-weight: 500 } /* Name.Entity */
|
||||||
body .nf { color: var(--c-text-highlight); font-weight: 500 } /* Name.Function */
|
.code .ne { color: var(--c-text-highlight); font-weight: 500 } /* Name.Exception */
|
||||||
body .nl { color: var(--c-text-highlight); font-weight: 500 } /* Name.Label */
|
.code .nf { color: var(--c-text-highlight); font-weight: 500 } /* Name.Function */
|
||||||
body .nx { color: var(--c-text-highlight); font-weight: 500 } /* Name.Other */
|
.code .nl { color: var(--c-text-highlight); font-weight: 500 } /* Name.Label */
|
||||||
body .py { color: var(--c-text-highlight); font-weight: 500 } /* Name.Property */
|
.code .nx { color: var(--c-text-highlight); font-weight: 500 } /* Name.Other */
|
||||||
body .nt { color: var(--c-text-highlight); font-weight: 500 } /* Name.Tag */
|
.code .py { color: var(--c-text-highlight); font-weight: 500 } /* Name.Property */
|
||||||
body .nv { color: var(--c-text-highlight); font-weight: 500 } /* Name.Variable */
|
.code .nt { color: var(--c-text-highlight); font-weight: 500 } /* Name.Tag */
|
||||||
body .ow { color: var(--c-text-highlight); font-weight: 500 } /* Operator.Word */
|
.code .nv { color: var(--c-text-highlight); font-weight: 500 } /* Name.Variable */
|
||||||
body .bp { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin.Pseudo */
|
.code .ow { color: var(--c-text-highlight); font-weight: 500 } /* Operator.Word */
|
||||||
body .ld { color: var(--c-text); font-weight: 600 } /* Literal.Date */
|
.code .bp { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin.Pseudo */
|
||||||
body .m { color: var(--c-text); font-weight: 600 } /* Literal.Number */
|
.code .ld { color: var(--c-text); font-weight: 600 } /* Literal.Date */
|
||||||
body .s { color: var(--c-text); font-weight: 600 } /* Literal.String */
|
.code .m { color: var(--c-text); font-weight: 600 } /* Literal.Number */
|
||||||
body .mb { color: var(--c-text); font-weight: 600 } /* Literal.Number.Bin */
|
.code .s { color: var(--c-text); font-weight: 600 } /* Literal.String */
|
||||||
body .mf { color: var(--c-text); font-weight: 600 } /* Literal.Number.Float */
|
.code .mb { color: var(--c-text); font-weight: 600 } /* Literal.Number.Bin */
|
||||||
body .mh { color: var(--c-text); font-weight: 600 } /* Literal.Number.Hex */
|
.code .mf { color: var(--c-text); font-weight: 600 } /* Literal.Number.Float */
|
||||||
body .mi { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer */
|
.code .mh { color: var(--c-text); font-weight: 600 } /* Literal.Number.Hex */
|
||||||
body .mo { color: var(--c-text); font-weight: 600 } /* Literal.Number.Oct */
|
.code .mi { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer */
|
||||||
body .sb { color: var(--c-text); font-weight: 600 } /* Literal.String.Backtick */
|
.code .mo { color: var(--c-text); font-weight: 600 } /* Literal.Number.Oct */
|
||||||
body .sc { color: var(--c-text); font-weight: 600 } /* Literal.String.Char */
|
.code .sb { color: var(--c-text); font-weight: 600 } /* Literal.String.Backtick */
|
||||||
body .sd { color: var(--c-text); font-weight: 600 } /* Literal.String.Doc */
|
.code .sc { color: var(--c-text); font-weight: 600 } /* Literal.String.Char */
|
||||||
body .s2 { color: var(--c-text); font-weight: 600 } /* Literal.String.Double */
|
.code .sd { color: var(--c-text); font-weight: 600 } /* Literal.String.Doc */
|
||||||
body .se { color: var(--c-text); font-weight: 600 } /* Literal.String.Escape */
|
.code .s2 { color: var(--c-text); font-weight: 600 } /* Literal.String.Double */
|
||||||
body .sh { color: var(--c-text); font-weight: 600 } /* Literal.String.Heredoc */
|
.code .se { color: var(--c-text); font-weight: 600 } /* Literal.String.Escape */
|
||||||
body .si { color: var(--c-text); font-weight: 600 } /* Literal.String.Interpol */
|
.code .sh { color: var(--c-text); font-weight: 600 } /* Literal.String.Heredoc */
|
||||||
body .sx { color: var(--c-text); font-weight: 600 } /* Literal.String.Other */
|
.code .si { color: var(--c-text); font-weight: 600 } /* Literal.String.Interpol */
|
||||||
body .sr { color: var(--c-text); font-weight: 600 } /* Literal.String.Regex */
|
.code .sx { color: var(--c-text); font-weight: 600 } /* Literal.String.Other */
|
||||||
body .s1 { color: var(--c-text); font-weight: 600 } /* Literal.String.Single */
|
.code .sr { color: var(--c-text); font-weight: 600 } /* Literal.String.Regex */
|
||||||
body .ss { color: var(--c-text); font-weight: 600 } /* Literal.String.Symbol */
|
.code .s1 { color: var(--c-text); font-weight: 600 } /* Literal.String.Single */
|
||||||
body .il { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer.Long */
|
.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) {
|
@media (max-width: 40em) {
|
||||||
nav > div {
|
nav > div {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue