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 May 2018

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:

The official, ultimate solution is MathML. As of this writing, it should be a mature product, but it has suffered setbacks, and is supported by only one major browser, Firefox. MathML is not a lightweight solution, though. It is easy to generate automatically, but hard to type by hand (as opposed to ). If you want just 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 already programs that automated 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

In contrast to the graphics approaches, this has the added benefits that the text

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

x = α y

is a math formula set off for display.

Decorated characters

Using CSS positioning, decorated characters, such as

h- x¨ v· a˜ gˆ A v

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:

∀ ∴ ∃ ¬ ∋ ∅ ∈ ∉ ∧ ∨ ∩ ∪ ∂ ∇ ∏ ∑ ∫ √ − ∗ × ÷ ⋅ ± ⊕ ⊗ ∝ ∞ ∼ ≅ ≈ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊄ ⊆ ⊇ ∠ ⊥


← ↑ → ↓ ↔ ↵ ⇐ ⇒ ⇓ ⇔

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.


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:

<meta http-equiv="content-type" content="text/html; charset=UTF-8">

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:

<?xml version="1.0" encoding="UTF-8"?>

Perhaps the most attractive option is the HTML 5 standard, in which the encoding is specified by this line directly after the <head> tag:

<meta charset="UTF-8" />

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 &#x222c; 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


CSS2 adds

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.


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:


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 (they have slightly different lengths in different fonts).

Here we use CSS inline-table for browsers that have it, and inline for browsers that don’t


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.


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.

One would think the coolest would be to 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. One could set the width of the containing element explicitly, 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, I pick the numerator to be 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.

Aa0qp _________ b2

Superscripts and subscripts

HTML has always had built-in support for superscripts and subscripts, so most browsers support them to some degree.

r01 Bai

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:

r01 Bai

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 &int; 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:

10 f( x ) dx

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.

  f( x ) dx

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.


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?)

baseline n=0 xn baseline

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?)

xn baseline

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.

baseline n=0 xn baseline

Complex expressions can be formed by simply concatenating such constructions, but then the integrands won’t share the same baseline. Expressions that are made using HTML tables will normally appear on a new line because a table is a “block” element: to stop this behavior, use CSS to set its display property to inline. Unfortunately, the browsers don’t agree as to the handling of vertical placement of inline tables…

Ω  v Δu dx  =  Ω 
vxi uxi dx  +  ∂Ω   v  du ___ dn  dS

Wide accents

It is possible to perform elaborate decoration of characters with modern SVG vector graphics. This is especially nice for stretching a glyph, as in


There are several good candidates for wide accents. Besides the ASCII caron ^ and tilde ~ and the iso-8859-1 macron ¯ there are ⎴ ⎵ ⏜ ⏝ ⏞ ⏟ ⏠ ⏡


One down-side is that, for mathematics, positioning and shaping is very font-specific.


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 is to use the vertical bar character, if you’re not concerned with the distinction between the usual enclosing square brackets and simple horizontal lines. This matrix is readable in any browser that supports tables:

|a b|
| |
|b a|

Unicode defines some extensible character pieces that can be used, provided fonts are available that support them. Pieces are provided for square brackets, parentheses, and curly brackets. There are also parts for The application with HTML/CSS is tricky though. The approach taken here is to position the character pieces with CSS within a single long table cell. For this to look good, there must be no vertical spacing between the pieces.

a bc

d ef
g hi

CSS box borders may be employed to make very pretty brackets, but they are displayed only in browsers that support CSS.

a b
b a

Likewise, a matrix norm:

a b
b a

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 is that in this approach, the bracket characters appear in the HTML code.

a b
b a

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.


There is more than one way to write a square root in mathematics. One can always resort to a fractional superscript using HTML. If one really wants a traditional radical, this is also possible in HTML, using CSS, but it has limitations.

(x2 + y2 + z2)

In case the browser doesn’t have CSS, the overbar that binds the arguments of the root is lost. 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 I think I have seen glitches of this kind 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>.

a b
-b a

Standards wish list

Presentation vs content

I take the principle of separation of presentation and content to be an important principle 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 is not ready for general use at this time.

So 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 describe 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, one may consider specifying meaning locally.

Browser performance

  FFOSfD K C lk lx
Fraction with table, hyphens GGGGGUGG
Fraction with inline-table PPB(c)PB(c)PGB
Fraction hybrid PPPPPPBR
Fraction with inline-block PPPPPPBB
Superscript subscript PPGGPPRR
Sum with inline-block PPGGGPBB
Integral with CSS GPGGGGU(u)R
Integral with table GPGB(c)GPRU(t)
Complex expression concatenated GGB(c)B(c)B(c)B(c)B(u)B(u)
Complex expression with tables GGGGGU*R(u)B(ut)
Matrix with vert. bar GGGGGGGG
Matrix with CSS border PPPPPPBB
Matrix with frame PPPPPPRB
Matrix norm with frame PPPPPPGB
Matrix with frame and CSS PPPPPPR*B
Equation numbers PPPPPPPU(t)


FF Mozilla Firefox 30+
O Opera
Sf Safari for Windows 5.1.7
D Dolphin for Android
C Chrome 47.x
K Konqueror
lk Elinks
lx Lynx
P Perfect!
G Good
U Ugly—aesthetic problem only
B Bad—meaning may be lost
R Readable—good as possible under circumstances
s poor SGML support
c poor CSS support
u poor super/subscript support
t poor tables support
d fail to display SGML character entities
* see note below


BrowservP notes
FF2.0L Some matrix borders gray. I don’t understand the logic… Maybe fixed in 3.0.
No support for CSS inline-block. Fixed in 3.0.
Opera9.6 L Incorrect drawing of table rules
Math SGML chars take too much horiz. room. (6.1 vastly improved; fixed in 8.51)
Wrong positioning of table elements whose “block” attribute has been turned off in CSS. (fixed in 9.0)
Elinks0.10.4L 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?
lynx2.8rel4L Wrong handling of Greek entities
No table rules. Tables generally poor.
Konqueror4.1+L SGML problems finally resolved.
Chrome47+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 IE, Mozilla Firefox, Opera, Safari, and Elinks. Konqueror tries, but doesn’t get it right. Both Opera and Safari draw rules where they should not. (Opera started doing this at some point.)

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.

Opera doesn’t render at all lines with fractional CSS widths. This is wrong behavior. But many of the browsers have glitches in this regard. I think that implementing anti-aliasing to represent the fractional part of a line width is the solution.

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.


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.

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.

Printing on Windows is better: Opera 7 prints SGML entities quite beautifully.

To think about

Outside text-only browsers, SVG are very commonly supported nowadays. SVG could be used to stretch letters 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.