Cistercian SVG

Thanks to a tweet from UCL Department of Mathematics, I am one of the many people who stumbled across Cistercian numerals and fell down the rabbit hole. To over-simplify, they are single glyphs that can each represent a number from 1 to 9,999 that were developed by Cistercian monks to save space and maybe show off how smart they were.

7 different glyphs representing what the caption describes.
Numbers written with Cistercian numerals. From left to right: 1 in units place, 2 in tens place (20), 3 in hundreds place (300), 4 in thousands place (4,000), then compound numbers 5,555, 6,789, 9,394. From Wikipedia.

Of course after that tweet people started playing around with them. Tiro Typeworks made a proof of concept OpenType font, Clairvo. Two people made configurators that generate an SVG (one of them and the other one). And I rolled around the idea of making one out of a pile of <div>s where each quadrant would contain the number as plain text so it would still be accessible.

As I looked at the two SVG examples I noticed that they were both made with React. In each case the developer was using React to listen for the number change and then dynamically draw the shapes into an SVG. Granted, one of them also had some nifty visualization along with a range slider to show you how the numbers mapped, but the other almost definitely did not need React.

I also felt neither of them needed to dynamically draw the lines for the SVG. You could include every shape and just leave them hidden until needed. That meant the script would only need to modify styles, not shapes. Which also meant I could pre-draw the entire SVG.

I fired up Illustrator, the only drawing tool where I have some basic skill after Aldus Freehand died, and quickly put together a shape. Then I fed that through SVGOMG to compress it, and then pulled that file down and manually tweaked it so the nodes were ordered in a way that made sense to me.

Adobe Illustrator with the layers palette open showing each named group and line, with the canvas clipped to the art. The SVGOMG interface in Firefox, with all optimizations combined showing the file size dropping from 603 bytes to 445 bytes.
An unnecessary view into my Illustrator and SVGOMG process.

Since my intent was to embed the SVG into the page, I added role="img" so it would report as a graphic to assistive technology, and then added a <title> with aria-labelledby pointing to it for its accessible name — analogous to putting an alt on an image. The <title> holds the real number so a screen reader user still gets the meaning of the glyph. The <title> also acts as a tool-tip in many browsers, whether or not I want it to.

I also added <desc> with aria-describedby to provide an accessible description. It won’t be announced in all cases, but it is still there in case a screen reader user goes looking. Finally I put focusable="false" on the SVG for the rare case an IE user might stumble across, preventing it from treating the SVG as a tab-stop.

For the color I opted against black. I went with currentColor so it adopts the text color of the page (or whatever the cascade decides). An advantage to using currentColor is when the page sets no color it will fall back to the browser default text, which is typically black. For those of us who set the background and text to something else (gray and green, in my case), the SVG pulls that. As a nice bonus, it also works well in Windows High Contrast Mode, taking the system text color. If you are new to WHCM, I have a bunch of detail in my post WHCM and System Colors

Like any good recipe site, all that exposition brings me to the example, which you can also visit in debug mode on Codepen. I go into the scripting and classes after the demos.

See the Pen Cistercian Numbers by Adrian Roselli (@aardrian) on CodePen.

After all that talk about making the glyph accessible, I figure it is worth showing you. I dropped into WHCM, fired up a screen reader, and made a recording to demonstrate how a user in either or both of those modes would experience it.

JAWS 2020 with Edge 88 while running Windows High Contrast Mode

If you looked at the code you might have seen the SVG has a bunch of selectors and classes. Because each line only gets a color when a certain digit is set, and because some digits cause more than one line to get a color, I could give each line a class based on whether it gets used for a digit or not.

For example, in the hundreds range the more central horizontal line appears when the digit is 2, 8, or 9 and the outside vertical line gets used in 6, 7, 8, and 9. So the first gets classes corresponding to those three possibilities, and second gets four classes:

     <g id="Hundreds">
      <path class="h1 h5 h7 h9" d="M19.8 57.8h18.7"></path>
      <path class="h2 h8 h9" d="M38.5 38.4H19.8"></path>
      <path class="h3" d="M19.8 57.8l18.7-19.4"></path>
      <path class="h4 h5" d="M19.8 38.4l18.7 19.4"></path>
      <path class="h6 h7 h8 h9" d="M38.5 57.8V38.4"></path>

If the user provides number 808, then the SVG gets a class corresponding to the 8 in the hundreds part of the number:

<svg xmlns="" viewBox="0 0 39.5 58.8" […] id="CistercianGlyph" class="z0000 z800 z00 z8">

The CSS block at the top of the SVG has the 36 selectors (9 for each of the 4 units), so one particular selector from the full set applies to the two <path> elements with the the h8 class:

.z1 .c1, .z2 .c2, .z3 .c3, .z4 .c4, .z5 .c5, .z6 .c6, .z7 .c7, .z8 .c8, .z9 .c9, .z10 .t1, .z20 .t2, .z30 .t3, .z40 .t4, .z50 .t5, .z60 .t6, .z70 .t7, .z80 .t8, .z90 .t9, .z100 .h1, .z200 .h2, .z300 .h3, .z400 .h4, .z500 .h5, .z600 .h6, .z700 .h7, .z800 .h8, .z900 .h9, .z1000 .d1, .z2000 .d2, .z3000 .d3, .z4000 .d4, .z5000 .d5, .z6000 .d6, .z7000 .d7, .z8000 .d8, .z9000 .d9 {
         stroke: currentColor;

You can take this SVG code, save it as a stand-alone SVG file, and manipulate its classes to be any number you want. It would be weird to have 9,999 SVG files lying around that are all exactly the same except for the classes on the <svg>, but I am not hear to judge. At least not that decision.

My script is brute force, as usual. I pad the number to four digits and then split it as a string. I pre-pend z and append 0s to represent if it is tens, hundreds, or thousands, and that becomes the value of the class I write to the SVG.

The other two functions in the pen are there to handle the countdown timer.

Compared to the React-powered SVG experiments, which deliver 40 to 60 kilobytes of script over multiple requests to dynamically generate new SVG shapes, this effort is 804 bytes of script to style a 2.1 kilobyte SVG, all over a single request and without requiring the browser to compile a full library.

Plus this one is accessible.

Update: 31 Segment Cistercian

No dependencies, no third-party scripts, no ghost in the machine.

See the Pen 31 Segment Cistercian by Adrian Roselli (@aardrian) on CodePen.

Added 10 March 2021. View the 31 Segment pen directly, or view the 31 Segment in debug mode.

Update: ‟Stats of My Machine”

The Laptop with its 1981 album, ‟Stats of My Machine”:

See the Pen Stats of My Machine by Adrian Roselli (@aardrian) on CodePen.

Added 12 March 2021. View the ‟Stats of My Machine” pen directly, or view the ‟Stats of My Machine” pen in debug mode.

I might have a problem.



So much fun and useful stuff in one post. Thank you for sharing!

Jean Ducrot; . Permalink

So much better than thoughtlessly throwing React inti a mix that didn’t need it. Thank you!

Tudorminator; . Permalink

Awesome post and learned so much. Thank you. Sadly, the only part that struck a real chord was, “I fired up Illustrator, the only drawing tool where I have some basic skill after Aldus Freehand died…” RIP FreeHand.

In response to Kel. Reply

Pouring out some Hexachrome ink for Freehand.


Muy bueno !

Daniel Gutierrez; . Permalink

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>