Design
A terminal is one of the most proven, durable text interfaces ever built. It has survived every design trend for fifty years - flat design, skeuomorphism, neumorphism, glassmorphism - because it never participated. It just renders text, and people have read it productively for decades.
term.css applies that philosophy to the web. Every decision below exists because we asked: "Would a terminal do this?" If the answer was no, we didn't do it. That's a design constraint, not a universal principle - the web is not a terminal, and we don't pretend it is. But strong constraints produce coherent systems, and coherence makes a system easier to learn and predict.
This document explains the tradeoffs.
Classless-First Design
term.css styles every standard HTML element without any classes. Write
<table>, get a styled table. Write
<blockquote>, get a styled blockquote. Write
<nav>, get a tmux-like status bar.
This is the terminal philosophy applied to HTML. In a terminal, you don't add classes to your output. You write text, and the terminal renders it. term.css does the same: you write HTML, and the stylesheet renders it.
Classes exist for five optional components
(.t-card, .t-grid, .t-badge,
.t-callout, .t-table-bordered) that have no
semantic HTML equivalent, plus four semantic color utilities
(.t-error, .t-warn, .t-success,
.t-info) that compose with badges and callouts. But the base
experience - headings, paragraphs, lists, tables, code, forms,
navigation - requires zero classes.
This has practical benefits:
- Markdown compatibility. Markdown generates semantic HTML with no classes. term.css styles it perfectly out of the box.
- Less to learn. You don't need to memorize a class system. You just write HTML.
- Smaller HTML. No
class="container mx-auto px-4 max-w-7xl"on every element. The HTML is clean and semantic.
Full-Width Layout
The conventional wisdom says body text should be 45-75 characters wide. This comes from Robert Bringhurst's The Elements of Typographic Style - a book about print. Print has fixed page widths. The web does not.
A terminal never imposes max-width: 80ch on itself. The user
controls the window width. If they want 80 columns, they resize to 80
columns. If they want 120, they resize to 120. The content fills whatever
space the user chose.
The same applies to browsers. People don't read websites full-screen on 2560px monitors. They have windows side-by-side, or they use a laptop where the screen is 1200-1400px wide. At those widths, full-width monospace text at 14px produces comfortable line lengths - without the framework making assumptions about what's comfortable.
What you gain by removing max-width:
- Less scrolling. On a code-heavy article, this is significant - code blocks and paragraphs use available space instead of wrapping artificially.
- No empty gutters. Centered narrow content with decorative gutters is a web pattern, not a terminal pattern.
- The user is in control. The tool respects the user's environment rather than imposing its own.
The tradeoff
This is an honest trade: you give up a predictable reading measure for prose in exchange for density, simplicity, and user-controlled layout.
On a very wide window, long lines of prose can become harder to track. A
max-width can act as protective ergonomics for those users.
We accept this tradeoff because term.css targets content where code and prose are interleaved. Code blocks benefit enormously from wider layouts - they wrap less, they're more readable, and you scroll less. For a technical article with ten code blocks, the width savings compound. The prose between those code blocks is usually short enough that line length doesn't become a problem.
If your content is pure long-form prose with no code, a constrained width may serve you better. term.css is optimized for the mixed case.
Single Font Size
Most CSS frameworks give you six heading sizes. Headings get progressively larger: h1 is 2.5rem, h2 is 2rem, down to h6 at 0.875rem. This is a print convention carried to the web.
Terminals don't have font sizes. Every character is the same size. A heading in a man page is differentiated by weight (bold) and whitespace, not by being 36px in a 14px document.
term.css follows this: 0.875rem (14px) everywhere. Headings use
font-weight: 700. That's it.
Why this works:
- Scannability through whitespace. Section headings (h2–h6) have 2lh of space above them; h1 uses 1lh since it typically follows the navigation directly. That vertical gap is the primary visual signal.
- Consistency. When everything is the same size, nothing screams for attention. The content is flat, calm, and even - like terminal output.
- No size coupling. Font size hierarchies create coupling problems. If you change the base size, every heading size needs adjustment. With a single size, you change one number.
- Density. Large headings waste vertical space. A 36px heading followed by 14px body text creates a size jump that interrupts reading flow. Same-size headings maintain density.
The tradeoff
Font size is a powerful scanning channel. Larger headings establish document structure faster, create stronger entry points, and help distinguish sections at a glance.
Open a terminal and run man git. You can scan that
document - the headings are bold, there's whitespace around them, and they're
in uppercase by convention. Three signals, none requiring a larger font size.
It works. But it works partly because the audience is practiced.
For a general audience, a modest size hierarchy likely helps more readers find what they're looking for, faster. For the audience term.css targets - people who are comfortable reading terminals - weight and whitespace are sufficient.
Monospace Everywhere
Most websites use a sans-serif font for body text and switch to monospace for code. term.css doesn't do this.
- Terminal authenticity. Terminals render everything in monospace. There is no font switching. If the goal is to look like a terminal, mixing fonts breaks the illusion immediately.
- Alignment. Monospace text aligns vertically. Tables, code, lists, and body text all share the same character grid.
- Tabular numbers for free. Numbers in monospace are already tabular - dates, prices, and version numbers line up naturally.
- One font to load. term.css bundles JetBrains Mono (Regular, Italic, Bold - ~280KB). One font family, three variants, no fallback dance between sans-serif and monospace.
The tradeoff
For extended prose, proportional fonts usually win on raw readability. Proportional type packs word shapes more naturally and reduces horizontal sprawl.
term.css accepts a likely small readability tradeoff in prose in exchange for consistency, terminal authenticity, and better code/content harmony. On a page where prose and code are interleaved - which is the primary use case - the consistency of a single font family outweighs the marginal readability advantage of switching fonts mid-page.
Why JetBrains Mono
System monospace fonts vary wildly across platforms - Menlo on macOS, Consolas on Windows, DejaVu Sans Mono on Linux. They look different, they space differently, and they render at different sizes.
term.css bundles JetBrains Mono as WOFF2 web fonts so the rendering matches across every platform and browser:
- Designed for prolonged reading, not just code snippets
- Programming ligatures (optional, not forced)
- Used by default in JetBrains IDEs and many terminal emulators
- Open source (SIL Open Font License)
- ~95KB per weight in WOFF2
We use font-display: swap to prevent invisible text while the
font loads. The browser shows the system monospace fallback immediately and
swaps in JetBrains Mono when it's ready. The layout doesn't shift because the
fallback is also monospace.
Vertical Rhythm with lh Units
Most CSS frameworks use rem or em for spacing.
term.css uses lh - a unit equal to the element's computed
line-height. At line-height: 1.5 and
font-size: 0.875rem, 1lh equals 1.3125rem (21px).
- Spacing is always a multiple of the line grid. 1lh between paragraphs, 2lh above headings, 0.5lh for tight contexts. Content stays on a consistent vertical rhythm.
- Self-adjusting. If you change the line-height or
font-size, all spacing adjusts automatically. With
rem, you'd need to recalculate every margin. - Conceptual clarity. "One blank line between paragraphs" is exactly 1lh. "Two blank lines before a heading" is exactly 2lh. The values map directly to how you think about terminal layout - terminals separate sections with blank lines.
The line-height: 1.5 value is the WCAG 2.1 AA minimum for
body text. It's also comfortable for monospace text, which needs slightly more
leading than proportional text because every character has the same width.
No Border-Radius
Every CSS framework ships with border-radius. Buttons are
rounded, cards have rounded corners, inputs have subtle curves. This is a GUI
convention inherited from physical buttons.
Terminals are rectangular. Every element is a rectangle. There are no
curves. term.css never sets border-radius - the browser default
of 0 does the work.
Rounded corners say "this is a GUI widget." Sharp corners say "this is text in a box." For a terminal-aesthetic framework, the latter is more consistent.
Minimal Hover States
CSS frameworks typically add prominent :hover states -
buttons change color, links get underlines or color shifts, cards lift with
shadow. term.css takes a minimal approach:
- Links lose their underline on hover - you already know it's a link from its color and underline, the hover confirms it by removing the underline
- Buttons and cards have no additional hover effect
- The cursor change provides basic feedback
Terminals don't have hover states. You navigate with the keyboard. When you move a cursor over text in a terminal, nothing changes.
The tradeoff
Hover states provide affordance, confirmation, and discoverability. Even terminal-comfortable users browse the web with a mouse sometimes.
The philosophy isn't "zero feedback" - it's "minimum effective feedback." The cursor change plus the link's existing visual distinction (color + underline) means the hover state doesn't need to do heavy lifting.
For interactive-heavy pages with many clickable elements, more prominent hover states would help discoverability. term.css is optimized for content-heavy pages where the primary interaction is reading, not clicking.
No Smooth Scrolling
scroll-behavior: smooth is a popular addition to CSS resets.
term.css explicitly does not use it.
Terminals don't smooth-scroll. When you press Page Down, the content jumps. When you scroll with a mouse wheel, the content moves in discrete line increments. There's no easing, no animation, no physics.
Smooth scrolling is also an accessibility concern. Users with vestibular
disorders can experience motion sickness from animated scrolling. The
prefers-reduced-motion media query can disable it, but not using
it in the first place is simpler and safer.
OKLCH Color System
term.css uses OKLCH for all colors. Every theme defines 10 variables in OKLCH format.
- Perceptual uniformity. In OKLCH, equal steps in
lightness look equal. In HSL, they don't -
hsl(60, 100%, 50%)(yellow) looks much brighter thanhsl(240, 100%, 50%)(blue) despite having the same lightness value. OKLCH fixes this. - Theme generation from a single hue. Because lightness is perceptually uniform, you can generate a complete dark and light theme by fixing lightness values and rotating the hue.
- Predictable contrast. WCAG contrast ratios correlate well with OKLCH lightness differences. If fg has L=0.85 and bg has L=0.15, you know the contrast is high without calculating.
- Future-proof. OKLCH is the CSS color specification's recommended color space. It's supported in all modern browsers.
Lightning CSS generates fallbacks for older browsers automatically, so OKLCH in source doesn't mean OKLCH-only in output.
light-dark() for Themes
Each term.css theme is a single :root block using the CSS
light-dark() function. Before this, each theme required four
blocks: @media (prefers-color-scheme: dark),
@media (prefers-color-scheme: light),
:root[data-theme="dark"], and
:root[data-theme="light"]. Every variable was duplicated four
times.
light-dark() collapses this to one block. The browser picks
the correct value based on color-scheme. A two-line override
(color-scheme: light or color-scheme: dark)
switches everything. The data-theme attribute does this for
manual control.
This reduced every theme file from ~85 lines to ~32 lines. Thirteen themes times ~50 lines saved is ~650 lines of CSS eliminated with zero functionality loss.
Nav and Footer as tmux Bars
In a terminal multiplexer like tmux, the top and bottom of the screen have thin status bars. They show the session name, window list, time, and other metadata. They're dense, full-width, and use a surface color to differentiate from the content area.
term.css styles <nav> and <footer>
the same way:
- Surface background for subtle contrast from the page
- Horizontal flex layout with gap
- Half a line-height of vertical padding, keeping the baseline grid
- Links inside nav/footer are plain text, not styled links
- Footer uses
margin-block-start: auto- pushes to the bottom of the viewport when content is short, like a tmux bar that always sits at the terminal bottom - Last child gets
margin-inline-start: auto, pushing it to the right - exactly how tmux puts the clock on the right side of the status bar
No Decorative Elements
term.css has no box shadows, gradients, background images, text shadows, backdrop filters, animations, transitions, or rounded corners. Each of these is a GUI convention. They exist to make web pages look like physical objects - cards that float above the page, buttons that press down, surfaces that blur what's behind them.
Terminals have none of this. They have text, borders made of characters, and colors.
The result is a design that loads fast, renders instantly, and never distracts from the content. There's nothing to animate, nothing to composite, nothing to blur. It's just text.
Who This Is For
term.css doesn't claim this approach works for every website. A marketing landing page needs visual hierarchy and emotional design. A dashboard needs data visualization. An e-commerce site needs trust signals and visual affordances that term.css deliberately omits.
term.css is for content-heavy technical sites: blogs, documentation, personal sites, technical writing. Places where the content is the interface and everything else is overhead.
The target audience is people who actively enjoy terminal-like presentation - who choose TUI tools over GUIs, who set their editor to monospace and don't look back, who feel more at home in a man page than a Medium article. Not every developer fits this description, and that's fine.
For a broader audience, some of these choices - same-size headings, full-width prose, monospace body text - may feel unfamiliar or less comfortable. That's a real cost. term.css prioritizes consistency, density, and terminal authenticity over conventional web typography. If that tradeoff doesn't work for your audience, it's the wrong tool.
Readable is not the same as universally optimal. term.css chooses a reading environment that is proven, coherent, and preferred by its target audience. It brings the terminal to the web - not because the web should be a terminal, but because for certain content and certain readers, the terminal's answers are still good ones.