What actually breaks resume parsing: a controlled experiment

We took 33 resume templates, changed one CSS declaration at a time, and extracted the text back out of every render. Which formatting choices destroy text, which silently reorder it, and which turn out to be harmless.

Last reviewed

Almost everything written about “ATS-friendly formatting” is assertion. Avoid tables, avoid columns, avoid graphics, avoid unusual fonts — advice repeated between blogs until it reads as established, with no measurement underneath it. Some of it is right. Some of it is folklore. Nobody publishes which is which, because finding out requires pointing a render pipeline and a text extractor at each other, and that is a product rather than a blog post.

We have both, so we ran the experiment. On 2026-08-14 we produced 330 resume PDFs — our 33 templates, each rendered once unmodified as a control and once for every treatment below — extracted the text back out of each one the way an applicant tracking system does, and checked 61 fields per render for three things: did the text survive, did it come out in the right order, and were the section headings still words.

A treatment is one CSS declaration added to a template’s own stylesheet. Control and treatment differ by that one line and nothing else, so whatever changed is attributable to it. Every treatment is something resume templates on sale today actually do; two of them are defects we shipped ourselves and had to find the hard way.

The result, in one chart

How many of our 33 templates each single declaration damaged. The control damaged none.

  • Bullet marker as an absolutely positioned ::before33 of 33
  • letter-spacing: 0.18em on section headings33 of 33
  • letter-spacing: 0.12em on section headings33 of 33
  • text-transform: uppercase with 0.14em tracking33 of 33
  • Sections floated into a second column29 of 33
  • font-variant-numeric: tabular-nums4 of 33
  • letter-spacing: 0.10em on section headings4 of 33
  • letter-spacing: 0.08em on section headings0 of 33
  • letter-spacing: 0.06em on body text0 of 33

What each one actually did

DeclarationText lostOrder brokenHeadings broken
Bullet marker as an absolutely positioned ::beforeThe standard way to get a custom bullet glyph with exact control over its position. Every CSS tutorial teaches it.0330
letter-spacing: 0.18em on section headingsWide tracking on small uppercase headings is the single most common typographic move in paid resume templates.0033
letter-spacing: 0.12em on section headingsHalfway between the ceiling and the value that visibly fails, to place the boundary rather than bracket it.0033
text-transform: uppercase with 0.14em trackingThe two moves together, which is how they are almost always shipped — a heading is rarely tracked without also being capitalised.0033
Sections floated into a second columnThe oldest way to build a two-column resume, and still the most common outside of grid-based templates.0290
font-variant-numeric: tabular-numsMakes columns of figures line up. Recommended constantly for dates and metrics, and it is genuinely better typography.400
letter-spacing: 0.10em on section headingsOne notch past the ceiling. A value nobody would think twice about writing.004
letter-spacing: 0.08em on section headingsThe value this codebase treats as its ceiling. Included to test whether the ceiling is a guarantee or a guideline.000
letter-spacing: 0.06em on body textAirier body copy. Common in templates that describe themselves as modern or minimal.000

Counts are templates out of 33, not fields. A template counts as losing text if any of its 61 fields did not come back out.

The three findings worth your time

1. The worst failure destroys nothing and is completely invisible

Building a bullet marker out of position: relative plus an absolutely positioned ::before — which is what every CSS tutorial teaches, and the only way to get precise control over a custom bullet glyph — lost zero fields and broke the reading order of every single one of our 33 templates.

The mechanism: Chromium writes PDF text in paint order, and a positioned element paints in a later layer than its unpositioned siblings. So every bullet in the document is emitted after every employer. Nothing is missing. The page looks perfect. What a parser reads is a list of companies followed by a detached list of achievements, with no way to tell which belonged to which — and a screener seeing your accomplishments attributed to nobody is reading a different document from the one you proofread.

This is the finding we would most like people to take away, because it is the one that cannot be caught by looking. We shipped it, and we found it with an extraction test rather than with our eyes.

2. Letter-spacing has a threshold, and it is lower than it looks

Wide tracking on small uppercase headings is the most common typographic move in paid resume templates. Past a point, the PDF stops containing a word and starts containing a sequence of separate characters. We measured where that point is:

  • letter-spacing: 0.08em no template affected
  • letter-spacing: 0.10em 4 of 33 templates broken, extracting as Exper ience
  • letter-spacing: 0.12em 33 of 33 templates broken, extracting as E X P E R I E N C E
  • letter-spacing: 0.18em 33 of 33 templates broken, extracting as E X P E R I E N C E

The interesting part is the middle. At 0.10em the failure is partial — a handful of typefaces split and the rest hold — which is the worst possible place for a boundary to sit, because it means the same declaration is safe in one template and broken in another and nothing about either tells you which. By 0.12em every template has failed.

We hold our own templates at 0.08em or below. That number was a guess when we wrote it down; it is a measurement now.

3. A typographic refinement can delete every digit

font-variant-numeric: tabular-nums makes figures line up in columns. It is genuinely better typography for dates and metrics, and it is recommended constantly. On four of our 33 templates it deleted 96 fields — email addresses, phone numbers, every dated role — while rendering them all correctly on the page.

On the other 29 it did nothing at all. The difference is the typeface, not the declaration, which means the advice “don’t use tabular-nums” is wrong for most people and catastrophic for the rest, and no one reading a resume template can tell which group they are in.

