JSPM

  • Created
  • Published
  • Downloads 1145
  • Score
    100M100P100Q118750F
  • 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

Feature Before After
Smart quotes "straight" “curly”
'apostrophes' ‘apostrophes’
Em dashes wait - why did you wait—why did you
En dashes 1-5 1–5
January-March January–March
Minus signs -5 −5 (proper Unicode minus)
Ellipsis ...
Multiplication 5x5 5×5
3*4 3×4
Math symbols !=
+- ±
<=
>=
~=
Legal symbols (c) ©
(r) ®
(tm)
Arrows ->
<-
<->
Prime marks 5'10" 5′10″
Fractions (optional) 1/2 ½
3/4 ¾
Degrees (optional) 20 C 20 °C

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."

// Symbol transforms are included by default
transform('Wait... 5x5 != 25 (c) 2024')
// → Wait… 5×5 ≠ 25 © 2024

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

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

Transform Options

import { transform } from 'punctilio'

// Enable optional transforms
transform('Add 1/2 cup at 20 C', {
  fractions: true,  // 1/2 → ½
  degrees: true     // 20 C → 20 °C
})
// → Add ½ cup at 20 °C

// Disable symbol transforms if you only want quotes/dashes
transform('5x5 = 25', { symbols: false })
// → 5x5 = 25 (unchanged)

// Punctuation style: american (default), british, or none
transform('"Hello".', { punctuationStyle: 'american' }) // → "Hello."
transform('"Hello."', { punctuationStyle: 'british' })  // → "Hello".

// Dash style: american (default), british, or none
transform('word - word', { dashStyle: 'american' }) // → word—word
transform('word - word', { dashStyle: 'british' })  // → 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. Options:

  • separator: Boundary marker for HTML elements (default: "\uE000")
  • symbols: Include symbol transforms (default: true)
  • fractions: Convert common fractions like 1/2 → ½ (default: false)
  • degrees: Convert temperature notation like 20 C → 20 °C (default: false)
  • punctuationStyle: "american" (default) puts periods/commas inside quotes; "british" puts them outside; "none" leaves unchanged
  • dashStyle: "american" (default) uses unspaced em dash (—); "british" uses spaced en dash ( – ); "none" skips dash conversion

Quote Functions

niceQuotes(text, options?)

Converts straight quotes to curly quotes. Options: separator, punctuationStyle.

Handles: opening/closing quotes, contractions (don't), possessives (dog's), year abbreviations ('99), special cases ('n').

Dash Functions

hyphenReplace(text, options?)

Converts hyphens to proper dashes. Options: separator, dashStyle.

Handles: em dashes (word - wordword—word), en dashes for ranges (1-51–5, Jan-MarJan–Mar), minus signs (-5−5). Preserves horizontal rules and compound words.

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

Symbol Functions

ellipsis(text, options?)

Converts three periods to ellipsis: ...

multiplication(text, options?)

Converts multiplication patterns: 5x55×5, 3*43×4

mathSymbols(text)

Converts math operators:

  • !=
  • +- or +/-±
  • <=
  • >=
  • ~= or =~

legalSymbols(text)

Converts legal symbols:

  • (c)©
  • (r)®
  • (tm)

arrows(text, options?)

Converts arrow patterns:

  • -> or -->
  • <- or <--
  • <-> or <-->

primeMarks(text, options?)

Converts straight quotes after numbers to prime marks:

  • 5'10"5′10″ (feet and inches)
  • 45° 30' 15"45° 30′ 15″ (coordinates)

fractions(text)

Converts common fractions: 1/2½, 1/4¼, 3/4¾, etc.

degrees(text)

Converts temperature notation: 20 C20 °C, 68 F68 °F

symbolTransform(text, options?)

Applies all symbol transforms except fractions and degrees.

Constants

  • DEFAULT_SEPARATOR: The default separator character ("\uE000")
  • months: Regex-ready string of month names for date range detection

Character Reference

Input Output Unicode Name
" " U+201C Left double quotation mark
" " U+201D Right double quotation mark
' ' U+2018 Left single quotation mark
' ' U+2019 Right single quotation mark (apostrophe)
-- U+2014 Em dash
- (range) U+2013 En dash
- (negative) U+2212 Minus sign
... U+2026 Ellipsis
x (multiply) × U+00D7 Multiplication sign
!= U+2260 Not equal
+- ± U+00B1 Plus-minus
<= U+2264 Less than or equal
>= U+2265 Greater than or equal
(c) © U+00A9 Copyright
(r) ® U+00AE Registered
(tm) U+2122 Trademark
-> U+2192 Right arrow
<- U+2190 Left arrow
<-> U+2194 Left-right arrow
' (after digit) U+2032 Prime (feet, arcminutes)
" (after digit) U+2033 Double prime (inches, arcseconds)

License

MIT © Alexander Turner