JSPM

  • Created
  • Published
  • Downloads 1145
  • Score
    100M100P100Q118746F
  • License MIT

Smart typography transformations: curly quotes, em-dashes, en-dashes, and more

Package Exports

  • punctilio

Readme

punctilio

punctilio (n.): a fine point of conduct or procedure

Smart typography transformations for JavaScript/TypeScript. Converts ASCII punctuation to typographically correct Unicode characters. Originally built for my personal website.

Features

  • Smart quotes: "straight""curly" and 'apostrophes''apostrophes'
  • Em dashes: word - word or word--wordword—word
  • En dashes: 1-51–5 (number ranges), January-MarchJanuary–March (date ranges)
  • Minus signs: -5−5 (proper Unicode minus)
  • Handles edge cases: contractions, possessives, nested quotes, year abbreviations ('99), "rock 'n' roll"

Why another typography library?

Existing solutions like SmartyPants struggle with:

  • Apostrophe ambiguity: Is 'Twas an opening quote or apostrophe? (It's an apostrophe)
  • Cross-element text: When quotes span <em>"Hello</em> world", most libraries fail
  • Context sensitivity: '99 (year) vs 'hello' (quoted) vs don't (contraction)

punctilio handles these through thorough regex patterns and an optional separator character for processing text that spans HTML elements.

Installation

npm install punctilio
# or
pnpm add punctilio

Usage

Basic

import { transform, niceQuotes, hyphenReplace } from 'punctilio'

// Apply all transformations
transform('"Hello," she said - "it\'s pages 1-5."')
// → "Hello," she said—"it's pages 1–5."

// Or use individual functions
niceQuotes('"Hello", she said.')
// → "Hello", she said.

hyphenReplace('word - word')
// → word—word

With HTML Element Boundaries

When processing text that spans multiple HTML elements, use a separator character to mark boundaries:

import { transform, DEFAULT_SEPARATOR } from 'punctilio'

// Your HTML: <p>"Hello <em>world</em>"</p>
// Extract text with separator between elements:
const text = `"Hello ${DEFAULT_SEPARATOR}world${DEFAULT_SEPARATOR}"`

const result = transform(text, { separator: DEFAULT_SEPARATOR })
// → "Hello \uE000world\uE000"
// The separator is preserved; split on it to restore to your elements

For a complete implementation showing how to use this with a HAST (HTML AST) tree, see the transformElement function in TurnTrout.com.

API

transform(text, options?)

Applies all typography transformations (quotes + dashes).

niceQuotes(text, options?)

Converts straight quotes to curly quotes. Handles:

  • Opening/closing double quotes: "" or "
  • Opening/closing single quotes: '' or '
  • Contractions: don'tdon't
  • Possessives: dog'sdog's
  • Year abbreviations: '99'99
  • Special cases: 'n' in "rock 'n' roll"

hyphenReplace(text, options?)

Converts hyphens to proper dashes. Handles:

  • Em dashes: word - wordword—word
  • En dashes for number ranges: 1-51–5
  • En dashes for date ranges: Jan-MarJan–Mar
  • Minus signs: -5−5
  • Preserves: horizontal rules (---), compound words (well-known)

enDashNumberRange(text, options?)

Converts number ranges only: pages 10-20pages 10–20

enDashDateRange(text, options?)

Converts month ranges only: January-MarchJanuary–March

minusReplace(text, options?)

Converts hyphens to minus signs in numerical contexts: -5−5

License

MIT © Alexander Turner