Case

Sealed — envelopes that seal only when your text fits

A character counter for X, Bluesky and one SMS. Each envelope seals only if your words truly fit, and it names the curly apostrophe that shrinks a text from 160 characters to 70.

  • 6 images
Open the pageOpens boloop-works.pages.dev in a new tab. It is a separate site, not part of this one.

Image credit: Tink

A runnable character counter made of three envelopes: "X post", "Bluesky post" and "One text message". Each envelope seals with red wax only if your text really fits.

It opens with a sample letter already in place: "Running 10 minutes late — the train’s stuck outside Hongqiao…". Within the first second the slips slide in, X (125 / 280) and Bluesky (125 / 300) close and get sealed, and the text-message envelope can't close. Its slip sticks out above the flap, and the words above the red dashed fold line are the exact part that doesn't fit, with "Open · 55 over · sends as 2 texts" underneath. Why only 70? A dashed panel names the culprits: one em dash and a curly apostrophe. A single character outside the GSM alphabet switches a text message from 160 characters to 70. Press "Make it SMS-safe (swap 3)" and the third envelope seals at 125 / 160.

Type and the envelopes react to every keystroke. "Trim to fit" cuts an open envelope's text to fit, never inside a character. Undo brings the text back. Typing is never rewritten: only these buttons change your text. Emoji are listed as "can't swap" rather than silently removed. Copy text, and errors such as a blocked clipboard are shown and announced. Reduced motion shows the final state at once. Works down to 360 px phones. Nothing is stored or sent.

How it counts. X uses X's own open-source twitter-text 3.1.0, bundled unmodified: links count 23, most CJK characters and emoji count 2. Bluesky uses the post schema's two limits, 300 graphemes and 3,000 UTF-8 bytes. Text messages follow GSM 03.38: 160 seven-bit characters (153 per part when split, with € ^ { } [ ] ~ | \ taking two), or 70 (67) once any Unicode character appears. Limits: emoji newer than twitter-text 3.1.0 are counted the library's way and may differ on live X; Bluesky's app can shorten long links; national SMS shift tables are not modelled.

Try the live demo: https://boloop-works.pages.dev/works/sealed/

Download the prototype files (zip, 152 KB): https://boloop-works.pages.dev/works/sealed/sealed.zip

Actual design brief

Design a small, genuinely useful character counter made of three envelopes — "X post" (280, counted with X's open-source twitter-text 3.1.0, bundled unmodified), "Bluesky post" (300 graphemes and 3,000 UTF-8 bytes, from the app.bsky.feed.post schema) and "One text message" (GSM 03.38: 160 7-bit characters, or 70 once a single Unicode character appears) — as a runnable single-page web app (plain HTML/CSS/JS, no backend, no build, English UI, local fonts, inline SVG favicon).

First second, desktop and 390 px phone: a sample letter is already in place. Paper slips slide into the envelopes; X and Bluesky close and get a red wax seal pressed on; the SMS envelope cannot close — its slip sticks out above the flap and the part above a red dashed fold line is the actual text that does not fit. Under it: "Open · 55 over · sends as 2 texts". A dashed panel names the culprits ("These 2 marks shrink one SMS from 160 to 70 characters: — ’") with one button: "Make it SMS-safe (swap 3)".

Operable: every keystroke moves the slips, opens/closes flaps and presses or lifts seals. Explicit buttons only ever change the text: "Make it SMS-safe" (swaps ’ ‘ “ ” — – … and no-break space for plain ones; emoji are listed as "can't swap"), "Trim to fit" per open envelope (cuts to fit, never inside a grapheme), and one-level Undo. Typing is never rewritten, there is no maxlength. Errors are visible and announced (role=alert): empty copy, blocked clipboard, browser without Intl.Segmenter. Status line (aria-live polite) settles 0.7 s after typing. Reduced motion: final state at once. Nothing is stored or sent.

Inspired by @binniebunnnies and @marcgmbh.