Kite docs

Deploying

Build, shrink, ship.

Build

flutter build web --wasm --release --base-href /kite/ --dart-define=KITE_DEMO=true
node tools/strip-fonts.mjs build/web
python3 tools/subset-lucide.py build/web

--base-href must match the path you serve from, or every asset 404s. Omit it when serving from a domain root.

The two font tools

They are not optional; together they take the payload from about 2.2 MB to 1.6 MB.

strip-fonts.mjs

shadcn_ui depends on lucide_icons_flutter, which declares seven font families — a base face plus six variable-weight instances. Flutter web preloads every family named in FontManifest.json, so all seven download on first paint even though nothing references the numbered weights.

Saves about 1.15 MB.

subset-lucide.py

Then subsets the base face to the glyphs actually drawn. --tree-shake-icons only manages ~12% here, because shadcn reaches its icons through wrappers that defeat Flutter's static analysis — but it only ever draws eleven of them.

1,776 glyphs to 12. 732 KB to 2 KB. Needs pip install fonttools.

Keep the base Lucide family. Dropping it entirely leaves tofu boxes where shadcn's own chevrons and checkmarks should be. The tools keep exactly one family; CI fails the build if that count changes in either direction.

Measuring

Do not measure by summing build/web. It ships six mutually-exclusive renderer variants plus .symbols debug files — the directory total is roughly 5.5× what a browser actually downloads.

npm i playwright && npx playwright install chromium
node tools/measure-bundle.mjs build/web "label" throttle

That serves the build, loads it in headless Chromium and reports real encoded transfer plus FCP, throttled to Fast 4G with 4× CPU.

Any static host

Serve build/web. The only requirement is the index.html fallback for unknown paths — Cloudflare Pages, Netlify and Vercel do it by default. Locally:

node tools/serve.mjs build/web 8722

python3 -m http.server will not do: it serves .wasm as octet-stream and the app refuses to boot.

Cloudflare R2

How this project deploys. rclone sync deletes remote files missing from the source — dry-run first, every time.

rclone sync build/web/ r2pro:dashboardpack-demos/kite/ --dry-run
rclone sync build/web/ r2pro:dashboardpack-demos/kite/
rclone check build/web/ r2pro:dashboardpack-demos/kite/    # expect "0 differences"

ssh hetzner sudo cf-purge-url https://demo.dashboardpack.com/kite/

Cloudflare caches per colo, so replacing a file without purging means visitors keep the old one. See docs/DEPLOY.md in the repo for the docs bucket and the SEO worker's behaviour.

Removing the sidebar promo

Kite is free, and the sidebar carries a card pointing at the paid DashboardPack templates. It is one flag to remove from your own build:

flutter build web --wasm --release --dart-define=KITE_PROMO=false

To take it out permanently, delete lib/shared/layout/promo_card.dart and its two references in app_shell.dart. Nothing else depends on it.

CI

Every push runs eleven jobs:

JobChecks
analyzedart format --set-exit-if-changed . then flutter analyze --fatal-infos
test26 tests with coverage
architectureNo shadcn_ui import outside lib/kite_ui/
fontsExactly one Lucide family survives the build
build ×6web-wasm, web-js, iOS, Android, macOS, Windows
sizeApp payload under budget

A weekly scheduled run catches Flutter SDK drift before a user reports it.