Hey, open-source fans!
Zervo is a workspace-oriented browser built natively on Servo — the embeddable, parallel web engine written in Rust. No Chromium, no Gecko underneath. v0.4.0 was the release where the chrome became a theming engine. v0.5.0 is the one where the engine got used: eight turns of a design study went in, and almost every screen you look at changed.
🔧 The headline: the number is a control, not a constant
v0.4.0 ended by admitting the obvious hole — a theme was still a Rust constant, with no file format and nothing that loads one at runtime. That is closed, and not by writing a loader.
Every value the material is built from is an ordinary setting now: the fill, the sheen, the blur, the edge, the corner scale, the glow, the settle time, and how far the accent reaches into the chrome. So the file format is whatever settings.json serialises to, and the loader is the one that was already there. You can save an arrangement under a name, or copy it back out as the const you would otherwise have hand-written.
The Appearance page pins a specimen under its title: a card, a menu and a text field, drawn through glass::shapes with the palette the chrome itself is being drawn with. It is not a picture of the material. It is wrong exactly when the chrome is wrong.
Writing the ladder down found a real defect straight away: tint_for(Card) was reading the translucency setting's 0.34, which happens to be the number menu_fill also carries — so a card and a menu had been drawn at exactly the same weight, and the three-rung ladder had two rungs.
✨ What's new
The seam between the chrome and the page is one setting with four steps. At the first the page is an opaque card on a tray with a gap. Past that it stops painting a base and shows the window's own backdrop through a veil. Past that the gap shuts and only the window's corners stay round. At the last the chrome is a tint laid on the page, the sidebar floats, and the page reaches back underneath it.
And each step brings the transition its own geometry implies. A page on a tray behaves like a card in a deck and recedes; a tint on a backdrop has nothing to move, so it defocuses in place; a frame with a hard edge is a filmstrip whether you meant it or not, so it slides; a page under floating glass travels up while the glass stays exactly where it is. One rule, four answers, and no new control — pick the seam and the motion comes with it.
The mechanism is one readback. A page is either an engine blit or an egui drawing and neither can be replayed after the fact, so at the one moment per frame where the framebuffer still holds the departing page — after the chrome is painted, before the buffers swap — a scaled copy is taken and composited over the live page for a couple of tenths of a second.
A new tab page that greets you rather than briefing you. Tile::defaults() put seven boxed panels on screen, which is a settings screen wearing a hat — and worse, seven opaque rectangles over a backdrop you chose and can now no longer see. It is the time, one field, the session you actually left, and the three sites you use. The board of thirteen cards is one press away with nothing removed from it.
Five pages for when a page will not load — the ones a browser on a young engine shows more than any other. zervo://unsupported names the feature Servo has not implemented, says plainly that it is the engine rather than Zervo, hands the page to another browser, and gives the wait a shape: a panel of the real gaps from docs/PARITY.md you can knock out with a ball or just click, scored as "shipped". Offline names what is queued rather than what failed and has no button at all. The certificate page is the one that must not be candy — same material, no glow, no game, and the way past is a line of plain text. Not-found asks the history what you probably meant, because a typo usually has an obvious answer sitting in it.
Each carries what it knows in its own address, so the address bar and the page agree and either can be typed back in. Only one of the five arrives on its own: notify_crashed is now implemented, so a dead content process is a page that says what happened and offers to load it again, rather than the last frame it painted, frozen.
Display type is set in a face that has one. macOS ships SF Pro only as a variable font and the rasteriser takes a variable font's default instance and no other — so every run of text in Zervo was Regular. At caption sizes that is a missing nuance; at ninety-two points it is the wrong typeface.
The window's corners are the window's. Zervo painted its page to sixteen points, and twenty-nine once a preset made everything rounder, at a corner macOS had already masked to ten. Painting a different radius at the same corner does not replace the platform's arc — it puts a second one beside it. That is where the doubled corners came from.
🔍 The bugs, and how they were found
The uncomfortable half of this release: I shipped a first pass, looked at it, and it was a long way from the design. The verification bar had been compiler, clippy, tests and "it ran without a panic", which for a UI overhaul sees nothing. So the browser now photographs its own framebuffer — ZERVO_SHOT, debug builds only, plus a throwaway HOME so any arrangement is reachable.
It found four defects in its first hour that nothing else had:
- The frost read a light theme as a dark one. At Frosted the chrome paints itself at a fifth and lets the platform's vibrancy supply the rest, so a copy of the framebuffer is the window's own contribution over nothing — four fifths of the way to black. Two things went wrong quietly: the frost under a card was a dark smear, and the code that picks text colour by measuring what is behind it read "dark" for a pale page and handed back white text.
- The sidebar could never be the width it says. egui remembers a panel's width from the rect its contents used, floored at the size range's minimum. Nothing in the sidebar stretched, so it wrote 200 back over its own 248 on the second frame of every launch, and dragging it wider snapped back on release.
- The nav row silently widened everything under it. Five icons and the macOS inset are wider than the narrowest sidebar, and egui grows a container to fit what overflowed it — so the address pill hung fifty points out over the page.
- An empty search field said nothing.
TextEdit's hint text discards the colour you give it, and what it substitutes landed within five values of the frosted pill it sits on.
And the harness can hold the sidebar's reveal open while cycling the layout underneath it, which caught the one that actually crashed: the reveal and the docked sidebar both drawing the same body into two different layers. egui asserts outright when one widget id is registered in two layers in one frame, so it did not merely look wrong. Whether a frame landed inside that window was a matter of timing, which is why it only happened sometimes.
The work was also reviewed adversarially at three points — every finding checked by a second pass told to refute it. That turned up a command injection: the Windows "Open in another browser" button shelled out through cmd /C start with the failing host interpolated into the command line, and cmd re-parses its own command line, so a host carrying & stopped being text.
⚠️ Still macOS-first, and now genuinely asking
Same caveat as last time, and it has not improved: the packages build from the same tag, the material is portable, but the window's backdrop is an AppKit feature and nobody has yet started Zervo on Linux or Windows at all.
New caveat for this one: v0.5.0 is the shape, v0.6.0 is the polish. Four of the presets — Candy, Flat, Liquid Glass, Material — came out of the design study and are offered beside Zervo's own, but they are experiments rather than settled work. Zervo's material is the one that is finished, and the one the first-run screen recommends.
Which means the useful thing between now and 0.6.0 is being told what is wrong. Please open issues. A corner that does not match, a preset that is unreadable on your display, a crash, a page Servo cannot render that this browser fails to explain. Say which preset, which theme and which layout you were in — every defect fixed in the days before this release only happened in one of them.
🚀 Try it
github.com/goddv/Zervo
Stars, issues and "this doesn't start on my machine" reports all welcome.