<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zion Sati</title>
    <description>The latest articles on DEV Community by Zion Sati (@zionsati).</description>
    <link>https://dev.to/zionsati</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3972208%2Fc32ce580-3e63-4cad-a182-c78a359f5178.png</url>
      <title>DEV Community: Zion Sati</title>
      <link>https://dev.to/zionsati</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zionsati"/>
    <language>en</language>
    <item>
      <title>HTML, JavaScript, CSS should have died long ago</title>
      <dc:creator>Zion Sati</dc:creator>
      <pubDate>Mon, 10 Aug 2026 08:56:32 +0000</pubDate>
      <link>https://dev.to/zionsati/html-javascript-css-should-have-died-long-ago-1mm3</link>
      <guid>https://dev.to/zionsati/html-javascript-css-should-have-died-long-ago-1mm3</guid>
      <description>&lt;p&gt;&lt;strong&gt;EffinDOM:&lt;/strong&gt; &lt;a href="https://effindom.dev/" rel="noopener noreferrer"&gt;https://effindom.dev/&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Rust UI Framework - FUI-RS:&lt;/strong&gt; &lt;a href="https://github.com/zion-sati/fui-rs" rel="noopener noreferrer"&gt;https://github.com/zion-sati/fui-rs&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;FUI-RS Live Demo:&lt;/strong&gt; &lt;a href="https://fui-rs-demo.effindom.dev/" rel="noopener noreferrer"&gt;https://fui-rs-demo.effindom.dev/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ever since WebAssembly (WASM) was just a JavaScrpt file, I've already dreamt of the death of HTML, CSS and JavaScript itself.&lt;/p&gt;

&lt;p&gt;Death. Gone from the face of the earth.&lt;/p&gt;

&lt;p&gt;Okay, that's a bit much - but the dream was it should never have been the de facto stack of Web Apps.&lt;/p&gt;

&lt;p&gt;HTML, CSS and JavaScript won. There are millions of developers, libraries for everything, answers on forums and AI trained against existing them.&lt;/p&gt;

&lt;p&gt;But ask around and you'll quickly find all good developers hate them. These are three historical accidents that turned into a badly mashed up application model. The world is now tightly coupled with these accidents, making replacing them extremely difficult.&lt;/p&gt;

&lt;p&gt;You'll find bad UX every time HTML quietly gives up. A workflow graph is a good example; so is a tmeline, waveform editor, diagram surface, node editor, etc. Let alone a combination of them on the same page. The page itself remains HTML. The difficult bits that HTML can't do becomes canvas, SVG, WebGL or a stupid pile of absolutely position divs...&lt;/p&gt;

&lt;p&gt;Sure, we see the pixels appear and at first glance it looks great. Until someone tries to use it.&lt;/p&gt;

&lt;p&gt;Does the mouse wheel scroll the page, pan the surface, or zoom it? Does trackpad pinch mean browser page zoom or graph zoom? What happens when a drag leaves the surface? Which layer owns the pointer capture? What about keyboard? Does keyboard focus enter it and tab across its inner elements? Can a screen reader understand them? Does its hover state look the same as the one next to it?&lt;/p&gt;

&lt;p&gt;The custom control provider decides and every provider invents a different answer. Sure, you might be thinking that they'd have all sorts of knobs for you to fine tune all of these. But that simply leads to a combinatorial explosion, and prohibits unified design systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The custom control surface has become a second UI system embedded inside the first one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why beautiful and sophisticated HTML5 controls often feel so wrong. The control simply has no knowledge of the application - mouse handling, wheel momentum, pinch-to-zoom, pull-to-refresh, touch arbitration, focus, selection, keyboard shortcuts, context menus, accessibility, gestures, etc etc etc are rebuilt inside an island. That is if they go as far as that to begin with.&lt;/p&gt;

&lt;p&gt;Even if they do. You could argue, sure, it works. But so does a lot of tech debt.&lt;/p&gt;
&lt;h2&gt;
  
  
  The hack inside a hack
&lt;/h2&gt;

&lt;p&gt;HTML is excellent at documents (do we need to be reminded what DOM stands for? No, not the latex kinda DOM... the document kind). It was also pretty good for building conventional forms (also a document). Somewhere along the way, we turned it into a general application platform because it was already everywhere, not because a document tree, a cascade and a scripting language somehow formed a brilliant UI architecture.&lt;/p&gt;

