Show HN: Justif – Knuth-Plass justification and microtypography for the web (justif.lyall.co)

198 points by lyall ↗ HN
Justif is a drop-in JavaScript library that progressively enhances web pages to TeX-level text justification. Installation is a single <script> line, standard text and accessibility affordances are unchanged, and users with JS disabled get native browser rendering.

I made justif because I've long been a fan of justified text. I think it looks clean and elegant, and makes reading more enjoyable. But bad justification is the opposite, with gaping spaces that distract me to the point of making the text near unreadable.

Browsers have got better recently at handling justified text [0][1], but still use suboptimal greedy algorithms for the most part, and are not near that TeX/InDesign level of quality that I crave. `text-wrap: pretty` exists but is far from a panacea, as you can see for yourself in the demo.

Justif also has the benefit of providing more consistent text layout across browsers. Blink (Chrome), Gecko (Firefox) and WebKit (Safari) all justify text differently, so normally what the user sees depends on what browser they use.

Take a look at the demo, play around with it, see if you can break anything. I'm open to improvements of the API design as well, so please let me know your thoughts.

For more details, see the README: https://github.com/lyallcooper/justif

0: https://cloudfour.com/thinks/justified-text-better-than-expe...

1: https://owickstrom.github.io/the-proportional-web/

27 comments

[ 3.2 ms ] story [ 40.2 ms ] thread
This is great! Loved the demo and controls (and nice touch to include The Frog Prince as one of the sample texts lol). I think by doing tracking and expansion you're going even further than original Knuth TeX did/does. Maybe for the demo when hyphenation is turned off, the browser side should also turn it off? Or leave it like this, justif without hyphenation still does better than browser with hyphenation :)

Wish this project great success!

Side-by-side didn’t do it for me but flicker made all the difference!

This is great, wish that browsers would implement a better algorithm.

Also good execution with the demo page.

For OP: I think "Type specimen" should be the default sample text, just to get readers into the right spirit. :)

For anyone else reading this: make sure you click on the "controls & metrics" at the bottom to try out all the options (such as comparing with your browser's "text-wrap: pretty"); it's really cool!

This demo feels about as good as eating a spoonful of tiramisu
Wow! I've spent the past few weeks setting 18th-century newspaper text on the web - justified, up to three narrow columns, period ligature, and the gap between CSS justification and real typesetting was a big part of the whole project. The browser's greedy line breaker plus hyphens: auto gets you maybe 80% there, and the last 20% is rivers, loose lines, and archaic words the hyphenation dictionaries have never met. "Fœderal" produces some genuinely creative break points. The result I got to I feel like is good, but not great.

Questions, from someone who wants this to work:

* Does Justif do its own hyphenation (Liang patterns?) or lean on the browser's? And is there a way to feed it exceptions for unusual or archaic spellings?

* How does it behave at narrow measures, say 35–40ch columns? That's where Knuth-Plass should shine most, but it's also where badness compounds fastest.

* What's the reflow cost on long documents (5–10k words) when the container resizes or the user scales the text — full re-run or incremental?

* Any thoughts on coexisting with text-wrap: pretty as browsers slowly grow real paragraph-level breaking?

Congrats on shipping this. I miss producing documents with TeX!

Follow-up, since I asked the questions above: I spent the evening running Justif against the hardest page I have: three 356px columns of 18th-century text set in IM Fell at ~17.6px with 24.3px leading. I came in curious and left planning the integration.

The numbers were better than I hoped. A full ~5k-word essay justified in about 280ms, and ~36KB gzipped including the en-US patterns. And the output is the part that sold me. The columns look like typesetting rather than CSS, which is the whole thing I'd been grumbling about.

The only snag I hit: a floated initial letter (drop cap) trips it — the first line ignores the float and overflows the gutter. Excluding that paragraph and leaving it on native justify renders perfectly, so it's cosmetic to work around, but a first-class exclusion selector would be lovely. I'll file an issue with the details and our measurements.

Best part: this experiment exposed that our sheets are currently set ragged right, which no printer in 1787 would have tolerated. Your library may be what finally makes us historically honest. Test page for context: federalistreader.org/papers/10/

Thank you for building this!

I haven't looked at the API yet, but the demo is great on my phone. Instead of just justified blocks, can you have it do a "justified" (even amount of spacing) from an irregularly shaped right edge (in my case, an image on the right side of a design template I made)?
The fact TeX can do stuff like this is crazy when you think about it. Especially because it was all a yak shave while Knuth was writing TAOCP.
I remember when I was learning CSS back in 2012, the guides said to never center-justify text as browsers did a terrible job. Since then, CSS has become way nicer, and yet justified text still looks awful. Great (or even good) typography always feels impossible on the web.

But this demo blew me away! Fantastic!

A most welcome addition to a webmaster’s toolkit. Congratulations!
Wow, I'm seriously impressed! Questions for the author:

I haven't had a chance to read the code yet, but how does this actually adjust the text? I get that it uses Knuth-Plass to calculate layout, but what does it do in CSS (or SVG?) to apply the layout to the DOM?

Looks like the first commit was sometime last week, which is pretty tight turnaround for something like this. For my own sanity when reading the code (no judgment either way), did you use LLMs to help write this? Knowing is helpful for me to calibrate what I pay attention to when reading through the code.

Beware: the TeX justification algorithm is quadratic-time.
This is awesome ! How hard would it be to get something similar in React Native ?
This is great! I was avoiding justify because it never looks good on web.
Great work. It looks as the space in "No. 12" is too large. Is it possible to distinguish between space and sentence-end-space?
Good,this is very helpful for handling the layout in the print template.
Absolutely stunning! Thank you so much for sharing!
As a little public resource announcement:

    <script type="module" blocking="render"
    src="https://cdn.jsdelivr.net/npm/justif@0.5.0/dist/auto.js"
    ></script>
Users of something like this should ALWAYS use subresource integrity, and the example script tag should, too.
Does this work with drop caps or could it be made to work with them? Cool work by the way.
This is great and it makes me wonder why the algorithm isn’t standard for browsers as a built-in feature (also for word processors).

Sure, it takes a bit longer, but LaTeX pretty much instantly produces a PDF nowadays even for long documents. 30 years ago, I scheduled a coffee break for that.

Will be switching my blog to this.
Very cool. This is near and dear to my heart. I've been doing something similar in my apps, but I took a different approach using WASM to measure fonts. I'll admin, I used agents to implement, but I'm happy with the solution. I'll compare more today, but I'd love your thoughts on what limitations you've run into with your approach, and if you're interested, to compare notes. https://github.com/currentspace/knuth-plass-wrap