This document can be viewed properly only in modern browsers with good support for CSS2 and the full HTML character set.
Math in HTML with CSS
Experiments in simple mathematical formatting
using common web technologies
updated Jan 2025
The state of the art
Mathematical formatting is hard.
There hasn’t been very good support for math on the Web. Some of the approaches to it that have been developed are:
-
ASCII-type formatting inside
HTML
<pre>
tags. - Pictures of equations and in-line mathematical entities
- Documents for download in LATEX format, or Adobe PostScript or PDF
- Browser plug-in that renders LATEX (obsolete)
- MathML
The official, ultimate solution is MathML. As of this writing, it should be a mature product, but it has suffered setbacks, and isn't universally supported. MathML is not a lightweight solution, though. It is easy to generate automatically, but hard to type by hand (as opposed to LATEX). For a single formula in a web page, the difficulties of using MathML might not seem worth the effort of learning it. (Of course, there are programs that automate the generation of MathML code.)
This is an investigation of solutions for formatting math in standard HTML, enhanced by the widely-supported formatting language CSS. I have striven for
- Beauty in most modern browsers
- Readability in older browsers
- Readability in text browsers
- Separation of presentation and content
In contrast to the graphics approaches, this has the added benefits that the text
- can be searched for keywords
- can be copied and pasted meaningfully
- looks good when scaled (especially important to those with poor eyesight)
A practical principle of contemporary web design is “separate presentation from content”. It means to put the information in one place, and describe how to present it in another. This is opposed to, for example, explicitly setting the font of each word of text explicitly in the HTML code.
The most common means of describing presentation is the style language CSS. With it, you can specify the presentation of a certain kind of HTML entity in a separate document, or at the top of the page of HTML code.
For our purposes, this rendition of “separate presentation from content” is useful: Use standard characters and HTML to produce content, use CSS to beautify it.
Math symbols
It is convenient to define a CSS class selector .math
. With
this, any text contained in an element of class math
can be
specially formatted. For single-letter symbols, it’s convenient to use
the selector i.mi
, which will cause the symbol to be rendered
in italics in older, non-CSS browsers, but leave the HTML tag
<i>
free to indicate generic italics.
For example, x, y, z are single math symbols in text, and
is a math formula set off for display.
Decorated characters
Using CSS positioning, decorated characters, such as
can be constructed, but won’t look good in most browsers. They are also font-dependent. I recommend avoiding them.
Most of these marks have Unicode composing character equivalents.
If you must have fancy characters such as h-bar, you might consider using the Unicode character ħ for it.
Previously, this web page has preferred using ISO-8859-15 characters and the HTML 4 standard character entities. Since 2010 however, all major web browsers on desktop computers support the Unicode encoding standard (and all browsers on smartphones do).
SGML character entities
One alternative to using Unicode is
SGML
character entities. These are names for characters in HTML code.
They are always placed
between an ampersand (&
) and a semicolon (;
).
One conceivable reason for using these nowadays would be that you somehow are pressed for time to look up the Unicode character. Or somehow you are working in some archaic environment that doesn’t support Unicode.
The HTML 4 standard includes most of the Greek alphabet:
α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ σ ς τ υ φ χ ψ ω Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω ϑ ϒ ϖ
a fairly rich selection of math symbols:
∀ ∴ ∃ ¬ ∋ ∅ ∈ ∉ ∧ ∨ ∩ ∪ ∂ ∇ ∏ ∑ ∫ √ − ∗ × ÷ ⋅ ± ⊕ ⊗ ∝ ∞ ∼ ≅ ≈ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊄ ⊆ ⊇ ∠ ⊥
arrows:
← ↑ → ↓ ↔ ↵ ⇐ ⇒ ⇓ ⇔
fancier brackets:
⌈ ⌉ ⌊ ⌋ 〈 〉
a few fancy script letters:
ℵ ℘ ℑ ℜ µ
and these:
◊ ♠ ♣ ♥ ♦
Some old browsers will display a textual version of the entity if they can’t display a glyph for it, so that the text is still readable.
Unicode
The Unicode encoding specifies hundreds of math and technical symbols, The only question is, do fonts on the viewer’s system contain graphical representations for these characters? For example, there is the beautiful Unicode character range 8704–8959.
It is crucial to specify the character encoding at the beginning of the HTML file. The exact syntax for doing this has changed over time, and depends on the specific version of HTML (or XHTML) that you are using.
In HTML 4 and previous, the most portable approach was to place this tag:
in the <head>
block. In the more modern standards
XHTML and HTML 5, Unicode is the default encoding. But in XHTML 1.1 the
encoding was specified by the first line of the file, introducing it as
an XML document:
Perhaps the most attractive option is the HTML 5 standard, in which the
encoding is specified by this line directly after the <head>
tag:
There are many means of putting Unicode characters directly into a text document. This way, you can see a representation of the letter directly in the text.
It is also possible to specify the character you want using a hexadecimal
numerical character reference. For example, the Unicode numerical
character reference ∬
is a nice double integral sign.
There is also a h-bar defined in Unicode (u+0127).
CSS Positioning
HTML tables can be used to do much of the positioning required by math, but the code required to do this can be very cumbersome, so that the bulk of the code is about arranging the formula as a table, rather than about the formula and its parts. (Moreover, some browsers don’t even support tables).
In CSS2 came a technology that we had hoped would allow us to do very flexible positioning with simple HTML. It doesn’t always work that way. CSS positioning was very quirky, and seems to be simply missing features that one would want.
Finally, the CSS3 standard provides a set of positioning features, well enough described to display mathematics.
The main CSS1 positioning properties are
vertical-align
text-align
CSS2 adds
position
top bottom left right
It probably isn’t a good idea to mix the CSS1 properties with the position property within the same element.
The main trick with CSS2 positioning is this: absolute positioning is
relative to its innermost positioned ancestor element (or the body element,
if no nearer one exists). So one normally creates an element with
position: relative
, then within that element, other elements
with position: absolute
. These inner elements can be placed
rather precisely, in principle.
A weakness in this scheme is that, although one can position precisely, there is no well-defined way to center positioned elements relative to one another, unless their sizes are known beforehand. So for example, one can use it to place an element above and below another element, but then there is no way to say to center the elements horizontally. One might think that, by simply not specifying the horizontal position of an absolutely-positioned elements, the positioning would somehow be governed by the CSS1 text-align property. Well, some browsers implement this, and others don’t.
Fractions
The absence of standard fractions in HTML is a tragedy. Fractions are used
by so many people, from auto mechanics to cooks, but there’s no nice way to
write “A over B” in HTML. Even more
tragic: there was a frac
tag for this purpose in the HTML 3.0
standard, but it was dropped in HTML 3.2.
Fractions written with the horizontal divider are in any medium difficult to fit in-line with text, so are usually displayed separately. In the examples below, however, I both display them separately and put some text around them to indicate the position of the baseline, to see if the technique used could be applied for in-line expressions.
The most portable fraction is made using a table with three rows, the middle one being the dividing line made of hyphens:
Aa0qp |
--------- |
b2 |
This is readable even in text browsers, so long as they support tables. But sometimes the hyphens don’t connect, and you get a dotted line, and also, it’s hard to guess how many hyphens to use (hyhens having slightly different lengths in different fonts).
Here is a fraction that uses CSS
inline-table
for browsers that have it, and
inline
for browsers that don’t
Aa0qp |
b2 |
This hybrid uses just div
and span
with line breaks,
with a divider element that contains dashes for non-CSS browsers, but
uses border
instead for CSS browsers.
_______
b2
While this approach is attractive in that it doesn’t use tables, the absolute positioning unfortunately rules out automatic allocation of space for the fraction’s contents: to leave proper vertical space, it must be tailored to fit the contents.
This is all fine for separate display of fractions, but to display them inline properly is much harder. None will work without revision.
The coolest solution would use CSS positioning to make a fraction. There are several difficulties. The first is: if all the elements are absolutely positioned, they lose their natural widths. Optionos include: specify the width of the containing element, or, pick the widest of the numerator and denominator, and make that one non-positioned, or possibly, include a string of non-breaking spaces to force the width. A related problem is the horizontal centering of the items. But all of these measures are messy, artificial, and concern more a weakness in CSS than the typesetting problem.
If the numerator and denominator are block elements (or are separated by line breaks), there are HTML ways to center them horizontally, but then putting them in-line becomes a problem. If they aren’t block elements, they can be positioned vertically with CSS, but I don’t know how to arrange for horizontal centering.
In an example below, the numerator is statically positioned to give the fraction a width. It’s picky (depends on whether the numerator or denominator is the longer) but it seems the least intrusive of the methods.
This example uses the inline-block
value (now supported by
most browsers) for the display
property of the outermost element.
The code is very clean, and centers everything nicely.
Superscripts and subscripts
HTML has always had built-in support for superscripts and subscripts, so most browsers support them in some fashion.
This is a place where a little CSS can make all the difference, aesthetically. The trick is the right balance of font size and vertical offset. For legibility on a computer screen, the minimum font size is about 7 points (7 points). In printed math texts, much smaller fonts are sometimes used. On the other hand, most browsers make it easy for the user to increase the font size…maybe this is a non-issue.
To put the superscript and subscript directly over one another, there are
a couple of approaches. You can try CSS positioning,
but there are various technical questions. A position
of
absolute
allows you to place an element where you like
relative to the containing (relatively-positioned) element, but leaves
no space on the line for the contents. A position
of
relative
leaves space on the line where the element
was. So if you know which of the sub- or superscript is longer
horizontally, you can position that one relatively (to leave space for
it), and position the other absolutely:
Another approach is to use tables. Tables may sometimes be necessary in more complex expressions.
Integrals and Sums
A very nice integral can be assembled using the SGML entity
∫
and some CSS.
There are three popular typesetting forms for integrals and sums, differing in the placement of limits relative to the integral or sum symbol. In display form, limits are centered over and under the symbol. In inline form, limits are placed as super- and subscripts to the symbol. In compact form, limits are placed to the right of the symbol, and given enough vertical spacing so that they may flow around an integrand.
Here is a compact form using sup
and sub
, with
CSS to permit the flowing behavior:
This displays well in text browsers, so long as they have a way of
displaying <sup>
and <sub>
.
Here, tables align an integral to achieve traditional typographic “compact” form, wherein the upper and lower limits flow around an integrand.
∫ | 1 | |
f( x ) dx | ||
0 |
Tables can be used to align everything. A text baseline can be maintained within a line of cells. Indeed, for very complex formulas displayed out of text, this may be the best solution. However, the problem of aligning that baseline with the text baseline surrounding the table seems unsurmountable, so this technique is usually unusable for inline math. In displayed math, too: once you start aligning with a table baseline, everything has to go into the table. Furthermore, to typeset a complex formula using tables can require very cumbersome HTML. Then again, very complicated formulas aligned by tables can be rendered impressively well in a text browser.
|
xn |
The pure CSS solution is to separately vertically align the summation symbol and limits: the main problem here is the horizontal centering of the limits on the symbol. I know of no resolution. (Is it possible with CSS to achieve horizontal centering of inline items of indeterminate width without making use of a block item?)
A hybrid approach is perhaps best: use a table to align the limits to the
symbol horizontally, then use CSS to adjust the position of the symbol
relative to the baseline (which is less critical than keeping the baseline
straight). The display
property of the table
is
then set to inline
so that it doesn’t break the line it’s in.
(This works for browsers that handle this CSS property correctly; that
includes most modern browsers.)
(Why a table again, rather than a div? Because I couldn’t control the width of the div. Why not?)
∞ ∑ n=0 |
Here’s an element using the CSS 2.1 inline-block
value of
display
.
This way, a positioned expression can be placed in a line, without forcing
a line break in browsers that don’t know CSS.
Complex expressions can be formed by simply concatenating such constructions, but then the integrands won’t share the same baseline.
∑ i |
Wide accents
Modern SVG vector graphics can perform elaborate decoration of characters, when it is available. It is especially nice for stretching a glyph, as is done here with the hat ‘ˆ’ character:
abc |
Here is a bottom-bracket, using the unicode character "bottom curly bracket"
(&23DF;
):
abc |
There are several good candidates for wide accents. Besides the ASCII caron ^ and tilde ~ and the iso-8859-1 macron ¯ there are ⎴ ⎵ ⏜ ⏝ ⏞ ⏟ ⏠ ⏡
The main down-side is that, for mathematics, positioning and shaping is very font-specific. This technique can be used consistently only in pages that load a specialized font.
Matrices
HTML tables are ideal for placing matrix elements, but the traditional brackets around matrices are a problem.
It’s not beautiful, but a fair work-around uses the vertical bar character, if the distinction between the usual enclosing square brackets and simple horizontal lines is unimportant. This matrix is readable in any browser that supports tables:
| | a | b | | |
| | | | ||
| | −b | a | | |
Unicode defines some extensible character pieces, provided fonts are available that support them. It provides slots for square brackets, parentheses, and curly brackets, as well as lines to connect those. The application with HTML/CSS is tricky though.
This positions the character pieces with CSS within a single long table cell. To look good, there must be no vertical spacing between the pieces.
⎡ ⎢ ⎢ ⎣ |
a | b | c | ⎤ ⎥ ⎥ ⎦ |
d | e | f | ||
g | h | i |
CSS box borders may be employed to make very pretty brackets, but they are displayed only in browsers that support CSS.
|
Likewise, a matrix norm:
|
but braces and parentheses are beyond this approach.
The SVG standard now common in modern browsers, and could probably be employed in more than one way to make brackets as well as other enclosing symbols. Some hurdles include: simply stretching a glyph tends to distort the glyph, and that it is difficult within the web page to know the physical height of the thing being bracketed. One advantage of this approach is that the bracket characters appear in the HTML code.
|
Table frames have been dropped from the HTML 5 standard, perhaps with the idea that frames are in most cases a matter of presentation rather than content. Of course, it’s easy to imagine tables where important information is conveyed by borders and lines. Ah well, principles are hard to stick to. I personally liked frames because they work in text-only browsers.
Radicals
There is more than one way to write a square root in mathematics. One resort is always a fractional superscript using HTML. For a traditional radical, this is also possible in HTML, using CSS, but it has limitations.
In case the browser doesn’t have CSS, it loses the overbar that binds the arguments of the root. For this reason, it is safer to parenthesize the arguments.
But the worst problem comes from the fonts themselves. There seems to be no agreement as to how the radical sign will connect with a horizontal line. (You would think that it would simply extend to the height of the font, but no. In some fonts, it extends beyond the font height, but in others, it doesn’t reach the font height. But glitches of this kind appear in printed text, as well.)
Equation numbers
Equation numbers are pretty easy to achieve by wrapping an expression in
the middle cell of a table of width 100%
, and putting the equation
number in the rightmost cell with text-align
set to
right
. (To make the right-alignment function in Links, you
have to use the deprecated align
attribute of
<td>
.
|
10.1 |
Standards wish list
- All browsers should render all the SGML characters specified in HTML 4.0 intelligibly.
- Browsers should print what they display.
-
Text-based browsers should learn to support some CSS, especially
the properties
text-align
,text-indent
,vertical-align
,display
,white-space
,list-style
,border
,direction
-
Better control of the
hr
element. Especially, standardize what thesize
attribute means, and at least mention how CSS controls it. -
Support of the HTML 4 table
frame
andrules
attributes. It needs to be made clear how CSS interacts with the rendering of frames and rules.
Presentation vs content
I take the principle of separation of presentation and content to be important, and a practical necessity. Yet here we're faced with a dilemma.
With the demise of table frames and rules, the only way to draw a horizontal
line is with an <hr>
element, and there is no option
whatever for drawing a vertical line.
On the other hand, MathML was explicitly designed to encode the meaning of mathematical expressions, rather than the typesetting. (In this sense, it is superior even to LaTeX, which never made this distinction.) But the grand solution of MathML has been a long time coming.
Math needs to look reasonably right, but at the same time, the code should reflect the meaning. Is there any expedient that would satisfy both requirements?
I think it can be done, at least on an ad-hoc basis.
HTML class
attributes can be defined to indicate the meaning of
the content of the tag to which they're applied.
For instance, a <div class="fract">
tag containing two
further tags <div class="numer">
and
<div class="denom">
can serve both to indicate CSS
formatting and to make the meaning of the content very clear.
Furthermore, if consistently applied, the code could be automatically
converted to a standardized markup language such as MathML whenever it
becomes appropriate to do so.
So, rather than on relying on a global standard for specifying the meaning of math expressions, a local specification of meaning may be something to consider.
Browser performance
outdated material
The page rendering functionality of most modern GUI browsers now share one of a very small collection of "HTML rendering engines". Browsers with a given rendering engine show little variation in performance in rendereing web content. The two main engines are free and open-source software (FOSS).
The FOSS Gecko engine is used by Mozilla products such as Firefox.
The FOSS KHTML rendering engine was developed by the KDE group for their product, Konqueror. As far as I know, that is the only browser still using KHTML directly. But KHTML was the basis for WebKit.
The WebKit engine was developed by Apple from the KHTML engine for use in Safari, and was taken by Google for their Blink engine which is used in their Chrome browser. WebKit and Blink are now the most widely-used browser engine.
The Opera browser used to have its own proprietary engine, Presto, but in recent versions, it has switched to WebKit.
The open-source browsers lynx, elinks, links, NetSurf, Dillo either have no distinct web browser engine or use their own.
G | W | D | K | N | lk | lx | |
---|---|---|---|---|---|---|---|
SGML | P | P | B(*) | ? | P | G | B |
Unicode | P | P | B(w) | P | P | G | B |
Fraction with table, hyphens | G | G | G | G | U | G | G |
Fraction with inline-table | P | P | B(c) | B(c) | P | G | B |
Fraction hybrid | P | P | B(c) | P | P | B | R |
Fraction with inline-block | P | P | B(c) | P | P | B | B |
Superscript subscript | P | G | B(*) | P | P | R | R |
Sum with inline-block | P | G | G | G | P | B | B |
Integral with CSS | G | G | G | G | G | U(u) | R |
Integral with table | G | G | B(c) | G | P | R | U(t) |
Complex expression concatenated | G | G | B(c) | B(c) | B(c) | B(u) | B(u) |
Matrix with vert. bar | G | G | G | G | G | G | G |
Matrix with CSS border | U | P | G | P | P | B | B |
Matrix with frame | P | P | P | P | P | R | B |
Matrix norm with frame | P | P | P | P | P | G | B |
Matrix with frame and CSS | P | P | P | P | P | R* | B |
Equation numbers | P | P | P | P | P | P | U(t) |
|
|
Browser | v | P | notes |
---|---|---|---|
Gecko | 133 | L | Some matrix borders gray, and flash when the window moves. I don’t understand the logic… |
Dillo | 3.0.5 | L | No option to set font, and the font it usees misses some HTML 4 SGML entities. Subscript-on-superscript is completely mis-placed. |
Elinks | 0.10.4 | L |
Very impressive implementation of table frames and rules.
Issue with choice of frame logic (see below). Greek entities are greatly improved -- perhaps due to improvements in the terminal emulator? |
lynx | 2.8rel4 | L |
Wrong handling of Greek entities No table rules. Tables generally poor. |
Konqueror | 4.1+ | L | SGML problems finally resolved. |
Chrome | 47+ | L | Doesn’t seem to support inline-table properly in complex expression. |
Support for CSS has greatly improved since 2010. Only vertical positioning remains an issue among browsers — but without much finer control than CSS can provide, that is, without font- and glyph-specific behavior, such positioning will remain crude.
So far, table frames and rules are supported by Gecko and Webkit-based growsers, and Elinks. Konqueror tries, but doesn’t get it right.
Frame logic in Mozilla, MSIE, and Elinks is different. Both MSIE and Elinks
treat frame
and rules
as an enhancement to
an existing border
attribute, while Mozilla treats them
as an override to border
. So MSIE and Elinks show nothing
if border
is absent, but frame
are specified.
But even at that, MSIE puts rules on either side of each thing, rather
than between them, and Mozilla and Elinks do. Oh, dear. And
frame="lhs"
puts a line at the left of each column, not just to
the left of the table.
The control of table frames and rules, and of horizontal rule, using CSS is also muddled. The Mozilla browsers take the border properties as definitions of how to render frames and rules. Konqueror gives no (obvious) control of frame and rule presentation, and considers them to be independent of CSS borders.
Links fails to display Unicode correctly even on a terminal that supports Unicode. For example, in a terminal that can display the h-bar Unicode character, Links displays a capital H. Curiously, Lynx will display correctly Unicode test, but if a Unicode character appears in a web page, it is also incorrectly rendered.
Printing
While most graphical browsers provide good support for most SGML entities on the screen, many fail to print them properly.
In most cases, display is better than printing. Mozilla doesn’t print all these characters correctly, at least on Linux. Lynx and links should have no problem, but they transliterate Greek SGML characters to Latin! It’s an easy fix, and their maintainers have been contacted.
This is particularly pronounced in Linux/Unix, where there has never been a unified printing interface. Firefox for Linux fails to print most SGML symbol characters, while Opera 9 is only missing a dozen or so.
<--p> There is a different printing interface for Mozilla called xprint that uses X Windows rather than PostScript to print. I couldn’t get it to work, but it is said to fix the problem.To think about
Outside text-only browsers, SVG are very commonly supported nowadays. SVG can stretch characters such as parentheses, brackets and vertical bars, as well as perhaps integral signs.
As to the issue of presentation vs. content: in the case of merely scaling or stretching a letter, the letter remains as the content of the SVG markup. That is, if the SVG were entirely disabled, the character would still be there. The meaning of the SVG markup can be indicated by SVG tag attributes, just as with CSS.