How comparable projects structure their docs
Generated from docs/meta/docs-study.md when the site was built at 7aa23e2.
Preparation for niso’s docs: what fourteen cross-platform projects and runtimes
do, what the web’s compatibility dashboards do, and what that suggests for niso.
Pages were read in November 2026; links go to the page each claim comes from.
The prototype in docs/site/ (see its README) builds the information
architecture and tooling proposed at the end, so the shape can be judged on a
running site rather than on this page.
Comparison
Section titled “Comparison”| Project | Top-level sections | Platforms and languages | “What works” and compatibility | Native features | Tooling, reference generation | Versions, search, playground |
|---|---|---|---|---|---|---|
| Tauri | Start, Concepts, Guides (develop, distribute, security), Plugins, Reference (JS, Rust, config, CLI) | Package-manager tabs on every install command (npm, yarn, pnpm, deno, bun, cargo) | Per-plugin “Supported Platforms” table with a level and notes per OS (notification) | One page per plugin: platform table, setup (automatic and manual tabs), usage, permissions | Starlight; Rust reference on docs.rs, linked from each plugin page | Major version in the host name (v2.); latest only |
| Capacitor | Getting started, Basics, Main concepts, Guides, iOS, Android, Web, Plugins/APIs, CLI | Separate iOS, Android and Web sections; per-plugin ## Android and ## iOS blocks |
None for web APIs: “If it works in the browser, it probably works” | Plugin pages with permissions, manifest entries, per-OS caveats, and a “Since” column per option (local notifications) | Docusaurus; plugin API from TypeScript JSDoc by capacitor-docgen into each README, then into the site | Version dropdown (v8) |
| React Native | Guides, Components, APIs, Architecture, Contributing | Android/iOS/Web tabs for prose aimed at each background; @platform badges on props (platform-specific code) |
Not about web APIs; per-prop platform badges | Native modules and components as guides | Docusaurus, hand-written component pages | Version dropdown; embedded Snack runs examples on iOS, Android and web |
| Expo | Home, Guides, EAS, Reference (SDK per version), Learn | Platform chips at the top of each SDK page (“Android, iOS”) | Platform chips, known issues on the page | One SDK page per capability with config-plugin setup and permissions (notifications) | Custom site; SDK reference generated from the packages’ types | Reference per SDK version (/versions/v58.0.0/), Snack, llms.txt and a .md for every page |
| Flutter | Get started, UI, Data, Platform integration (per OS), Packages, Testing, Deployment; API on a separate site | “Platform integration” section per OS | Per-platform support shown per package on pub.dev | Per-OS pages such as iOS app extensions (widgets, share) | Custom Dart site rebuilt on Jaspr; API at api.flutter.dev by dartdoc | DartPad embeds |
| Lynx | Guide, API (elements, CSS, Lynx API), React, Rspeedy, Help | Tabs for the sandbox (iOS Simulator, Android, HarmonyOS), remembered in the URL | A BCD-shaped database, lynx-compat-data, renders a Baseline-style badge (“Widely available”) and an MDN-style table with versions per platform (fetch, how it works) |
Native modules and custom elements guides | Rspress; an <APITable> component reads compat JSON named in front matter |
llms.txt, llms-full.txt, “Copy Markdown”; live web preview plus QR code per example |
| NativeScript | Introduction, Setup, Guide, UI, Plugins, Reference | Per-flavor (Angular, Vue, React, Svelte, Solid) code | Not applicable (no DOM) | Platform APIs from JS as a concept | VitePress | Preview app |
| Electron | Tutorial, Processes, Best practices, Examples, API, Glossary | Platform tags on API members (Notification.handleActivation is Windows; app events tagged macOS) |
Points renderers at the web Notifications API (Notification) | API pages name the OS framework and its requirements (code signing for macOS notifications) | Docusaurus; API Markdown in the main repo, parsed by docs-parser into JSON | “Open in Fiddle” on examples |
| Dioxus | Tutorial, Core concepts, Guides | Per-platform guides (web, desktop, mobile, fullstack) | Not applicable | Platform guides | Custom site built with Dioxus; Rust API on docs.rs | Version in the URL (/learn/0.7/), live examples |
| Slint | Guide, Concepts, Reference, Tutorial, Language integrations | Separate reference per language binding (Rust, C++, JS, Python) | Not applicable | Backends and platforms in the guide | Starlight; bindings by each language’s doc tool | latest/ and next/ in the URL with a banner; SlintPad “run” button on snippets |
| Ionic | Getting started, Developing, Layout, Theming, Components, Native, CLI | Two switchers per example: framework (Angular, JS, React, Vue) and mode (iOS, MD) | Not applicable | Native plugins come from Capacitor | Docusaurus; component API from Stencil’s generated docs | Version dropdown, editable example per switcher state |
| Servo | Running servoshell, Hacking, Architecture, Embedding | Per-OS build pages | Not covered in the book | Not applicable | mdBook; embedding API by rustdoc | Latest only; unedited chapters marked with * |
| Deno | Runtime, Deploy, Examples, API (Deno, Web, Node) | Not applicable | Web API reference by category, plus a Web Platform APIs page with spec deviations per API; for availability it sends readers to MDN’s tables | Not applicable | Custom site; API pages generated from type declarations | Latest only |
| Bun | Runtime, Package manager, Bundler, Test runner, Guides | Not applicable | Node.js compatibility per module, 🟢/🟡/🔴 plus the share of Node’s own test suite that passes; Web APIs as a table of names linking to MDN | Not applicable | Mintlify | Latest only |
| MDN | Learn, Guides, Reference per interface and member | Not applicable | Baseline banner at the top, BCD table at the bottom, specification table | Not applicable | Markdown in mdn/content on MDN’s own renderer; BCD and web-features are separate data packages | Unversioned; interactive examples |
| webstatus.dev | Feature list, a page per feature | A column per browser | Baseline status, availability, WPT pass rate per browser, usage, CSV export | Not applicable | web-features, BCD, WPT; an API that the <baseline-status> widget reads |
Filters, sortable columns |
| wpt.fyi | Results by test directory | Columns per browser and channel | Pass counts per directory; browser-specific failures over time | Not applicable | Uploaded runs | Query language, links per run |
| caniuse | One page per feature | Rows per browser version | Support per version, notes, known issues, and a Baseline line | Not applicable | Its own data plus BCD | Search-first |
Patterns worth copying
Section titled “Patterns worth copying”-
A compatibility database separate from prose, rendered by a component. Lynx keeps
lynx-compat-datain BCD’s shape and places a table with<APITable />or anapi:front matter key (Documenting APIs); the same data drives the badge at the top of the page. niso already has the data: the IDL says what exists,tests/wpt/baseline.jsonsays how much of each web feature passes, and web-features’compat_featuresmaps BCD keys (api.WebSocket.send) to feature ids. A page can join all three. -
Baseline’s vocabulary for status. MDN’s banner and Lynx’s “Widely available” badge show readers already know it. niso can reuse it with its own measurement: “Baseline widely available on the web; 88% of its WPT subtests pass in niso” is two facts a web developer can act on. Never say a feature is Baseline in niso: Baseline is about browsers.
-
Deviations per API, next to the API. Deno’s Web Platform APIs says, in the
fetchsection, that Deno doesn’t follow the same-origin policy and so doesn’t implement CORS. niso has the same kind of fact (fetch has no CORS,docs/wpt.md) and should put it on thefetchpage, not only in a test log. -
A coarse status, backed by the test suite’s number. Bun marks each Node module 🟢/🟡/🔴 and says how much of Node’s test suite passes (Node.js compatibility). niso’s number is the WPT pass rate per web feature; the readable status (works, partial, mostly missing, not run) comes first and the number backs it.
-
Synced platform and language tabs. Starlight’s
syncKey(Tabs) and Lynx’s URL parameters remember “I’m on Android” or “I write Kotlin” across pages. niso has both axes, so two sync keys:platformandlanguage. -
Platform badges on members, not only on pages. Electron tags single methods Windows or macOS; React Native badges single props. niso’s differences are mostly per member (a control drawn natively on iOS and Android but not by iced), so its notes belong on members.
-
One page per native capability: platform table, setup per OS, usage. Capacitor’s local notifications and Tauri’s notification plugin both do this. niso’s surfaces need it too, since an App Group or a Glance receiver is setup a page can’t do.
-
“Run this” next to examples. Snack, Fiddle, SlintPad and Lynx’s web preview plus QR code cut the distance between reading and seeing. niso’s cheapest version is a side-by-side of the same page in Chrome and on each device, which
tests/screensalready renders; later, a QR code that opens a page in the host app. -
Machine-readable docs. Expo and Lynx publish
llms.txtand Markdown for each page. Cheap to add, and agents are a real audience for an API this broad. -
Reference from the source of truth, in the build. Electron (docs-parser), Capacitor (docgen), Deno and Expo (types) never hand-write member lists. niso’s WebIDL is that source, and generating it at build time fits DECISIONS’ “no generated source”: nothing generated is committed.
Pitfalls to avoid
Section titled “Pitfalls to avoid”- “If it works in the browser it probably works.” Capacitor can say that because it ships a browser; niso can’t, and a vague claim is worse than an honest 19% (grid’s pass rate today). Every API page shows its status, even when that’s “not tested”.
- A status without its reason. A pass rate alone hides that grid’s 727
reftests don’t run yet.
baseline.jsonsays why (notRun,needs-reftests); the docs must show it, asdocs/wpt.md’s caveats do (“Unknown isn’t failing”). - A separate site for the API. Flutter keeps api.flutter.dev apart from docs.flutter.dev, so search is split and guides and reference link to each other less. One site, one search.
- Hand-maintained compatibility lists. Bun’s Web APIs table is names linking to MDN, with no per-member status, and it will drift. niso generates from the IDL and test results.
- Copying MDN’s prose. MDN content is CC BY-SA. Link to MDN for semantics and spend niso’s words on what differs: rendering, missing members, deviations.
- Versioning before there’s a release. Expo needs per-SDK reference because
apps pin an SDK; niso has no release. Ship latest only, stamped with the
commit it was built from, and add versions with the first release (Slint’s
latest/andnext/is the light way). - Per-platform sections that repeat each other. Capacitor’s iOS and Android sections each carry setup prose; prefer one page with platform tabs, and keep platform pages for what only that platform has (entitlements, App Groups, Glance, signing).
- Switchers that multiply examples. Ionic’s four frameworks times two modes is eight versions of each example. niso should show plain HTML and DOM first, Svelte only where it adds something.
Proposed information architecture
Section titled “Proposed information architecture”One site, eight top-level sections. The prototype builds this sidebar; pages marked stub there are placeholders.
Start
- What is niso: the DOM as a native UI API, for web and for native developers.
- Getting started: a Svelte page on the iOS Simulator, the Android emulator, a desktop window and headless.
- Native apps: the same app driving the DOM from Rust, C, Zig, Go or Java (
examples/lang). - niso and WebViews: what’s native, what’s niso’s own, what’s missing.
Concepts
- The DOM as the API: node ids, handles, the C ABI, bindings generated from WebIDL.
- Rendering: the render stream, native views at the core’s frames.
- Native controls: which elements are native controls, their CSS property sets, radio groups as segmented controls.
- Surfaces: widgets, Live Activities and ongoing notifications from
data-niso-surface; timers that count by themselves. - Platform style: the
niso-platform-stylemeta,--niso-*tokens, safe areas. - System settings: text size, bold text, contrast, dark mode and accent colors as CSS.
- Text: Parley’s layout, the platform’s drawing.
- Engines and the event loop: QuickJS, Porffor, Node-API; who owns the loop.
- Storage and the app origin.
- Native modules: native code giving pages an API from one IDL file.
- Accessibility: the AccessKit tree from the DOM and ARIA.
Guides
- Build a Svelte app; build a plain HTML app.
- Add a home screen widget; add a Live Activity; media session (stub).
- Write a native module.
- Port a web app with
niso-run --missing. - Test pages headless with
niso-run. - Navigation and system back.
Platforms
- iOS, Android, Desktop (iced), Web, Headless and servers, Planned (Windows, watchOS, CarPlay, MCUs).
Languages
- JavaScript (QuickJS, Porffor, Node, Deno, Bun), Rust, C, Zig and Go, Java and Kotlin, Swift (stub).
Reference
- Web APIs: one generated page per WebIDL interface, grouped by IDL file, with members, exposure, C function names, per-platform notes and WPT/Baseline status.
- HTML elements and native controls; CSS (
@media (platform), prefixed properties,--niso-*tokens); niso attributes (data-niso-surface,data-timer-*); meta tags. - C API (
niso.h,niso_runtime.h,niso_app.h); Rust (rustdoc); CLI (niso-run,niso-node,niso-wpt); IDL extended attributes (Niso*).
Compatibility
- Baseline dashboard: every web feature with its Baseline status, niso’s pass rate, coverage and why tests didn’t run; filterable. Fed from
tests/wpt/baseline.json. - Real apps: Bluesky, VS Code for the web, the Svelte playground (today’s
docs/compat). - Known differences from browsers, per API.
- How niso is tested: WPT and its gate, Acid, screen comparisons, test262.
Internals (for contributors)
- Architecture, decisions, crate contracts, repros, running WPT: mostly the repo’s existing docs.
The per-API status shows in two places on purpose: on each reference page, where a developer decides whether to call an API, and in the dashboard, where someone decides whether niso can run their app or what to work on next. Both read the same JSON, so they can’t disagree.
Tooling recommendation: Astro Starlight
Section titled “Tooling recommendation: Astro Starlight”Starlight, with a build-time generator script for the reference and an Astro page for the dashboard.
- Reference from niso’s WebIDL, with nothing generated committed. A Node
script parses
idl/*.webidlwith webidl2 (all 19 files parse as they are,Niso*extended attributes included) and writes Markdown into a git-ignored directory beforeastro build. Comments above members (// NisoHeld: …) come through webidl2’s trivia, so the IDL can carry short notes. - It joins the data niso already has. The same script reads
tests/wpt/baseline.jsonand web-features (pinned to the versiontests/wpt/fetch.shuses) to give each member its web feature, Baseline status and pass rate. - Per-platform notes as data. Notes the IDL can’t hold live in one YAML file keyed by interface, member and platform, merged by the generator, which fails on a key that names no member, so notes can’t go stale silently.
- Synced tabs for platform and language (
syncKey), asides, badges and MDX components: the patterns above without a custom theme. - Search by Pagefind, built with the site; no service.
- Static output and one build command.
- Tauri and Slint, the nearest projects in the Rust ecosystem, use it.
Alternatives: Rspress (Lynx) has the compat-table pattern proven, but a smaller ecosystem and nothing niso needs that Starlight lacks. Docusaurus has built-in versioning, which niso doesn’t need yet, for a heavier React stack. mdBook is Rust-native and simple, but has no tabs, no components and no good way to render generated data; it would suit only the Internals section. VitePress is close to Starlight; Starlight wins on content collections and synced tabs.
One change in niso would help later: niso-idl could emit its model as JSON,
with the C and JVM names it already computes, so the docs generator doesn’t
reimplement naming. The prototype derives C names the way
crates/niso-idl/src/emit.rs does and marks that with a TODO.