Guide Developer Tools 6 min read

Chinese text pulls padded Markdown table pipes out of line in Chrome on macOS

Markdown tables are padded so their pipes line up, usually by counting each Chinese character as two columns. Measured in Chrome on macOS with its default fonts, a Han character drew about 1.66 columns wide, so padded pipes still missed, though GitHub's rendered table ignores the padding.

Maya Lin
Digital Platforms Analyst
Published 9 Nov 2026, 9:08 AM (SGT)
Share:
Lines of colour-highlighted program code on a dark screen, viewed at an angle. Lines of colour-highlighted program code on a dark screen, viewed at an angle. Photo by Simon Petereit on Pexels
Advertisement

A Markdown table is plain text: cells between pipes, with a row of hyphens under the header. The usual padding rule, which lines the pipes up, counts each Chinese character as two columns. We tested that rule on 8 November 2026 in Chrome on macOS with its default monospace fonts. A cell holding the name 李小龍 (Lǐ Xiǎolóng), padded exactly so, left its pipe about one column left of the rows around it, because Chrome drew each Han character about 1.66 ASCII cells wide. A five-syllable Korean cell missed by about 2.8 columns. None of it changes the table GitHub renders, which ignores the padding.

Delimiter rows form up with a single hyphen under each column

Tables are an extension in the GitHub Flavored Markdown spec, version 0.29-gfm of 6 April 2019, section 4.10; core CommonMark has none. What makes a table is the delimiter row, each cell of it one hyphen or more, with an optional colon at either end to set alignment.

Outer pipes are optional, though a pair "is also recommended for clarity of reading". And "Spaces between pipes and cell content are trimmed", which is why ragged example 199 still renders as a clean two-column table. The spec is licensed CC BY-SA 4.0, as the front matter of spec.txt in GitHub's cmark-gfm repository states.

Headers that break ranks with the delimiter row leave GitHub no table to render

"The header row must match the delimiter row in the number of cells. If not, a table will not be recognized," the spec says at example 203; what remains is a paragraph with pipes in it. A short body row gets empty cells, and a long one loses its surplus, because "the excess is ignored" (example 204). A sixth value under a five-column header vanishes.

We sent all eight table examples, 198–205, through GitHub's own renderer with gh api markdown on 8 November 2026. In its document mode, each came back as the HTML the spec gives.

Plain text with no pipe falls in as a row until a blank line halts the table

"The table is broken at the first empty line, or beginning of another block-level structure," says the spec. A line starting with > opens a quotation and ends it (example 201). A plain line straight under the last row, with no pipe at all, becomes a row instead (example 202). On GitHub, in our test, a line opening an HTML block such as <div>, or indented four spaces, also ended the table. Renderers differ: pandoc 3.13 ended the table before the plain line, while marked 4.3.0 kept it as a row.

Until 8 November 2026 this Markdown table formatter deleted every blank line before parsing, so two tables separated by one came out as a single table, the second delimiter row becoming literal --- cells, with no note.

The revision made that day reads a paste as a document. Tables end at a blank line or another block, an HTML block or an indented line included; surrounding text and fenced code come back line for line, and each table is formatted separately. Paste a two-column table, a blank line and Source: our example.: the source line returns as text, with a note that it was kept. Through GitHub's renderer the revised output matches its input on six of the eight examples; on 203 and 204 it repairs the cell counts deliberately and says so.

An escaped pipe stays in line even inside a code span

A literal pipe in a cell is written \|, and example 200 shows the rule holding "including inside other inline spans", so a pipe inside backticks still splits the cell unless escaped. GitHub's reference implementation, cmark-gfm, scans a cell the same way.

A doubled backslash does not cancel the escape. We rendered | x\\|y | z | under a two-column header on GitHub and got two cells, x|y and z. Pandoc agreed; marked split the first cell. On GitHub a backslash pressed against a pipe always escapes it.

Advertisement

Wide characters march two abreast on a fixed cell grid

Unicode's East Asian Width property, defined in UAX #11 (revision 46, 31 July 2026), has Wide and Fullwidth classes. In EastAsianWidth.txt for Unicode 18.0.0, they cover 183,866 code points, of which 44,837 are unassigned by our count. Two cells per such character is a grid convention, written down in Markus Kuhn's wcwidth.c of 2007. UAX #11 says the property is "not intended for use by modern terminal emulators without appropriate tailoring".

Unicode 18.0's emoji-test file lists 3,963 fully qualified emoji, and only 1,193 are a single code point. Padding by code points is right only for the 1,141 that are exactly two code points long, and wrong for the other 2,822 (71.2%, our arithmetic). Counting each emoji as one wide grapheme gets all of them to two columns, a formatter's convention: 482, flags and keycaps among them, hold no Wide code point. The counting rules for punctuation are in our guide to Chinese punctuation width.

Han characters drawn by Chrome on macOS fall out of step by about a third of a column

We drew an 11-row table in a <pre> block at 14.4 px, Chrome on macOS, default fonts, stack ui-monospace, 'SF Mono', Menlo, Consolas, monospace (Menlo, going by its 0.602 em cell), measuring each pipe in ASCII cells of 8.67 px. Han characters and kana drew about 1.66 cells wide, Hangul syllables about 1.44.

CellPadded by code pointsPadded at width 2
李小龍+1.98−1.02
北京大学 (Běijīng Dàxué)+2.64−1.35
서울대학교+2.18−2.82
👨‍👩‍👧 (five code points)−2.92+0.08

Offsets are from the header's pipe, in ASCII cells; plus is rightward. Width-2 padding over-pads about a third of a column per Han character and 0.56 per Hangul syllable, so the miss grows with the cell: 北京 missed by 0.68. Emoji drew about 2.08 cells, so ten in a cell come to about 0.8 of a column.

Font metrics, read from advance widths rather than drawn text, agree: PingFang SC's 中 is 1.661 Menlo cells. UAX #11 puts a Courier-style Latin cell at "generally 3/5 of an Em" and an ideograph at one em; dividing the two, as we did, gives about 1.67. We found no published measurement of the drift, though the problem is older than our test: the markdown-table README warns that "Full-width characters and emoji mess up delimiter alignment".

Raw pipes keep step only in the font a reader's screen supplies

Plain-text alignment depends more on the reader's font than on the file itself. Padding that holds on a terminal's grid came out short in Chrome on macOS. A CJK monospace font, drawing those characters at exactly two Latin cells, would be expected to line it up; we measured none, nor any named editor. As the same README puts it, what is visible "depends on where the text is displayed".

The rendered table is the steady part. GitHub's renderer still follows every table example in the 2019 spec, on our test of 8 November 2026, and trims every space the padding adds, whichever font drew the source.

Advertisement
Maya Lin
Digital Platforms Analyst

Maya Lin covers SaaS platforms, workflow automation, creator tools, and productivity software for RECATOOLS.

View author profile → · Editorial policy

About this byline Maya Lin is a RECATOOLS editorial persona used for platform and productivity coverage. Articles are produced and reviewed under RECATOOLS editorial supervision.

Corrections policy

Advertisement