From 98aa3a2f4ff2c4ca077f81e0198cb5054a06b866 Mon Sep 17 00:00:00 2001 From: Sipho Nkebe Date: Wed, 10 Jun 2026 20:53:59 +0200 Subject: [PATCH 1/6] docs(flutterbits): charter + structure/routing + registry/CLI design specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brainstormed and documented the move into the flutterbits component layer. Adds three design specs under docs/superpowers/specs and reconciles every doc that the plan extended or contradicted. New specs (2026-06-10): - flutterbits-charter — identity (shadcn parity + an opinionated, intention- revealing structure layer), the four tiers (primitives/structure/blocks/ templates; "tools" dropped), the full catalog with shadcn re-homing (sheet/dialog/sidebar/tabs become structure), mobile-first platform stance, unprefixed-component naming, and the per-component dependency policy. - flutterbits-structure-and-routing-design — Layout/Screen as composed widgets (not base classes; dissolves "ScreenSpec"), header/body/footer slots, typed hand-written routing over go_router (no codegen), FwRoute/FwRoutePattern, presentation-as-a-knob (FwPresentation page/sheet/dialog/fullScreen, deep-linkable), guards, transitions, imperative overlay helpers. - flutterbits-registry-cli-design — install-types (component/util/barrel), manifest schema, flutterbits.json, init/add/diff, barrel regen rules, the anchor/overlay substrate as flutterbits cn.ts, hosting via apps/docs. Reconciliations (no-drift, AGENTS.md s12): - AGENTS.md s1 identity expanded (structure layer + mobile-first); s4 scoped the Fw-prefix rule to the engine and made flutterbits components unprefixed (the old FwButton example contradicted this); s8 added init/barrel/install- types/flutterbits.json; s12 sanctioned go_router (+ by-demand flutter_svg). - README + apps/docs flutterbits overview updated to the same plan. - core-engine-design spec: FwButton -> Button example fixed. Decisions deferred by explicit sign-off: state-management conventions; the desktop-centric component set and SidebarLayout (by-demand); routing codegen. Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 16 +- README.md | 4 +- apps/docs/content/docs/flutterbits/index.mdx | 20 +- ...06-05-flutterwindcss-core-engine-design.md | 2 +- .../specs/2026-06-10-flutterbits-charter.md | 174 ++++++++++ ...6-06-10-flutterbits-registry-cli-design.md | 147 +++++++++ ...lutterbits-structure-and-routing-design.md | 304 ++++++++++++++++++ 7 files changed, 651 insertions(+), 16 deletions(-) create mode 100644 docs/superpowers/specs/2026-06-10-flutterbits-charter.md create mode 100644 docs/superpowers/specs/2026-06-10-flutterbits-registry-cli-design.md create mode 100644 docs/superpowers/specs/2026-06-10-flutterbits-structure-and-routing-design.md diff --git a/AGENTS.md b/AGENTS.md index 07e225c..7242b38 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,7 +11,7 @@ Operating manual for coding agents working in this repository. Read this fully b Two products in one monorepo: - **`flutterwindcss`** — Tailwind CSS's *design system and styling vocabulary* for Flutter. A real pub dependency. It provides design tokens (spacing, radius, semantic colors) and a utility authoring API over Flutter's **primitive** widgets. This is the "Tailwind" layer. -- **`flutterbits`** — shadcn/ui for Flutter. **Copy-paste** components (the developer owns the source, fetched via a CLI from a registry), styled entirely through `flutterwindcss` and semantic tokens. This is the "shadcn/ui" layer. +- **`flutterbits`** — shadcn/ui for Flutter, **plus an opinionated app-structure layer**. **Copy-paste** components (the developer owns the source, fetched via a CLI from a registry), styled entirely through `flutterwindcss` and semantic tokens. Two things in one: (1) shadcn's familiar primitives (parity), and (2) an **intention-revealing "structure" layer** — `Layout`, `Screen`, and typed routing that model how a Next.js/Expo dev thinks, replacing `MaterialApp`/`Scaffold`/`Navigator` ceremony while staying Material-free. This second part is the differentiator. **flutterbits is mobile-first** (portable to web/desktop, matured by demand). Full design: `docs/superpowers/specs/2026-06-10-flutterbits-charter.md` and its two companions (structure+routing, registry+CLI). The headline feature is **theme portability**: a developer pastes any [tweakcn](https://tweakcn.com)/shadcn theme into a web generator and gets a working Flutter `theme.dart`. No other Flutter UI library does this — protect it. @@ -70,7 +70,7 @@ These encode hard-won decisions. Violating any of them is a review failure. ## 4. Coding conventions (Dart) -- Public types are prefixed `Fw` (`FwStyle`, `FwButton`, `FwColors`, `FwButtonVariant`). +- **`flutterwindcss` (engine) public types are prefixed `Fw`** (`FwStyle`, `FwColors`, `FwTokens`, `FwBreakpoint`), and so are the routing/structure **library** base types that ship as engine-style code (`FwRoute`, `FwRoutePattern`, `FwPresentation`, `FwStatusBar`). **flutterbits *components* are UNprefixed** — `Button`, `Card`, `Screen`, `Layout` — because they are copy-paste source the developer owns (shadcn-style) and need no namespace. (Material name clashes, only possible in the rare Material-interop case, are resolved by namespacing the barrel `import '.../ui/ui.dart' as ui;`, not by prefixing — see the charter §5.) When you see `Fw` inside a component, that is the engine showing through. - Prefer `const` constructors wherever the analyzer allows; leaf widgets that never change should be `const`. - Variants are **typed enums + exhaustive `switch`** (the cva equivalent). No stringly-typed variant maps. The `switch` must be exhaustive so the compiler catches a missing case — do not add a `default:` that papers over new enum values. - Every file passes `dart format` (100-col) and `flutter analyze` with **zero** warnings before you call a task done. @@ -137,10 +137,12 @@ If a desired behavior genuinely cannot be done in Flutter, do not fake it — ad ## 8. Registry & CLI -- Registry manifest per component (JSON): `{ name, description, pubDeps: [...], registryDeps: [...], files: [{ path, content }] }`. `registryDeps` resolves inter-component needs (e.g. `dialog` pulls in `button`). -- `flutterbits add ` writes files into the host project (default `lib/components/ui/`), installs `pubDeps`, recursively adds `registryDeps`, then runs `dart format`. -- `flutterbits diff ` shows upstream changes vs the developer's copy (the copy-paste survival mechanism — analogous to shadcn's diff). -- The registry build step (`tooling/`) generates manifests FROM the `registry/*.dart` source. Never hand-edit a manifest's `content`. +- Registry manifest per item (JSON): `{ name, type, description, exported, pubDeps: [...], registryDeps: [...], files: [{ path, type, target, content }] }`. **Install-types** (shadcn's `registry:ui`/`lib`/`hook` analog): `component` → `lib/components/ui/.dart`; `util` → `lib/components/ui/_utils/.dart` (shared lib utilities — the `anchor`/overlay substrate, `showConfirm`/`showSheet`/`showToast`; Flutter has no native "hooks", so these are plain utilities). `registryDeps` resolves inter-item needs recursively (e.g. `dialog` pulls in `button`; `popover` pulls in `anchor`). +- `flutterbits init` scaffolds a project: adds `flutterwindcss`, creates `lib/components/ui/` + `_utils/`, writes a starter `theme.dart`, an initial barrel, an optional starter `Layout`, and `flutterbits.json` (the config — `components.json` analog: registry URL + target paths + theme path). +- `flutterbits add ` writes files into the host project (default `lib/components/ui/`), installs `pubDeps`, recursively adds `registryDeps`, **regenerates the barrel**, then runs `dart format`. +- **The barrel `lib/components/ui/ui.dart`** is CLI-**generated, never hand-edited** — it re-exports every `exported` item so devs import the whole set from one place (and `import … as ui;` namespaces it for the rare Material-interop clash). Internal utils (e.g. `anchor`) are imported directly and stay out of the barrel. +- `flutterbits diff ` shows upstream changes vs the developer's copy (the copy-paste survival mechanism — analogous to shadcn's diff); it **shows, never auto-merges** (the dev owns their edits). +- The registry build step (`tooling/build_registry.dart`) generates manifests FROM the `registry/*.dart` source. Never hand-edit a manifest's `content`. Full design: `docs/superpowers/specs/2026-06-10-flutterbits-registry-cli-design.md`. --- @@ -217,7 +219,7 @@ Everything **not** in 11a or 11b is fair game and must not be refused on cost gr - **Verdict before "won't" (MUST).** Before declaring anything a Non-Goal or limitation, write a one-paragraph feasibility verdict: the mechanism that *would* implement it, its rough cost, and the honest call. Attach it to the spec. Default to building; only the §11 bar excuses not building. - **Read before you edit.** Open the canonical sources (`lib/src/style/`, `lib/src/theme/`, `lib/src/tokens/`, and once it exists `registry/button.dart`) and match their patterns before writing new code. - **Small, focused changes.** One component or one utility group per change. Don't refactor unrelated code in passing. -- **No new dependencies without justification.** Prefer the framework's widgets layer. Known sanctioned deps: `lucide_icons_flutter` (icons), `flutter_animate` (animation). Anything else needs a reason in the PR description. +- **No new dependencies without justification.** Prefer the framework's widgets layer. Known sanctioned deps: `lucide_icons_flutter` (icons), `flutter_animate` (animation), and **`go_router`** (the routing engine the flutterbits structure layer *wraps* — not forks — declared as a `pubDep` of the structure components only; never a `flutterwindcss` dep). `flutter_svg` is sanctioned **by-demand** for blocks that render real SVG illustrations. Anything else needs a reason in the PR description. Deps are declared **per-component in the manifest `pubDeps`**, never globally: a copied `Button` drags in nothing; `add toast` pulls `flutter_animate`; `add layout` pulls `go_router`. - **Don't invent APIs.** If unsure whether a Flutter symbol exists in the widgets layer, verify before using it. Do not assume Material symbols are available. - **Surface assumptions.** If a task is ambiguous (which variant set? which platforms?), state the assumption you made inline rather than guessing silently. - **Never weaken the rules in §3 to make a task pass.** If a rule blocks you, stop and flag it — don't import Material, don't hardcode a color, don't nest wrappers to ship faster. diff --git a/README.md b/README.md index c42ab98..3bf5f7d 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Two products in one monorepo, built so that **any [tweakcn](https://tweakcn.com)/shadcn theme you paste into a web generator becomes a working Flutter `theme.dart`** — theme portability no other Flutter UI library offers. - **`flutterwindcss`** — Tailwind CSS v4's *design system and styling vocabulary* for Flutter. Design tokens (spacing, radius, semantic colors, typography, shadows) and a typed, compile‑time utility API (`.tw`) over Flutter's primitive widgets. This is the **Tailwind** layer. -- **`flutterbits`** — shadcn/ui for Flutter. **Copy‑paste** components you own, fetched via a CLI from a registry, styled entirely through `flutterwindcss` and semantic tokens. This is the **shadcn/ui** layer. +- **`flutterbits`** — shadcn/ui for Flutter. **Copy‑paste** components you own, fetched via a CLI from a registry, styled entirely through `flutterwindcss` and semantic tokens — **plus** an opinionated, intention‑revealing **structure layer** (`Layout`, `Screen`, typed routing) that models app‑building the Next.js/Expo way. Mobile‑first, portable. This is the **shadcn/ui** layer (and a bit more). ## Install & docs @@ -88,7 +88,7 @@ Flutter has no structure/style split and no CSS cascade — the widget tree *is* **🚧 Next on the roadmap:** -1. **`flutterbits` components** and the **registry + CLI** (`flutterbits add` / `diff`). *(The engine is ready — ~96% of daily-driver Tailwind is covered and a full-catalog audit found no engine blocker.)* +1. **`flutterbits` components** and the **registry + CLI** (`flutterbits init` / `add` / `diff`) — primitives (shadcn parity), the **structure layer** (`Layout`/`Screen`/routing over `go_router`), blocks, and templates. Design specs: [`flutterbits-charter`](docs/superpowers/specs/2026-06-10-flutterbits-charter.md), [structure + routing](docs/superpowers/specs/2026-06-10-flutterbits-structure-and-routing-design.md), [registry + CLI](docs/superpowers/specs/2026-06-10-flutterbits-registry-cli-design.md). *(The engine is ready — ~96% of daily-driver Tailwind is covered and a full-catalog audit found no engine blocker.)* 2. **Remaining engine long-tail (by demand only)** — niche/feasible items (`inset-shadow`, `mask-*`, backdrop color filters, `columns`, negative margins, `scroll-margin/padding`, decoration styling) built when a real component needs one; the rest is the [coverage & roadmap](docs/superpowers/specs/2026-06-07-flutterwindcss-coverage-and-roadmap.md)'s delegated (animation → [`flutter_animate`](https://pub.dev/packages/flutter_animate); forms/prose/tables/SVG → flutterbits) / impossible sets. See [`docs/superpowers/specs`](docs/superpowers/specs) for the full engine design and [`docs/superpowers/plans`](docs/superpowers/plans) for the implementation plans. diff --git a/apps/docs/content/docs/flutterbits/index.mdx b/apps/docs/content/docs/flutterbits/index.mdx index 8984489..d72b477 100644 --- a/apps/docs/content/docs/flutterbits/index.mdx +++ b/apps/docs/content/docs/flutterbits/index.mdx @@ -3,12 +3,18 @@ title: Overview description: flutterbits is shadcn/ui for Flutter — copy-paste components you own, styled entirely through flutterwindcss semantic tokens. --- -**flutterbits** is *[shadcn/ui](https://ui.shadcn.com) for Flutter*: a registry of **copy-paste -components** you own. You don't +**flutterbits** is *[shadcn/ui](https://ui.shadcn.com) for Flutter* — a registry of **copy-paste +components** you own — **plus an opinionated, intention-revealing app-structure layer**. You don't add a versioned widget library — you pull a component's source into your project and it's yours to read and edit. Every component is styled entirely through [flutterwindcss](/docs/flutterwindcss) semantic tokens, so a theme reskins them all. +Beyond shadcn's primitives, flutterbits ships a **structure layer** — `Layout`, `Screen`, and typed +routing that model how a Next.js/Expo developer thinks (layouts wrap screens, screens compose blocks, +blocks compose primitives), so the code reads top-down as the rendered artifact instead of as +`MaterialApp`/`Scaffold`/`Navigator` plumbing. flutterbits is **mobile-first** (portable to web and +desktop, maturing there by demand). + The styling engine ([flutterwindcss](/docs/flutterwindcss)) and the [Theme generator](/theme-generator) are shipped and stable. The component registry and CLI @@ -21,10 +27,12 @@ semantic tokens, so a theme reskins them all. - **Copy-paste, not a dependency.** Components are source you own — fetched from a registry, written into your project (default `lib/components/ui/`). You can change anything; nothing is locked behind a package version. This is the model shadcn/ui pioneered. -- **A CLI to fetch and update.** `flutterbits add ` writes a component's files into your app, - installs its package dependencies, and pulls in any other components it needs. `flutterbits diff - ` shows what changed upstream versus your copy — the survival mechanism for code you've since - edited. +- **A CLI to scaffold, fetch, and update.** `flutterbits init` sets up your project (the styling + engine, a `theme.dart`, the components folder, and a generated barrel). `flutterbits add ` + writes a component's files into your app, installs its package dependencies, pulls in any other + components it needs, and regenerates the barrel so you import the whole set from one place + (`components/ui/ui.dart`). `flutterbits diff ` shows what changed upstream versus your copy — + the survival mechanism for code you've since edited (it shows, it never overwrites your edits). - **Styled through tokens.** Components reference only semantic roles (`primary`, `muted`, `border`) via `context.fw` — never raw colors. So a `theme.dart` generated from any tweakcn/shadcn theme reskins every component at once. diff --git a/docs/superpowers/specs/2026-06-05-flutterwindcss-core-engine-design.md b/docs/superpowers/specs/2026-06-05-flutterwindcss-core-engine-design.md index 1988ba3..a1f9353 100644 --- a/docs/superpowers/specs/2026-06-05-flutterwindcss-core-engine-design.md +++ b/docs/superpowers/specs/2026-06-05-flutterwindcss-core-engine-design.md @@ -28,7 +28,7 @@ Flutter has no structure/style split and no CSS cascade — the widget tree *is* These are **separate products** in the monorepo, each with its own spec → plan → build cycle. They are not part of the styling engine and their absence here is a scope boundary, not a stub: -- **flutterbits components** (`registry/`) — `FwButton` et al. The engine is their dependency; they are authored against it later. +- **flutterbits components** (`registry/`) — `Button` et al. (flutterbits components are **unprefixed** — copy-paste source the dev owns; the `Fw` prefix is for engine types only, see AGENTS.md §4 and the flutterbits charter §5.) The engine is their dependency; they are authored against it later. - **The theme generator** (`apps/docs`, TypeScript) — the tweakcn→`theme.dart` OKLCH pipeline. Per AGENTS.md §7, color math lives **only** there. This engine consumes `FwTokens`; it never parses or converts CSS color strings. - **The registry builder + CLI** (`flutterbits_cli`, `tooling/`). - **The docs site + example showcase app** beyond the minimal golden-test harness this engine needs. diff --git a/docs/superpowers/specs/2026-06-10-flutterbits-charter.md b/docs/superpowers/specs/2026-06-10-flutterbits-charter.md new file mode 100644 index 0000000..19c8d5d --- /dev/null +++ b/docs/superpowers/specs/2026-06-10-flutterbits-charter.md @@ -0,0 +1,174 @@ +# flutterbits — product charter (identity, taxonomy, platform, catalog) + +**Status:** design · **Date:** 2026-06-10 · **Audience:** anyone building the flutterbits component layer. +**Supersedes/extends:** the "flutterbits" framing in `AGENTS.md` §1, the roadmap note in the coverage doc, and `apps/docs/content/docs/flutterbits/index.mdx`. Companion specs: `2026-06-10-flutterbits-structure-and-routing-design.md` (the structure layer) and `2026-06-10-flutterbits-registry-cli-design.md` (the registry + CLI). + +--- + +## 1. What flutterbits is (the identity, sharpened) + +flutterbits is **two things in one library**, and the second one is the differentiator: + +1. **shadcn/ui for Flutter** — a registry of **copy-paste components you own** (Button, Card, Input, Dialog…), styled entirely through `flutterwindcss` semantic tokens, fetched via a CLI. This is the trust layer: a dev who knows shadcn finds the familiar set and it just works. + +2. **An opinionated "structure" layer that models how app developers think** — `Layout`, `Screen`, navigation, and routing that read like a Next.js / Expo app rather than Flutter's `MaterialApp`/`Scaffold`/`Navigator` ceremony. **No other Flutter UI library ships this.** It is the reason flutterbits is more than "shadcn ported." + +> **AGENTS.md §1 reconciliation.** The manual currently calls flutterbits "shadcn/ui for Flutter — copy-paste components." That is necessary but no longer sufficient: flutterbits *also* ships the opinionated structure/app-framework layer above. The identity is **"shadcn's copy-paste components *plus* an opinionated, intention-revealing app skeleton."** + +### 1.1 The governing design principle — intention-revealing structure + +> The widget tree should read like a **description of the app**, not its plumbing. `Screen`, `Layout`, `header`/`body`/`footer` say *what the thing is*; `MaterialApp`/`Scaffold` say *what framework you imported*. flutterbits models the mental model of a Next.js/Expo developer — **layouts wrap screens, screens compose blocks, blocks compose primitives** — so the code reads top-down as the rendered artifact. + +This is consistent with — not a violation of — the engine's Material-free stance: a `Screen` is built from `package:flutter/widgets.dart` primitives + `context.fw`, never a wrapper around `Scaffold`. + +### 1.2 The "Material-like" framing + +flutterbits' structure layer plays the **role** Material plays — the batteries-included app skeleton you reach for first — **without** Material's visuals or ceremony, and as **copy-paste source you own** (so you can rip it open). It is "the opinionated default way to build a flutterbits app." + +### 1.3 The three rules (design north star) + +Every flutterbits artifact is held to the author's three rules, in order of weight: + +1. **Works good** — complete, correct, accessible, tested (goldens + the `apps/example` compile target). No demoware. +2. **Looks good** — semantic-token styled so it reskins with any pasted theme; polished defaults. +3. **Feels good (most important)** — the API is a joy to write. Minimal concepts, call sites that read like sentences, one idea expressed one way. + +These are not decoration; "feels good" is an explicit acceptance criterion. Where two designs are equally correct, the more pleasant-to-write one wins. + +--- + +## 2. Taxonomy — the tiers + +Four tiers (the originally-considered "tools" tier is **dropped** — what would have lived there is either a registry *install-type* (`util`), see the registry spec, or belongs in `flutterwindcss`). + +| Tier | What it is | shadcn analog | The "feel" | +|---|---|---|---| +| **primitives** | Button, Badge, Input, Card, Switch, Dialog… | shadcn components (parity) | trust / familiarity | +| **structure** | `Layout`, `Screen`, nav, app-shell, routing | *(none — the differentiator)* | "reads like Next.js/Expo" | +| **blocks** | music-player card, stat grid, charts, auth form, splash | shadcn blocks | "feels like a real app" | +| **templates** | full app scaffolds wiring all three | *(none)* | "clone-and-go" | + +**Altitude rule:** `structure` is a *different altitude* from the engine's layout primitives. `FwRow`/`FwColumn`/`FwGrid` (flutterwindcss) are flex/grid **layout primitives**; `Layout`/`Screen` (flutterbits) are **app-shell semantics** — safe areas, status-bar styling, nav regions, scroll ownership, routing. No overlap. + +### 2.1 Components that *re-home* into the structure layer + +A naïve clone of shadcn's flat list is wrong for Flutter. Several shadcn "components" are, in flutterbits, expressions of the structure layer rather than standalone primitives: + +- **Sheet / Drawer / Dialog** → presentation modes of a `Screen` (`FwPresentation.sheet/dialog/fullScreen`), not separate component APIs. What remains is content + chrome (`SheetHandle`). (See structure/routing spec.) +- **Sidebar** → `SidebarLayout`, a `Layout` shell (sibling to `TabsLayout`). Structure, not a primitive. (Desktop/web → by-demand, §4.) +- **Tabs** → splits in two: *navigation* tabs = `TabsLayout` (structure); *in-page* content tabs = a `Tabs` primitive. + +--- + +## 3. The catalog + +Organised by tier and by "maps cleanly / re-homes / adapt-by-demand." **None of the by-demand items is impossible** — each has a Flutter idiom and ships when a real need appears (AGENTS.md §11b discipline). + +### 3.1 Structure (flutterbits-native — the differentiator) + +`Layout`, `Screen`, `TabsLayout`, `SidebarLayout` (by-demand, desktop/web), `TopBar`, `BottomNav` + `NavDestination`, `BackButton`, `SheetHandle`. Routing types (`FwRoute`, `FwRoutePattern`, `FwPresentation`, `FwTransition`). Full design in the structure/routing spec. + +### 3.2 Primitives — shadcn parity that maps cleanly to Flutter + +Button, Badge, Card, Input, Textarea, Label, Checkbox, RadioGroup, Switch, Slider, Select, Accordion, Alert, Avatar, Progress, Separator, Skeleton, Toggle, ToggleGroup, Tooltip, Popover, DropdownMenu, Tabs (in-page), Collapsible, AspectRatio, Spinner, Pagination, Carousel, Calendar, DatePicker, InputOTP. + +- **Variant/size naming mirrors shadcn** for familiarity: variants `default / secondary / destructive / outline / ghost / link`; sizes `sm / default / lg / icon`. Implemented as **typed enums + exhaustive `switch`** (the cva equivalent; AGENTS.md §4). +- All require `flutterwindcss` (semantic tokens) — that is the whole styling model. + +### 3.3 App-feel components shadcn lacks (because shadcn is web-only) + +`ThemeToggle`, `Splash`, `PullToRefresh`, `SegmentedControl`, `SearchBar`, `EmptyState`, `ListItem`. Plus the imperative feedback helpers `showConfirm` / `showSheet` / `Toaster` + `showToast`. + +### 3.4 Blocks (tier 2 — "feels like an app") + +music-player card, stat/metric grid, Chart(s), auth form, profile header, settings list, onboarding carousel. Blocks compose structure + primitives. + +### 3.5 shadcn web-centric — adapt or defer (by-demand, none impossible) + +Command (⌘K palette), Combobox, DataTable (Flutter wants a different idiom than a web grid), Resizable, Menubar, NavigationMenu, HoverCard (no hover on touch), Breadcrumb, ContextMenu (→ long-press on mobile). Each needs an honest Flutter rethink; scheduled, not refused. + +### 3.6 The overlay substrate (cross-cutting dependency) + +Popover, DropdownMenu, Tooltip, Select, Combobox, and ContextMenu all sit on the **same** behavioral plumbing — an **anchored overlay** (`OverlayPortal` + `CompositedTransformFollower` + edge-flip positioning, floating above the screen, dismiss-on-outside-tap). This is flutterbits' equivalent of shadcn's Radix dependency. + +It is built **once** as a shared **lib util** (`anchor`) — the flutterbits analog of shadcn's `cn.ts`: a predictable, pasted-once file every overlay component imports. It must be built **before** the components that depend on it. (Install mechanics in the registry/CLI spec.) + +--- + +## 4. Platform — mobile-first, portable, matures by demand + +**Decision: mobile-first as the design *center*; portable but not desktop-tuned.** + +- **Design center = phones.** Touch targets ≥ ~44px, safe-area aware, `TabsLayout`/`BottomNav` + sheets are the default navigation idioms, status-bar styling is first-class. +- **Portable, not exclusive.** Every component still *runs* on web/desktop (they are plain widgets) and keeps full **keyboard + focus-ring + `Semantics` accessibility** (AGENTS.md §6 requires this regardless of platform). They are simply not *optimized* for desktop density/hover. +- **Hover is enhancement-only** — never required to operate anything, so touch loses nothing. +- **Catalog impact.** `TabsLayout`/`BottomNav` are v1 core; `SidebarLayout` and the desktop-centric shadcn set (§3.5) are **by-demand**, not v1. +- **Tablets / large phones** are handled by the engine's existing responsive + container-query variants — no new work. +- **Maturation.** Desktop/web become first-class "with need" — the §11b "feasible, scheduled" framing, not a "can't." + +**Boundary (no drift):** this is a **flutterbits** stance only. **`flutterwindcss` stays fully platform-agnostic** (the universal styling engine), and the **web theme generator is unaffected** (a build-time web tool). Mobile-first describes how the *components* are designed, not the engine. + +--- + +## 5. Naming — unprefixed components, `Fw` engine + +- **flutterbits components are unprefixed**: `Button`, `Card`, `Badge`, `Screen`, `Layout`. This is the shadcn DX, and these are **source you own** (copied into your project), so they need no namespace. +- **`flutterwindcss` engine types stay `Fw`-prefixed**: `FwColumn`, `FwTokens`, `FwStatusBar`, `FwRoute`. When you see `Fw` in a flutterbits component, that is the engine showing through. + +> **AGENTS.md §4 reconciliation.** "Public types are prefixed `Fw`" is an **engine** rule (`flutterwindcss`). It does **not** apply to flutterbits *components*, which are deliberately unprefixed (copy-paste, shadcn-style). The routing/structure *base types* that ship as library code (e.g. `FwRoute`, `FwPresentation`) keep `Fw`; the *components* a dev composes (`Screen`, `Layout`, `Button`) do not. + +### 5.1 The Material name-clash, resolved + +Material defines many of these nouns (`Card`, `Badge`, `Switch`, `Slider`, `Checkbox`, `Drawer`, `Tooltip`, `BackButton`). But they live in `package:flutter/material.dart`, **not** `package:flutter/widgets.dart`. A flutterbits app is Material-free by design (imports `widgets.dart` + flutterwindcss), so **in the intended world there are zero clashes**. + +For the supported-but-rare interop case (a dev mixing Material in — possible because flutterwindcss can theme Material via the `FwThemeExtension` bridge): + +- The **barrel** (`ui.dart`) is also the **clash escape hatch**: `import 'components/ui/ui.dart' as ui;` → `ui.Card`, `ui.Button`. Explicit namespacing on demand, with no `Fw` noise forced on the 99% case. +- When authoring, each component name is **checked against `widgets.dart`**; any genuine collision there is renamed. + +This is strictly better than prefixing every component `Fw` (which would make the engine and components look identical and kill the shadcn feel). + +--- + +## 6. Dependency policy + +- **Core stays dependency-free.** Primitives (Button, Card, Input…) need only `widgets.dart` + `flutterwindcss`. A copied `Button` drags in nothing. +- **Sanctioned deps are declared per-component in the manifest `pubDeps`**, never globally — exactly as shadcn lists a component's npm deps: + - **`lucide_icons_flutter`** (sanctioned) — icons. + - **`flutter_animate`** (sanctioned, AGENTS.md §11b) — the motion layer. Toast slide-in, Skeleton shimmer, Accordion/Collapsible expand, Splash, the `ThemeToggle` icon crossfade compose it. The engine deliberately ships no element-animation subsystem; components own their state machines and animate with `flutter_animate`. + - **`go_router`** (newly sanctioned — see §6.1) — the routing engine the structure layer wraps. + - **`flutter_svg`** — **by-demand**, only on blocks that render real SVG illustrations (illustrated `EmptyState`, onboarding art). Not core; most things use lucide or simple shapes. +- **The payoff:** `add button` installs zero deps; `add toast` installs `flutter_animate`; `add layout` installs `go_router`. Every component is honest about exactly what it costs. + +### 6.1 `go_router` — newly sanctioned dependency (justification) + +> **AGENTS.md §12 reconciliation.** §12 lists `lucide_icons_flutter` and `flutter_animate` as sanctioned; **`go_router` is added** as a sanctioned dep for the structure layer. Justification: building a router de-novo over Navigator 2.0 is a tar pit and violates "don't rebuild what works"; `go_router` is the Flutter-team-endorsed router and provides deep links, web URLs, and `ShellRoute` (persistent shells) for free. flutterbits **wraps** it in a thin, typed, intention-revealing face — it does not fork it. The router engine is a trusted dependency; only the ergonomic wrapper is owned. `go_router` is a `pubDep` of the structure components **only** — it never touches `flutterwindcss`. + +--- + +## 7. Where everything lives (package vs registry) + +- **`flutterwindcss` stays styling-only** (tokens + utilities + `FwRow`/`FwColumn`/`FwGrid`). It never learns routing, components, or app-shell concepts exist. Folding any of that in would blur "the Tailwind layer" and force unrelated deps (e.g. `go_router`) onto token-only users. +- **`flutterbits` is copy-paste source** (the registry): primitives, structure (`Layout`/`Screen`/routing), blocks, templates, and `util`s (`anchor`, overlay helpers). Components **import** `flutterwindcss` (which is why flutterbits cannot work without it) and declare `pubDeps` for anything else. +- **No new runtime package is introduced for v1.** Routing is copy-paste source + a `go_router` `pubDep`, not a new published package. If a genuinely shared runtime emerges later, it is promoted to a package then — not pre-emptively. + +--- + +## 8. Decomposition & sequencing + +This charter is the umbrella. Implementation is decomposed into specs, each its own spec → plan → build cycle: + +1. **This charter** — identity, taxonomy, platform, catalog, naming, deps. *(done)* +2. **`2026-06-10-flutterbits-structure-and-routing-design.md`** — `Layout`, `Screen`, routing, presentations, navigation DX. +3. **`2026-06-10-flutterbits-registry-cli-design.md`** — manifest schema, install-types, `init`/`add`/`diff`, barrel regen, `flutterbits.json`, hosting. +4. **Per-component specs** — small, one component (or tight group) each, with `button` as the canonical template. + +**First vertical slice (proves the whole stack end-to-end):** `Layout` + `Screen` + routing + `Button` + `ThemeToggle`, rendered in `apps/example`. `ThemeToggle` is the chosen first concrete component — tiny, pure "feel good," and it forces every layer to play together (`Layout` owning theme → the `Switch` primitive → semantic-token reskin → the engine's `FwAnimatedTheme` transition). + +--- + +## 9. Open questions (deferred, by explicit decision) + +- **State management / data conventions** — deferred. flutterbits blesses **no** controller/view-model/folder-structure pattern in v1; Screens stay pure UI. Conventions are introduced later "when we have earned the right to impose them" (recorded decision, 2026-06-10). This is a deliberate de-scope, not an omission. +- **`SidebarLayout` and the desktop-centric set** — by-demand (§4). +- **Codegen routing** — explicitly rejected for v1 (forces `build_runner` on every consumer; awkward in a copy-paste model). The typed hand-written route is the chosen DX. May be offered as an *optional* layer later — capability-raising, not locked out. diff --git a/docs/superpowers/specs/2026-06-10-flutterbits-registry-cli-design.md b/docs/superpowers/specs/2026-06-10-flutterbits-registry-cli-design.md new file mode 100644 index 0000000..07eee87 --- /dev/null +++ b/docs/superpowers/specs/2026-06-10-flutterbits-registry-cli-design.md @@ -0,0 +1,147 @@ +# flutterbits — registry & CLI (design) + +**Status:** design · **Date:** 2026-06-10 · **Audience:** implementers of the `flutterbits` CLI and registry tooling. +**Parent:** `2026-06-10-flutterbits-charter.md`. **Sibling:** `2026-06-10-flutterbits-structure-and-routing-design.md`. +**Extends:** `AGENTS.md` §8 (registry & CLI) — adds `init`, the barrel, install-types, and `flutterbits.json` that §8 did not yet specify. + +--- + +## 1. Model recap (the copy-paste contract) + +Components are **source the developer owns**, not a versioned dependency. The CLI fetches a component's files from a **registry** into the host project; the dev reads and edits freely. `registry/*.dart` is the **single source of truth**; JSON manifests are **generated** from it, never hand-edited (AGENTS.md §8). + +--- + +## 2. Install-types (the shadcn `registry:ui`/`lib`/`hook` analog) + +The registry needs to know *what kind* of thing each item is so the CLI places it in the right predictable path. Three types: + +| `type` | Default target | Purpose | In barrel? | +|---|---|---|---| +| `component` | `lib/components/ui/.dart` | a primitive / structure / block / template | yes | +| `util` | `lib/components/ui/_utils/.dart` | shared lib utility (the `anchor`/overlay substrate, formatters, the `cn`-equivalents, `showConfirm`/`showSheet`/`showToast`) | only if `exported: true` | +| `barrel` | `lib/components/ui/ui.dart` | the regenerated re-export file (managed by the CLI; never authored) | n/a | + +**On "hooks":** Flutter has no native hook concept (`flutter_hooks` is a separate dep we are **not** taking — charter §6). What shadcn calls `registry:hook` is, here, a plain Dart `util` — a utility / widget / extension. Same *role*, different mechanism. + +### 2.1 The `anchor` util = flutterbits' `cn.ts` + +In shadcn, `cn()` lives in a predictable `lib/utils.ts` every project has, and components import it. The flutterbits analog is the **overlay substrate** (`anchor`): the anchored-overlay behavior (`OverlayPortal` + `CompositedTransformFollower` + edge-flip positioning) that Popover, DropdownMenu, Tooltip, Select, Combobox, and ContextMenu all share (charter §3.6). It is a `util`, pasted once into `_utils/anchor.dart`, pulled in as a `registryDep` by every overlay component, and **built before** them. + +`anchor` is **internal** (`exported: false`): components import it by relative path; it does not appear in the barrel. Dev-facing helpers like `showConfirm`/`showToast` are `util`s with `exported: true`. + +--- + +## 3. Manifest schema (extends AGENTS.md §8) + +```jsonc +{ + "name": "popover", + "type": "component", // component | util (barrel is CLI-managed, not authored) + "description": "An anchored floating panel.", + "exported": true, // does the barrel re-export it? (components: true; internal utils: false) + "pubDeps": ["flutter_animate"], // pub packages to `flutter pub add` + "registryDeps": ["anchor"], // other registry items to install first (recursive) + "files": [ + { "path": "popover.dart", "type": "component", "target": null, "content": "" } + ] +} +``` + +- `type`/`target` per **file** lets one item ship files to different predictable locations; `target: null` uses the type default (§2). A `target` override allows custom paths for advanced cases. +- `registryDeps` resolves inter-item needs recursively (e.g. `dialog` → `button`; `popover` → `anchor`). `pubDeps` are real pub packages. +- Manifests are produced by `tooling/build_registry.dart` **from** `registry/*.dart` — `content` is never hand-edited (AGENTS.md §8). + +--- + +## 4. `flutterbits.json` (the project config — shadcn `components.json` analog) + +`init` writes this to the project root; every other command reads it. It records where things go so the CLI never guesses: + +```jsonc +{ + "registry": "https://flutterbits.vercel.app/r", // registry endpoint (served by apps/docs) + "componentsDir": "lib/components/ui", + "utilsDir": "lib/components/ui/_utils", + "barrel": "lib/components/ui/ui.dart", + "theme": "lib/theme.dart", // where the generated theme.dart lives + "interop": false // true if the app also uses Material (affects nothing yet; reserved) +} +``` + +--- + +## 5. Commands + +### 5.1 `flutterbits init` + +Scaffolds a flutterbits-ready project: + +1. Ensures `flutterwindcss` is a dependency (`flutter pub add flutterwindcss`). +2. Creates `lib/components/ui/` and `lib/components/ui/_utils/`. +3. Writes a starter `theme.dart` at the configured `theme` path (a default `FwTokens` light/dark, or prompts the dev to paste a tweakcn theme via the generator). +4. Writes an initial barrel `ui.dart` (empty export list, with the generated-file header). +5. Optionally scaffolds a starter `Layout` (root wiring) so `runApp` is intention-revealing from line one. +6. Writes `flutterbits.json`. + +### 5.2 `flutterbits add […]` + +1. Fetch the manifest(s) from `registry`. +2. Resolve `registryDeps` **recursively** (so `add popover` also installs `anchor`; `add dialog` also installs `button`); de-duplicate. +3. Write each file to its `type`/`target`-determined path (§2). Refuse to clobber a modified file without `--overwrite` (the dev owns it). +4. `flutter pub add` every collected `pubDep`. +5. **Regenerate the barrel** (§6). +6. `dart format --line-length 100` the written files. + +### 5.3 `flutterbits diff ` + +The copy-paste **survival mechanism**: shows what changed in the upstream registry version **vs the dev's local copy**, so they can merge upstream improvements (bug fixes, new variants, a11y fixes, Flutter-API-driven changes) **on their own terms**. It **does not auto-merge** — the dev may have customized their copy; merging is their call (like reviewing a PR against a fork). This is why a single canonical `registry/*.dart` source matters: `diff` always has one version to compare against. + +### 5.4 `flutterbits remove ` (by-demand) + +Removes a component's files and **regenerates the barrel**. Leaves `pubDeps` in place by default (other components may share them); `--prune-deps` to attempt removal of now-unused deps. Scheduled by-demand, not v1-blocking. + +--- + +## 6. The barrel (`ui.dart`) — regeneration rules + +The barrel is the convenience the dev wanted: import the whole component set from one place instead of file-by-file. + +- **Fully regenerated** by the CLI on every `add`/`remove` — **never hand-edited**. It carries a header making that explicit: + ```dart + // GENERATED BY flutterbits — do not edit. Run `flutterbits add/remove` to update. + export 'button.dart'; + export 'card.dart'; + export 'theme_toggle.dart'; + export 'screen.dart'; + export 'layout.dart'; + export '_utils/overlays.dart'; // exported helper util (showConfirm/showSheet/showToast) + ``` +- **Exports `exported: true` items only** — every `component`, plus dev-facing `util`s. **Internal utils** (`anchor`) are imported directly by the components that need them and are **not** in the barrel (keeps plumbing out of the public surface, so no `show`-clause gymnastics are needed). +- **Usage:** `import 'package:my_app/components/ui/ui.dart';` — one import for all installed components. +- **Interop escape hatch (charter §5.1):** a dev mixing Material in can namespace the whole set — `import '.../ui/ui.dart' as ui;` → `ui.Card`, `ui.Button` — resolving any Material name clash explicitly, with no `Fw` prefix forced on the common case. The barrel thus doubles as the clash resolver. + +--- + +## 7. Hosting & tooling + +- **Registry source of truth:** `registry/*.dart` (one component per file; no barrel that re-exports registry components — they are copied individually, AGENTS.md §4/§8). +- **Manifest build:** `dart run tooling/build_registry.dart` generates the JSON manifests from the Dart source (AGENTS.md §10 "planned commands"). +- **Registry endpoint:** served by `apps/docs` (the Next.js site already plans a registry endpoint, AGENTS.md §2). The CLI's `registry` URL points at it. Each component also gets a docs page with source + a Copy button (shadcn-style). +- **CLI package:** `packages/flutterbits_cli` (Dart), exposing the `flutterbits` command (AGENTS.md §2). The CLI **fetches components only** — it does **no** color math or theme generation (that lives solely in `apps/docs`; AGENTS.md §7). + +--- + +## 8. Build/verify discipline (inherited) + +- A component is "done" per AGENTS.md §6: styled through `.tw` with semantic tokens, typed-enum variants with exhaustive `switch`, Material-free interaction states, keyboard + focus ring, `Semantics`, directional layout, a manifest entry (§3), goldens for every variant × size × brightness in `apps/example`, and rendered there so CI compiles it. +- `diff` correctness depends on the canonical `registry/*.dart` source staying authoritative; the manifest's `content` is always regenerated, never edited (§3). + +--- + +## 9. Sequencing + +1. **Registry plumbing first** — `tooling/build_registry.dart`, the manifest schema (§3), `flutterbits.json` (§4), and the `apps/docs` registry endpoint. +2. **CLI** — `init`, `add` (with recursive `registryDeps` + barrel regen), `diff`. `remove` by-demand. +3. **First vertical slice** (charter §8) — `Layout` + `Screen` + routing + `Button` + `ThemeToggle`, installed via the real CLI into `apps/example`, proving manifest → fetch → place → barrel → compile → golden end-to-end. +4. **The overlay `anchor` util** before any overlay component (Popover/Dropdown/Tooltip/Select/Combobox/ContextMenu). diff --git a/docs/superpowers/specs/2026-06-10-flutterbits-structure-and-routing-design.md b/docs/superpowers/specs/2026-06-10-flutterbits-structure-and-routing-design.md new file mode 100644 index 0000000..738d7fb --- /dev/null +++ b/docs/superpowers/specs/2026-06-10-flutterbits-structure-and-routing-design.md @@ -0,0 +1,304 @@ +# flutterbits — structure layer & routing (design) + +**Status:** design · **Date:** 2026-06-10 · **Audience:** implementers of the flutterbits structure layer. +**Parent:** `2026-06-10-flutterbits-charter.md`. **Sibling:** `2026-06-10-flutterbits-registry-cli-design.md`. + +This spec designs the **differentiator**: the opinionated, intention-revealing app skeleton (`Layout`, `Screen`) and the routing that wraps `go_router`. All names are unprefixed *components* the dev composes; `Fw`-prefixed types are routing/engine **library** types (charter §5). + +--- + +## 1. Philosophy recap (the bar this design must clear) + +- **Intention-revealing** — `Layout`, `Screen`, `header`/`body`/`footer` read like the rendered artifact (charter §1.1). +- **Composition, not inheritance** — components are **widgets you return**, never base classes you extend. This is the single most important API decision in this spec (§2.1). +- **Feels good** — one concept expressed one way; a sheet is "a `Screen` mounted differently," not a separate API (§4). +- **Material-free** — built on `package:flutter/widgets.dart` + `context.fw`; never `Scaffold`/`MaterialApp`/`Navigator` directly in component code (the router wrapping is the one place `go_router` — itself Material-free-capable — is touched). + +--- + +## 2. The vocabulary + +``` +Layout ← app root. Holds config: theme, router, global providers. + ← "the better MaterialApp." Also nestable (a section Layout = + ← persistent chrome: bottom nav / sidebar). + └─ Screen ← a routable destination. Owns screen-level concerns: safe area, + ← status-bar style, header/footer regions, scroll ownership, + ← background. "the better Scaffold." This is what the router targets. + └─ blocks → primitives +``` + +`Page` was considered and **dropped**: a universal `Screen` covers mobile/desktop/web ("everyone knows what a screen is"), and a sub-`Screen` concept was unnecessary surface. + +### 2.1 Composition, not inheritance — the decision that dissolves `ScreenSpec` + +An earlier sketch had `Screen` as a **base class** whose `build` returned a *description* object (`ScreenSpec`). That forces a non-widget return type — the smell that made the name feel wrong. **Rejected.** + +`Screen` and `Layout` are **composed widgets you return**: + +- `Screen` is "the better `Scaffold`" — screen-level concerns are **intention-revealing named slots** (`header`/`body`/`footer`) plus props (`statusBar`, `background`). +- `Layout` is "the better `MaterialApp`" — you return `Layout(theme:, routes:, shell:)`; there is no `LayoutConfig` return object. +- A screen's **identity** comes from its file + its route, **not** a base class. So there is no `ScreenSpec`, no `extends Screen`, nothing un-Flutter. + +**Why this is the strongest design:** zero new return-type concepts; plays with `const`; the slots read like semantic HTML; defaults are semantic-token-aware; everything stays a plain widget droppable anywhere. The "framework feel" comes from the *vocabulary*, not from inheritance. + +No base class is introduced in v1 (no lifecycle hooks, no view-model binding — that is the deferred state-management decision, charter §9). A screen is an ordinary `StatelessWidget`/`StatefulWidget` that returns a `Screen`. + +--- + +## 3. `Layout` and `Screen` — the API + +### 3.1 `Layout` (root) + +```dart +/// The app root. Config lives here; everything renders inside it. +class AppLayout extends StatelessWidget { + const AppLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Layout( + theme: appTheme, // generated FwTokens (light/dark) — see charter + title: 'Tunes', + // A nested layout: a persistent shell across the screens inside it. + shell: TabsLayout( + destinations: const [ + NavDestination(icon: LucideIcons.house, label: 'Home', route: HomeRoute.pattern), + NavDestination(icon: LucideIcons.user, label: 'Profile', route: ProfileRoute.pattern), + ], + ), + routes: const [HomeRoute.pattern, ProfileRoute.pattern], // the table, in plain sight + redirect: _authRedirect, // optional global guard (§5.2) + ); + } +} +``` + +- `theme` — the `FwTokens` light/dark bundle; `Layout` owns the light↔dark swap and drives the engine's `FwAnimatedTheme` so a toggle crossfades every `context.fw`-styled descendant. +- `shell` — an optional nested `Layout` (`TabsLayout`/`SidebarLayout`) providing persistent chrome. Maps to `go_router`'s `ShellRoute`. +- `routes` — the explicit route table (a plain `const` list of `FwRoutePattern`s; no codegen). +- `Layout` internally constructs the `go_router` configuration and the root `WidgetsApp`-family widget (Material-free). + +### 3.2 `Screen` (destination) — slots + +```dart +class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Screen( + statusBar: FwStatusBar.lightIcons, + background: context.fw.colors.background, + header: TopBar(title: const Text('Home')), //
+ body: FwColumn(gap: 4, children: [...]), //
+ footer: null, //