What we did not find

Two treatments had no measurable effect on any template: letter-spacing on body text, and heading tracking at 0.08em. That is worth saying as loudly as the failures. A study that only reports damage is one that went looking for it, and a good deal of the formatting advice in circulation is a warning about something that turns out to be fine.

Floated two-column layouts came out worse than we expected — broken reading order on 29 of 33 — but not universally, which matches our own experience: a side column can be made to extract in the right order, and doing it takes deliberate work rather than a float.

What this study does not show

It measures our own templates. That is a limitation and it is also what makes it a controlled experiment: we can change one declaration in a document we control and hold everything else still, which is not possible with somebody else’s PDF export. It does not tell you how any particular competitor’s template behaves, and we are not going to guess.

It also measures extraction, not any specific employer’s software. Real applicant tracking systems are dozens of separate products that do not publish their behaviour. What we can say is that text which is not in the file cannot be read by any of them, and text in the wrong order is in the wrong order for all of them.

And our own templates are not exempt from trade-offs. The ones with a side column pay for it: their text comes out page by page, so a column lands after however much of the main flow fitted on page one. We publish that on each template’s own page and our scorer prints a note about it rather than quietly not counting it.

Method, in full

Reproducible, and we would rather you checked it than believed us.

  • Render. Each template is rendered to PDF through headless Chromium at Letter size with the template’s own @page margins, from the same sample resume, at normal density. Density is not CSS and the question here is what CSS does.
  • Treat. One stylesheet is appended after the template’s own, containing exactly one declaration block. No !important — it wins on cascade order, the way a designer’s own CSS would.
  • Extract. Text is pulled back out with pdfjs-dist, page by page, in the order the file emits it.
  • Measure text. 61 known values from the sample resume — name, contact details, every employer, role, bullet, school, skill, project and certificate — are each looked for with whitespace collapsed, so a value that merely wrapped across two lines is not counted as lost.
  • Measure order. Employer, then that employer’s first achievement, then the next employer. When any member of that sequence did not survive at all, order is recorded as unanswerable rather than as broken — losing your employer and printing it in the wrong place are two different findings and they get two different columns.
  • Measure headings. This one deliberately does not collapse whitespace, and getting that wrong is how our first run reported that wide tracking was harmless: E X P E R I E N C E collapses to experience and passes a check whose entire purpose is to notice it has been split into ten runs. An ATS looking for a section heading is doing a word match.

The script is scripts/extraction-study.ts and it runs with npm run study:extraction. It writes every per-template result, not just the summary above.

Summary you can paste

If you are writing about this, here is the whole thing in plain text. Please date it — templates change, and we re-run this when ours do.

GetFinalCV extraction study — measured 2026-08-14
Method: 33 resume templates rendered to PDF through headless Chromium, then the text extracted back out with pdfjs-dist, the way an applicant tracking system reads a file. Each template was rendered once unmodified as a control and once per treatment, where a treatment is a single CSS declaration added to the template's own stylesheet. 330 renders in total; 61 fields checked per render.

Control: 33 of 33 templates extracted every field, in the right order, with every section heading intact.

Findings:
- Bullet marker as an absolutely positioned ::before: broke reading order on 33/33.
- letter-spacing: 0.18em on section headings: broke section headings on 33/33.
- letter-spacing: 0.12em on section headings: broke section headings on 33/33.
- text-transform: uppercase with 0.14em tracking: broke section headings on 33/33.
- Sections floated into a second column: broke reading order on 29/33.
- font-variant-numeric: tabular-nums: lost text on 4/33 (96 fields total).
- letter-spacing: 0.10em on section headings: broke section headings on 4/33.
- letter-spacing: 0.08em on section headings: no measurable effect on any of 33.
- letter-spacing: 0.06em on body text: no measurable effect on any of 33.

Full method, per-template results and the code that produced them: https://getfinalcv.com/guides/what-css-breaks-resume-parsing

What to do with this

If you are choosing a resume template, none of this is checkable from the outside — which is the uncomfortable conclusion. A template that does any of the above looks completely normal, and the damage happens in a file you never open as text.

The one thing you can do is read your own PDF the way a parser does. Our checker does that for free with no account: it opens your file, pulls the text back out, and shows you what came out and in what order. If your bullets appear after your employers, you have found this exact bug in whatever produced your resume.

Related

  • What an ATS reads from your PDF

    The same resume, shown twice: the page as it prints, and the literal text a parser pulls out of it, in order. Four ways that text comes out wrong while the page looks perfect.

  • Are two-column resumes ATS-safe?

    Mostly yes, with one real cost nobody mentions. What decides whether a side column extracts in the right order, and what a column does to a two-page resume.

  • Fonts that break resume parsing

    A font can embed correctly, print perfectly, and still lose every digit in your dates. Three measured failures, including one that cost us a shipped typeface.

  • PDF or Word: which should you send?

    PDF, unless the posting says otherwise — and the reason is not the one usually given. What each format actually guarantees about the text a parser receives.

  • Turning your LinkedIn profile into a resume

    LinkedIn will hand you a PDF of your own profile. What that file actually contains, the three things in it that are the wrong shape for a resume, and what to fix first after importing it.