macOS
AppKit
macos-appkitThe Mac’s native toolkit: the same NSButton, NSTableView, and menu bar that Finder and Mail are made of. Packs as a signed, notarized .dmg.
Building for macOSDay is a Rust framework that builds your app for Android, iOS, HarmonyOS, Windows, macOS, Linux, and the web — with each platform’s own native controls. Your product looks and works the way each platform’s users expect.
cargo install day-cliInstalls the day CLI from crates.io. Requires a stable Rust toolchain (rustup).
Runs natively on
macOS
macos-appkitThe Mac’s native toolkit: the same NSButton, NSTableView, and menu bar that Finder and Mail are made of. Packs as a signed, notarized .dmg.
Building for macOSiOS & iPadOS
ios-uikitApple’s UI framework for iPhone and iPad: real navigation pushes, tab bars, and system text handling. Ships as a normal .ipa.
Building for iOS & iPadOSAndroid
android-mdcGoogle’s native widgets over android.view, on phones, tablets, and foldables, themed by the OS and packaged as .apk or .aab.
Building for AndroidLinux
linux-gtkGNOME’s toolkit, styled by libadwaita. It is the desktop Ubuntu, Fedora, and Debian install by default. Ships as a flatpak.
Building for LinuxLinux
linux-qtThe toolkit beneath KDE Plasma: the desktop of Kubuntu, openSUSE, and the Steam Deck. Portable enough that it is also a Day target on macOS and Windows.
Building for LinuxWindows
windows-xamlWindows 11’s native controls, hosted in a Win32 window with no framework runtime to install. Packs as .msix plus an installer.
Building for WindowsHarmonyOS
harmony-arkuiHarmonyOS Next’s native UI framework, driven through its C node API on Huawei phones and tablets. Packs as a .hap.
Building for HarmonyOSWeb
web-domThe browser’s own widgets: the same Rust compiled to WebAssembly, driving real <button> and <dialog> elements. Hosts as static files. Open the live build.
One codebase, every platform
This is a complete, working counter, the whole thing. The same function becomes a real AppKit control on macOS, UIKit on iOS, Material on Android, and native GTK, Qt, XAML, and ArkUI widgets everywhere else. Those are the screenshots in the carousel above: real platform components, from this exact kind of code.
use day::prelude::*;
fn counter() -> AnyPiece {
let count = Signal::new(0i64);
column((
label(move || format!("{} clicks", count.get())),
row((
button("–").action(move || count.update(|c| *c -= 1)),
button("+").action(move || count.update(|c| *c += 1)),
)).spacing(8.0),
))
.spacing(12.0)
.padding(16.0)
.any()
}One function, a native counter on all twelve targets.
Why Day
Cross-platform tools usually make you choose between apps that feel native and a codebase you only write once. Day keeps both.
Every control is the platform’s own: a real Mac button on macOS, real Material on Android. Text, scrolling, shortcuts, and dark mode behave the way each platform’s users already expect, and keep improving with OS updates you never have to ship.
Day apps are small native binaries that talk directly to the operating system. With no embedded browser and no rendering engine, they start in milliseconds and install in megabytes.
Six operating systems and the web from a single Rust program. A feature lands once and ships everywhere at the same time, instead of one implementation per platform drifting apart as schedules diverge.
Screen-reader support uses each platform’s own accessibility tree, and every user-facing string is localized with Mozilla Fluent. The groundwork for accessibility requirements and new markets is in the framework, not on your backlog.
Each commit builds a full demo app on all twelve targets, drives it end to end, and captures screenshots. The gallery on this site is those exact artifacts.
One command line creates, runs, tests, and packages signed installers for every platform: dmg, App Store, Android, flatpak, Windows. The same automation scripts drive the app everywhere, for CI and for AI-assisted development.
Menu bar, keyboard shortcuts, a preferences window, toolbars, multi-window: Day treats the desktop as a first platform, not a stretched phone screen. The Window menu on macOS even assembles itself.
When you need the platform’s own UI framework, use it: SwiftUI views embed in the Day tree on Apple platforms with typed Rust constructors, and per-platform tweaks reach any native widget’s real API. Day is a default, not a cage.
One dayscript walkthrough is an end-to-end test, an accessibility pass, and your screenshot shoot — per locale and theme. The captures feed the store listings in store/ and a generated app website, so shipping collateral stays as current as the build.
Agents & automation
Every Day app embeds dayscript, the same automation engine that drives Day's own release pipeline. A YAML script, an agent over MCP, or your CI taps buttons, types text, asserts what's on screen by stable element ids, and captures screenshots, identically on every platform.
Evaluating your options
Electron, Tauri
Fast to staff from a web team, but the interface runs in a browser engine, bundled with the app (Electron) or the system’s (Tauri). That brings the browser’s memory profile and its patch cadence.
Flutter, Iced
Pixel-identical screens everywhere, drawn by the framework rather than the platform. Text, scrolling, and accessibility are re-implementations, and the app looks the same everywhere instead of at home anywhere.
Swift + Kotlin + C++ teams
The most flexibility and fidelity, but paid for once per platform. Every feature is built, reviewed, and maintained N times, and the versions drift apart when schedules diverge.
Just a Mac app? Just Linux?
A Day app on one platform is a native app on that platform — real chrome, native packaging, scriptable testing — with no cross-platform tax paid up front. A second platform later is one command (day app add-toolkit), not a rewrite.
Day keeps the platform's own components and the single codebase. It has limits too: no hot reload, a young ecosystem, and deliberate constraints on pixel-level branding.
Read the comparison →How it works
Day builds your interface from real platform components once, then keeps each one connected to your application's state. Nothing re-renders and there is no shadow tree to reconcile: a change runs the closures that read it and ends in one native setter call. Day apps perform like hand-written platform code because, at runtime, that's what they are.
let section = Signal::new(String::new());
selector(section)
.style(SelectorStyle::Sidebar) // native split view / tabs / pushing list
.item("home", "Home", home_page)
.item("settings", "Settings", settings_page)