Integrate Typograph into this app's assistant responses. Inspect the framework, package manager, renderer, and installed versions first. Preserve the product's fonts, styling, components, and existing behavior.
SOURCE AND INSTALLATION
Source: https://github.com/calebduren/typograph
Install @calebduren/typograph using this app's package manager (npm install @calebduren/typograph for npm). Read the installed package's README and https://typograph.dev/integration.md. The package is ESM, runs in the browser, and requires Node 22+ for server use. Its source and integration fixture are in the repository above.
SELECTED SETTINGS
Smart punctuation: on
Non-breaking spaces: on
Hanging punctuation: on (opening quotes only)
Apply this exact configuration: Smart punctuation · Non-breaking spaces · Hanging punctuation. Demo highlighting is not a product feature and must not be installed.
INTEGRATION
Choose the integration that matches the app's existing renderer. Add the Remark plugin once in the assistant text-part renderer, preserving default and app-specific plugins and sanitization. Typography belongs in the presentation layer; keep transports, tool components, and message storage unchanged.
If the app uses AI Elements, find its owned MessageResponse component. With Streamdown, use remarkPlugins and preserve its defaults. If rules can change at runtime, inspect the installed versions and check MessageResponse and Streamdown memoization against examples/chat-integration/src/typography-response.tsx in the source repository. Do not force React key remounts.
If the app uses Cloudflare Agents, find the renderer fed by useAgentChat. Keep the Worker, Durable Object, WebSocket transport, persistence, and reconnect behavior unchanged. The same renderer integration applies.
With unified, supply the original Markdown to the transformer: processor.runSync(processor.parse(markdown), markdown). This preserves escaped punctuation and unfinished syntax. If the app has no compatible Markdown pipeline, explain the smallest compatible integration before introducing a new renderer.
Import typography from '@calebduren/typograph' and register [typography, { locale: 'en', punctuation: true, spacing: true }] only for responses known to be English. This is English-only, with one house style and no language detection. Other-language, mixed-language, or verbatim responses should opt out. If the app has no per-response language signal (a user or tenant language setting, the answer language in the system prompt, or a structured output field), register [typography, { locale: 'en', punctuation: { quotes: false, apostrophes: true } }] instead: the typographic apostrophe is correct across Latin-script languages, and quotation marks stay as written. Interface language alone is not a signal. Enable conservative unit, initial, and abbreviation joins. Leave shortWords and lastWords off. Keep plugin configuration stable between renders and preserve the original Markdown source for the Remark transform.
Import hangingPunctuation from '@calebduren/typograph/hanging' and import '@calebduren/typograph/hanging.css'. Add [hangingPunctuation, { locale: 'en' }] to the existing rehype pipeline AFTER sanitization. In Streamdown, preserve ...Object.values(defaultRehypePlugins) before it. In unified, use the existing Remark-to-Rehype conversion before it. This helper hangs opening quotes at the start of English, left-aligned paragraphs and headings outside lists. Quotes in bulleted and numbered lists stay inline. Preserve its generated span classes and data-typograph-hanging attribute in custom components; reserve gutter space and avoid clipping. It uses real text and standard CSS, with no native hanging-punctuation dependency or DOM measurements. It does not hang marks at every wrapped line ending. Check renderer memoization if this setting can change at runtime.
Preserve raw SDK messages for storage, tool execution, and Copy original. Transform rendered prose only. Keep code, math, HTML, URLs, link destinations, and escaped punctuation protected. Retain the existing renderer's security settings; this plugin is not a sanitizer.
FINISHED TEXT
If the app also shows AI text that is complete before display (scheduled briefs, email, summaries, titles, notifications), typeset it once when it is finished. For Markdown, use typeset(markdown, { target, locale: 'en', punctuation: true, spacing: true }) from '@calebduren/typograph/markdown'. Target 'web' returns HTML with hanging markup that needs the same hanging.css, 'email' returns HTML without hanging markup, and 'markdown' returns typeset Markdown with its formatting kept. Install unified, remark-parse, remark-gfm, remark-rehype, and rehype-stringify alongside it. For finished HTML, such as an email template that already contains the text, use typeset(html, { target: 'email', locale: 'en', punctuation: true, spacing: true }) from '@calebduren/typograph/html'; it returns the HTML with only typographic characters changed, curls template-escaped quotes such as ", and needs unified and rehype-parse. For plain strings that are not Markdown, use the synchronous typesetText(text, { locale: 'en', punctuation: true, spacing: true }) from '@calebduren/typograph'. Never typeset a stored string that also feeds SMS: one curly quote or nonbreaking space switches GSM-7 to UCS-2 and cuts a segment from 160 to 70 characters. If email is rendered outside JavaScript, typeset a separate copy with target 'markdown' at generation time and keep the stored original. Do not use @calebduren/typograph/static in a bundled app; it needs all seven parser peers installed. Skip this section if the app has no such text.
PARAGRAPH ENDINGS
For orphan control, prefer progressive-enhancement CSS text-wrap: pretty on completed assistant prose paragraphs, leaving normal wrapping during streaming. Do not force nonbreaking spaces between the final two words. Keep spacing.lastWords off unless explicitly requested; it requires phase: 'complete' after a verified successful finish for that message, never simply status === 'ready'. Unsupported CSS should fall back to normal wrapping.
VERIFY
Check English quotes and apostrophes, links and emphasis, literal code and math, streaming prefixes, stop/error/retry, and original-text copying. Verify narrow screens and long replies. Run the app's relevant tests and typecheck, then summarize the changes, checks, and any limitations.
Includes installation, English-only defaults, streaming behavior, and checks for your app.