Web Design for Terminal Browsers
This is a purely-for-fun exercise: a guide to web design for terminal browsers. Starting from the text browsers people actually use, I am going to pull out a set of rules in case you ever decide to build a site meant to read and look its best in a terminal. It is not an alternative to Gemini, Gopher or a smallweb attempt, but a set of best practices so your site reads well in a terminal browser, and even so you can build Web Apps for the terminal.
Five engines, one test
The first step is to install the five text engines people actually use and feed them the same test pages, isolating one feature at a time.
- w3m (0.5.6): the most capable with tables, and it even draws images in terminals that allow it.
- lynx (2.9.3): the patriarch, the lowest common denominator since 1992.
- links (2.30): fast, with a little bit of CSS.
- elinks (0.20.0): the cousin with bigger ambitions, it even has an optional CSS engine.
- EWW: the Emacs one.
Now we look for the compatibility crossovers. You test HTML and CSS features and see how each engine interprets them.
The results:
| Feature | EWW | w3m | lynx | links | elinks |
|---|---|---|---|---|---|
| JavaScript | No | No | No | No | No |
<style> / <link> sheets |
No | No | No | No | Partial |
display:none inline |
Hides | Shows | Shows | Hides | Shows |
display:none by class |
Shows | Shows | Shows | Shows | Shows |
inline color |
Yes (with contrast) | Depends on the terminal | Depends on the terminal | Depends on the terminal | Depends on the terminal |
text-align |
No | No | No | No | Yes |
| Data tables | Yes | Yes | Yes (no borders) | Yes | Yes |
colspan / rowspan |
Yes | Yes | Flattens | Yes | Yes |
| Graphical images | Yes | In the terminal | No | No | No |
alt of a broken image |
Yes | Yes | Yes | Yes | Yes |
srcset |
Yes (picks resolution) | alt/src | alt/src | alt/src | alt/src |
data: URI in an image |
Yes | alt | alt | alt | alt |
| GET/POST forms | Yes | Yes | Yes | Yes | Yes |
Collapsible <details> |
No | No | No | No | No |
Spacing of <article>, <section>... |
No | No | No | No | No |
<base href> |
Yes | Yes | Yes | Yes | Yes |
<title>, <pre>, <hr>, lists |
Yes | Yes | Yes | Yes | Yes |
From this we draw some general conclusions:
- JavaScript does not exist. In any of them. It is not slow or partial. There simply is no interpreter.
- CSS is almost a mirage. Stylesheets, whether
<style>or<link>, are ignored in all of them except elinks, which applies a few things liketext-align. Four out of five engines do not read your CSS. Design as if it were not there. display:noneis a trap. If you hide something with a class (class="hidden"and.hidden{display:none}in your sheet), all five engines show it. Every one. Because they do not read the sheet: do not hide anything important with CSS. If it should not be seen, do not put it in the HTML.- Images are text. Only EWW (and w3m in some terminals) actually draws the image. In the rest, the image is its
altattribute. All five fall back toaltwhen the image is broken, and an image with noaltleaves you a[hero]with the file name, or nothing at all.altis not accessibility for others, it is your content. <details>does not fold. None of the five make it interactive. The summary and the body always show, one after the other.- HTML5 semantic tags are invisible.
article,section,nav,header,footer,main,aside: they are transparent containers. They do not add a single line break. Their value is semantic, not visual.
With that, we can lay down a few design lines and best practices.
8 rules for publishing to the terminal
1. DOM order rules
There is no float, no flex, no grid, no order. Whatever you put first in the HTML comes first on screen. So place the content right after opening the <body> and send the long navigation and the footer to the end. A reader who opens your article does not want to tab through thirty menu links before reaching the first sentence.
2. Mark structure with tags, not styles
Real headings <h1>..<h6> for the hierarchy, never a <div class="big-title">. Lists with <ul>/<ol>, definitions with <dl>. Quotes with <blockquote>, which all of them indent. Code with <pre> and <code>. Each engine gives them its own treatment: use them for what they mean.
3. Your page must read with CSS turned off
This is the touchstone. If you disable CSS and your page becomes unreadable, it is not the terminal browser's problem, it is your HTML's problem. Spacing (margin, padding, line-height) does not exist: the separation comes from paragraphs. Structure with real <p>, not with loose <br>.
4. Do not convey information with color alone
A "required field in red" or a "green = correct" evaporate. Inline color is the most fragile thing in the table: it depends on the terminal and its configuration, and in many cases it does not even show. Always pair color with text or a symbol. An "Error:" in front, an asterisk, anything.
5. Tables for data only, never for layout
EWW and w3m draw a surprisingly good ASCII grid, colspan and rowspan included. But a layout table produces an absurd, unreadable grid. Watch the width: if the columns add up to more than the terminal's, the experience degrades. Fewer columns and short cells win.
6. Images with a descriptive alt and srcset
alt is what you see in four out of five engines. Make it a sentence, not an image1.png. And if the image is pure decoration, give it an explicit alt="": that way the reader ignores it instead of reading you the file name. A missing alt and an empty one are not the same thing. Offer srcset with several resolutions, and the engine that can show images will pick the right one. And do not rely on an image to communicate anything critical, because in most engines they do not load at all.
7. Real forms
No JavaScript submissions. A <form> with its action and its method (GET or POST), and an <input type="submit"> or a <button>. Put a name on every field: the engines collect by name, and a field without one is lost. Associate a <label> with each one.
8. Headers that do count
<title> always, descriptive: lynx and links center it at the top, and all of them use it to identify the page. <meta charset> in UTF-8 as an encoding fallback. <base> if you use relative links, which all of them respect. And serve over HTTPS, since several engines flag the certificate status.
TerminalSpeed Insights
A guide you cannot run is a list of good intentions. So I wrote a prototype. It is a Python script that reads an HTML file, or a URL, and gives you back a readability score with the specific warnings. The number is a heuristic, not a science. I hand out the points by eye, an error weighs more than a warning and that is that. What really matters is the list of warnings, not the scoreboard.
You can download it from its repository, TerminalSpeed Insights, and run it against any page:
python3 terminalspeed.py https://your-site.dev/
Running it over a few popular sites, the ones leaning more on content than design, I get some numbers:
| Site | Score |
|---|---|
| andros.dev (this blog) | 100 |
| text.npr.org (NPR's text-only version) | 100 |
| motherfuckingwebsite.com | 100 |
| emacswiki.org | 94 |
| suckless.org | 94 |
| gnu.org | 88 |
| man pages on man7.org | 88 |
| Wikipedia (an article) | 46 |
| Hacker News | 55 |
At the top are the wikis, the docs, the text sites and the ones that fly the minimalism flag. At the bottom, curiously, two of the sites most loved by people who read in the terminal. It is no coincidence: the ones that score high serve the content first and lean on the tags, not on CSS. EmacsWiki, for instance, is almost plain HTML, and that is why it reads on anything that can show text.
Still, these are numbers not every browser shares. Open Hacker News in w3m and you will see the front page perfectly readable, with its numbered list of headlines, despite that 55. w3m draws tables so well that it survives. The validator is not wrong to penalize it, it flags real friction, but friction is not always a death sentence. Take the score as a guide, not a verdict: a 100 almost guarantees it reads well, a low number tells you where to look.
Not just documents: a Terminal Web App
So far I have talked about documents: articles, cards, docs pages. We still have to squeeze the forms: GET and POST work in all five engines. And a form that works is, no more and no less, an application.
Let me show you with a coffee shop:
<form action="/order" method="post">
<p><label>Your name <input type="text" name="name"></label></p>
<p><label>Quantity <input type="number" name="qty" value="1"></label></p>
<p><label><input type="checkbox" name="no_milk" value="yes"> No milk</label></p>
<p><button type="submit">Order coffee</button></p>
</form>
And this is how it looks in EWW, the Emacs browser. The fields are edited with the keyboard; here I have already filled in the name, the quantity and checked "No milk":
:quality(85)/https://andros.dev/media/blog/2026/08/coffee-order-form.png)
You hit submit and the <form> does a POST. The server responds with a redirect (the good old Post/Redirect/Get pattern) and the browser paints the confirmation, all without leaving the keyboard or touching the mouse:
:quality(85)/https://andros.dev/media/blog/2026/08/coffee-order-confirmed.png)
Look at the detail: heading in bold, the link colored, the quantity and the no-milk option collected correctly. It is a stateful app, interactive, with no JavaScript and without a single kilobyte of framework. And yes, it scores 100 with the validator.
It is a real application: it lives in the text browser, but it looks just as good in Chrome or Firefox, because underneath there is only HTML. You get a terminal interface without touching ncurses or any TUI library. Your toolkit is HTML, your renderer is the browser and your logic lives on the server. The same app, a language you already know, and it works everywhere.
Conclusion
It has been a fun investigation. Almost nobody designs their sites for the terminal, it is a minority and, even so, it is an audience that exists. And there is no shortage of reasons: these are visitors who want accessibility or speed, who are on low resolutions or who work with pipes. More than once I have found myself doing quick searches in EWW, or reading an article there for the visual comfort.
You can take it as a curious article or as another approach to web design. I, at least, will start looking at my pages with different eyes.
I hope you enjoyed this other point of view on web design.
- Five engines, one test
- 8 rules for publishing to the terminal
- 1. DOM order rules
- 2. Mark structure with tags, not styles
- 3. Your page must read with CSS turned off
- 4. Do not convey information with color alone
- 5. Tables for data only, never for layout
- 6. Images with a descriptive `alt` and `srcset`
- 7. Real forms
- 8. Headers that do count
- TerminalSpeed Insights
- Not just documents: a Terminal Web App
- Conclusion
This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.
Help me keep writing
Every coffee gives me a push toward the next article.
Sure, it's on me!
Comments
There are no comments yet.