Skip to content

Add interactive Advanced Forms documentation site - #68

Merged
KamilSztandur merged 44 commits into
mainfrom
docs
Sep 8, 2026
Merged

Add interactive Advanced Forms documentation site#68
KamilSztandur merged 44 commits into
mainfrom
docs

Conversation

@konowrockis

@konowrockis konowrockis commented Aug 20, 2026

Copy link
Copy Markdown
Member

Docs site redesign: Jaspr homepage at /, Fumadocs documentation at /docs

One Vercel deployment, one domain (advanced-forms.leancode.co), two parts:

  • / — the homepage, a static Jaspr site in landing/. Rendered once at build time to a single index.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 in docs_app/, 43 MDX pages in docs/ 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 from public/ (//index.html rewrite), so the two ship together.

What changed

  • Content: pages rebuilt from the knowledge base and the package source; long pages split into subpages; FAQ, migration, agent-skill, example-app and internals pages.
  • Design: the LeanCode design system as on ciach.leancode.co — #050505 ground, #101013 surfaces, lime #edff2f accent, 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.
  • Live examples: prettier, richer islands (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.
  • Pipeline: examples:format formats the Dart fences with dart format (language version from the Flutter package), examples:generate / examples:check keep the committed manifest in step with the MDX, landing:build renders the Jaspr page into public/.
  • Deployment: .github/workflows/docs.yml builds and deploys with vercel build + vercel deploy --prebuilt (Vercel's own Git deployments are off; the build needs Flutter 3.41.4, Dart 3.13 and jaspr_cli 0.23.4). Push to main = production, push to docs = preview of the same project. Requires the VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID secrets.

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 a next start smoke test of /, /docs/*, /docs/*.md, /api/search, /flutter-examples/* and the landing assets.

🤖 Generated with Claude Code

konowrockis and others added 7 commits August 19, 2026 18:52
…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>
Comment thread docs_app/.gitignore
KamilSztandur and others added 18 commits September 2, 2026 19:38
… 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>
KamilSztandur and others added 11 commits September 7, 2026 14:50
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>
@mateusz-pietras

Copy link
Copy Markdown
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

KamilSztandur and others added 7 commits September 8, 2026 16:51
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>
@KamilSztandur
KamilSztandur merged commit 6488fb5 into main Sep 8, 2026
5 checks passed
@KamilSztandur
KamilSztandur deleted the docs branch September 8, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants