Reading Level

Flags copy that reads above its audience's grade level and points at the sentences and wordy phrases that cause it.

StopsCopy harder to read than its audience needs

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/reading-level.json

The files land in your project. They're yours to edit.

Usage

reading-level-usage.tsx
import { ReadingLevel } from "@/components/ui/reading-level"

return <ReadingLevel label="Refund email" defaultValue={NOTICE} targetGrade={8} />

What it catches

  • Grade above the target you set (default 8)
  • Sentences that are both long and dense, tinted in place
  • Wordy words and phrases with a plainer swap
  • Non-English text, where it shows a notice instead of a grade

Props

ReadingLevel props
PropTypeDefault
label

Visible label.

string"Text"
value

Text (controlled).

string—
defaultValue

Starting text when uncontrolled.

string""
onValueChange

Called with the new text on every edit or swap.

(value: string) => void—
targetGrade

US school grade to aim for. 8 suits most public writing.

number8
swaps

Wordy word → plain swap.

Record<string, string>PLAIN_WORDS
maxSentences

How many of the hardest sentences to list.

number3
bands

Plain-English band labels per grade range. Override to translate.

typeof READING_BANDSREADING_BANDS
gradeText

`{grade}` is filled in.

string"Grade {grade}"
targetText

`{target}` is filled in.

string"aim for {target} or lower"
hardTitle

Heading above the list of hardest sentences.

string"Hardest sentences"
longText

`{words}` is filled in.

string"{words} words. Try splitting it."
denseText

Reason shown for a sentence that is dense rather than just long.

string"Lots of long words."
swapsTitle

Heading above the plain-word swaps.

string"Plainer words"
swapLabel

Accessible label of a swap. `{word}` and `{plain}` are filled in.

string"Replace “{word}” with “{plain}”"
aboveTargetText

Announced when the grade is above the target.

string"Harder than your target."
onTargetText

Announced when the grade is at or below the target.

string"Within your target."
unsupportedText

Shown instead of a grade when the text isn't English (Hindi, Marathi, French...), where the formula means nothing.

string"Readability grade is English-only"
unsupportedHint

Second line of the unsupported state.

string"This text looks like another language, so no grade is sh…

* required. Also accepts the native props of <textarea> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible <label> tied to the textarea, with the grade wired via aria-describedby
  • On/above-target status announced through a polite sr-only live region
  • Each swap button has an aria-label naming the word and its replacement
  • After a swap, focus moves to the next swap button (or back to the field)
  • The tinted backdrop is aria-hidden; sentence difficulty also shown as text

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Syllables are estimated from vowel groups (roughly ±10%), so grades are approximate
  • Flesch-Kincaid is an English-only formula and ignores meaning, layout and audience
  • The swap list is a starter set; it will miss domain jargon

What it stops

Copy harder to read than its audience needs

Also known as

readability · plain language · flesch-kincaid · writing assistant · content editor · cms · copywriting · grammarly

Field type

Dependencies

Pairs well with