Kite docs

Project structure

How Kite is laid out, and the one architectural rule that matters.

lib/
├── main.dart          usePathUrlStrategy() + ProviderScope
├── app.dart           ShadApp.router, theme and locale wiring
├── kite_ui/           the ONLY folder that imports the design library
│   ├── _shadcn.dart   single import surface — the swap point
│   ├── tokens.dart    KiteSpace, KiteRadius, KiteBreak, KiteColors, KiteText
│   └── …              button, card, chart, data_table, menu, inputs, …
├── core/
│   ├── router/        routes.dart (paths + nav) and app_router.dart
│   ├── theme/         light + dark × 3 accents
│   ├── data/          DataProvider + mock / REST / Supabase adapters
│   ├── auth/          session controller, guard wiring
│   └── l10n/          locale controller
├── l10n/              .arb sources and generated localisations
├── shared/
│   ├── layout/        AppShell — sidebar, top bar, mobile bottom bar
│   └── widgets/       empty, loading and error states
└── features/          one folder per screen group

The one rule

No shadcn_ui type may appear outside lib/kite_ui/.

It is a pre-1.0 library (0.55.x, capped by trina_grid) that ships breaking changes. Confining it means the blast radius of an upgrade is one folder rather than every screen. Features import Kite widgets; Kite widgets import the design library.

CI enforces this with a grep, so it cannot quietly rot:

if grep -rn --include='*.dart' "package:shadcn_ui" lib | grep -v '^lib/kite_ui/'; then
  echo "::error::shadcn_ui imported outside lib/kite_ui/"
  exit 1
fi

The practical effect: features never name a Shad* type. They read colour through KiteColors.of(context) and type through KiteText.of(context), both of which are extension types over the design library's own — zero runtime cost, and the only thing that has to change if the library does.

Adding a screen

Four edits, in this order:

  1. Create lib/features/<group>/<name>_screen.dart.
  2. Add a path constant to R in lib/core/router/routes.dart.
  3. Add a NavItem to the right NavGroup in the same file, and a case to NavItem.labelOf if it should localise.
  4. Add a GoRoute inside the ShellRoute in app_router.dart.

Screens inside the shell get the sidebar, top bar and auth guard for free. Screens declared outside it (the five auth screens, the 500 page) render standalone.

Why features never import each other

Anything two features both need lives in shared/ or kite_ui/. It keeps deletion cheap — removing a feature you do not want is deleting its folder and its route, not untangling it from three others.

Tests

test/
├── data_provider_test.dart   the contract: totals, search, filters, sort, mutations
├── rest_provider_test.dart   both HTTP adapters against a fake client
├── format_test.dart          money formatting
└── session_test.dart         sign in, initials, sign out

26 tests. They cover the parts where being wrong is silent — a total that reports the page length instead of the row count still renders a table, it just paginates wrongly.