The earlier pages chose typefaces and loaded them. This one works at the level of single characters: which digits a table uses, whether an abbreviation shouts, which dash goes in a range, and why a font full of pictures turned out to be a bad idea.
1. Features live in the font
A font is more than one shape per character. Besides its default glyphs, an OpenType font can carry alternates: digits of equal width, small capitals, ligatures, fractions, a zero with a slash through it. Each set of alternates is a feature, named by a four-letter tag such as tnum (tabular numbers) or smcp (small caps), and stored as substitution rules in the font's GSUB table, the table that Web fonts and performance showed in the pipeline from character to glyph.
CSS cannot add a feature to a font. It can only ask for one, and the answer depends on the file. Some features are on by default, because the shaping engine applies them to all text: kerning (kern), standard ligatures (liga), contextual alternates (calt) and the features complex scripts need. The rest are off until a style turns them on. When a font lacks a requested feature, nothing happens, except in a few cases, small caps above all, where the browser fakes the result.
What this section's fonts can do
Before promising a feature in a design, check the font file, because one typeface can arrive with very different features. This section ships Inter and Literata twice: as Fontsource's Latin builds, which kept only a handful of features, and as cuts from the upstream fonts made with --layout-features="*", which kept them all. Subsetting a font, which Web fonts and performance recommended for speed, can silently remove features a design depends on.
| File | Bytes | Features |
|---|---|---|
| Inter, Fontsource build | 72,920 | calt ccmp dnom frac locl numr pnum tnum; kern mark mkmk |
| Inter, all features | 91,388 | aalt calt case dlig dnom frac locl numr ordn pnum salt sinf subs sups tnum zero, ss01–ss08, cv01–cv13; cpsp kern |
| Literata, Fontsource build | 110,080 | ccmp dnom frac liga locl numr pnum rvrn tnum; kern mark |
| Literata, all features | 138,100 | aalt c2sc case ccmp dlig dnom frac liga lnum locl numr onum ordn pnum rvrn sinf smcp ss02 subs sups tnum zero; case cpsp kern |
| Bitter | 34,072 | calt ccmp dnom frac liga lnum locl numr pnum rvrn tnum; kern mark mkmk |
| Recursive | 128,708 | dnom frac locl numr pnum rvrn; kern (the full font adds case dlig ordn sups zero and thirteen stylistic sets) |
| Atkinson Hyperlegible Next | 33,996 | ccmp frac locl pnum tnum; kern mark mkmk |
The feature-complete Inter costs 18 KB more than Fontsource's for a similar character set; the feature-complete Literata costs 28 KB more and covers fewer accented letters. Features cost bytes, so a production subset keeps the ones the design uses.
2. Numbers
Digits come in two independent pairs of styles, and a good text font offers all four combinations.
Lining and old-style figures
Lining figures all stand on the baseline and reach about the height of the capitals, like a row of soldiers. Old-style figures (also called text figures or lowercase numerals) behave like lowercase letters: 0, 1 and 2 sit within the x-height, 6 and 8 rise like ascenders, and 3, 4, 5, 7 and 9 drop below the baseline. Old-style figures blend into a paragraph the way lowercase does; lining figures match capitals and tables. Georgia, drawn for the screen in 1996, uses old-style figures by default, which is easy to see beside Times New Roman:
Georgia: old-style figures
Times New Roman: lining figures
The Georgia that ships with macOS has no feature for lining figures, so font-variant-numeric: lining-nums changes nothing in it, a check made in Chrome.
Proportional and tabular figures
The lecture slide "Numbers and Type" makes the second distinction. Proportional figures are designed for text: each digit is as wide as its shape needs, so a 1 is narrow and an 8 is wide. Tabular figures are designed for tables: every digit has the same width, like a monospaced font, so columns of numbers line up. The slide's point that is easy to miss: tabular figures are not limited to monospaced fonts, and many proportional text fonts include them as a feature.
Width matters twice. In a column, tabular figures line up place values, so the eye compares magnitudes by length. In a value that changes, such as a timer or a price ticker, proportional figures make the number shiver as narrow and wide digits replace each other; tabular figures keep it still.
Tables and dashboards
Choosing and pairing took the lecture's advice for charts and dashboards from Tiffany France's article in Nightingale: a large x-height, open counters, and tabular figures, with old-style figures avoided because their rising and falling digits look ragged on an axis. In CSS, that is one declaration on the table, plus alignment:
Numbers align on the end of the cell because place values start from the right: ones under ones, thousands under thousands. Align a numeric column's header with its numbers, too. With tabular figures, end alignment also lines up decimal points, as long as every value has the same number of decimal places; format them so, with Intl.NumberFormat and minimumFractionDigits in JavaScript or on the server.
True decimal alignment, where 3.5 and 108.00 align on the point whatever their digits, has been in the CSS Text Level 4 draft as text-align: ".", but no browser supports it; Chrome rejects the value in CSS.supports(). Equal decimal places are the practical answer. A font without tabular figures, like Recursive's file here, drifts whatever the CSS says.
Alignment does not reach a screen reader, which reads cell by cell; header cells with scope, units in the headers and a caption do, as tables explains.
Fractions, ordinals and the slashed zero
The rest of font-variant-numeric asks for special-purpose glyphs:
| Value | Feature | What it does |
|---|---|---|
lining-nums / oldstyle-nums | lnum / onum | Capital-height or lowercase-style figures |
proportional-nums / tabular-nums | pnum / tnum | Figures of natural or equal widths |
diagonal-fractions | frac | Turns 1/2 into a fraction with a slanted bar |
stacked-fractions | afrc | A horizontal bar with the numbers above and below; few fonts have it |
slashed-zero | zero | A zero with a slash or dot, distinct from the letter O |
ordinal | ordn | Raised letters in ordinals such as 1st, or the º of Spanish 1.º |
Values from different pairs combine, as in lining-nums tabular-nums slashed-zero. Three cautions come from testing them:
- Apply fractions to the fraction. In Inter,
fracis contextual and touches only digits around a slash, but in Literata it turns every digit into a numerator or denominator, so a price of 3.50 inside the same element becomes a jumble of small digits. Wrap fractions in aspan, or use the precomposed ½, ¼ and ¾. - The slashed zero is a legibility tool for codes, serial numbers and passwords, where O and 0 must not be confused, a problem Why type matters showed across seven faces. Here only the feature-complete Inter and Literata have
zero; a monospaced face that distinguishes the two by design needs no feature at all. - Ordinals belong to a language. English writes 1st and 2nd, French 1er and 2e, Spanish 1.º and 1.ª, and German simply "1." with a period. The
ordnfeature only raises letters; generate the right suffix withIntl.PluralRulesand{ type: "ordinal" }.
3. Small caps
Small capitals are capital letterforms drawn at about the height of the lowercase. They let a run of capitals, such as an abbreviation, sit in a line of text without shouting. Real small caps are drawn by the type designer: not just smaller, but with heavier strokes and wider spacing, so they match the weight of the text.
font-variant-caps asks for them:
| Value | Feature | Effect |
|---|---|---|
small-caps | smcp | Lowercase letters become small caps; capitals stay full size |
all-small-caps | smcp, c2sc | Both lowercase and capitals become small caps: the one for abbreviations |
petite-caps, all-petite-caps | pcap, c2pc | Still smaller capitals, about the x-height in fonts where small caps are taller; falls back to small caps |
unicase | unic | A mix of capital and lowercase forms at one height |
titling-caps | titl | Lighter capitals designed for large sizes |
Real and faked small caps
When the font has no smcp, browsers synthesize small caps by drawing full capitals at a smaller size. In Chrome, asking Inter for small-caps does exactly that, and petite-caps falls back to the same faked small caps, as the specification says it should. Faked small caps are thinner and lighter than the letters around them, because shrinking a capital shrinks its strokes; they look like a mistake in body text. Two properties control this:
font-synthesis: noneturns off all faking: bold, italic, small caps and position.font-synthesis-small-caps: noneturns off only the small caps. With synthesis off, a font withoutsmcpsimply shows the original letters, and the design degrades to ordinary text instead of thin imitation capitals. Both are Baseline features, and the longhand reached Safari in version 16.4 in 2023.
Small caps have a quiet advantage over capitals typed in uppercase or produced by text-transform. The characters stay what the author wrote: a screen reader, a search or a copy of "Html" set in small caps gets "Html", and an abbreviation typed as "HTML" stays "HTML". Since screen readers work from the characters, the source decides what is heard and CSS only what is seen. The abbr element marks them.
4. Ligatures and contextual alternates
A ligature joins two or three characters into one glyph, as Anatomy and vocabulary showed with Literata's "fi", where the hook of the f would otherwise hit the dot of the i. font-variant-ligatures controls four groups:
- Common ligatures (
liga,clig): fi, fl, ff, ffi. On by default.no-common-ligaturesturns them off. - Discretionary ligatures (
dlig): decorative joins such as ct or st. Off by default, for display text only. - Historical ligatures (
hlig): forms like the long s, for period flavor. Off by default. - Contextual alternates (
calt): glyphs chosen by their neighbors, as in a script font whose letters join differently by position. On by default.no-contextualturns them off.
The keyword none turns off every group, and normal restores the defaults. Ligatures change only glyphs, not characters, so copying "office" from a page with an "ffi" ligature still gives six letters. Letter-spacing also suppresses them: CSS Text says browsers should not apply optional ligatures when spacing between letters is not zero, so a tracked heading in Literata loses its "fi" by itself.
Contextual alternates are more powerful than their name suggests. In this section's Inter, calt turns -> into an arrow, => into a double arrow, 1x2 into a multiplication sign between the digits, while leaving 0x2F alone. Pleasant in prose; not in code.
Ligatures in code
Code is text where every character matters and where the reader often needs to see exactly what was typed. A ligature that joins "fi" in a variable name hides nothing important, but a contextual alternate that draws -> as an arrow hides two characters behind one glyph, and a reader cannot tell whether the source holds the arrow character U+2192 or a hyphen and a greater-than sign. For code in a text face, turn the substitutions off:
Some programmers want the opposite. Programming ligature fonts such as Fira Code, JetBrains Mono and Cascadia Code draw != as ≠, >= as ≥ and => as an arrow, on purpose, as one glyph spanning two or three character cells so the code's alignment survives. Supporters find operators easier to read; the typographer Matthew Butterick's essay "Ligatures in programming fonts: hell no" makes the case against: the ligatures look like real Unicode characters, so != and ≠ become indistinguishable, and a substitution cannot know the language, so it will sometimes join characters that do not form an operator. A fair compromise: ligatures belong in a programmer's own editor, while code on a web page, which others copy and learn from, shows the characters it contains.
5. Case forms, stylistic sets and position
Case-sensitive forms
Hyphens, parentheses and the @ sign are positioned for lowercase, so in a line of capitals they sit too low. The case feature raises them to suit capitals. No font-variant value asks for it, so it is one of the few features that needs font-feature-settings: "case", applied only to text set in capitals. The feature-complete Inter and Literata have case; the Fontsource builds do not.
Stylistic sets and character variants
Many fonts offer alternative designs for some letters: a single-story a, a g with an open tail, an l with a tail so it cannot be mistaken for a capital. A stylistic set (ss01 to ss20) switches a group of them together, and a character variant (cv01 to cv99) switches one character. The numbers mean nothing outside one font, which is why the CSS route has two steps: @font-feature-values gives the font's numbers names for one family, and font-variant-alternates uses the names.
The numbers are Inter's own, from the names stored in the font: ss01 is "Open digits", ss02 "Disambiguation" and cv05 "Lower-case l with tail"; other fonts number differently, so read each font's list. The pair is supported in every major browser and has been Baseline since March 2023, when Chrome 111 shipped it; Firefox and Safari had it for years before. It is verbose, but the names keep meaning in the style sheet instead of magic numbers, and apply only to their family, so a fallback font never gets another font's set 2.
Superscripts and subscripts
font-variant-position: super and sub ask for the font's own superscript and subscript glyphs (sups, subs), which are drawn at the right weight and sit on the line without changing its height. HTML's <sup> and <sub> shrink and shift ordinary glyphs, which looks lighter and can open a gap between lines. Support, though, favors the elements. When the font has no superscript glyphs, Firefox synthesizes them, while Chrome and Safari do nothing at all: in Chrome, asking the Fontsource Inter for super leaves the 2 of "mc2" full size on the baseline, which turns a formula into a different number.
So use the elements for meaning: an exponent, a chemical formula, a footnote marker. They carry semantics into the document and work in any font. font-variant-position is a refinement for fonts known to have sups and subs, set on the elements and not instead of them.
font-feature-settings: the last resort
Below the named properties sits a low-level one that takes raw feature tags:
The sample is a mistake, and it shows why the CSS Fonts specification tells authors to prefer font-variant and to keep font-feature-settings for features with no other way in. It is one property holding one list, and a later declaration replaces the list rather than adding to it, so the code in the table loses its tabular figures, as a check in Chrome confirmed. The font-variant-* properties are separate, so font-variant-numeric: tabular-nums on the table and font-variant-ligatures: none on the code compose as expected.
Use font-feature-settings for features that have no named value, such as case, and put it where nothing else needs to change. When a feature should apply wherever a font is used, the same descriptor can go inside the font's @font-face rule, which keeps it with the font instead of every selector; that descriptor reached Chrome only in version 140, in 2025, so it is Baseline only since then.
Try the features
The lab sets four samples in one of this section's fonts, twice: as the font draws them by default, and with the features chosen in the form. Every option names its feature tag and says whether the chosen file has it.
6. Typographer's punctuation
The typewriter, and after it the ASCII character set, saved keys by merging marks that typography keeps apart. One straight " stood for opening and closing quotes, one ' for single quotes, the apostrophe and the prime, and one hyphen for every dash. Unicode has all the separate marks, every font in this section except the script samples has them, and using them is most of what makes text look professionally set.
Quotes and apostrophes
Typographer's quotes, often called curly or smart quotes, come in pairs with different shapes: “double” and ‘single’ in English. The apostrophe is the same character as the closing single quote, U+2019, which is the form the Unicode Standard prefers. Straight quotes in prose read as unfinished.
A common slip is a backward apostrophe at the start of a word, as in ‘90s, made by software that assumes every quote after a space opens a quotation. It should be ’90s: the mark stands for missing digits.
Primes, multiplication and minus
Several marks look like punctuation but are something else:
- Primes mark feet and inches (6′ 2″) and minutes and seconds of arc (32° 42′ 51″). They are
U+2032andU+2033, straight but slanted, not quotes. - The multiplication sign × (
U+00D7) belongs in dimensions and arithmetic: 1920 × 1080, 3 × 4. The letter x is a letter, to a search engine and to a screen reader. - The minus sign − (
U+2212) is longer than a hyphen and, in a well-made font, as wide as the plus sign and at its height, so −5 and +5 line up in a table.
Hyphens and dashes
English uses three horizontal marks, plus the minus, and the differences are length and meaning:
Style guides differ on the em dash. American usage closes it up—like this—while British usage, and German and French, prefer a spaced en dash – like this. Either is right if consistent; a hyphen or two standing in for a dash is not.
Ellipses and spaces
The ellipsis … (U+2026) is one character with spacing chosen by the font's designer; three periods typed in a row are spaced as three separate periods. Some editors prefer spaced periods; it is a house-style decision.
Spaces come in several widths and with different line-breaking rules, and the rules matter more than the widths:
| Space | Code point | Breaks a line? | Used for |
|---|---|---|---|
| No-break space | U+00A0, | No | Keeping a number with its unit (10 km), a title with a name (Dr. Rivera), and before the French colon |
| Narrow no-break space | U+202F, no named entity | No | French spacing inside guillemets and before ; ! ?; thousands separators in French and in SI style (10 000) |
| Thin space | U+2009,   | Yes | A little air, for example around a spaced dash; it can break, so it does not hold anything together |
The narrow no-break space is newer than the others, and fonts lag: of this section's files, only the feature-complete Inter has a glyph for U+202F, so elsewhere the browser takes it from a fallback font. For a space that is usually harmless, but check a font's character set as well as its features. The no-break space has a cost of its own: a long run of them makes text that cannot reflow, so use one where two words must stay together, not to push things into place.
Typing and encoding the marks
With UTF-8, which every page should declare, any of these characters can simply be typed into the HTML. HTML syntax explains why UTF-8 made most character references unnecessary, and character references covers the cases where they are still needed: when a character is invisible in the source, like the no-break spaces, or when an editor or a pipeline cannot be trusted with it.
On the keyboard, macOS types the curly quotes with Option and the bracket keys, the en dash with Option-hyphen and the em dash with Shift-Option-hyphen. Windows has Alt codes on the numeric keypad (Alt+0150 for the en dash, Alt+0151 for the em dash, Alt+0147 and Alt+0148 for the double quotes), and Linux desktops offer a Compose key. In practice, most curly quotes on the web come from software that converts as you type or at build time.
Quotation marks in other languages
Quotation marks are not universal. German opens low and closes high, „so“ with ‚single‘ quotes inside, and German books also use »reversed guillemets«. French uses « guillemets » with narrow spaces inside. Japanese uses corner brackets, 「like this」. Type for people, page 10, takes these languages further.
HTML can choose the marks for you. The q element, for a short inline quotation (text-level semantics), gets its marks from CSS through the quotes property, which defaults to auto: marks appropriate to the element's language. In Chrome, a nested q inside <p lang="de"> renders „…‚…‘…“, in lang="ja" 「…」, and in lang="fr" «…» but without the narrow spaces French typography expects. CSS for people shows :lang(), which can set quotes explicitly where the defaults fall short:
This keeps the marks out of the text, so the lang attribute, not the author's keyboard, decides them.
Never in code
Every one of these conversions is wrong inside code. A curly quote is not a string delimiter, an en dash is not a minus or an option flag, and a no-break space is not whitespace to most programming languages. Commands copied from a blog that "smartened" them fail in confusing ways. Any converter, whether a CMS plugin, a Markdown processor's typography option or a hand-written build step, must skip code, pre, kbd and samp, and the attribute values in the HTML around them.
Hanging punctuation
A paragraph that opens with a quotation mark looks indented by the mark's width, because the mark sits inside the text block. Typographers hang it in the margin, so the letters align with the lines below. The CSS property is hanging-punctuation, with values first, last, allow-end and force-end:
As of October 2026 only Safari supports it, and only in Safari 26.5 does first also hang straight quotes; Chrome and Firefox ignore the declaration. It is a pure enhancement: where it is not supported, the quotation simply sits inside the block, as it always has, so it is safe to use.
7. Dingbats and symbol fonts
Printers always had ornaments cast in metal like letters. Digital dingbat fonts, whose glyphs are pictures, continue the tradition, and two of them show the right and the wrong way to do it.
Zapf Dingbats, from the characters U+2701 to U+271D
Wingdings, from the letters A to Z
ITC Zapf Dingbats, drawn by Hermann Zapf and released in 1978, became one of the 35 fonts built into PostScript printers with Apple's LaserWriter Plus, so it was everywhere desktop publishing went. When Unicode was assembled, its symbols were given code points of their own, most of them in the Dingbats block that starts at U+2700: the scissors are U+2702, the check mark U+2713. The first specimen was produced by typing those characters. Text that contains them says "check mark" to any software that reads it, and any font with the character can draw it.
Wingdings, which Microsoft made in 1990 from symbol fonts licensed from Charles Bigelow and Kris Holmes, took the other route. Its pictures sit on the code points of ordinary letters: the second specimen was produced by typing A to Z. The text is really letters, and the font only paints over them. That breaks as soon as the text leaves the font:
- Copy and paste, screen readers and fallback all get the letters: a row of "check marks" is read, and shown without the font, as a string of capitals.
- Meaning appears that nobody intended. In 1992, shortly after Windows 3.1 shipped, people noticed that typing "NYC" in Wingdings gives a skull and crossbones, a Star of David and a thumbs up, and read it as an antisemitic message. Microsoft called the arrangement coincidence; when it made the Webdings font in 1997, the same letters were chosen to give an eye, a heart and a city.
Many Wingdings pictures later received their own Unicode code points, in Unicode 7.0. The rule that follows applies beyond dingbats: the characters in a document should mean what they show. A font changes how a character looks, never what it is.
8. Icon fonts, and why SVG replaced them
Around 2012, the Wingdings idea came back for web icons. An icon font, such as early Font Awesome or GitHub's Octicons, packs a site's icons into one web font, usually on code points in Unicode's Private Use Area, a range set aside for characters whose meaning only the font knows. CSS then inserts them:
It was attractive: one request, and icons that take the text color and scale with the text. It also inherited every weakness of a font, plus those of Wingdings:
- Accessibility. The icon is a character with no meaning outside the font. A screen reader may announce a Private Use Area character as nothing, as an unknown symbol or as a code point, and a button containing only an icon has no accessible name at all. Authors had to add
aria-hidden="true"to the icon and hidden text to the control, and often did not. - Font loading failures. If the font is blocked, slow or fails, the reader sees empty boxes or nothing, for the very controls they need.
- Font overrides. A reader who replaces web fonts with one they read more easily, such as a dyslexia-friendly face, replaces the icons too, and gets boxes.
- Rendering. Icons get anti-aliasing and hinting meant for letters, which can blur them, and one glyph is one color.
GitHub's post "Delivering Octicons with SVG" (February 2016) gave these reasons for switching its whole site: icons that were blurry in some browsers, a flash while the font loaded, and readers who override fonts seeing empty squares. SVG icons are images, not text, so none of the font problems apply: an inline svg can have a title or be marked decorative, uses currentColor to take the text color, renders crisply, and can have several colors. SVG icon systems shows the symbol and use pattern, and accessible SVG how to name or hide an icon:
Icon fonts survive in older projects; for new work, SVG is the default.
9. Emoji
Emoji look like pictures, but they are characters, in the same text stream as letters, with Unicode code points, names and properties. A font makes them pictures; being text makes them meaningful to software. That puts them on the right side of the rule from section 7.
Emoji fonts
Ordinary glyphs are outlines filled with one color. Emoji need color, and the industry produced four formats inside OpenType, which is why the same emoji looks different on each platform:
- COLR/CPAL: glyphs built from layers of ordinary outlines, each with a color from a palette. Version 0 has flat colors, works in every major browser and is used by Microsoft's Segoe UI Emoji. Version 1 adds gradients; Chrome has supported it since version 98 and Firefox since 107, Safari not yet.
- sbix and CBDT/CBLC: bitmap images per glyph, from Apple (Apple Color Emoji) and Google (the older Noto Color Emoji).
- SVG in OpenType: an SVG document per glyph, from Adobe and Mozilla, supported by Firefox and Safari but not by Chrome.
For web developers, the consequence is that emoji come from the color emoji font on the reader's system, so the page cannot know exactly what the reader will see; font-variant-emoji, below, can only ask for a form, and Fonts in CSS explains why the emoji generic family was dropped from the specification.
Text or emoji presentation
Some characters existed long before emoji, such as the heart U+2764, the check mark U+2714 and the smiling face U+263A, and they can be drawn either as a black-and-white text symbol or as a color emoji. Two invisible variation selectors choose: U+FE0E after the character asks for text presentation, U+FE0F for emoji presentation. Without one, the platform decides.
CSS adds font-variant-emoji, with values normal, text, emoji and unicode (follow Unicode's default for each character). Chrome supports it from version 131 and Firefox from 141; Safari has it only in its Technology Preview, so as of October 2026 it is not Baseline. It is a request, not a command. In Chrome, emoji turned the plain smiling face into color and text turned it back, but a grinning face stayed in color: no font had a text version. For a symbol that must look like text, such as a check mark in a table, the variation selector in the content is the more dependable choice.
Modifiers and sequences
Many emoji are several characters drawn as one glyph. Five skin tone modifiers (U+1F3FB to U+1F3FF), added in Unicode 8.0 in 2015, follow a person or a hand. The zero-width joiner (U+200D) glues emoji into a sequence that a font may draw as one picture:
The sequence is one grapheme, one character as a reader perceives it, but four code points and seven UTF-16 code units, so "…".length in JavaScript says 7. Code that truncates text, counts characters for a limit or reverses a string must use Intl.Segmenter with granularity: "grapheme", or it will cut emoji in half and leave broken pictures, a point Anatomy and vocabulary made about characters and glyphs in general.
Emoji and accessibility
Because emoji are characters, screen readers read them, by their Unicode names: a party popper is read as "party popper", and five clapping hands are read as "clapping hands" five times. An emoji used as punctuation in every heading, or a row of them as decoration, becomes a recitation. Names describe the picture, not the intent. Three habits help:
- Use emoji in moderation and never as the only label of a control or the only sign of a status.
- When an emoji is decoration, hide it from assistive technology:
<span aria-hidden="true">around it. - When it carries meaning that its name does not, give it one:
<span role="img" aria-label="Launch day">.
Emoji also cross cultures less smoothly than they seem: a gesture friendly in one place is rude in another, and one platform's smile is another's grimace.
10. Summary
| Concept | Key points |
|---|---|
| Features | Alternates live in the font as OpenType features with four-letter tags; CSS asks, the font answers. Subsets can drop features; check the file. |
| Figures | Lining or old-style; proportional or tabular. lining-nums tabular-nums for tables, dashboards and changing values. |
| Tables | Align numbers to the end with equal decimal places; text-align: "." is unsupported. |
| Fractions, zero, ordinals | Apply diagonal-fractions to the fraction only; slashed-zero for codes; ordinals differ by language, so generate them with Intl.PluralRules. |
| Small caps | all-small-caps for abbreviations. Without smcp the browser fakes thin capitals unless font-synthesis-small-caps: none. |
| Ligatures | Common and contextual on by default; turn them off in code. Programming ligatures are a personal editor setting. |
| Alternates and position | case needs font-feature-settings; stylistic sets use @font-feature-values and font-variant-alternates (Baseline 2023). font-variant-position does nothing in Chrome and Safari without the font's glyphs; keep sup and sub. |
font-feature-settings | One list that each declaration replaces. Prefer font-variant-*; raw tags only for features with no named value. |
| Punctuation | Curly quotes and the apostrophe ’; primes; × and −; en dash for ranges, em dash for breaks; ellipsis; no-break spaces. Quotes vary by language, and q with lang chooses them. Never convert code. hanging-punctuation is Safari only. |
| Dingbats | Zapf Dingbats became Unicode characters (from U+2700), so the text means what it shows; Wingdings paints over letters, which copy, screen readers and fallback reveal. |
| Icon fonts | Private Use Area characters that screen readers cannot name and that turn to boxes when the font fails or is overridden. SVG replaced them. |
| Emoji | Characters drawn by system color fonts. Variation selectors and font-variant-emoji (Chrome, Firefox) choose the form. Sequences are one grapheme of many code points. Screen readers read their names. |
