Repository navigation
Releases
Sashfold 0.2.0
Sashfold 0.2.0 is the release where pages built out of components come apart into what they are: 64 commits since 0.1.5 on 2026-10-01. Shadow trees, Web Animations, the CSS object model and the Streams Standard are written, so the sites that build themselves from web components and style themselves from script render as they are meant to. The script engine has a compiler that writes machine code, and its objects keep their properties in shapes with caches that remember them. Speed work runs through all of it. Still every byte is written in this repository, and still it does not pretend to do what it cannot.
What's New
- Machine code for scripts — a baseline compiler turns a script's hot code into machine code for x86-64 and AArch64, on Linux, Windows and Apple silicon, over a new engine core: eight-byte values, frames on the machine's own stacks, objects that keep their properties in shapes, and property reads and writes that remember what they found.
- Shadow trees —
attachShadow, slots and slot assignment, the standard's event path through them, declarative shadow roots in markup, and styles scoped to each tree with:host,::slotted()and::part(). - Web Animations, CSS animations and CSS transitions —
element.animate(),@keyframes, transitions on style changes, and a real rendering update that runsrequestAnimationFramecallbacks together at one time. - Styles written from script — the CSS object model, so a page that builds its stylesheets with
insertRuleor constructed sheets shows them; and nested rules and cascade layers reach the cascade. - Streams — readable, writable and transform streams, so the frameworks that stream their pages into place load.
- Shadows —
box-shadowandtext-shadow, blurred, inset and animated, drawn with the corners the standard gives them. - Hover, focus and outlines —
:hover,:focus,:active,:focus-visibleand the other state pseudo-classes match, andoutlinedraws. - Import maps, and classes that extend the platform's own interfaces,
blob:URLs that load, and scripts inserted by scripts that wait their turn. - Placeholders that take a page's style —
::placeholder, and fields that keep only the half of their built-in look a page leaves them. - Pages that start loading sooner — the scripts and stylesheets a document names are asked for while the document is still arriving, and the parts of the engine a page never touches are never set up: a new tab's realm costs a fraction of what it did.
What's Fixed
- Pictures that were a web page — every request asked for a document, so a server that answers one address with a picture or with a page to show it in sent the page, and a site's post pictures never drew; each request now asks for what it is for.
- Content shown twice — a component moved from one place in the page to another was never told it had left, so it drew its content a second time; a video site's suggestions came out doubled.
- Carousels and galleries drawn at the picture's full width — a scrolling box in a flex row held its row as wide as its content, a flex row ignored its own maximum height, and a box's own maximum width did not count when its parent measured it.
- A tiny logo — a logo drawn as an SVG with only its proportions given came out square when it sat in a flex row.
- A frame drawn inside a styled search box — a field whose page took its border away still drew the engine's own frame and a second placeholder.
- Slow pages built from components — every style change read every component's stylesheets afresh; on a social site that made a page's busiest moment about ten seconds long, and it is about six now, its restyles three times as fast.
- Pages that use
:has()through:not()— no longer recompute the whole document's styles for a change one element away. - Sign-in buttons that never appeared — the user agent now carries Gecko's compatibility token, as other engines of their own do, and the sign-in services that refused the bare one serve it; and every request carries the Fetch Metadata headers every browser sends, which some sites require of anything that claims to be one.
What's Not Written Yet
- Painting gaps — transforms still translate but do not scale, rotate or skew; filters, clip paths, masks and
text-overfloware not painted. - Slow reactions — when a script reads layout after changing the page, the whole page is laid out again; a page that does that many times in a row stays busy for seconds after it has drawn (the window itself still answers).
- Script speed — the compiler is the first, simple one; a video site's player takes about three seconds to show its first frame where other browsers take one and a half.
- What
getComputedStylesays — flex, grid, gap, border and alignment properties read as empty strings. - The newer edge of scripting — WebAssembly, WebGL, Web Audio, service workers,
WebSocketand the regular-expressionvflag are not written. - Media gaps — video is VP9 in WebM and sound is Opus and WAVE; WebP and AVIF are not read. Windows has no sound and shows no video pictures yet, and on Linux video needs a graphics driver that decodes VP9 through Vulkan Video or VA-API — there is no software path for playback yet.
- macOS — the engine and its command line build and test on macOS in CI, but there is no window there yet.
Measured
- CSS reference tests — 10,272 of 15,682, up from 9,877 of 15,186. The table is at sashfold.com/wpt.html.
- WPT scripted tests — 157,846 of 192,269 subtests over 6,343 files, up from 145,667 of 174,114; the suite grew with the shadow-tree, animation, background and text-decoration directories. The table is at sashfold.com/wpt-harness.html.
- test262 — 41,845 of 45,367, the same count as 0.1.5 with the engine underneath rebuilt; it is run with the compiler off, eager and tiered, and all three agree. The table is at sashfold.com/test262.html.
- At 100% — both html5lib suites, WPT URL parsing, and the Unicode bidirectional and line-breaking conformance suites.
- The Sashfold 100 — 82 of a hundred pages of the live web render on the Windows build and 85 on the Linux build, against 80 and 82 in 0.1.5, with the five deliberately bad certificates refused on both. What each page asks for that is not written yet is listed at sashfold.com/sashfold100.
- Every score is held by CI: a test that stops passing fails the build.
Running It
- The downloads —
sashfold-0.2.0-windows-x64.zip, unzipped withthemeskept besidesashfold.exe, andsashfold-0.2.0-linux-x64.tar.gz, unpacked withthemeskept besidesashfold.sashfold https://en.wikipedia.orgopens the window on a page; with no address it opens its own new-tab page. Downloads go to your Downloads folder. - A Debian package and an AUR package —
sashfold_0.2.0_amd64.debinstalls on Debian 13, Ubuntu 24.04 and their derivatives withsudo apt install ./sashfold_0.2.0_amd64.deb, which putssashfoldon PATH and in the applications menu with its icon; on Arch,sashfold-binon the AUR installs the same build the same way. - Requirements — Windows 10 or 11, 64-bit; the binary is statically linked and imports only the operating system's own libraries, which is checked in CI. Linux needs a 64-bit machine with glibc 2.39 or newer (Ubuntu 24.04, Debian 13, Fedora 40 and their successors, Arch) and a Wayland compositor; video needs a graphics driver that decodes VP9 through Vulkan Video or VA-API, and sound a PulseAudio or PipeWire server, which the Debian package recommends; the binary carries its own C++ runtime and imports only libc and libm, checked the same way.
bash tools/install-desktop-linux.sh ./sashfold --desktopputs it in the applications menu and on the desktop with its icon; that script needs ImageMagick for the icon sizes. - Profiles and headless rendering —
--profile <dir>keeps a browser's cookies, storage and session in that folder between runs.sashfold --render page.html -o out.pngdraws a page to a picture with no window at all.SASHFOLD_USER_AGENTsets the user agent string to anything you like. - Source — macOS builds from source with CMake and Ninja; the window itself is Windows and Linux for now.
Sashfold 0.1.5
Sashfold 0.1.5 is the release where pages move: 172 commits since 0.1.0 on 2026-09-16. Video and sound play, and a video site's watch page plays through with its player's controls answering. Every tab's page runs on a thread of its own, so the window never waits on a page; the network speaks HTTP/2; and the DOM and Web API surface is much larger and much closer to the standard, with pointer events, WebIDL receiver checks, Trusted Types and many interfaces a page expects to find. Speed work runs through all of it. Still every byte is written in this repository, and still it does not pretend to do what it cannot.
What's New
- Video plays on Linux — a WebM parser and Media Source Extensions feed a VP9 decoder on the machine's video hardware, through Vulkan Video and then VA-API, with pictures decoded off the page's thread, scaled smoothly and shown at their time.
- Sound on Linux — an Opus decoder and a WAVE reader written here, played straight to the desktop's PulseAudio or PipeWire server; a video's sound plays, and a page's volume and mute reach the mixer.
- A player's own controls — play and pause, volume, seek, settings, theater mode and full screen answer on a video site's watch page. The Fullscreen API gives an element the whole window, and Esc gives it back.
- A page on a thread of its own — each tab's scripts, styles, layout and paint run in an engine on a thread of their own, so a busy page no longer holds the strip, the address bar or the window; dedicated workers get threads too.
- Pages arrive sooner — stylesheets, scripts, fonts and pictures are fetched together and ahead of the page's asking, the document comes while the window stays live, and a page shows with the pictures that have come and swaps in its fonts as they land.
- HTTP/2 — a page's requests to one host share one connection, with HPACK and the framing written from the RFCs; a request that cannot safely be repeated is never sent twice.
- Components and observers — custom elements, a
MutationObserverthat delivers real records, anIntersectionObserverthat measures, andloadanderrorevents on pictures and stylesheet links. - Canvas, IndexedDB and Intl — a canvas draws through a full 2D context, databases are kept per origin in the profile, and
Intlformats numbers, dates, lists, units and relative times in English. - Pointer events — a page hears the pointer as the standards have it: over, enter, move, down, up, out and leave with their mouse counterparts, a click at the common ancestor, double and auxiliary clicks, and real movement.
- A far larger Web API surface — WebIDL receiver checks, event handlers on the interfaces that own them,
windowandnavigatorobjects as real interfaces, Trusted Types,TreeWalkerandNodeIterator, resource-management stacks, and regular-expression lookbehind and\p{}escapes. - More CSS, and forms that post —
@supports,display: contents,aspect-ratio,object-fitandpointer-events; a form whose method is post sends URL-encoded, multipart or plain-text bodies. - Menus and tabs — a right click opens a menu of what it landed on, the toolbar has a main menu, Ctrl+Shift+T brings a closed tab back, and a tab can be duplicated or pinned.
- Bookmarks — a star and Ctrl+D, a bar under the toolbar, folders, a manager at about:bookmarks with search and undo, import from other browsers, export and dated backups.
- Themes from other browsers — a Firefox
.xpior a Chrome.crxis converted into a theme, from the profile's folder or at one click on about:themes, with pictures behind the tabs, moving ones included.
What's Fixed
- Pages that would not scroll or click — a page whose
htmlandbodyare 100% tall now scrolls to what reaches out of them, and what is under the pointer is found in painting order, so a link laid over a whole card answers andz-indexcounts. - Dialogs nobody could see — a fixed box inside a fixed backdrop was never laid out, so a page held still under a dialog showed none.
- Relative links and the address field —
<base href>was read by nothing, andpushStateleft the address field behind so that Back left the site; both follow the page now. - A window that stopped answering — loading a page held the window until it drew, and dragging a window's edge queued half a minute of layout; the document comes on another thread, and the drag settles half a second after it ends.
- Pictures stretched out of shape —
height: autoover a picture'sheightattribute came out as tall as the attribute; pictures keep their shape now, andcompletesays whether the picture is in. - Buttons drawn empty — an icon button showed an empty face, and a field's placeholder, a control's padding and its baseline were missing or wrong.
- A video that stopped partway — a video site's watch page gave up about 44 seconds in; Web API methods now refuse to run on the wrong kind of object, as every browser's do, and the video plays through.
- Players that stalled — a stylesheet link never said its sheet had come, so a player stayed in its loading skeleton and theater mode and full screen did nothing; a click on pause was read as a click on the picture and played again;
play()before the source was chosen was refused; and video stuttered while its page was busy. - Pages that use
:has()— one:has()in a page's styles made every style change recompute the whole document; now only what the change can reach is recomputed, and entering full screen on a video player went from about three seconds to half a second. - Scripts that threw where other browsers do not — recursion stopped at a thousand calls where a browser reaches twelve and a half thousand, and
JSON.parseand syntax errors now carry V8's wording. - A runaway page took the machine — a script that allocated without end now stops at a heap ceiling of its own, and the other tabs go on.
- The web driving the browser's own pages — a link or a script could name
about:bookmarks?remove=and have it done; only the page itself can ask now.
What's Not Written Yet
- Hover and focus styling —
:hover,:focus,:activeand:focus-visiblenever match yet and there is nooutline, so hover and focus styles do not show and a clicked button shows the engine's own focus ring. - Painting gaps — a transform can translate but not scale, rotate or skew, so a video player's progress bar draws its fill at full width; CSS shadows, filters, clip paths and masks are not painted.
- Slow reactions — layout after a script reads it is recomputed for the whole page, which makes some page reactions slow.
- The newer edge of scripting — WebAssembly, WebGL, Web Audio, streams, service workers, import maps,
WebSocket, shadow trees and the regular-expressionvflag are not written. - Media gaps — video is VP9 in WebM and sound is Opus and WAVE; Opus frames of the SILK and hybrid kinds play as silence, and any other codec or container, WebP and AVIF are not read. Windows has no sound and shows no video pictures yet, and on Linux video needs a graphics driver that decodes VP9 through Vulkan Video or VA-API — there is no software path for playback yet.
- Layout and text gaps —
subgrid, masonry, multi-column layout and several table behaviors; Arabic letters do not join, and Thai, Lao, Khmer and Myanmar words break between clusters. - macOS — the engine and its command line build and test on macOS in CI, but there is no window there yet, and no TLS client of its own either.
Measured
- CSS reference tests — 9,877 of 15,186, up from 9,427 of 14,616. The table is at sashfold.com/wpt.html.
- WPT scripted tests — 145,667 of 174,114 subtests over 5,555 files, against 65,822 of 74,294 in 0.1.0; the suite grew with many more directories. The table is at sashfold.com/wpt-harness.html.
- test262 — the ECMAScript conformance suite: 41,845 of 45,367, up from 40,358 of 41,791. The table is at sashfold.com/test262.html.
- At 100% — both html5lib suites, WPT URL parsing, and the Unicode bidirectional and line-breaking conformance suites.
- The Sashfold 100 — 80 of a hundred pages of the live web render on the Windows build and 82 on the Linux build, against 83 and 81 in 0.1.0, with the five deliberately bad certificates refused on both. What each page asks for that is not written yet is listed at sashfold.com/sashfold100.
- Every score is held by CI: a test that stops passing fails the build.
Running It
- The downloads —
sashfold-0.1.5-windows-x64.zip, unzipped withthemeskept besidesashfold.exe, andsashfold-0.1.5-linux-x64.tar.gz, unpacked withthemeskept besidesashfold.sashfold https://en.wikipedia.orgopens the window on a page; with no address it opens its own new-tab page. Downloads go to your Downloads folder. - A Debian package and an AUR package —
sashfold_0.1.5_amd64.debinstalls on Debian 13, Ubuntu 24.04 and their derivatives withsudo apt install ./sashfold_0.1.5_amd64.deb, which putssashfoldon PATH and in the applications menu with its icon; on Arch,sashfold-binon the AUR installs the same build the same way. - Requirements — Windows 10 or 11, 64-bit; the binary is statically linked and imports only the operating system's own libraries, which is checked in CI. Linux needs a 64-bit machine with glibc 2.39 or newer (Ubuntu 24.04, Debian 13, Fedora 40 and their successors, Arch) and a Wayland compositor; video needs a graphics driver that decodes VP9 through Vulkan Video or VA-API, and sound a PulseAudio or PipeWire server, which the Debian package recommends; the binary carries its own C++ runtime and imports only libc and libm, checked the same way, and the window is a Wayland client of its own with no toolkit underneath it.
bash tools/install-desktop-linux.sh ./sashfold --desktopputs it in the applications menu and on the desktop ...
Sashfold 0.1.0
Sashfold 0.1.0 is the first release that is not a pre-release, and the largest by far: 142 commits since 0.0.7 on 2026-09-04. Its theme is that pages now run their scripts, the window came to Linux, and the network stack is entirely the engine's own. A JavaScript engine written in this repository runs a page's scripts against its DOM, with events, timers, promises, modules and the rest of the language behind them, and reaches out over fetch and XMLHttpRequest with the cross-origin rules a browser is supposed to keep. The window is now a Wayland client of its own on Linux, and HTTPS there runs over a TLS 1.3 and 1.2 client written here too, with no operating system TLS stack behind it at all. Frames, iframes and framesets carry real windows now, each running its own scripts and taking its own scrolling, typing and clicks. The CSS reference tests went from 8345 of 14,616 to 9427; the scripted tests are a yardstick of their own now, at 65,822 of 74,294 subtests, and so is test262, the ECMAScript conformance suite, at 40,358 of 41,791. Still every byte is written in this repository, and still it does not pretend to do what it cannot.
What's New
- A JavaScript engine of its own — lexer, parser, an object model, and an interpreter that runs on a bytecode machine of its own devising: ES5 with
let/const, arrows, template literals, destructuring, classes with private fields, generators, async functions and async generators,MapandSet,Promise, typed arrays andArrayBuffer,BigInt,ProxyandReflect, and ES modules withimport(),import.metaand top-levelawait. It is scored against test262, the ECMAScript conformance suite, below. - Pages run their own scripts — a realm gives each document's scripts the DOM: elements, attributes,
innerHTML, events with capture and bubbling,setTimeout,setInterval,requestAnimationFrame,queueMicrotask,location,history,localStorageand the rest, wired into the shell, the headless renderer and the test runner alike. A page that builds its own menus and banners with a script now shows them. - fetch and XMLHttpRequest, with CORS —
Headers,Request,Response,FormDataandAbortController, and the rules above them a browser is supposed to keep: a preflight before a request that needs one, an opaque response forno-cors, and a cross-origin response read only when its own header allows it. - A window for Linux — the browser's window is a Wayland client of its own now, spoken over the compositor's socket with no toolkit underneath it. It draws at the display's scale, fractions included, takes a touchscreen's taps and drags, composes with an input method at the caret, and draws its own frame — minimize, maximize, close, a drag handle and a resize band — where the compositor draws none.
- HTTPS on Linux, homemade down to the primitives — a TLS 1.3 and TLS 1.2 client written in this repository, over cryptographic primitives also written here, checks a server's certificate against a trust store and its issuer's revocation list, and resumes a second connection to the same host from the first one's session ticket.
- iframes, frames and framesets — each shows its document in a real window of its own, running its own scripts, held to the page's framing rules, and taking the wheel, the keyboard, typing, selection and links exactly as the page itself does; a frameset is laid out as the grid its rows and columns name, with a frame drawn in each cell.
- object and embed elements — each shows a fetched document in a window of its own, a picture, or its fallback content, decided the way HTML asks and drawn live from whichever it turns out to be.
- window.open — a script opens a named frame, or a new tab in the page's own container, gated on the reader's own click or keypress so a page cannot pop one open unasked.
- Content Security Policy — a page's own headers and
<meta>policies judge every fetch and every redirect hop it makes, every inline script and style by nonce or hash, and every string a script would compile, with each refusal on the page's console. - Content blocking and cosmetic filtering — Adblock-syntax filter lists judge every request before it leaves the machine, local phishing and malware lists keep the browser off a nefarious site with no lookup sent anywhere, and a list's element-hiding rules become a stylesheet added to the page.
- Profiles and containers — a profile keeps its cookies and its session between runs, and containers keep a window's tabs apart from one another, each with a cookie jar of its own and a colour of its own along its tab's edge.
- Storage —
localStorageis the shell's now, one area per container and origin, kept for as long as the browser runs and written to the profile as it changes, so a page's storage outlives the page and comes back on the next start. - Session restore — every tab, its history and where it was scrolled comes back on the next start, written to the profile as they change.
- The command palette and four theme presets — Ctrl+Shift+P opens a list over every command the shell has, narrowed by typing: a new tab in any container, switching to an open tab, or choosing a theme by name. Nord, Solarized Dark, High Contrast and Sashfold Light ship beside the default theme.
- The new-tab page — a clock and a greeting over the theme's colors, or over the theme's own pictures, crossfading from one to the next.
- An HTTP cache that matures — a stale copy with an ETag or a Last-Modified is revalidated rather than refetched, a response with no explicit freshness gets a sensible guess from its own age, and every entry is kept on disk in the profile between runs.
- document.write — a script that writes its own markup as the parser reaches it lands in order, at the point the specification puts it.
- SVG, drawn by a rasterizer of its own — paths, shapes, groups and transforms, linear and radial gradients, clip paths and a first cut of text, inline in a page or decoded as a picture's source.
- CFF fonts — a Type 2 charstring interpreter reads CFF and CFF-flavored OpenType outlines, which is how most CJK faces and many web fonts are actually drawn.
- Web fonts in WOFF and WOFF2 — the second unwrapped through a brotli decoder written in this repository, with font matching by stretch, style, weight and
unicode-rangefor a family declared in pieces. - Kerning and Unicode line breaking — pairs are kerned from a font's own tables, and the complete UAX #14 line-breaking algorithm sits behind
word-break,line-breakandoverflow-wrap. - Colour emoji — pictures from a font's own CBDT and sbix tables, and layered glyphs from COLR and CPAL.
- Favicons — a page's own icon, decoded from BMP, ICO or PNG, drawn in its tab.
- Ranges —
RangeandStaticRange, and a live range that every mutation moves along with the text it spans. - blob: URLs, structured clone and postMessage —
URL.createObjectURLand a store of its own; a value posted between windows, sent through aMessagePort, or pushed into history, is cloned properly in the realm that receives it, cycles, typed arrays, dates and all.
What's Fixed
- Minified CSS combinators failed to parse — a selector written as
a>b,a+bora~bwith no spaces around the combinator was dropped whole, which cost 70 of the Sashfold 100 their rules; a script callingquerySelector,matchesorclosestwith the same kind of selector threw instead of answering. - Fixed and sticky boxes did not track the page's scroll — a
position: fixedbox drifted along with the page instead of staying at the viewport, and aposition: stickybox did not re-stick as the page scrolled past it. - Content painted in the wrong order — floats, block backgrounds and inline text now paint in the order CSS 2.1 Appendix E gives them, so a float sits over the blocks it overlaps and under their own words, where before the layering could go either way.
- A float's percentage height did not resolve, and an out-of-flow box's margin collapsed where it should not — a floated box's percentage height now resolves against a containing block whose height is written, as it does for any other block child, and an absolutely positioned box's top margin no longer collapses into its parent's, which used to push the parent down by a margin the positioned box does not even occupy space for.
- A hostile JPEG could overflow the decoder's arithmetic — a stream whose dequantized coefficients reached the edge of what the format allows overflowed the inverse transform's math; the transform now runs with room to spare, and every well-formed picture still decodes to the same byte as before.
What's Not Written Yet
- The newer edge of scripting — regular-expression lookbehind, property escapes and the
vflag are not written, so a script built by a bundle that needs them fails at parse time with a syntax error that names it, and the page arrives as its markup alone; import maps,WebSocket, streams, shadow trees andMutationObserverrecords are not written either. - Layout gaps —
subgridand masonry are passed over;margin-trim, multi-column layout and several table behaviors are not read. A word in a language a dictionary segments — Thai, Lao, Khmer, Myanmar — breaks between clusters rather than at its own boundaries, and hyphenation only ever breaks at a soft hyphen already written into the text. - Painting gaps — shadows, outlines, filters, clip paths, masks and
border-image; a transform can translate but not rotate or scale; a page fetches at most sixty-four of its pictures, so a gallery longer than that shows blank boxes past the sixty-fourth. - No complex-script shaping — Arabic letters do not join, a combining mark inside a right-to-left run can land on the wrong side of its base, and font hinting is not applied.
- Media gaps — WebP, AVIF, an...
Sashfold 0.0.7
The fourth pre-release of Sashfold, and the largest so far: forty-seven changes since 0.0.6. The theme of it is that a page no longer has to read left to right and across the page to be laid out correctly. The whole bidirectional algorithm is written and scored, so an Arabic or Hebrew page comes out with its boxes on the right side; the vertical writing modes are laid out, so a Japanese page can run down the page and across it; and the flow-relative properties modern stylesheets are actually written in — margin-inline, padding-block and the rest — are read at last, where before they gave a page no padding at all. Boxes that scroll became boxes you can scroll. The CSS reference tests went from 7215 of 13,477 to 8345 of 14,616, the count having grown because the writing-modes directory joined the scored set. Still every byte is written in this repository, and still it does not pretend to do what it cannot.
What's New
- Right-to-left pages —
direction, thedirattribute,dir="auto", andunicode-bidi's embeddings, isolates and overrides, over the complete Unicode bidirectional algorithm — 861,948 conformance cases, all passing. A line is reordered as the algorithm says, a run that reads the other way has its characters reversed and its brackets mirrored, and the boxes follow: a flex container's two axes, a grid's columns, a table's columns, the first line's indent, the room a width leaves over, and where a box out of the flow stands when it names no offsets. - Vertical writing —
writing-mode: vertical-rl,vertical-lr,sideways-rlandsideways-lr. Lines stack across the page, text runs down it, every glyph of a horizontal script turns a quarter circle, and a box whose mode differs from the one around it is laid out in a frame of its own and turned back into the page when it is done. - The flow-relative properties —
margin-inline,padding-block,inset-inline-start,border-block-end, the logical sizes and the four-side shorthands. Modern stylesheets are written in these, and until now they were not read at all. - Boxes that scroll —
overflow: autoandscrollare scroll containers, not just clipping. A wheel over a pane moves the pane and passes the notch outward when the pane has no room left, exactly as a browser should; a pane that can only move across takes a plain wheel that way, which is what a wide table in a narrow column needs. Scrollbars are drawn, their thumbs can be dragged, the track beside them pages, the keyboard follows whatever you last moved, and find-in-page and#anchorlinks reach inside. - See-through boxes —
opacitybetween zero and one paints what it should. A box at half strength is composited as one group, so where two things inside it overlap the one underneath does not show through the one above. - Highlighted spans join the stack — An inline box can now carry a
z-index, a transparency, a transform orposition: sticky, and its words travel with it instead of being buried under its own background. - Boxes that stick —
position: stickyholds a box inside what its offsets leave of the scrollport it is in, without letting it leave the block that contains it. :has()and:nth-child(… of S)— A selector can ask what an element holds, and count only the children that match a selector of its own.- Lists that number themselves — Markers are placed as the specification asks, inside or outside; the automatic
list-itemcounter carriesstart, an item's ownvalueand an author'scounter-resetthrough to the number that is drawn;counter()andcounters()see what their scope can see. - Justified text —
text-align: justifyshares out the room a line did not use, withtext-align-lastandjustify-allfor the line that ends the block. - Text cased on the way out —
text-transform: uppercase,lowercaseandcapitalize. - Intrinsic sizes —
min-content,max-contentandfit-contentas widths, so a box can be asked to be the size of what it holds. box-sizing: border-box— A written width can name the border box, which is how most stylesheets are written.- Flex auto margins —
margin-left: autoon a flex item takes the room the line has left, which is the "push this one to the far end" navigation bar idiom.flex-basis: contentwith it. - A grid row shares a baseline — Items in a row are measured, the furthest baseline wins and the rest are pushed down to meet it.
- A picture sits on its line properly — An image in a line of text is placed against the line's baseline, and down a vertical page it is set astride the line rather than standing on it.
What's Fixed
- A modern stylesheet gave a page no padding —
padding-inline: 1remand its relatives were parsed as nothing at all, on an English page as much as an Arabic one. - A relatively positioned
<span>moved nothing — offsets on an inline box were dropped, which was also why most of the line-height reference tests failed. margin-left: autodid not push a box to the far end of its block.- A table cell's written height now belongs to its row, so a row is at least as tall as the cell that asked for it.
- An absolutely positioned box beside a float began one float-width in and left what it was meant to cover showing. It now starts where its block's content starts.
What's Not Written Yet
- No scripting — Pages render as served; JavaScript is not run. Pages that build themselves with scripts arrive blank, their menus and banners do not toggle, and a benchmark that drives itself cannot start.
- Layout gaps —
subgridand masonry are passed over; a grid's columns do not share a baseline yet, and neither do a flex line's items;line-clamp;margin-trim. Scrollbars are drawn over the content rather than taking room from it, the way an overlay scrollbar does. - Painting gaps — Shadows, outlines, filters,
border-image; a transform can translate but not rotate or scale;background-attachment: fixedscrolls with the page. - Text gaps —
text-overflow, hyphenation,word-break, ligatures and complex-script shaping, so Arabic letters do not join and a combining mark in a reversed run lands on the wrong side.text-transformuses the simple case mappings, so sharp s stays sharp s. In vertical text,text-orientationandtext-combine-uprightare not written. - Media gaps — WOFF fonts, WebP, AVIF and SVG pictures, animated GIF beyond the first frame, audio and video.
- Windows only for the window — The engine and its command line build and test on Linux and macOS in CI; there is no Linux or macOS window yet.
Measured
- CSS reference tests — 8345 of 14,616 over CSS 2 and sixteen CSS module directories, up from 7215 of 13,477; the count grew because the writing-modes directory joined the scored set. The table is at sashfold.com/wpt.html.
- At 100% — Both html5lib suites (7032 and 1784), WPT URL parsing (893), and the Unicode bidirectional conformance suites (861,948 cases).
- The Sashfold 100 — 88 of a hundred pages of the live web now render, up from 82. What each page asks for that is not written yet is listed at sashfold.com/sashfold100.
- Every one of these is held by CI: a test that stops passing fails the build.
Running It
- Unzip and run
sashfold.exe— Keepthemesbeside it.sashfold.exe https://en.wikipedia.orgopens the window on a page; with no address it opens the about page. Downloads go to your Downloads folder. - Requirements — Windows 10 or 11, 64-bit. The binary is statically linked and imports only the operating system's own libraries, which is checked in CI.
- Source — Linux and macOS build from source with CMake and Ninja; the window itself is Windows for now.
Sashfold 0.0.6
The third pre-release of Sashfold. Since 0.0.5 the work has gone into the things that make a page look like itself rather than like a wireframe: rounded corners, table borders that collapse into one line, backgrounds that tile the way they were asked to, spacing between letters and words, an indented first line, a dressed first letter, and inline boxes that finally draw their own backgrounds and borders. The CSS reference tests of the Web Platform Tests went from 6245 to 7215 of 13,477 — past half for the first time. Still every byte is written in this repository, and still it does not pretend to do what it cannot.
What's New
- Rounded corners —
border-radiusin all its forms: one radius or four, elliptical corners through the slash syntax, percentages, and the pill that settles to a stadium. A rounded box clips what it holds, its border curves with it, and each side keeps its own colour around the bend. - Table borders that collapse —
border-collapse: collapseresolves one line where two boxes meet, the widest winning and ties going to the cell, withborder-style: hiddentaking a line away entirely. - Backgrounds that tile as asked —
background-repeat: spaceshares the remainder between the tiles and keeps the ends flush;roundrescales so a whole number fits. A background written onbodyis handed to the canvas and laid out as if it had been written on the root, so a page's backdrop covers the window. - Letter and word spacing —
letter-spacingandword-spacingadd room after every character and after every space, in the layout and in the drawing alike, so an underline reaches the last gap and a spaced line wraps where it should. - An indented first line —
text-indentmoves the first line of a block, in pixels or as a percentage, and a negative one hangs it out to the left. It is measured from the edge a float leaves, and it inherits into the blocks inside. - The first letter, dressed —
::first-letter(and the one-colon spelling) in a colour, a weight or a size of its own — the drop cap a page opens with. It takes the punctuation on either side of the letter, in every script Unicode calls punctuation, and finds the letter wherever it was written, including inside the first block of a block. - Inline boxes draw themselves — A span's own background, border and padding are painted and take room on the line, so highlighted phrases, code spans, tags and pills look like themselves. A box broken across lines is sliced at the break, as it should be.
- Truer text measurements — The
exandchunits are read from the font actually in use — its x-height and the width of its zero — instead of standing for half the font size, so boxes sized in them come out the width their author meant. - Absolutely positioned grid children — A grid container places an absolutely positioned child in the area it names.
- Links look like links, and only links — An
<a>with no address is no longer coloured and underlined as one. - Standards scores you can check — The CSS reference tests, 13,477 of them over CSS 2 and fifteen CSS module directories, score 7215 passing — up from 6245 — held by CI so the number only rises; the table is at sashfold.com/wpt.html. The Sashfold 100 at sashfold.com/sashfold100 shows a hundred pages of the live web rendered every night, with what each page asks for that is not written yet.
What's Not Written Yet
- No scripting — Pages render as served; JavaScript is not run. Pages that build themselves with scripts arrive blank, their menus and banners do not toggle, and a benchmark that drives itself cannot start.
- Layout gaps —
subgridand masonry are passed over; grid items align to the start rather than a shared baseline;min-content,max-contentandfit-contentare not read as sizes;text-align: justifylays out asleft; counters and list markers are not placed per the specification; right-to-left pages are shown left-to-right. - Painting gaps — Shadows, outlines, filters,
border-image; rotations and scales are passed over;opacitybetween zero and one paints as opaque;background-attachment: fixedscrolls with the page. - Text gaps —
text-transform,text-overflow, hyphenation,word-break, ligatures and complex-script shaping;font-synthesiscannot be turned off. - Media gaps — WOFF fonts, WebP, AVIF and SVG pictures, animated GIF beyond the first frame, audio and video.
- Windows only for the window — The engine and its command line build and test on Linux and macOS in CI; there is no Linux or macOS window yet.
Running It
- Unzip and run
sashfold.exe— Keepthemesbeside it.sashfold.exe https://en.wikipedia.orgopens the window on a page; with no address it opens the about page. Downloads go to your Downloads folder. - Requirements — Windows 10 or 11, 64-bit. The binary is statically linked and imports only the operating system's own libraries, which is checked in CI.
- Source — Linux and macOS build from source with CMake and Ninja; the window itself is Windows for now.
Sashfold 0.0.5
The second pre-release of Sashfold. Since 0.0.3 the engine has grown the layout the readable web is built on — floats, flexbox, grid, tables, positioning — and the browser around it has grown selection, find, reader mode and devtools. It scores itself on the Web Platform Tests' CSS reference tests now, 6245 of 13477 passing, and renders a hundred live pages every night at sashfold.com. Still every byte is written in this repository, and still it does not pretend to do what it cannot.
What's New
- Floats, flexbox, grid and tables — Boxes float left and right with lines flowing beside them and clearing below them; flex rows and columns that wrap, grow, shrink and align, with gaps; grid containers with explicit and implicit tracks,
fr,repeat(), named lines and areas, spans, auto-placement and alignment; tables with automatic and fixed column widths,colspanandrowspan, captions, header and footer groups, and cells aligned byvertical-align. The old attributes work too:cellpadding,cellspacing,border,bgcolor,align,valign,widthandheight. - Positioning and stacking —
position: relative,absoluteandfixedwith their offsets, and painting in CSS 2.1's stacking order withz-index, so menus, badges and overlays land where their pages put them. - Background images and gradients — Pictures by
url()placed, sized, tiled and clipped asbackground-position,-size,-repeat,-originand-clipsay, in layers; linear gradients by angle or toward a side or corner, and radial gradients, with color stops; thebackgroundshorthand. - More of CSS — Custom properties and
var(),calc(),min(),max()andclamp(), the viewport units,inherit,initialandunseton every property,inline-block,vertical-align,line-height,min-andmax-widths and heights, percentage heights,visibility,opacity: 0,overflowclipping, translate transforms,::beforeand::afterwith strings,attr()and quotation marks,hsl()and all 148 named colors, and thefontshorthand. - Web fonts — A page's own TrueType fonts through
@font-faceanswer itsfont-family. - Text selection and the clipboard — Select with the mouse or the keyboard, copy with Ctrl+C, paste into a field with Ctrl+V.
- Find in page — Ctrl+F opens a bar that counts the matches, highlights them and walks them with Enter.
- Reader mode — A page's article alone, on a clean page of its own.
- Keyboard link-hints — Press
fand every link in view gets a label; type a label to follow it. - Devtools — F12 opens a panel with the document's tree, and the box and computed style of the element under inspection.
- Forms without scripts — Text fields, checkboxes, radio buttons, selects and buttons are drawn, take input, and submit by GET.
- Faster loads — Connections are kept and reused across a page's requests; an
<img>picks its source fromsrcset,sizesand<picture>for the window it is shown in; revoked certificates are refused. - Standards scores you can check — The CSS reference tests of the Web Platform Tests, 13,477 of them over CSS 2 and fifteen CSS module directories, score 6245 passing, held by CI so the number only rises; the table is at sashfold.com/wpt.html. The Sashfold 100 at sashfold.com/sashfold100 shows a hundred pages of the live web rendered every night, with what each page asks for that is not written yet.
What's Not Written Yet
- No scripting — Pages render as served; JavaScript is not run. Pages that build themselves with scripts arrive blank, and their menus and banners do not toggle.
- Layout gaps — Table borders do not collapse (each box draws its own);
subgridand masonry are passed over; inline boxes do not paint their own backgrounds and borders; right-to-left pages are shown left-to-right. - Painting gaps — Rounded corners, shadows, outlines, filters;
background-repeat: spaceandroundtile plainly; rotations and scales are passed over;opacitybetween zero and one paints as opaque. - Media gaps — WOFF fonts, WebP, AVIF and SVG pictures, animated GIF beyond the first frame, audio and video.
- Windows only for the window — The engine and its command line build and test on Linux and macOS in CI; there is no Linux or macOS window yet.
Running It
- Unzip and run
sashfold.exe— Keepthemesbeside it.sashfold.exe https://en.wikipedia.orgopens the window on a page; with no address it opens the about page. Downloads go to your Downloads folder. - Requirements — Windows 10 or 11, 64-bit. The binary is statically linked and imports only the operating system's own libraries, which is checked in CI.
- Source — Linux and macOS build from source with CMake and Ninja; the window itself is Windows for now.
Sashfold 0.0.3
The first pre-release of Sashfold: a web browser engine, and a Windows browser around it, written from scratch. Every byte that runs is in this repository: the HTML and CSS parsers, layout, text, the font engine, the image decoders, HTTP, cookies, the cache, and the window. It is early. It reads the readable web, and it does not pretend to do the rest.
What's New
- A window that browses the live web over HTTPS — Tabs (Ctrl+T, Ctrl+W, Ctrl+Tab, middle-click on a link), history that keeps each page's bytes so Back never refetches, an address bar, link hit-testing, wheel and keyboard scrolling. Every pixel of the chrome comes from
themes/default.json, which reloads while the window is open. - Your installed fonts — A TrueType reader and a rasterizer written here, a font manager that catalogues the system's fonts by their naming tables alone and matches CSS
font-family, weight and slant, and a fallback chain by coverage, so a page mixes its own fonts with whatever the machine has for the characters they lack. Sashfold Mono, an original monospace face, is the last fallback and draws an honest box for anything no font has. - Images — PNG (every color type, bit depth and filter, interlaced), GIF (LZW, interlace, transparency; the first frame), and JPEG (baseline and progressive), each decoder written and fuzzed here, sized by CSS, attributes or the picture itself, drawn scaled with integer arithmetic.
- A site's own stylesheets —
<link rel="stylesheet">and@import, decoded by the CSS charset rules, with media queries evaluated against the real window size and re-evaluated when it changes. - Rendering that is the same everywhere — The reference tests render with the built-in face alone and compare PNG bytes, so the same page produces byte-identical pixels on Windows, Linux and macOS, and the outline rasterizer is held to the built-in face's pixels exactly.
- Privacy and safety defaults — Third-party cookies blocked, HTTPS first for typed addresses,
strict-origin-when-cross-originreferrers, a certificate error page with no way through, downloads saved with the mark of the web and never opened, no default search, no telemetry, no self-updater. - Standards scores you can check — The HTML tokenizer at 7032/7032 and tree construction at 1784/1784 on the html5lib suites; the URL parser at 893/893 on WPT's
urltestdata. Both are enforced in CI by baselines that only ratchet upward. - A command line for the engine —
sashfold --render page.html -o out.pngrenders any page or URL to a PNG;--benchtimes parse, stylesheets, style, layout and paint;--dump-domprints the tree;--font-infoand--font-listshow what the font engine sees.
What's Not Written Yet
- Layout gaps — Floats, flexbox, grid, tables and positioning are not written, so pages built on them fall into one column. Nothing stands in for them: what Sashfold cannot do, it does not do.
- No scripting — Pages render as served; JavaScript is not run.
- No text selection or clipboard yet, no find-in-page, no animated GIF — The first frame of a GIF is shown.
- Windows only for the window — The engine and its command line build and test on Linux and macOS in CI; there is no Linux or macOS window yet.
- Loading blocks while it fetches — Each stylesheet and image is fetched in turn; a page with many pictures takes a moment to appear.
Running It
- Unzip and run
sashfold.exe— Keepthemesbeside it.sashfold.exe https://en.wikipedia.orgopens the window on a page; with no address it opens the about page. Downloads go to your Downloads folder. - Requirements — Windows 10 or 11, 64-bit. The binary is statically linked and imports only the operating system's own libraries, which is checked in CI.
- Source — Linux and macOS build from source with CMake and Ninja; the window itself is Windows for now.