Here's a concise checklist for font designers who want all these stretchy math constructs (parentheses, brackets, braces, integrals, summations, etc.) to render beautifully and correctly in KaTeX, MathJax, TeX, or any math-aware engine.
- Math layout engines assemble tall symbols (like
⎛⎜⎝) by stacking multiple glyph parts, not by scaling one shape. - Each symbol family (parentheses, brackets, braces, integrals, summations) has top, middle/extender, and bottom pieces.
- Modern math fonts encode their geometry and stacking rules in an OpenType MATH table.
Include all the segment glyphs KaTeX and TeX expect:
- Parentheses:
⎛ ⎜ ⎝ ⎞ ⎟ ⎠ - Square brackets:
⎡ ⎢ ⎣ ⎤ ⎥ ⎦ - Curly braces:
⎧ ⎨ ⎩ ⎫ ⎬ ⎭ - Integrals and sums:
⌠ ⌡ ⎲ ⎳ ⎰ ⎱ - Bars and ceilings/floors:
⎪ ⎮ ⌈ ⌉ ⌊ ⌋
Check that each one:
- Aligns vertically on the baseline grid,
- Has consistent stroke thickness and curvature,
- Touches or slightly overlaps when stacked.
- Set bounding boxes tightly around visible outlines (no excess vertical padding).
- Keep consistent side bearings so pairs like
⎛⎞visually balance. - Match vertical advance height across top/middle/bottom parts for perfect joins.
- Ensure vertical alignment for bars (
⎪,⎮) and extenders.
If you want full TeX-style quality (beyond KaTeX's heuristics):
Add an OpenType MATH table specifying:
- MathVariants: which glyphs form stretch assemblies (e.g.
(→ top=⎛, mid=⎜, bottom=⎝). - MathConstants: axis height, rule thickness, fraction gaps, etc.
- Italic corrections for slanted symbols.
You can build or inspect this using FontTools:
ttx -t MATH MyFont.otfor reference STIX Two Math or Latin Modern Math as a model.
Use:
-
KaTeX demo: https://katex.org
-
MathJax demo: https://mathjax.github.io/
-
hb-view (HarfBuzz) to test vertical joining.
-
Your font in:
.katex { font-family: "MyMathMono"; }
Render tall structures like:
\left( \begin{matrix}a\\b\\c\\d\\e\\f\\g\\h\\end{matrix} \right)and verify seamless joins, balanced spacing, and good line alignment.
- It's fine for code, but monospacing breaks math proportions.
- Narrow symbols (parentheses, brackets) will appear too far apart.
- You can mitigate this by using smaller horizontal side bearings or variable width exceptions for math glyphs.
Design top/mid/bottom pieces to connect cleanly, define tight metrics, include a proper MATH table, and visually test tall stacks in KaTeX/TeX. Smooth joins + balanced spacing = beautiful stretchy symbols.
Here is a complete yet concise reference for what a font designer must include so that all tall math delimiters and operators (parentheses, brackets, braces, integrals, sums) render correctly in KaTeX, MathJax, or TeX.
Everything below is self-contained: what you need to know, include, and add to your font's OpenType MATH table.
Math layout engines (TeX, KaTeX, MathJax) build tall symbols by stacking three or more glyphs:
- Top
- Middle (extender)
- Bottom
The font's MATH table defines which glyphs to use and how they connect.
Each group must exist as real glyphs in the font (usually from Unicode block U+239B–U+23B3):
| Group | Glyphs | Unicode | Names |
|---|---|---|---|
| Left parentheses | ⎛ ⎜ ⎝ | 239B–239D | uni239B–uni239D |
| Right parentheses | ⎞ ⎟ ⎠ | 239E–23A0 | uni239E–uni23A0 |
| Left square brackets | ⎡ ⎢ ⎣ | 23A1–23A3 | uni23A1–uni23A3 |
| Right square brackets | ⎤ ⎥ ⎦ | 23A4–23A6 | uni23A4–uni23A6 |
| Left curly braces | ⎧ ⎨ ⎩ | 23A7–23A9 | uni23A7–uni23A9 |
| Right curly braces | ⎫ ⎬ ⎭ | 23AB–23AD | uni23AB–uni23AD |
| Integral | ⌠ ⌡ ⎲ ⎳ | 2320–23B3 | uni2320–uni23B3 |
| Summation top/bottom | ⎰ ⎱ | 23B0–23B1 | uni23B0–uni23B1 |
| Bars / pipes | ⎪ ⎮ | 23AA, 23D0 | uni23AA, uni23D0 |
| Ceiling / floor | ⌈ ⌉ ⌊ ⌋ | 2308–230B | uni2308–uni230B |
Each should share stroke weight, side bearings, and vertical join points.
<MATH>
<MathVariants>
<!-- Parentheses -->
<VertGlyphAssembly glyph="parenleft">
<Part glyph="uni239B" fullAdvance="350"/>
<Part glyph="uni239C" fullAdvance="400" isExtender="1"/>
<Part glyph="uni239D" fullAdvance="350"/>
</VertGlyphAssembly>
<VertGlyphAssembly glyph="parenright">
<Part glyph="uni239E" fullAdvance="350"/>
<Part glyph="uni239F" fullAdvance="400" isExtender="1"/>
<Part glyph="uni23A0" fullAdvance="350"/>
</VertGlyphAssembly>
<!-- Square brackets -->
<VertGlyphAssembly glyph="bracketleft">
<Part glyph="uni23A1" fullAdvance="350"/>
<Part glyph="uni23A2" fullAdvance="400" isExtender="1"/>
<Part glyph="uni23A3" fullAdvance="350"/>
</VertGlyphAssembly>
<VertGlyphAssembly glyph="bracketright">
<Part glyph="uni23A4" fullAdvance="350"/>
<Part glyph="uni23A5" fullAdvance="400" isExtender="1"/>
<Part glyph="uni23A6" fullAdvance="350"/>
</VertGlyphAssembly>
<!-- Curly braces -->
<VertGlyphAssembly glyph="braceleft">
<Part glyph="uni23A7" fullAdvance="350"/>
<Part glyph="uni23A8" fullAdvance="400" isExtender="1"/>
<Part glyph="uni23A9" fullAdvance="350"/>
</VertGlyphAssembly>
<VertGlyphAssembly glyph="braceright">
<Part glyph="uni23AB" fullAdvance="350"/>
<Part glyph="uni23AC" fullAdvance="400" isExtender="1"/>
<Part glyph="uni23AD" fullAdvance="350"/>
</VertGlyphAssembly>
<!-- Integrals -->
<VertGlyphAssembly glyph="integral">
<Part glyph="uni2320" fullAdvance="400"/>
<Part glyph="uni23AE" fullAdvance="400" isExtender="1"/>
<Part glyph="uni2321" fullAdvance="400"/>
</VertGlyphAssembly>
<!-- Summation (⎰⎱) -->
<VertGlyphAssembly glyph="summationdisplay">
<Part glyph="uni23B0" fullAdvance="400"/>
<Part glyph="uni23B1" fullAdvance="400"/>
</VertGlyphAssembly>
<!-- Vertical bars -->
<VertGlyphAssembly glyph="bar">
<Part glyph="uni23AA" fullAdvance="400" isExtender="1"/>
</VertGlyphAssembly>
<VertGlyphAssembly glyph="doublebar">
<Part glyph="uni23D0" fullAdvance="400" isExtender="1"/>
</VertGlyphAssembly>
</MathVariants>
<MathConstants>
<MathValue name="AxisHeight" value="0"/>
<MathValue name="RadicalRuleThickness" value="40"/>
<MathValue name="DisplayOperatorMinHeight" value="1200"/>
<MathValue name="DelimiterPercent" value="90"/>
<MathValue name="ScriptPercentScaleDown" value="80"/>
<MathValue name="ScriptScriptPercentScaleDown" value="60"/>
<MathValue name="MathLeading" value="100"/>
</MathConstants>
</MATH>-
Decompile font
ttx MyFont.otf
-
Insert the XML snippet above (inside
<MATH>). -
Rebuild
ttx MyFont.ttx
-
Test
<style>.katex { font-family: "MyFont"; }</style> <script> katex.render("\\left(\\begin{matrix}a\\\\b\\\\c\\\\d\\\\e\\\\f\\\\g\\\\h\\end{matrix}\\right)", el, {displayMode:true}) </script>
- Vertical joins: each piece's top and bottom edges must align exactly.
- Extender height ≈ 1 em; enough to repeat without visual break.
- Side bearings: balanced (≈ 1/4 em for curved shapes).
- No vertical padding beyond the actual glyph outline.
- Stroke weight and curvature consistent across pieces.
- Integrals and sums may be stylistic — just ensure top/bottom join neatly.
Use KaTeX or MathJax:
katex.render("\\left\\{ \\begin{matrix}a\\\\b\\\\c\\\\d\\\\e\\\\f\\\\g\\\\h\\end{matrix}\\right\\}", el)All stacked delimiters should connect seamlessly, with balanced spacing and consistent thickness.
Provide top, middle, bottom glyphs for each delimiter, define them in
MATH > MathVariants, ensure precise bounding boxes, matching strokes, and balanced spacing. This lets KaTeX/TeX dynamically assemble perfectly smooth, tall symbols for every case.
When every glyph must occupy exactly one cell width, tall math delimiters can still look correct — but you must treat proportions, spacing, and joins with surgical precision. Here’s the complete, compact guide.
All glyphs, including tall delimiters, must share a fixed advance width (e.g. 600 units). However, the drawn shape should optically center within that width:
- Place vertical strokes (|, [, ]) centered exactly.
- Place curved glyphs (parentheses) slightly inward (≈ 5–8%) to appear balanced in monospaced grids.
Keep identical side bearings across related pairs:
| Glyphs | Left / Right bearing |
|---|---|
⎛ ⎝ |
equal left / right |
⎞ ⎠ |
mirror of left parens |
⎡ ⎣ |
equal |
⎤ ⎦ |
equal |
Use narrow visible shapes inside the full cell, not narrower advances.
- Align all top/middle/bottom parts so vertical connectors meet exactly on the baseline grid.
- The vertical advance (
fullAdvancein the MATH table) must equal the actual join distance, not the entire em box. - Test stacking: ⎛⎜⎝, ⎡⎢⎣, ⎧⎨⎩.
Because monospaced text compresses context visually:
- Thicken curved outlines slightly (≈ +5%) to match the apparent weight of vertical bars.
- Ensure top and bottom strokes of ⎛⎝ curve inward, not outward — avoids “leaning”.
- All vertical pieces (⎜, ⎢, ⎨, ⎮) share identical horizontal position and width inside the cell.
- Use the same centerline x-position across all left/right delimiter sets.
- Verify that stacked composites align pixel-perfect in raster and vector views.
Inside VertGlyphAssembly, keep:
<Part glyph="..." fullAdvance="X"/>where X = exact vertical connection step, typically equal across all monospaced pieces (e.g. 400 units).
No negative connector lengths — rely on perfect box-to-box fit.
Use KaTeX display mode with your monospaced font:
katex.render("\\left(\\begin{matrix}a\\\\b\\\\c\\\\d\\\\e\\\\f\\\\g\\\\h\\end{matrix}\\right)", el, {displayMode:true})Confirm:
- No visible gaps between segments.
- Left/right pairs visually centered over text columns.
- Multiple lines of parentheses stack evenly within the grid.
To preserve full monospacing and elegant proportion:
- Keep true monospace advance, but use slightly asymmetric contours so
(x)and[x]look balanced. - Apply hinting zones or vertical metrics alignment so joins remain exact across renderers.
Summary:
Keep every glyph the same width, center or offset outlines for optical balance, ensure vertical joins meet precisely, unify metrics across pairs, and fine-tune curvature and stroke weight. Done right, tall delimiters will look clean, aligned, and perfectly proportional — even in a fully monospaced grid.
Below is a clean, structured outline of the exact components required to make CrowMarkMono into a proper OpenType Math font. This is the “math-font spec” broken into actionable work units.
No fluff, just the essentials.
This table contains three major blocks.
Hundreds of numeric parameters, including:
- ScriptPercentScaleDown
- ScriptScriptPercentScaleDown
- RadicalVerticalGap
- RadicalRuleThickness
- FractionNumeratorShiftUp
- FractionDenominatorShiftDown
- FractionRuleThickness
- UpperLimitGapMin
- LowerLimitGapMin
- SubscriptShiftDown
- SuperscriptShiftUp
- AccentBaseHeight
- StretchStackTopShiftUp
- IntegralTopShiftUp / BottomShiftDown
- LargeOpVerticalGap
- UnderbarVerticalGap
- OverbarVerticalGap
(These values make MathML layout work.)
Required for stretchable glyphs (e.g., parentheses, brackets):
- Top glyph
- Bottom glyph
- Middle extender
- Repeatable extender
- Alignment anchors
Multiple size variants of:
- Parentheses
- Brackets
- Braces
- Vertical bars
- Integral signs
- Summation / product symbols
- Arrows / fences
- Radical signs
- Large operators
- Script and calligraphic letters
MathML scales glyphs by switching between these variants.
This is the full “Mathematical Operators” block.
∀ ∃ ∄ ∧ ∨ ⊤ ⊥ ¬
∈ ∉ ∊ ∋ ∌ ⊂ ⊃ ⊄ ⊅ ⊆ ⊇ ⊈ ⊉ ∪ ∩ ∅
≤ ≥ ≠ ≡ ≜ ≝ ≲ ≳ ≪ ≫ ≈ ∼ ∝ ∞
± ∓ × ÷ ⋅ ∘ ∗ ∑ ∏ ∐ ∫ ∮ ∯ ∰
← ↑ → ↓ ↔ ↕ ↖ ↗ ↘ ↙ ⇐ ⇑ ⇒ ⇓ ⇔ ⇦ ⇧ ⇨ ⇩ and dozens more (full range)
⋮ ⋯ ⋰ ⋱ ‰ ‱ ⊕ ⊗ ⊘ ⊖ ⊢ ⊣ ⊨ ⋃ ⋂ ⋁ ⋀
A real math font needs all of them.
These are from U+1D400–1D7FF.
- Bold
- Italic
- Bold Italic
- Script
- Bold Script
- Fraktur
- Bold Fraktur
- Double-struck
- Sans-serif
- Sans-serif Bold
- Sans-serif Italic
- Sans-serif Bold Italic
- Monospace
- Bold
- Italic
- Bold Italic
- Sans-serif Bold
- Sans-serif Bold Italic
(There is no script Greek.)
Digits exist for:
- Double-struck
- Sans-serif
- Sans-serif Bold
- Monospace
MathML expects these alphabets for variables, constants, vector notation, and stylistic math.
MathML requires stretchable versions of:
- ( )
- [ ]
- { }
- |
- ‖
- ⟨ ⟩
- ⟪ ⟫
All directions including long, double, harpoons.
∫ extended to any height.
√ must stretch horizontally with extenders. Requires:
- radical base
- radical bar
- optional degree position anchor
| ∥ fences for cases & matrices
— extenders for long accents
Each stretchy construction requires “parts” and rules defined in the MATH table.
MathML expects multiple sizes of:
- ∑
- ∏
- ∐
- ⋁ ⋂ ⋃
- ∫ ∮ ∯
- Double/triple integrals must scale
- Slanted integrals
- Loop integrals
Parentheses, braces, brackets in sizes 1–5 (minimum), plus extender mode.
Large versions of ≤ ≥ = ≠ etc. for display math.
These must have variant glyphs named and registered.
These numeric values are essential so MathML knows:
- where superscripts go
- where subscripts go
- how big radicals are
- thickness of fraction bars
- spacing above/below operators
- shifting of scripts
- proper assembly of tall operators
This is almost always tuned manually.
Math accents differ from text accents.
- ˙ (dot)
- ¨ (double dot)
- ^ (hat)
- ˇ (check)
- ˜ (tilde)
- ˉ (overbar)
- stretched ^
- stretched ~
- vector arrows above characters
Every base glyph needs at least:
- top accent anchor
- bottom accent anchor
- math top accent position
- scriptstyle accent position
Anchors needed for assembly pieces:
- left piece
- extender piece
- right piece
MathML builds long accents over long expressions using these anchors.
Here is the full outline you asked for, reduced to its core:
-
Generate full OpenType MATH table Math constants, glyph assemblies, variant sizes.
-
Add all math symbols U+2200–U+22FF Logic, arithmetic, sets, arrows, operators.
-
Add all Mathematical Alphanumeric Symbols Italic, bold, bold-italic, script, double-struck, Fraktur, sans-serif, monospace.
-
Implement stretchy operator constructions Brackets, braces, bars, arrows, radicals, integrals.
-
Provide large operator variants Summation, product, integrals, oversize brackets.
-
Define math metrics Superscript/subscript offsets, fraction rules, radical thickness, spacing constants.
-
Add diacritic anchors For math-specific accents and wide accents.
The MATH table is mandatory. It has three major substructures.
You must define numeric values for:
- ScriptPercentScaleDown
- ScriptScriptPercentScaleDown
- DelimitedSubFormulaMinHeight
- DisplayOperatorMinHeight
- RadicalVerticalGap
- RadicalDisplayStyleVerticalGap
- RadicalRuleThickness
- RadicalExtraAscender
- RadicalKernBeforeDegree
- RadicalKernAfterDegree
- RadicalDegreeBottomRaisePercent
- SubscriptShiftDown
- SubscriptTopMax
- SubscriptBaselineDropMin
- SuperscriptShiftUp
- SuperscriptShiftUpCramped
- SuperscriptBottomMin
- SuperscriptBaselineDropMax
- FractionNumeratorShiftUp
- FractionNumeratorDisplayStyleShiftUp
- FractionDenominatorShiftDown
- FractionDenominatorDisplayStyleShiftDown
- FractionNumeratorGapMin
- FractionNumeratorDisplayStyleGapMin
- FractionDenominatorGapMin
- FractionDenominatorDisplayStyleGapMin
- FractionRuleThickness
- UnderbarVerticalGap
- UnderbarRuleThickness
- UnderbarExtraDescender
- OverbarVerticalGap
- OverbarRuleThickness
- OverbarExtraAscender
- RadicalDegreeBottomHeight
- StackTopShiftUp
- StackTopDisplayStyleShiftUp
- StackBottomShiftDown
- StackBottomDisplayStyleShiftDown
- StackGapMin
- StackDisplayStyleGapMin
- StretchStackTopShiftUp
- StretchStackBottomShiftDown
- StretchStackGapAboveMin
- StretchStackGapBelowMin
- AxisHeight
These define how every math expression is built and spaced.
For each glyph that may need to grow:
- Parentheses: (, )
- Brackets: [, ]
- Braces: {, }
- Vertical bars: |, ∥
- Angle brackets: ⟨ ⟩ ⟪ ⟫
- Integrals: ∫ ∮ ∯ ∱ ∲ ∳
- Sum/product: ∑ ∏ ∐
- Large operators: ⋃ ⋂ ⋁ ⋀
- Arrows: → ← ↔ ⇒ ⇐ ⇔ and long forms
- Radicals: √ (radical with extender)
For each, provide variant glyphs:
- Size 1
- Size 2
- Size 3
- Size 4
- Size 5
- Unlimited size via extender piece
And connect them with MATH table links.
Each stretchable operator needs “parts”:
- Top piece
- Bottom piece
- Middle piece
- Extender (repeatable)
And anchors specifying:
- Vertical alignment
- Horizontal alignment
- Extender repeat positions
You must include the following for A–Z and a–z:
- Mathematical Bold
- Mathematical Italic
- Mathematical Bold Italic
- Mathematical Script
- Mathematical Bold Script
- Mathematical Fraktur
- Mathematical Bold Fraktur
- Mathematical Double-Struck
- Sans-serif
- Sans-serif Bold
- Sans-serif Italic
- Sans-serif Bold Italic
- Monospace
Total glyphs: ~1300+
These sets must be included:
- Bold Greek
- Italic Greek
- Bold Italic Greek
- Sans-serif Bold Greek
- Sans-serif Bold Italic Greek
(Other Greek styles do not exist in Unicode.)
Include digits in:
- Double-struck
- Sans-serif
- Sans-serif Bold
- Monospace
You must implement every glyph from the Unicode block:
∀ ∁ ∂ ∃ ∄ ∅ ∆ ∇
∈ ∉ ∊ ∋ ∌ ⊂ ⊃ ⊄ ⊅ ⊆ ⊇ ⊈ ⊉ ∪ ∩ ∅ ⋃ ⋂ ⋁ ⋀
= ≠ ≡ ≅ ≈ ≤ ≥ ≺ ≻ ≼ ≽
± ∓ × ÷ ⋅ ∘ ∗ ⊕ ⊖ ⊗ ⊘ ⊙
← ↑ → ↓ ↔ ↦ ↤ ↦ ↣ ⇐ ⇑ ⇒ ⇓ ⇔ ↮ ↭ Long arrows: ⟶ ⟵ ⟷ Harpoons: ⇀ ⇁ ⇂ ⇃ Double arrows, triple arrows
⅟ ½ ⅓ ¼ etc. But more importantly:
- Fraction slash (U+2044)
- Division slash (U+2215)
⋮ ⋯ ⋰ ⋱
∞ ∝ ∠ ⦀ ⧴ ⋒ ⋓ ⟂ ⟘ ⫯
(You must include all glyphs, even rarely used ones.)
These require assembly rules.
- Parentheses
- Brackets
- Braces
- Vertical bar
- Double vertical bar
- Fences (⟨ ⟩)
- Cases brackets
- Arrows (must stretch horizontally)
- Long equal signs / equivalence signs
- Overlines / underlines
- Radical bar extension
Stretchable forms of:
- ∫
- ∬
- ∭
- ∮
- ⨏ (rare)
(Change slant angle and height.)
Must include display-math sizes for:
- ∑
- ∏
- ∐
- ⋃
- ⋂
- ⋁
- ⋀
- tall ∫ ∮ integrals
- very tall parentheses
- very tall brackets
- oversized relation symbols (≤ ≥ = ≠)
These variants must be declared in the MATH table.
Every glyph involved in math must have:
- top anchor (superscript)
- bottom anchor (subscript)
- cramped positioning values
- numerator shift
- denominator shift
- rule thickness
- min gaps
- radical rule thickness
- extra ascender
- degree kern before/after
- percentage shift
- min height for display operator
- vertical gaps around limits
- accent base height
- superscript drop values
Without these, the browser falls back.
Math accents behave differently from text accents.
For each glyph (a–z, Greek, etc.):
- TopAnchor
- BottomAnchor
- TopAccentPosition
- BottomAccentPosition
You must anchor:
- hat (ˆ)
- tilde (˜)
- wide hat
- wide tilde
- macron
- overline
- dot
- double dot
- vector arrow
- breve
- caron
Each needs:
- Left anchor
- Right anchor
- Extender anchor (for wide accents)
Provide extender pieces and:
- left glyph
- extender glyph
- right glyph
MathML assembles these to stretch over long expressions.
While not required, real math fonts also include: