Generated Rails UI
Status: Implemented architecture. The evidence index owns the current tested and distributed boundary.
A generated application uses ERB for pages and Rails forms, Basecoat's Vega style for HTML components, and selected shadcn React components for richer controls. Both consume one semantic theme. The application remains a normal Rails repository: developers continue by editing its source after Compilation.
The compiled baseline should look intentional from its first launch: clear hierarchy, compact detail and related record layouts, consistent controls, readable interaction states, and useful empty and validation states. Judge that with populated records and narrow layouts as well as empty scaffolds. The first UI continuation task adapts this coherent foundation to the owner's product design.
Ownership and composition
| Surface | Owner |
|---|---|
| Layout, navigation wrapping, flash, error views, legal pages, common partials | Rails Core |
| Error-controller account helper when an Account is realized | Compiler renderer |
| App-specific routes, labels, fields, authorized actions, query results | Compiler renderers |
| Authored theme mode, metadata, selected bookmark icons, static error mode | Appearance renderer |
| Copied shadcn primitives and the React mount adapter | Rails Core, then the generated application's owner |
| How to extend the chosen UI | Generated UI.md |
Ordinary pages remain ERB. Their partials compose Basecoat components using data-variant and data-size. Shared
page headings, empty states, form errors, field errors, inputs, and actions prevent each renderer from inventing a
different composition. Rails continues to own form names, values, routes, CSRF, validations, authorization, and
response status. Partials receive already-authorized choices; they do not query or widen a relation.
Generated record presentations live beside their resource views. Entity partials own the selected fields, labels, and separately authorized links, with meaningful locals and contextual DOM IDs. Their ordinary Rails and Turbo extension points add no subscription or automatic live delivery.
Validation summaries use an unboxed heading and list. Rails error attributes map to visible control IDs so ERB can render links directly; errors for omitted fields and record-wide errors remain plain text. The small Stimulus action scrolls the control's associated label into view before focusing the control, following GOV.UK's error summary. Native links remain usable without JavaScript.
View copy uses Rails' relative lookup at the actual action or partial path, such as t(".heading") in an index or
t(".cancel") in _form. Resource and authentication locale files are scaffolds.en.yml, accounts.en.yml, and
authentication.en.yml.
Shared primitives keep their shared.* scopes; Active Record, helpers.submit, and Rodauth keep their conventional
keys. An action and a model-named partial can share a scope, as shows/show and shows/_show do; merge their leaves
and preserve existing Rails scopes when a resource is named Helper. Cross-template references, including Core's
legal-page headings, stay explicit. A strict-local default cannot use a relative lookup: Rails sets the partial's
virtual path after evaluating those defaults. Put that lookup in the partial body, or name the shared scope
explicitly. Authored enum and registration labels use escaped translation lookups even when an authored key ends in
_html.
Rails 8.1.3.1 supplies this through collection rendering, strict-local compilation, and model-bound forms. No presenter or translation library is needed for these view boundaries.
A selected form helper can differ from Rails model inference. A new New model infers news_path, while
resources :news names its collection helper news_index_path. The Compiler supplies that explicit create URL;
persisted records still use the inferred member URL. Associated forms compare their own nested helper, retaining
model inference whenever it matches. A canonical create URL applies only without a parent. Rails'
model naming owns this
comparison; no model, route, or parameter rename is needed.
Denied HTML actions render Core's translated errors/forbidden view through its shared errors/error partial,
which adapts Shadcn Admin's error composition, and the full application layout. The response keeps HTTP 403.
Turbo form submissions receive text/html, allowing Turbo to display the denial and its ordinary Home link;
other formats retain the empty response. The view receives no protected record content.
Policies (repository-only) owns the distinct hidden-record 404 and
partially visible Account profile behavior.
Scaffold and Rodauth forms use novalidate so browser validation bubbles do not intercept submission. Rails returns
validation errors using the shared form styles. Scaffold forms retain submitted values, inline errors, and a linked
error summary. Inputs retain their types, requiredness, and date bounds for semantics and control behavior. This keeps one error presentation across native and enhanced
controls. Client validation can be added later using the same accessible error presentation when it helps users.
The consistency tradeoff follows
GOV.UK's rationale.
Record titles wrap within their card. Collection pages keep Add beside a shrinking parent-destination link; long link labels truncate visually while retaining the full accessible name. Ordinary detail action groups can wrap.
Account settings use one /account destination. Its record toolbar groups Edit details, Change email, and Change
password as stock ghost actions with pencil, mail, and key icons. Default details and editing use signup Fields;
explicit profile projections, update inputs, and Policies still apply. See the
Scaffold owner (repository-only) for those selection rules.
Credential actions link to Rodauth's built-in change-login and change-password routes. Their forms share the Account field partial, card, heading, spacing, and error associations. Rodauth owns the current-password check, credential validation, and email-change verification; Rails owns the form and CSRF token. Successful changes return to Account. The Account owner (repository-only) distinguishes these signed-in flows from signed-out recovery.
The shared React islands enhance searchable References, dates, destructive confirmation, navigation, and success
feedback. They use copied shadcn base-vega source. Turbo Mount attaches them through the existing
Stimulus application. There is one successful form value per field. A Rails control remains usable until the island
commits; loading or rendering failure restores that control. The date control retains Rails' ISO date value at the
form boundary. The enhanced destruction control confirms before submitting the existing Rails form. Its native
Rails fallback submits directly when enhancement is unavailable.
Turbo owns navigation. Mounting, morphing, cache restoration, unmounting, portals, focus, and modal scroll state are part of the adapter's browser verification. React does not own page routes, shared application state, or a second data API. There is no server-side React rendering. Existing esbuild, Propshaft, and Tailwind build paths remain.
Error pages and navigation
Core's 404, 422, and 500 HTML error pages retain the application layout, branding, and available navigation.
Its ErrorsController inherits directly from ActionController::Base, so application authentication callbacks do
not prevent an error response. Rails supplies a plain-text failsafe if the error renderer itself raises. JSON
requests to these endpoints keep their status and JSON error response.
The shared ERB error partial adapts Shadcn Admin's 404 and 500 compositions, extending the same treatment to 422. A large responsive status number, descriptive heading, concise explanation, and stock Basecoat button use the application's existing theme. Back to home is an ordinary Rails link that works without JavaScript or browser history. Required locals and translated copy keep the partial easy to edit; Core includes the upstream MIT notice.
When an Account is realized, ApplicationController supplies the authenticated current_account helper and
ErrorsController supplies the same helper returning nil. Error pages deliberately use public navigation without
an account lookup. The Compiler knows both controller interfaces, so the shared navigation calls the helper
directly. Rodauth and Action Policy also install their own helpers on ActionController::Base.
Navigation still checks whether Rodauth middleware populated the request before rendering session links: an error
can occur before that middleware runs. This also covers default Account navigation without an authored Scaffold.
Normal application pages retain their account preloads and Policy-controlled links. Both public Scaffold navigation
and the private Policy qualification path skip Policy checks when the Account helper returns nil; this also
avoids Action Policy's application-only authorization context on error pages. The private path remains qualification
evidence, not additional public Policy coverage. Apps without an Account need neither an account helper nor
account-specific navigation.
Rambulance 3.3.0 provides a configurable exception application, error templates, JSON responses, and development previews. Its exception controller uses the same separate Rails base class, so adopting it would still require the account-context handling above. The current three error pages keep Core's small Rails controller. Reconsider the gem if broader exception mappings or error-preview tooling become useful. A standalone static page remains a valid application choice; removing the shared layout merely to simplify navigation would discard useful UI.
Sources: Core error controller (repository-only), Rails 8.1.3.1 exception rendering, Rodauth Rails controller integration, and Action Policy controller integration.
The original qualification (repository-only) records the reviewed predecessor and its browser/runtime inputs. The evidence index routes current qualification.
Theme and native behavior
Basecoat 1.0.2 Vega and the selected shadcn base-vega components share variables in
app/assets/stylesheets/theme.css. Import order is Tailwind, the complete Basecoat Vega bundle, then the app's
theme and small composition overrides. The standard tokens include background, foreground, card, primary,
muted, accent, destructive, border, input, and ring.
application.appearance.theme selects fixed light (also the omission default), fixed dark, auto, or toggle.
Fixed modes render their class and attributes on the server and ignore the OS and stored browser preferences.
Their output omits the theme module, prepaint script, selector/controller, and OS listener. auto emits only
OS-following behavior and its prepaint script. toggle offers Light / Dark / System, initially
System. Light and Dark persist in browser storage; System removes the override and follows live OS changes.
No Account preference is added. Both token palettes and reusable components remain in every output.
This uses Tailwind's class-based three-way pattern and a labeled native select, styled by Basecoat. It needs no React provider or theme package. Core keeps reusable mode behavior and upstream multi-mode qualification. The Appearance renderer selects emitted source through the existing Core replacement/omission registry. Toggle applications receive one browser scenario that chooses Light against a dark OS preference and retains it after reload. Automatic applications receive one live OS-change scenario, exercised against the Compiler's selected runtime. Fixed applications receive no dedicated theme browser spec. Core keeps the full browser/storage/Turbo/first-paint and fixed-mode qualification, including native rejection of an opposing saved browser preference. Shared navigation and browser/native layout request assertions remain emitted. Baseline accessibility sweeps audit only the configured palette for fixed-light and fixed-dark apps; automatic and toggle apps audit both. Core retains both-palette qualification upstream. A fixed-mode app accepts that regressions confined to its unused palette are not detected. Extend its audits when enabling another palette. Dynamic modes resolve before styles load, update theme-color metadata, and reapply before Turbo renders its next body and after navigation. The selector uses Turbo's permanent-element support across page and stream morphs. This also covers cached-body restoration using Turbo's existing lifecycle. CSS provides fixed/system fallback without JavaScript; the disabled selector then stays on System. If browser storage throws SecurityError or QuotaExceededError, a manual choice survives Turbo visits in memory, but not a full reload.
Native toggle output is an explicit partial: selected shells and embedded content use automatic appearance,
without the browser selector or preference. The reviewed GapSet names the missing native preference control and
its emitted clients. Browser selection does not change a native shell. Fixed and automatic modes retain their
cross-target mappings; native coverage owns that separate proof boundary.
Specialization trades one-initializer continuation for a smaller selected runtime. Adding an omitted behavior is
ordinary app development across the layout/scripts/control and browser checks; generated UI.md says so. No
runtime performance improvement is claimed. Static error pages use OS appearance for toggle, while manifest
colors retain their light fallback; those static values cannot read the browser preference. Bookmark artwork stays
black on opaque white in every theme.
Web content, including embedded web content, uses stock Zinc tokens. Native tint/background colors stay in native assets; they do not recolor components, browser or manifest metadata, bookmark artwork, or static errors. Controls use the stock component variants and sizes. A Tailwind token is still a local size override when an upstream preset fits. The deliberate native touch-target and wrapping adaptations remain shared. Confirmation uses a standard primary action with explicit Delete copy and initial focus on Cancel.
Base UI 1.8.0 owns Combobox, Popover, AlertDialog, and Sheet interaction. DayPicker remains the calendar. ReferencePicker has one Base UI named validatable input;
the Rails fallback is disabled only after enhancement commits. The shared adapter preserves values, portals, and
fallbacks through Turbo cache, frame, stream, and morph paths. Explicit reduced-motion CSS covers portaled overlays.
Routine success opts into a quiet Sonner toast with a visible Rails fallback. Persistent notice guidance and alert
failures stay in page flow. Toast consumption is retained in the Turbo snapshot so Back does not repeat delivery.
Sonner inserts its normal package stylesheet; application overrides use the app-toaster scope.
Account form cards follow Core's feedback composition (repository-only): set content_for :inline_flash, true and
render shared/flash, inline: true inside the card. The layout then omits its page-level copy. Keep the in-card
render unconditional so both live regions exist even without a message. Successful sign-in/out needs no additional
announcement when the destination makes the result clear; Rodauth's email next-step notices remain persistent.
Core keeps the web header out of native responses, retains concise native titles, and preserves native text scaling and touch targets. New/edit cards keep their complete title-only header screen-reader-only in native responses; hiding just its heading would leave the card's empty border and padding visible. React controls need native presentation checks because utility classes can override component sizing. Browser tests with a native user agent do not replace WebKit and Android WebView qualification.
Appearance lowering
Project application.appearance into one immutable target value with an authored bit, effective auto, light,
dark, or toggle theme, and typed light and dark modes. Omitted theme means fixed light, including absent
Appearance. Fixed output ignores OS/stored preferences and renders the selected class and attributes on the server.
Automatic output follows the OS. Toggle output offers Light / Dark / System, initially System, persisting browser
preference only. The UI owner defines the selected runtime, fallback, Turbo
lifecycle, and maintenance tradeoff.
Core owns stock Zinc web tokens, controls, and responsive navigation. Native tint/background colors never
replace those tokens. Authored Appearance replaces the theme initializer, neutral metadata, and static 400 and
unsupported-browser pages. Every application selects the renderer's manifest and retains Core's useful manifest
request checks. The renderer also specializes
layout, prepaint and controller registration for fixed/automatic modes; fixed output omits the theme module,
selector, and controller. Automatic output retains a small OS module and omits selector/storage behavior.
Both palettes remain. Automatic and toggle apps receive one selected behavior scenario; fixed apps receive no
dedicated theme browser spec. Core's multi-mode qualification stays upstream.
Core's multi-mode tests exercise first paint, stored preferences, OS changes, returning to System, no-JavaScript fallback, Turbo history, and native selector suppression. The Compiler also specializes omitted Appearance as fixed light, independently of bookmark artwork.
A selected iPhone shell receives the same typed projection through seven application tasks. Generated Swift and
fixed-theme xcconfig values select light by default, or explicit system/dark appearance. Scalar colors expand to both
modes; paired sRGB AccentColor and FoundationBackground assets supply tint and launch/runtime background. Omitted
tint keeps the empty named accent, and omitted background keeps white/black values independently, so absent or
theme-only Appearance has neutral colors. iOS Core applies them to the launch storyboard, window, Hotwire controllers,
and Web views. Android consumes the same projection through Material light/dark colors and theme selection. For
toggle, both selected native shells use automatic appearance; the reviewed GapSet names the missing native
preference control. Both native launcher icons remain stock Core assets outside this Web icon baseline. Their artwork
remains deferred to Issue #564 (repository-only).
ApplicationAppearance projects the sealed graph without SQL into an immutable value with an authored bit, the
effective auto, light, or dark theme, and typed light and dark modes. Each mode has optional tint and background
colors. A scalar expands to both modes; a pair retains its authored values; and absent Appearance becomes unauthored
light with both modes empty. Explicit auto follows the OS; the default is light in web and native output.
That value contains target inputs, not derived CSS or UIKit representations.
The SQL-free ApplicationAppearance target projection consumes that sealed Project. Its immutable result retains
Project, graph, target, and profile provenance plus an authored bit, one effective theme, and typed light and
dark modes. Each mode carries optional tint_color and background_color values. A scalar authored color expands
to both modes; an authored pair retains its two values; and an omitted theme becomes light. An absent Appearance
projects as authored: false, theme: "light", and two empty modes rather than inventing serialized Plan values.
CompilationInput requires this exact projection to share provenance with its other target results.
The existing Appearance renderer owns each selected file completely. Authored Appearance replaces the theme-mode initializer, theme metadata, and two static error pages. Fixed/automatic modes specialize layout, prepaint, and theme wiring. The Core path registry omits unselected theme files. Appearance emits one selected automatic or toggle browser scenario; fixed modes have no dedicated theme spec. Broad theme qualification stays upstream in Core.
Core retains stock Zinc Basecoat/shadcn tokens independently of authored native colors. Metadata and static pages
select neutral web colors; native assets and fixed black-on-white bookmark artwork remain separate. Omitted theme
means light across web/native; explicit auto follows the operating system and fixed modes stay fixed. Generated
modes ignore persisted browser overrides. The Rails profile owns Appearance
lowering; the generated UI owner describes component composition and continuation.
Page reconciliation
Configure Turbo page refreshes to morph the current document and preserve scroll. This affects page-refresh visits rather than ordinary forward navigation. A future page with explicit live freshness could lower to an authorized connected invalidation followed by a current-URL request. No current Plan requests that behavior; see Screen freshness (repository-only).
Entity partials
The Compiler composes generated record presentations from meaningful application partials. It reuses an Entity's
presentation across its callers: credits/_list_item.html.erb and
actors/_list_item.html.erb represent distinct Entities. Reuse the same presentation across pages and stream
responses. Keep thin page templates and useful record, form, and section partials as building blocks for the
application's owner and agent.
Follow Rails' default record lookup for the main record presentation used on its show page.
Give the compact index presentation its own _list_item partial and reuse it on associated parent pages when the
presentation matches. When a parent needs different fields, links, or actions, put that contextual record partial
under the parent's views. These presentation choices use ordinary Rails APIs; Rails itself does not assign the
default partial to a particular page. Partial names add a descriptive suffix only when a real view-path collision
requires one.
| Partial | Purpose |
|---|---|
credits/_credit.html.erb |
Main Credit presentation used on show, selected by render credit |
credits/_list_item.html.erb |
Compact Credit presentation for index and matching associated collections |
movies/_credit.html.erb |
Movie-specific Credit presentation when the shared list item does not fit |
actors/_credit.html.erb |
Actor-specific Credit presentation when needed |
credits/_form.html.erb |
Shared form when the effective ordered controls match |
credits/_new_movie_form.html.erb |
Movie-associated New when its controls differ from other Credit forms |
credits/_edit_form.html.erb |
Concrete body used by Edit when other Credit forms differ |
Compare controls after omitting the route-bound inverse. Matching contexts can share a partial through ordinary model, parent, URL, and Cancel locals; different controls or order produce separate concrete forms. New and Edit select those partials explicitly and reuse them on failed submission. See Scaffold inputs (repository-only).
Emit only the presentations the Plan needs. Oscar Party's existing Credit rows have two actual presentations:
a Movie's Credits show role and Person, linking to that Person; a Person's Credits show role and Movie, linking to
that Movie. Put these in movies/_credit and people/_credit, reusing each between its preview and full collection
page. That Plan selects no Credit show page, so it needs no unused show presentation. The folder follows the
authored Entity name: people/ in this example, or actors/ for an Actor Entity.
The earlier shared row partial received arrays of captured field values. That preserved consistent styling and ordered properties when the shared UI was introduced, but carried generation structure into everyday ERB. Direct application markup now replaces that transport. The semantic theme, Basecoat composition, and useful shared UI primitives remain; a universal metadata-driven record partial is not the reuse boundary.
Preserve authored projections, field order, permitted destinations, authorization, and preload ownership. If an actual use of an Entity needs a different presentation, give it a purpose-specific partial. Do not silently combine projections or invent variants for hypothetical future differences.
Use native Rails strict locals, such as <%# locals: (credit:) %>, with keyword defaults for real
optional inputs. Pass records and necessary context explicitly; retain the existing policy context. Select the
appropriate native collection call for the shared or contextual presentation:
<%= render partial: "credits/list_item", collection: credits, as: :credit %>
<%= render partial: "movies/credit", collection: credits %>
Both collection calls supply a credit local; _list_item needs as: :credit, while _credit supplies that name
from its basename. The render credit shorthand resolves credits/_credit.html.erb. List rendering and its stream
responses select the list or contextual partial explicitly. Independently replaceable roots use Rails dom_id;
give repeated appearances of the same record distinct, descriptive context IDs when they share one document.
Ordinary Turbo Stream responses can reuse these partials and target those same IDs. This makes
continuation easier without adding automatic broadcasts or changing the separate
Screen freshness direction (repository-only).
Generated rows use the existing item composition and a small container-query grid: properties stack in a narrow card and use up to three columns when its container allows. The same partial can serve a narrow parent section or a full collection page.
Use Tailwind container queries where the layout should respond to the partial's available
width. Choose from the built-in container size scale based on the content, preserving intrinsic wrapping where it
is enough. For example, @container on a list item's root lets descendant @sm: utilities respond at 24rem; viewport
sm: responds at 40rem instead. Check narrow and wide placements at the same viewport width before settling the
breakpoint. Retain the shared theme, semantic markup, keyboard access, and readable long content.
Reuse the same presentation at different widths when its fields, links, and actions still match.
The earlier Rails 8.1.3.1 and Turbo Rails 2.0.23 model/rendering probe confirmed strict-local errors and defaults,
single/collection equivalence, escaping, and stream/root ID agreement. Those library results do not qualify the
combined application's output or browser layout. Issue #709 (repository-only)
retains the implementation and qualification history, including Core coordination and generated UI.md guidance.
Continuing with an agent
The generated AGENTS.md routes UI changes through UI.md's task index and the sections relevant to the change.
The guide names the selected style, shared components, extension points, and checks. Agents should
compare neighboring screens, reuse existing components and presets, and verify changes in a browser. Selection and
packaging of extension and design-review Skills are deferred until this infrastructure is qualified. The earlier
local Skill audition does not settle which Skills will ship.
Upstream shadcn Skills and MCP tools are optional aids for finding or adding a React component. They do not replace the app's Rails form conventions, theme authority, or Turbo lifecycle adapter. Components copied from the registry retain their source/version/license provenance and are reviewed as app-owned code.
Assets and selected widgets
The Compiler writes ordinary static imports and Turbo Mount registrations in
app/javascript/islands/register.ts. DatePicker and ReferencePicker follow inputs in emitted New, associated New,
Edit, and Account update forms. Account registration contributes its own emitted Reference controls; association
registration remains a private qualification path. A date or Reference elsewhere in the Plan does not select a
picker. ConfirmSubmit follows a Delete control on an emitted show page. Navigation and flash toasts retain their
shared consumers.
These decisions reuse the Scaffold renderer's form and show-control selectors. Component source, shared Rails partials, and npm dependencies stay available for continuation. An owner adding the first use of a previously unregistered widget adds its ordinary import and registration. This accepted extra step keeps unused picker code out of the initial JavaScript bundle while preserving the reusable UI kit.
The application's README links to UI.md's Assets section. It owns the application file map, esbuild/Tailwind/
Propshaft workflow, production checks, dependency and copied-source maintenance, and a complete first-DatePicker
example using an ordinary Rails scaffold. The example reuses the existing boundary/controller, translated shared
partial, page headings, form actions, Rails parameters, and native fallback. It replaces the scaffold's stock
navigation and both inline notices, and leaves Delete out of the DatePicker lesson. The no-form starting case
remains complete; an existing suitable form can reuse the registration and field steps. Keep that guidance in the
emitted application so a later agent does not need Compiler documentation to extend it.
Core owns the broad shared-adapter qualification under spec/system/core, spec/requests/core, and
spec/support/core; its ordinary CI still runs those examples with the complete registry. Compilation omits
those directories from applications, though the internal Core archive includes them. Native CSS variants,
touch targets, wrapping, simulated text scaling, and record-header/list galleries share that upstream ownership.
Their fixtures exercise maintained CSS and shared partials; actual generated record markup is qualified against
Compiler output. A self-inserted heading assertion supplies no useful regression and has no replacement.
Generated applications retain real request and baseline browser checks, ordinary flash/forbidden view specs, and
constructible form journeys. The synthetic UI controller, /__ui/*
routes, toast/history, inline-region, and forbidden-page browser scenarios stay upstream. Empty layout regions,
blank-message omission, and a single inline pair are distinct states. View specs prove rendering; actual
application journeys own visible feedback and integration behavior.
#678 (repository-only) owns broader application-test coverage.
The Assets example adds actual application browser scenarios for its new form.
Compiler selector tests and generated-form qualification remain upstream evidence, not tests shipped to owners.
An emitted test should help an owner develop the unfinished application. Demonstrating testing syntax or preserving disposable scaffold presentation does not alone justify one. Browser coverage stays focused on useful application behavior, such as persistence, binding, authorization, and invalid-input recovery. Moving a generic gallery does not require a replacement per Entity, viewport, or theme. Owners can add targeted tests as their actual native and record interfaces evolve. Automatic accessibility auditing remains on retained browser journeys; direct element interactions do not all pass through the audit gem's high-level wrappers.
Native browser simulation also stays upstream; emitted applications keep ordinary local/remote browser setup and native-header requests. Owners configure a driver when adding a native browser journey. The axe retry helper, Turbo synchronization, and auditing remain emitted; the retry-pattern self-tests stay upstream.
Base UI and DayPicker own their primitive keyboard and accessibility contracts. Core qualification covers the Rails/Turbo integration they cannot supply: submitted values, validation, fallback restoration, cache/morph/portal cleanup, and local native presentation adaptations. This keeps missing imports observable without runtime skips or a registry that selects tests from the JavaScript it is meant to check.
Maintenance and scope
Basecoat and shadcn remain separate upstream projects. Matching style names and variables reduce visual drift but do not guarantee it away. Updates compare paired ERB and React controls, supported palettes, keyboard behavior, mobile widths, validation errors, and native controls. The copied-source manifest distinguishes upstream source from local integration changes.
Selected widgets stay eagerly loaded in the existing single bundle. Turbo Mount 0.4.4 resolves registered
components synchronously and its React plugin mounts them with createRoot. Its ordinary registration API is
sufficient here. The pinned esbuild 0.28.1 build uses one entry and an outfile; no splitting or additional loading
lifecycle is introduced. Retained dependencies still install, so this selection does not reduce package-install
work. Production bundle sizes and actual browser loading are separate measurements from browser speed or Codespace
startup. Revisit lazy loading only when a measured startup cost warrants it.
This changes newly compiled Foundations. It does not restyle the First Draft service or automatically rewrite apps that developers already own. Rails Blocks Pro remains restricted to First Draft-owned surfaces.
The decision record (repository-only) preserves the library comparison, adapter boundary, and revisit conditions. The compiled-app qualification report (repository-only) records current observations. The earlier comparison (repository-only) retains its prototype and benchmark results.
Sources: Basecoat customization, shadcn theming, and Turbo Mount.