&lt;p&gt;When the document model runs out of vocabulary, the supposedly "clean" way is to insert a custom rendering surface and rebuild what's missing in JavaScript. However, that creates two owners for the same interaction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The browser owns page scrolling, page zoom, focus traversal, text input, text rendering, accessibility and platform conventions.&lt;/li&gt;
&lt;li&gt;The embedded surface owns its own hit testing, gesture state, inner scrolling, inner page zoom, inner focus traversal, inner text input, inner text rendering, inner... you get where this is going... But the point is, everything "inner" is often just an approximation of the browser's look and feel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The boundary is where applications become uncanny. GitHub Actions workflow diagram is a good example - it's so bad no matter how long you spent using it, it still feels uncanny. Wheen input is captured when the user meant to scroll. Pinch changes the wrong scale, drag gets stuck because the release happened elsewhere. Keyboard commands work until focus crosses into the island. The graph is themed, but separately so - chances are they'd be sutbly different.&lt;/p&gt;

&lt;p&gt;None of this is impossible to fix. Pointer events, touch actions, ARIA, DOM overlays and a crazy amount of JavaScript can reproduce almost anything.&lt;/p&gt;

&lt;p&gt;BUT. Once a custom widget has implemented all of that fully polished, it has built a small UI framework. The next control builds another one, often by another provider. Good luck convincing them what the middle wheel button's meant to do...&lt;/p&gt;
&lt;h2&gt;
  
  
  A canvas is not a UI framework
&lt;/h2&gt;

&lt;p&gt;So I hear you say - what if the entire page is one enormous canvas? It doesn't solve anything - a canvas gives us pixels. It doesn't give us retained control identity, layout, focus, text editing, IME, selection, clipboard behavior, password-manager integration, browser find, semantics, accessibility, touch conventions, or platform-aware shortcuts.&lt;/p&gt;

&lt;p&gt;This is where most canvas UI arguments go off the rails. A canvas is a framebuffer, not an application platform.&lt;/p&gt;

&lt;p&gt;EffinDOM exists to provide the missing application model. Canvas is simply one presentation surface used by the browser host.&lt;/p&gt;

&lt;p&gt;The runtime owns one retained tree for layout, controls, rendering, input, text, selection, scrolling and semantics - yes that's like exactly what the browser currently does. Except the bet is browsers will no longer have to do this in the future.&lt;/p&gt;

&lt;p&gt;We then project the parts that the host platform must own. In the browser, that includes a semantic DOM, mirrored editor state, IME integration and browser-facing behavior. On native desktop, it includes the OS accessbility and input adapters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       Your retained application
                    │
          ┌─────────┴─────────┐
          ▼                   ▼
       FUI-RS              FUI-AS
    Rust application    AssemblyScript web
         SDK                  SDK
          └─────────┬─────────┘
                    ▼
                EffinDOM
    layout | text | input | semantics
           ╱                  ╲
          ▼                    ▼
   Browser/WASM             Native desktop
  canvas + host         Metal | D3D12 | Vulkan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The normal application path would share most of the UI code. Genuine platform-specific capabilities would remain available behind clean boundaries.&lt;/p&gt;

&lt;p&gt;Native:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgipmlupj192vej233im.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgipmlupj192vej233im.png" alt=" " width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Web:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhud8fy8l38eg02zdwpcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhud8fy8l38eg02zdwpcl.png" alt=" " width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why start with the browser?
&lt;/h2&gt;

&lt;p&gt;Most custom-rendered UI frameworks start natively and add a WASM target later.&lt;/p&gt;

&lt;p&gt;That makes complete sense. Native is easier.&lt;/p&gt;

&lt;p&gt;You own the window. You own the event loop. You own rendering, input, clipboard, accessibility adapters and file access. There isn't a browser sitting around your application with thirty years of behaviors users expect you to preserve.&lt;/p&gt;

&lt;p&gt;I started with the browser precisely because it is harder.&lt;/p&gt;

&lt;p&gt;Drawing some buttons onto a canvas is easy. Drawing them while pretending text selection, IME, password managers, browser navigation, pull-to-refresh, trackpad gestures, accessibility, keyboard focus and find-on-page don't exist is even easier.&lt;/p&gt;

&lt;p&gt;But that's not a web application. That's a screenshot that responds to mouse clicks.&lt;/p&gt;

&lt;p&gt;For EffinDOM to be credible on the web, it had to participate in browser behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyboard, mouse and touch text selection.&lt;/li&gt;
&lt;li&gt;IME composition and CJK input.&lt;/li&gt;
&lt;li&gt;Clipboard operations and platform-specific shortcuts.&lt;/li&gt;
&lt;li&gt;Password-manager and form integration.&lt;/li&gt;
&lt;li&gt;ARIA and semantic inspection.&lt;/li&gt;
&lt;li&gt;Browser and retained find-on-page.&lt;/li&gt;
&lt;li&gt;Routing and independently loaded WASM pages.&lt;/li&gt;
&lt;li&gt;Async fonts and fallback.&lt;/li&gt;
&lt;li&gt;Page zoom, trackpad pinch and nested scrolling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yes, a lot of these are the boring things nobody puts in the first demo.&lt;/p&gt;

&lt;p&gt;They are also the difference between a demo and a UI framework.&lt;/p&gt;

&lt;p&gt;Once those worked, taking the same retained model to macOS, Windows and Linux was still a lot of engineering work.&lt;/p&gt;

&lt;p&gt;Metal, D3D12 and Vulkan did not wire themselves up while I slept.&lt;/p&gt;

&lt;p&gt;But architecturally, the direction was much cleaner.&lt;/p&gt;

&lt;p&gt;The runtime already knew what a button meant. It already knew what text selection meant. It already knew about focus, scrolling, semantics, editing and accessibility. The native hosts only had to connect that meaning to another platform (I used SDL here which gave me a leg up on unifying the 3 major platforms).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;EffinDOM isn't a native UI framework that happens to compile to canvas. It was born in the browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The hybrid route is still a hybrid
&lt;/h2&gt;

&lt;p&gt;Cross-platform UI reminds me of the car industry.&lt;/p&gt;

&lt;p&gt;Electron and WebView applications are conventional hybrids. They put a native body around a browser drivetrain.&lt;/p&gt;

&lt;p&gt;It works. It is incredibly practical. It also means every application drags a browser around just so we can pretend a website became a desktop application. That's your average Slack, GitKraken, VSCode etc. * cough give me back my RAM * Those things are more expensive than gold bars by weight these days!&lt;/p&gt;

&lt;p&gt;There is now a cleverer middle ground: remove the WebView and render an HTML-oriented application model natively.&lt;/p&gt;

&lt;p&gt;That's a pretty substantial engineering achievement. But it's still a hybrid - the equivalent of a plug-in hybrid.&lt;/p&gt;

