Add interactive Advanced Forms documentation site - #68
Merged
Conversation
…late. Replace the copied Patrol docs with a nested Next app and README-based MDX, keeping notebook navigation from `/` without Patrol branding. Co-authored-by: Cursor <cursoragent@cursor.com>
Dart written inline in MDX inside `<AdvancedFormsExample>` is extracted at build time into one Flutter web bundle, which renders each snippet as an auto-sized island above the code that produced it. Flutter's multi-view embedding gives one engine many host elements, so this needs no iframes and one download for the whole site. The demo is the visible half; the guarantee is the point. Every snippet is now compiled, analyzed, format-checked and laid out in a widget test, so a page can no longer document an API that has moved on. `docs/first-form.mdx` and `docs/validation.mdx` are the first two to use it — the latter demonstrates `subscribeToFields` clearing an error rather than only describing it. Building the site now needs the Flutter SDK, which Vercel's build container has not got, so `vercel.json` turns its Git integration off and the new docs workflow builds and deploys with `vercel deploy --prebuilt`. That needs `VERCEL_TOKEN`, `VERCEL_ORG_ID` and `VERCEL_PROJECT_ID` as repository secrets; without them the deploy job no-ops and the build job still guards every PR. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Compiling the Flutter bundle is too slow to sit in front of every change to the package, and it has no business holding up work on the library. A pull request now builds only when docs/ or docs_app/ change. A push to main keeps `lib/` in the filter, because the bundle embeds the package's own compiled source: without it the deployed examples would go on running the library from whenever the docs last changed, and a snippet the API has outgrown would surface later on an unrelated docs pull request. That is one build per merge rather than one per push. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
alszczep
approved these changes
Aug 25, 2026
KamilSztandur
requested review from
KamilSztandur,
PiotrRogulski and
mateusz-pietras
as code owners
September 2, 2026 16:08
… content and live examples - Landing page at / in the ciach.leancode.co style (dark, lime accent, Space Grotesk / JetBrains Mono); docs move under /docs. - Design tokens mapped onto the Fumadocs variables; light theme as a paper variant; branded OG images and favicon. - Docs expanded from 10 to 24 pages: concepts, validation modes, validators, async validation, cross-field logic, server errors, focus, lifecycle, testing, under the hood, FAQ, example app, migration. - 16 live Flutter examples (was 2), a themed island frame with status, Docs* helper widgets, and an Output panel. - npm run examples:format runs dart format over the fences inside <AdvancedFormsExample>; landing demos live in docs_app/content/landing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Core concepts, Rendering fields, Validation, Form-level state, Field controllers, Server errors, Subforms, Lifecycle and Under the hood each become a folder of one-lesson pages, most with a single live example. The rendering kitchen-sink example is split into four small ones. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… table with mode cards Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the favicon and OG images Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ne cached Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…etry Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The workflow deployed only on pushes to main, so nothing from the docs branch ever reached Vercel. Pushes to docs now deploy a preview of the same project (main stays production), Vercel's own Git builds are switched off for every branch since its container has no Flutter, and the README describes the single-deployment setup. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The homepage is now a static Jaspr site in landing/, rendered once at build time; the Fumadocs app in docs_app/ keeps the documentation under /docs and serves the landing's build output from public/ (/ is rewritten to /index.html), so both stay one Vercel deployment on one domain. - landing/: Jaspr 0.23.4 static site — hero, three sections with a live demo each, features, agent-skill band, footer. Demos are read from docs_app/content/landing/*.mdx and highlighted at build time; landing.js attaches them as views of the docs' Flutter bundle, mirroring runtime.ts. - docs_app: React landing removed (app/(home), components/landing, lib/version.ts, the landing collection and its CSS); scripts/landing.mjs + `npm run landing:build` build and copy the Jaspr output; the header logo is a plain link to / (nav-title.tsx), since / is outside the Next app. - CI: standalone Dart 3.13 + jaspr_cli next to Flutter; landing analyzed, format-checked and built on every run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
global.css sets smooth scrolling on <html>; Next 16 only turns it off for the scroll-to-top of a route change when the element carries data-scroll-behavior="smooth", so the animated scroll landed a little below the top after the prev/next footer links. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Next scrolls a route change to the top only when the new segment's first DOM node has a box. Fumadocs' DocsPage renders a `display: contents` <main> with no sibling to fall back to, so Next gave up and a page opened from the prev/next footer kept the previous scroll position. A 1px absolutely positioned anchor before DocsPage is the box it checks. Reproduced and verified with headless Chrome: footer and sidebar navigation from a scrolled page now land at scrollY 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Only the token is a secret; VERCEL_ORG_ID and VERCEL_PROJECT_ID are kept as repository variables, so the deploy job reads them from `vars`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`Deployed ($VERCEL_ENV): $(…)` contains a colon followed by a space, which YAML reads as a mapping inside a plain scalar. A block scalar keeps the shell line as it is. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Vercel project's Root Directory is docs_app and the CLI resolves it against the current directory, so running it inside docs_app looked for docs_app/docs_app/package.json. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- robots.txt and sitemap.xml for the whole site (landing page + every docs page), with the public origin in one place (`siteUrl`). - The landing page gets a social card rendered by the docs app (/og/landing.png), og:image/twitter:image, JSON-LD (WebSite, SoftwareSourceCode with repository, licence and version, WebPage), a keyword-bearing <title> and a description under 155 characters. - Docs pages get canonical URLs, full Open Graph and Twitter metadata; the root layout sets site name, robots and card type once. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
landing/ is its own Dart package (Jaspr) with its own dependencies and analysis options; analyzed from the package root its imports do not resolve, which failed the package's own analyze check. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The landing page maps its demos onto the compiled bundle through flutter/web/examples.json, which examples:generate writes and git does not track. In a fresh checkout `landing:build` ran first and failed on the missing file, which is what broke the Vercel build in the deploy job. `npm run build` now compiles the examples first, and `landing:build` generates the registry itself so it also works alone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Merges main (release 0.2.1) so the landing page reads the current version from pubspec.yaml. The overview on the docs index points at the single doc/overview.svg the release introduced (the per-theme files are gone), and the prose says 0.2 where it means the release line, not a patch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The paper plane read as "send" and the log-out arrow as leaving the app. An open hand says nothing happens until the user acts; a dashed focus ring is the field that just lost focus. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Member
|
tl;dr - looks really nice UX-wise, maybe a bit wordy but split well into multiple pages. I didn't find any obvious inconsistencies, only one bug in first interactive example - invalid inputs make error texts show up, while the form height is fixed, so the bottom buttons overflow |
mateusz-pietras
approved these changes
Sep 8, 2026
Easy to use, Time-saving and Out of the box join the version, Flutter and licence facts; No codegen goes, so the row stays readable. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two islands drawing in the same frame could paint one of them at the other's height: the engine's default rasterizer shares one offscreen surface between views and resizes it per draw, and draws are asynchronous (flutter/flutter#185034, open; also in 3.38 and in Skwasm). Reproduced with the async demo's spinner running while the hero grew its validation errors — the hero was cut at the async demo's height. With canvasKitForceMultiSurfaceRasterizer every view has its own surface and the race is gone. A surface costs two WebGL contexts that the engine never gives back on removeView, and browsers revoke the oldest live context past about sixteen — the flicker-and-blank islands seen earlier. So the runtimes (runtime.ts, landing.js) now keep a pool of four views and never remove one: an island that goes away parks its view off screen, the next island takes a parked view and points it at its own example through __advancedFormsIslandsSetExample (main.dart), and a reader scrolling back to a parked example gets it back as they left it. Verified in headless Chrome: contexts plateau at 11 across 18 navigations with zero "Too many active WebGL contexts" warnings, the hero renders fully 6/6 under the concurrent-draw test, and a re-aimed view renders pixel-identical to a fresh load of the same example. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The command box wrapped below 540px, leaving the Copy button alone on a second line. The prompt goes, the button shrinks to its icon and the command scrolls sideways on the narrowest screens. The header also fits now: Examples (reachable from the docs) is dropped and the logo shrinks, so the page no longer scrolls horizontally at 320–430px. The copy button in a code tab strip stays pinned while the strip scrolls. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A page whose body opens with an h2 stacked the description's margin, the body gap and the heading's own top margin, padding and rule: about 100px of nothing. The first h2 now sits at the same distance as any other element; the rule stays for every later section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A transcript, not a history: a reader who clicks a demo's button twenty times got twenty lines and an island as tall as the page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
pana copies the whole repository and runs `dart format` over all of it. The formatter takes a file's language version from the nearest .dart_tool/package_config.json, and in a fresh checkout only the root package has one — so the nested packages were formatted as Dart 3.3: the landing (dot shorthands) did not parse, the islands and the example came out differently, and the analysis section lost 10 points. - The pana job now writes each nested package a minimal package config with its own SDK floor before scoring, which is what `pub get` would do without needing the Flutter SDK two of them depend on. - landing/ joins docs/ and docs_app/ in .pubignore: it must not ship in the published package either. - example/lib/screens/step_form.dart is re-formatted for its own language version, with the trailing comma the lint wants. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Docs site redesign: Jaspr homepage at
/, Fumadocs documentation at/docsOne Vercel deployment, one domain (
advanced-forms.leancode.co), two parts:/— the homepage, a static Jaspr site inlanding/. Rendered once at build time to a singleindex.html; four live demos (sign-up, model, validation modes, async username check) run as views of the docs' Flutter bundle. Headline: Complicated forms. Simple code./docs— the documentation, Fumadocs on Next.js indocs_app/, 43 MDX pages indocs/split into Getting started / Guides / Reference folders (concepts, rendering, validation, form state, fields, server errors, subforms, lifecycle, internals). The Next app serves the landing's build output frompublic/(/→/index.htmlrewrite), so the two ship together.What changed
#050505ground,#101013surfaces, lime#edff2faccent, Space Grotesk + JetBrains Mono, window-framed code blocks. Dark by default, light as a paper variant; the theme choice is shared between homepage and docs. New logo (light/dark SVGs) and square favicon.Docs*helper widgets, LeanCode theme, status pill, file tabs with copy). One Flutter engine, multi-view embedding, capped at four attached views; the multi-surface rasterizer flag was removed because it leaked WebGL contexts and made the islands flicker and disappear.examples:formatformats the Dart fences withdart format(language version from the Flutter package),examples:generate/examples:checkkeep the committed manifest in step with the MDX,landing:buildrenders the Jaspr page intopublic/..github/workflows/docs.ymlbuilds and deploys withvercel build+vercel deploy --prebuilt(Vercel's own Git deployments are off; the build needs Flutter 3.41.4, Dart 3.13 andjaspr_cli 0.23.4). Push tomain= production, push todocs= preview of the same project. Requires theVERCEL_TOKEN,VERCEL_ORG_ID,VERCEL_PROJECT_IDsecrets.Checks run locally
dart analyze/dart format(landing and the example bundle),flutter analyze,flutter test(39 tests),types:check,lint,format,examples:check,next build, and anext startsmoke test of/,/docs/*,/docs/*.md,/api/search,/flutter-examples/*and the landing assets.🤖 Generated with Claude Code