3

I have read CSS - Make sans-serif font imitate monospace font but the CSS rule letter-spacing doesn't seem to be enough:

enter image description here

How to imitate a monospace fixed font from a standard sans-serif font?

This doesn't work perfectly:

.text {
  font-family: sans-serif;
  letter-spacing: 10px;
}
<div class="text">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789
</div>
Basj
  • 41,386
  • 99
  • 383
  • 673

2 Answers2

2

letter-spacing just evenly inserts whitespace between all letters (...hence the name).
It won't normalize characters/glyphs to have the same widths.
We would need a css property like letter-width which doesn't exist.

Apart from changing the actual font metrics in a font editor and compiling a new font you could split up all letters into an array of <span> elements via javaScript.

emulateMonospace();

function emulateMonospace() {
  let monoWraps = document.querySelectorAll(".toMonospace");

  monoWraps.forEach(function(monoWrap, i) {
    //remove all "\n" linebreaks and replace br tags with "\n"
    monoWrap.innerHTML = monoWrap.innerHTML
      .replaceAll("\n", "")
      .replaceAll("<br>", "\n");
    let text = monoWrap.textContent;
    let letters = text.split("");


    //get font-size
    let style = window.getComputedStyle(monoWrap);
    let fontSize = parseFloat(style.fontSize);

    //find maximum letter width
    let widths = [];
    monoWrap.textContent = "";
    letters.forEach(function(letter) {
      let span = document.createElement("span");
      if (letter == "\n") {
        span = document.createElement("br");
      }
      if (letter == ' ') {
        span.innerHTML = '&nbsp;';
      } else {
        span.textContent = letter;
      }
      monoWrap.appendChild(span);
      let width = parseFloat(span.getBoundingClientRect().width);
      widths.push(width);
      span.classList.add("spanMono");
      span.classList.add("spanMono" + i);
    });

    monoWrap.classList.replace("variableWidth", "monoSpace");

    //get exact max width
    let maxWidth = Math.max(...widths);
    let maxEm = maxWidth / fontSize;
    let newStyle = document.createElement("style");
    document.head.appendChild(newStyle);
    newStyle.sheet.insertRule(`.spanMono${i} { width: ${maxEm}em }`, 0);
  });
}
body{
  font-family: sans-serif;
  font-size: 10vw;
  line-height: 1.2em;
  transition: 0.3s;
}

.monospaced{
    font-family: monospace;
}

.letterspacing{
  letter-spacing:0.3em;
}

.teko {
  font-family: "Teko", sans-serif;
}

.serif{
    font-family: "Georgia", serif;
}

.variableWidth {
  opacity: 0;
}

.monoSpace {
  opacity: 1;
}

.spanMono {
  display: inline-block;
  outline: 1px dotted #ccc;
  text-align: center;
  line-height:1em;
}
<link href="https://fonts.googleapis.com/css2?family=Teko:wght@300&display=swap" rel="stylesheet">

<h3 style="font-size:0.5em;">Proper monospace font</h3>
<div class="monospaced">
WiWi</br>
iWiW
</div>

<h3 style="font-size:0.5em;">Letterspacing can't emulate monospaced fonts!</h3>
<div class="letterspacing">
WiWi</br>
iWiW
</div>


<hr>

<h3 style="font-size:0.5em;">Text splitted up in spans</h3>

<div class="toMonospace variableWidth">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789<br>
</div>

<div class="toMonospace variableWidth teko">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789<br>
</div>

<div class="toMonospace variableWidth serif">
This is<br>
not a<br>
Monospace<br>
font!!!
</div>

Each character will be wrapped in a span with an inline-block display property.

Besides all characters are centered via text-align:center.

The above script will also compare the widths of all characters to set the largest width as the span width.

Admittedly, this is not very handy
but this approach might suffice for design/layout purposes and won't change the actual font files.

As illustrated in the snippet:
In monospace fonts the widest letters like "W" get squeezed (not distorted) whereas the thinner ones like "i" get visually stretched (e.g by adding bottom serifs).

So proper monospace fonts are completely different and can't really be emulated.

herrstrietzel
  • 11,541
  • 2
  • 12
  • 34
1

I've spent too much time trying to find a good monospaced font that works with several alphabets and had the look I wanted. These are the solutions I found (in order of recommendation):

  1. Find a monospaced font that you like and use that.
  2. Use a font editor and change all the letters to the same width (there might be a Python program that can do that, but I haven't tried it). But changing a font to monospaced will not look as good, there are a lots of craftsmanship in creating each letter so it will fit properly in the monospaced box.
  3. Use letter-spacing to simulate a simple monospaced font.
emilsteen
  • 496
  • 4
  • 14
  • Thanks. I'm looking for 3. How do you do that? See screenshot in my question: letter-spacing alone doesn't work correctly: letters are not aligned. – Basj Sep 20 '22 at 13:26
  • 1
    @Basj That is how good it gets with `letter-spacing`, that is why it is the least recommended. – emilsteen Sep 20 '22 at 14:03