Getting started
setwell is one pass over HTML that applies the details of fine typesetting browsers don't do by themselves. It changes characters (curly quotes, dashes, minus signs), joins words that shouldn't be split across lines, and adds a few small spans that its stylesheet finishes (small caps, hanging quotes).
"It's NASA's call," she said -- on pp. 10-20 of the 1st report.
“It’s NASA’s call,” she said—on pp. 10–20 of the 1st report.
Install
npm install setwellNo dependencies. ESM and CommonJS, with types.
On a string
For server rendering, static sites and build steps:
import { setwell } from "setwell";
const out = setwell(html);
const out2 = setwell(html, { disable: ["hyphenate"], ignore: ".no-setwell" });Only text changes. Tags, attributes, comments and any text setwell didn't touch come out byte for byte as they went in, so diffs stay readable. Character references in text are understood (" is a quote) and kept as written where nothing changed.
On an element
For content that's already on the page:
import { setwellElement } from "setwell/dom";
const changes = await setwellElement(document.querySelector("article"), options);It works on the element in place and returns the changes it made. It's async because the hyphenation patterns (about 30 KB) load only when that rule is on. The result is the same as the string API's for the same HTML.
The stylesheet
import "setwell/setwell.css";
// or, as a string:
import { css } from "setwell/css";It only styles setwell's own spans (setwell-caps, setwell-ord, the hanging and optical pulls), so it can't change anything else on your page. The amounts are chosen to suit typical text faces. Each is a custom property with a default, so you can change one without copying the file:
.article {
--setwell-hang-double: 0.42em; /* how far an opening " hangs (0.46em) */
--setwell-hang-single: 0.25em; /* and an opening ' (0.27em) */
--setwell-caps-size: 0.85em; /* size of acronyms (0.9em) */
--setwell-caps-spacing: 0.04em; /* and their letter-spacing (0.03em) */
--setwell-ord-size: 0.65em; /* ordinal suffix size (0.7em) */
}Options
| Option | Default | Meaning |
|---|---|---|
disable | [] | Rule or group ids to turn off. See rules. An unknown id throws, so typos don't pass silently. |
ignore | Selector(s) for more elements to leave alone, added to the defaults below. | |
scope | Selector(s): only text inside matching elements is processed. | |
emDash | "closed" | "hair" puts hair spaces around em dashes. |
widows | { maxChars: 16, minWords: 4 } | The last two words are joined when together they're at most maxChars long, in blocks of at least minWords words. |
output | "unicode" | String API: "entities" writes new characters as —, and so on. |
In the string API, ignore and scope take type, *, #id, .class and attribute selectors, compounds, commas, and descendant and > combinators. The DOM API takes any selector the browser does.
Always ignored:
head,
title,
script,
style,
template,
noscript,
noembed,
noframes,
iframe,
object,
pre,
xmp,
plaintext,
code,
kbd,
samp,
var,
tt,
textarea,
input,
select,
datalist,
math,
svg,
[contenteditable],
[translate="no"],
[data-setwell="off"],
[class^="setwell-"],
[class*=" setwell-"]The last two are setwell's own spans, which is what makes a second pass change nothing. Add data-setwell="off" to any element to skip it.
Reports
import { report } from "setwell";
const { html, changes } = report(input);
// changes: [{ rule: "quotes.double", kind: "replace", before: '"', after: "“", offset: 3 }, …]Each change says which rule made it, what it was before and after, and where it is in the input. The playground lists them next to a before-and-after preview.
What it won't do
- Languages other than English. Quotes, spacing and hyphenation follow US English conventions.
- Know where lines actually break. Hanging quotes use a pull and a matching push so they only hang at the start of a line. For headings, CSS
text-wrap: balanceworks well alongside setwell.