&lt;p&gt;The browser engine may be gone, but the application still begins with concepts designed for the web, and written for devs who are now close to their fifties (I'm one of them).&lt;/p&gt;

&lt;p&gt;EffinDOM and FUI are the EV bet.&lt;/p&gt;

&lt;p&gt;I'm not trying to work out how to render HTML somewhere else. I'm asking what should replace it.&lt;/p&gt;

&lt;p&gt;Of course, HTML has had three decades to install petrol stations on every corner. Component libraries, browser tooling, integrations, hosting, documentation, experienced developers etc.&lt;/p&gt;

&lt;p&gt;FUI has a working drivetrain. Its charging network is young. The built-in controls, browser and desktop hosts, packaging, documentation, Agent Skills and access to the Rust ecosystem cover quite a lot. The third-party control ecosystem does not have thirty years behind it.&lt;/p&gt;

&lt;p&gt;Early adopters may occasionally have to plan the route themselves.&lt;/p&gt;

&lt;p&gt;That's the bet though. Useful applications create demand for more controls and tooling. Better tooling makes the next application cheaper. Eventually, if enough people use it, the ecosystem develops gravity of its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  The control that nearly broke the dream
&lt;/h2&gt;

&lt;p&gt;You might assume the hardest control was a graph, custom drawn surface or the interactive dancing yarn demo.&lt;/p&gt;

&lt;p&gt;Nope.&lt;/p&gt;

&lt;p&gt;It was the boring multiline text box everybody assumes the operating system simply gives you: &lt;code&gt;TextArea&lt;/code&gt;. But remember, we are bypassing the browser altogether here.&lt;/p&gt;

&lt;p&gt;Text editing exposes every shortcut a UI framework is tempted to take.&lt;/p&gt;

&lt;p&gt;Monospaced fonts can use specialized paths. Fixed line heights can be optimized differently to variable line heights (yes unicode chars have different heights encoded by their fonts). Non-wrapping text is different from wrapping text. Static text is different from editable text. Large documents should process only visible lines, otherwise the app crawls to a standstill. Incremental edits should not reshape the entire document.&lt;/p&gt;

&lt;p&gt;Then all of those optimizations combine.&lt;/p&gt;

&lt;p&gt;Every combination must still preserve Unicode boundaries, caret geometry, forward and reverse selection, scrolling, hit testing, tabs, newlines, clipboard operations, undo, accessibility and font fallback.&lt;/p&gt;

&lt;p&gt;On the web, EffinDOM renders the visible editor while keeping a mirrored DOM editor for IME and browser integration.&lt;/p&gt;

&lt;p&gt;Text inputs also need the correct semantics for password-manager extensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why retained FUI?
&lt;/h2&gt;

&lt;p&gt;I didn't want another framework that repeatedly rebuilds a description of the UI so it can compare that description with the UI it already had (ok fine, I hate react).&lt;/p&gt;

&lt;p&gt;FUI uses retained controls.&lt;/p&gt;

&lt;p&gt;Construct the control once. Keep its identity. Change the property that changed.&lt;/p&gt;

&lt;p&gt;Focus, selection, editor state, scroll position, subscriptions and overlays remain attached to the actual control the application created.&lt;/p&gt;

&lt;p&gt;FUI-RS exposes that model to Rust. FUI-AS exposes the same controls and fluent conventions to AssemblyScript.&lt;/p&gt;

&lt;p&gt;The syntax belongs to each language. The application model underneath does not change.&lt;/p&gt;

&lt;p&gt;The shared EffinDOM runtime is written in C++ deliberately. Rendering, layout, text shaping, input and semantics are infrastructure for FUI-RS, FUI-AS and future language SDKs such as FUI-KT (Kotlin) and FUI-CS (C#).&lt;/p&gt;

&lt;p&gt;A narrow ABI (Application Binary Interface) lets the runtime work with Skia, Yoga, HarfBuzz, ICU, SDL and native platform APIs without reimplementing the engine for every language.&lt;/p&gt;

&lt;p&gt;FUI-RS is the idiomatic Rust application surface over a language-neutral runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is any of this real yet?
&lt;/h2&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;EffinDOM currently runs in browsers through WASM (WebAssembly) and natively on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS ARM64 and x64 using Metal.&lt;/li&gt;
&lt;li&gt;Windows ARM64 and x64 using D3D12.&lt;/li&gt;
&lt;li&gt;Linux ARM64 and x64 using Vulkan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No Electron, no system WebView for native apps.&lt;/p&gt;

&lt;p&gt;FUI-RS covers browser and native desktop. FUI-AS currently targets the browser (with plans for native desktop in the future).&lt;/p&gt;

&lt;p&gt;A universal routed FUI-RS application can compile each browser route into an independently loaded WASM MFE (micro-frontend) while composing the same retained page crates into one native application.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cargo-fui&lt;/code&gt; creates, builds and packages the application. It produces DMG, MSIX and AppImage packages. Native-only Rust developers do not need Node.js or npm.&lt;/p&gt;

&lt;p&gt;The current uncached browser transfer (cold load) is roughly 7.6 MB compressed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EffinDOM core: about 1.9 MB.&lt;/li&gt;
&lt;li&gt;UI runtime: about 810 KB.&lt;/li&gt;
&lt;li&gt;ICU data: about 2.4 MB.&lt;/li&gt;
&lt;li&gt;Six built-in fonts: about 2 MB total.&lt;/li&gt;
&lt;li&gt;FUI-RS home route: about 321 KB.&lt;/li&gt;
&lt;li&gt;FUI-AS home route: about 122 KB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of that is shared infrastructure rather than application code. Within the same top-level site, routes and FUI language SDKs reuse the content-addressed runtime and fixed assets.&lt;/p&gt;

&lt;p&gt;A warm load of the FUI-RS demo home page transfers under 500Kb.&lt;/p&gt;

&lt;h2&gt;
  
  
  What doesn't exist yet?
&lt;/h2&gt;

&lt;p&gt;This is feature-rich early access, not 1.0.&lt;/p&gt;

&lt;p&gt;The APIs can still break if my work porting it to Android and iOS deemed it necessary. The third-party control ecosystem is young.&lt;/p&gt;

&lt;p&gt;There is one independent community application so far: &lt;a href="https://github.com/jatm80/galaga-rs/" rel="noopener noreferrer"&gt;Galaga-RS&lt;/a&gt;, a space shooter with a &lt;a href="https://jatm80.github.io/galaga-rs/" rel="noopener noreferrer"&gt;live browser build&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The point
&lt;/h2&gt;

&lt;p&gt;The web keeps proving applications need more than documents.&lt;/p&gt;

&lt;p&gt;Our answer has been to place increasingly sophisticated private UI frameworks inside HTML and try to patch the differences one control at a time. In the world of AI we live in currently, that's a pretty good way to burn tokens.&lt;/p&gt;

&lt;p&gt;I refuse to believe another layer of JavaScript is the best the human civilisation can do.&lt;/p&gt;

&lt;p&gt;EffinDOM is my attempt to replace the application UI model without throwing away the browser and OS integration users expect.&lt;/p&gt;

&lt;p&gt;FUI-RS is the flagship, FUI-AS is the AssemblyScript SDK, and FUI-KT and FUI-CS are early smoke-level language surfaces.&lt;/p&gt;

&lt;p&gt;The project is far enough along to build real applications, but early enough that somebody using it can still influence where it goes.&lt;/p&gt;

&lt;p&gt;&lt;a href="//fui-rs-demo.effindom.dev"&gt;Open the demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://crates.io/crates/cargo-fui" rel="noopener noreferrer"&gt;Scaffold an application&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Try to build something that isn't already in the showcase.&lt;/p&gt;

&lt;p&gt;Then tell me where it breaks.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webdev</category>
      <category>programming</category>
      <category>ui</category>
    </item>
    <item>
      <title>I built a 100KB WebAssembly UI engine that bypasses the DOM but keeps native VoiceOver accessibility</title>
      <dc:creator>Zion Sati</dc:creator>
      <pubDate>Tue, 09 Jun 2026 04:52:58 +0000</pubDate>
      <link>https://dev.to/zionsati/i-was-in-a-mobx-vs-redux-brownbag-my-mind-wandered-eight-years-later-i-shipped-a-webassembly-537c</link>
      <guid>https://dev.to/zionsati/i-was-in-a-mobx-vs-redux-brownbag-my-mind-wandered-eight-years-later-i-shipped-a-webassembly-537c</guid>
      <description>&lt;p&gt;It was a brownbag session. My colleagues were presenting MobX -- how it was superior to Redux, how it finally solved the state management problem. And my mind wandered.&lt;/p&gt;

&lt;p&gt;Not because it wasn't interesting. But because I kept taking steps back.&lt;/p&gt;

&lt;p&gt;Past React. Past the virtual DOM. Past JavaScript. All the way back to Delphi -- which if you used it, you know was Win32 done right. A beautiful retained-mode API sitting on top of a system layer, where you described what you wanted and the runtime figured out how to render it. No reconciler. No hooks rules. No "why did this re-render."&lt;/p&gt;

&lt;p&gt;I pulled a colleague aside afterwards. Told him I thought we'd built the entire web application platform on the wrong foundation, and that someone should fix it properly.&lt;/p&gt;

&lt;p&gt;He told me it was too hard. Impossible. Swimming against the tide.&lt;/p&gt;




&lt;h2&gt;
  
  
  Eight years of failing
&lt;/h2&gt;

&lt;p&gt;I want to be honest about what "eight years" actually means here, because it wasn't eight years of solid focused work. It was eight years of dabbling, failing, giving up, then being pulled back in by another brownbag or another frustrated frontend conversation.&lt;/p&gt;

&lt;p&gt;I've always called myself a backend engineer. I'd jump into frontend when I had to, and every time I did I'd come back with the same quiet frustration. Not because the DOM is bad -- it's genuinely excellent at what it was designed for. HTML is a document model. For content sites, blogs, marketing pages, e-commerce -- it's the right tool, and React and its ecosystem serve that use case well.&lt;/p&gt;

&lt;p&gt;The problem is that we started building &lt;em&gt;applications&lt;/em&gt; with it. Trading dashboards. Design tools. Kiosks. Data-heavy enterprise UIs. The kind of thing you'd build in WPF or SwiftUI -- retained-mode, stateful, performance-critical. And for that category, a document model is the wrong foundation. Some developers have never known anything else -- they were born into a world where React was already the default, where the reconciler mental model was just "how the web works." Like the Matrix: they can't see the document model underneath because they've never been outside it.&lt;/p&gt;

&lt;p&gt;For those of us who remember Delphi, WPF, Qt, SwiftUI -- the mismatch has always been visible.&lt;/p&gt;

&lt;p&gt;The moment that hit hardest was watching Blazor fail.&lt;/p&gt;

&lt;p&gt;I had real hope for Blazor. Microsoft, serious engineering investment, the promise of writing C# for the web. And it failed -- not catastrophically, but it never became what it could have been. I stopped thinking about this problem entirely for a while after that. Years, actually.&lt;/p&gt;

&lt;p&gt;But watching Blazor fail also taught me something. Every canvas framework that tried to target the web had the same fundamental problem. Flutter Web, egui, Blazor, Compose -- all ports, not designs. They were built somewhere else and shoved into the browser. They lost what the web actually offers -- the CDN, the browser cache, the URL distribution model -- and they fought the browser on everything else.&lt;/p&gt;

&lt;p&gt;The insight, when it finally crystallized: the only way to do this right is to design for the browser from day one. Not port to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I actually built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;EffinDom&lt;/strong&gt; is a POSIX-style display server for WebAssembly UI. The browser is our Win32. &lt;strong&gt;fui-as&lt;/strong&gt; -- the AssemblyScript SDK -- is our Delphi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb04vmiulwkf2wsi4o70n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb04vmiulwkf2wsi4o70n.png" alt="FUI-AS Demo home page -- virtual list with 100,000 items, controls showcase, routing between pages. Dark theme." width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The demo home page -- 100,000-item virtual list on the left, controls on the right. Three routed pages, all running in a canvas with zero DOM UI.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The architecture is a strict three-tier stack:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 1 -- Core (&lt;code&gt;effindom-core.wasm&lt;/code&gt;):&lt;/strong&gt; A stateless C++ WebGL microkernel. Raw drawing instructions. Knows nothing about UI or text. Dumb, fast, memory-safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 2 -- UI (&lt;code&gt;effindom-ui.wasm&lt;/code&gt;):&lt;/strong&gt; The retained-mode runtime. Yoga flexbox layout. HarfBuzz + ICU text shaping. Input routing, semantics projection, focus management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 3 -- FUI-AS:&lt;/strong&gt; The app-facing SDK. AssemblyScript -- TypeScript-style, compiles to WebAssembly. SwiftUI-inspired fluent API. No HTML. No CSS. No virtual DOM diffing.&lt;/p&gt;

&lt;p&gt;A minimal app looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SelectionArea&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Fui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clicked 0 times&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;buildPage&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;SelectionArea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SelectionArea&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillWidth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillHeight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Click me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;onClickWith&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HelloWorld&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clicked &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createHelloWorldPage&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;SelectionArea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;buildPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No JSX. No hooks. No &lt;code&gt;useEffect&lt;/code&gt;. A retained scene graph that does what you mean.&lt;/p&gt;




&lt;h2&gt;
  
  
  The thing that makes it actually web-native
&lt;/h2&gt;

&lt;p&gt;Every other canvas framework I looked at had the same payload problem. Flutter Web bundles the Skia engine, the Dart runtime, and your app into one monolithic blob. egui statically links its own text shaping and GPU rasterizer. Visit two Flutter apps, download the engine twice.&lt;/p&gt;

&lt;p&gt;EffinDom's answer is the Web DLL model.&lt;/p&gt;

&lt;p&gt;Tier 1 and Tier 2 are immutable, content-hashed WebAssembly modules served from a CDN. Once a browser downloads them, they're cached forever -- shared across every EffinDom app the user visits. The ICU data, the fonts, the HarfBuzz shaper: all cached, all shared.&lt;/p&gt;

&lt;p&gt;Your app payload is just your business logic. Under 100 KB over the wire with Brotli -- even for feature-heavy apps. Your app payload is just your business logic.&lt;/p&gt;

&lt;p&gt;This is what web-native actually means -- architecture designed for the browser's actual physics, not retrofitted onto them.&lt;/p&gt;

&lt;p&gt;From a web native feel point of view, you have to play around with the demo -- a huge amount of effort have been put into this.&lt;/p&gt;




&lt;h2&gt;
  
  
  Accessibility wasn't an afterthought -- it was the architecture
&lt;/h2&gt;

&lt;p&gt;Most canvas frameworks get accessibility wrong for the same reason: they design the rendering pipeline first and try to retrofit ARIA on top. Screen reader support becomes a feature request, not a foundation.&lt;/p&gt;

&lt;p&gt;I built EffinDom the other way around. &lt;strong&gt;ARIA was a first-class architectural requirement&lt;/strong&gt; before the first control was written. The browser bridge, the coordinate syncing, the real-time semantic tree -- these aren't features added later. They're load-bearing parts of how EffinDom works.&lt;/p&gt;

&lt;p&gt;The result: a full ARIA-compliant hidden DOM projected behind the canvas, perfectly synced to GPU coordinates. Every time the layout changes, the semantic tree updates. VoiceOver reads the mirror; the user sees the canvas. They're in sync.&lt;/p&gt;

&lt;p&gt;Every control ships with semantic roles and accessible labels baked into the constructor. Not bolted on. Not optional. Built in.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/hIqD7hXhd7w"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Try this on macOS
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://fui-as-demo.effindom.dev/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Turn on VoiceOver (⌘F5)&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;⌃⌥A&lt;/strong&gt; (Control + Option + A) to read all&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;VoiceOver highlights canvas-rendered text with the native macOS ring as it reads -- pixel-perfectly synced to the actual GPU coordinates of what's on screen. The native macOS VoiceOver highlight, on canvas text, because EffinDom gives the semantic tree real coordinates.&lt;/p&gt;

&lt;p&gt;Tab navigation works too -- that's a separate focus ring baked into the framework itself. Two independent accessibility systems, both working natively, on a canvas app with no HTML inputs anywhere.&lt;/p&gt;

&lt;p&gt;Checkbox state changes announce in real time. Slider values are read out. The semantic tree updates as the UI updates -- VoiceOver always reads what the user actually sees.&lt;/p&gt;

&lt;p&gt;Flutter Web still can't do this properly. Most canvas frameworks treat accessibility as out of scope. EffinDom treats it as infrastructure.&lt;/p&gt;

&lt;p&gt;And since I'm showing off -- the theme system works properly too. This is the same app in light mode on the Advanced page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqy1jpj5v6ocadlrxgv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqy1jpj5v6ocadlrxgv9.png" alt="FUI-AS Demo advanced page -- light theme, showing emphasized transition card in blue against white background" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ljiJCjQySqM"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lock-step OS theme interpolation -- frame-by-frame color transitions synced to system theme shifts. Same app, different theme.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What it took
&lt;/h2&gt;

&lt;p&gt;I started writing GUI code at 14 in 1992 -- selling an Editbox control for $1,000, manually programming VGA sequencer registers to switch between four 64 KB memory banks mid-draw. VGA's memory aperture was only 64 KB wide but 640×480×256 colors is 300 KB of pixel data. You had to program the sequencer to switch banks mid-operation, mid-line sometimes. Get it wrong and the screen turned to glitch-art. Get it right and you had a GUI.&lt;/p&gt;

&lt;p&gt;That's the kind of problem EffinDom is built on. Thirty years of knowing what good software feels like at the hardware level.&lt;/p&gt;

&lt;p&gt;I'll also say this honestly: WPF had a team of hundreds at Microsoft. SwiftUI had a team of hundreds at Apple. I'm one engineer building this at night while my kids sleep. Recent AI tooling is what made that gap closable -- not the architecture or the hard-won lessons, those were earned, but the grunt work that would have taken a team now takes one person. I think that's genuinely one of the most exciting things about where we are right now.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it is today
&lt;/h2&gt;

&lt;p&gt;Here's the Text &amp;amp; Fonts page -- rich text with inline styling, custom font stacks, emoji, and live TextArea configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwy6avwt7vpvnffd79sn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwy6avwt7vpvnffd79sn.png" alt="TextArea" width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zlaue6ssluclww1jajr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zlaue6ssluclww1jajr.png" alt="FUI-AS Demo Text &amp;amp; Fonts page -- rich text with colored spans, strikethrough, underline, inline emoji, and custom DejaVu font stack." width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real HarfBuzz text shaping. Not bitmap fonts. Not CSS tricks. Inline styled spans, emoji, custom font stacks -- all rendered natively on canvas.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The SDK ships today with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full control set: Button, Text, TextInput, TextArea, Checkbox, RadioButton, Dialog, VirtualList, RichText, ContextMenu and more&lt;/li&gt;
&lt;li&gt;Yoga flexbox layout with SwiftUI-style fluent API&lt;/li&gt;
&lt;li&gt;HarfBuzz + ICU text shaping, RTL/BiDi, surgical font subset injection&lt;/li&gt;
&lt;li&gt;Transitions and animations&lt;/li&gt;
&lt;li&gt;Drag and drop, external file drop&lt;/li&gt;
&lt;li&gt;Context-aware right-click menus, built-in Ctrl+F / ⌘F find engine&lt;/li&gt;
&lt;li&gt;Automatic IndexedDB state persistence, seamless back/forward navigation&lt;/li&gt;
&lt;li&gt;HTTP networking (GET, POST, PUT, DELETE) baked into the runtime&lt;/li&gt;
&lt;li&gt;Adaptive 4-flavor WASM compilation (wasm64+SIMD, wasm64, wasm32+SIMD, wasm32)&lt;/li&gt;
&lt;li&gt;npx scaffolder -- running in under 2 minutes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Is EffinDom right for you?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use EffinDom if you're building:&lt;/strong&gt; dashboards, editors, design tools, kiosks, data visualizations, trading UIs, simulators, enterprise web apps or anything that behaves more like a desktop application than a webpage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use HTML/React/Vue if you're building:&lt;/strong&gt; content sites, blogs, marketing pages, e-commerce stores, anything where the document model is a natural fit.&lt;/p&gt;

&lt;p&gt;This isn't a religious war. The DOM is the right tool for documents. EffinDom is the right tool for applications. Both can coexist -- they serve fundamentally different purposes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Zero third-party runtime dependencies. Zero vulnerabilities.
&lt;/h2&gt;

&lt;p&gt;Run &lt;code&gt;npm install&lt;/code&gt; on a scaffolded fui-as app. Check the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;found 0 vulnerabilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only runtime dependencies are EffinDom's own packages. No third-party code runs in the browser. Not a few dependencies -- none.&lt;/p&gt;

&lt;p&gt;A typical React app has hundreds of transitive dependencies. Every one is a potential supply chain attack vector. Log4Shell, XZ Utils, the Polyfill.io compromise -- enterprises and banks have entire security teams whose job is auditing that graph. Some organizations can't ship to production without a full dependency audit that takes weeks.&lt;/p&gt;

&lt;p&gt;EffinDom's answer is: there is no third-party runtime dependency graph to audit. The only code that runs in the browser is yours and EffinDom's own hermetically bundled, content-hashed packages.&lt;/p&gt;

&lt;p&gt;The runtime is hermetically bundled inside the npm package. No external CDN calls at build time. No transitive packages. The WASM binaries are content-hashed -- you know cryptographically exactly what you're running. Air-gapped build environments work out of the box.&lt;/p&gt;

&lt;p&gt;Your app is just your code. That's the entire attack surface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Honest alpha status (v0.1.x)
&lt;/h2&gt;

&lt;p&gt;Core rendering, the full control set, layout, theming, routing, accessibility, networking, and state persistence are all solid and heavily tested. No handle leaks -- incremental GC is running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile:&lt;/strong&gt; the demo is responsive across screen sizes. Touch input, fling scrolling, and pull-to-refresh work. Find-in-page works on mobile too -- it draws a text overlay above the canvas rather than highlighting canvas text directly, which is a known limitation of the approach. Pinch-to-zoom and long press aren't there yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layout edge cases:&lt;/strong&gt; conflicting constraint trees produce undefined behavior. Two sibling columns both calling &lt;code&gt;.fillWidth()&lt;/code&gt;, for example -- both asking to fill the parent's width, no defined winner. The framework won't crash but the result isn't guaranteed. Avoid ambiguous constraint trees and you'll be fine.&lt;/p&gt;

&lt;p&gt;This is early alpha. The foundation is solid. The edges are still being polished.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @effindomv2/create-fui-as-app my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://fui-as-demo.effindom.dev/" rel="noopener noreferrer"&gt;→ Live demo&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;(pinch-to-zoom and long press not yet supported)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/zion-sati/fui-as" rel="noopener noreferrer"&gt;→ fui-as SDK (AssemblyScript)&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/zion-sati/fui-kt" rel="noopener noreferrer"&gt;→ (under construction) fui-kt SDK (Kotlin)&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/zion-sati/fui-kt" rel="noopener noreferrer"&gt;→ (under construction) fui-rs SDK (Rust)&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/zion-sati/EffinDOM" rel="noopener noreferrer"&gt;→ EffinDOM runtime&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  On licensing and sustainability
&lt;/h2&gt;

&lt;p&gt;The runtime is MIT -- use it freely forever. The fui-as/fui-kt/fui-rs SDKs are dual-licensed: AGPL for open source use, commercial license for proprietary products.&lt;/p&gt;

&lt;p&gt;I'm a solo maintainer with a young family building this after hours. AGPL means open source use is genuinely free, and commercial use funds the project's survival. If you're at a company building something commercial on top of this and AGPL doesn't fit, that commercial license is how this stays alive long term. If you know someone who should be talking to me, reach out: &lt;a href="mailto:zionsatidev@gmail.com"&gt;zionsatidev@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The web deserves better infrastructure than a 1995 document viewer. I've spent eight years and a lot of failed experiments believing that. I think I've finally built something worth believing in.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ui</category>
      <category>web</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
