Releases: wheels-dev/wheels-basecoat
v3.0.0 — the killer-feature release
What's new
The v3.0 release positions wheels-basecoat as the killer feature of Wheels 4.0 — every common UI pattern has a one-line helper, every form has a Wheels-bound variant, every AI coding assistant can pick up the package and write idiomatic code without trial-and-error.
From "I have a Wheels app" to a polished UI
wheels packages add wheels-basecoat
cp -r vendor/wheels-basecoat/assets/basecoat public/assets/basecoatThen in your view:
#uiErrorSummary(post)#
#startFormTag(action="create")#
#uiBoundField(objectName="post", property="title", required=true)#
#uiBoundField(objectName="post", property="body", type="textarea", rows=10)#
#uiBoundField(objectName="post", property="status", type="select",
options="draft:Draft,published:Published,archived:Archived")#
#uiBoundCheckbox(objectName="post", property="featured", switch=true)#
#uiButton(text="Publish", type="submit", icon="check")#
#endFormTag()#Six lines for a fully styled, model-bound, error-aware, Turbo-friendly form. Every helper renders shadcn/ui-quality basecoat-css 0.3.x markup, and they're all CSP-safe.
Highlights
AI / dev experience (the meta-killer-feature)
The package now ships a comprehensive CLAUDE.md + .ai/ documentation bundle that AI coding assistants read first. The result: an AI assistant working in any wheels-basecoat app can find the right helper, write idiomatic code, and avoid the known footguns on the first try.
CLAUDE.md— read order, package architecture, helper inventory, the 14 coding rules every generated CFML must follow..ai/HELPERS.md— formal signature reference for every public helper..ai/EXAMPLES.md— scenario-driven recipes (layout, posts CRUD, login/signup, settings, wizard, dashboard, ⌘K palette, Turbo Stream delete, resource scaffold)..ai/SCAFFOLDS.md— copy-paste templates for every common page type..ai/PATTERNS.md— when-to-use decision trees..ai/PITFALLS.md— 14 known footguns with symptom + cause + fix..ai/ARCHITECTURE.md— design rationale, naming, the PackageLoader mixin gotcha, CSP safety, Hotwire integration, version history.examples/showcase/— drop-in mountable controller + view that gives every Wheels app a/basecoat-showcaseURL with every helper rendered live.INSTALL.md— three-step install guide.
Form binding round-trip is complete
Every common input type has a Wheels-bound helper that auto-resolves value, name, error, and label from the model:
| Input type | Helper |
|---|---|
| Text / textarea / select / date / etc. | uiBoundField |
| Rich combobox (search, multi-select) | uiBoundSelect |
| Range slider | uiBoundSlider |
| Single boolean | uiBoundCheckbox (with hidden falsy companion for unchecked submissions) |
| Multi-checkbox collection | uiBoundCheckboxGroup |
| Single-choice radio | uiBoundRadioGroup |
| File upload | uiBoundFile (drag-and-drop) |
| Tag chips | uiBoundTagInput |
| Model-level error rollup | uiErrorSummary(model) |
New helpers in v3.0
uiCallout— boxed inline note (info / tip / warning / success).uiEmptyState— centered zero-data placeholder with optional CTA.uiAccordionfamily — CSS-only collapsible sections.uiTimelinefamily — vertical timeline with markers.uiCodeBlock— styled code display with copy-to-clipboard.uiTagInput+uiBoundTagInput— multi-value chip entry with autocomplete.uiFileUpload+uiBoundFile— drag-and-drop file zone.uiDatePicker— basecoat-styled date input with Lucee-quoted-datetime coercion.uiPaginationFor(query)— pagination from a Wheels paginated query.uiResourceTable(query, columns)— auto-build a table from a Wheels query.uiResourceForm(model)— auto-build a Wheels-bound form from a model's properties.
Plus extras CSS for every new component, JS shim handlers, 4 new Lucide icons, comprehensive BasecoatV30Spec.cfc.
Cumulative since v2.0
If you're upgrading from v1.x, v3.0 also brings everything that landed in v2.0 → v2.4:
- v2.0 — bundled basecoat-css 0.3.11 + basecoat-js + CSP-safe shim,
uiBoundField, toasts (basecoatFlashToastsreads Wheels'flash()), popover, avatar, kbd, button group, fieldset, dark mode (uiThemeToggle+ pre-paint script), Turbo Stream helpers (turboStream/turboStreamHeader), argument validation throwingWheelsBasecoat.InvalidArgument. - v2.1 — Tabs / Dropdown / Sidebar reworked to ARIA roles + basecoat-js contracts.
- v2.2 — extras CSS for breadcrumb / pagination / steps / rating / radio,
uiCommandfamily +uiCommandDialog(⌘K palette),uiSelectrich combobox. - v2.3 —
uiSlider+uiBoundSlider,uiStepswizard family,uiBoundSelect. - v2.4 —
uiBoundCheckbox(with falsy companion),uiBoundCheckboxGroup,uiBoundRadioGroup,uiErrorSummary,uiRating.
See CHANGELOG.md for the full per-version detail.
Installation
wheels packages add wheels-basecoat
cp -r vendor/wheels-basecoat/assets/basecoat public/assets/basecoat
wheels reloadThen in app/views/layout.cfm:
<head>
<cfoutput>
#basecoatThemeScript()#
#csrfMetaTags()#
#basecoatIncludes()#
</cfoutput>
</head>
<body>
...
<cfoutput>#basecoatFlashToasts()#</cfoutput>
</body>See INSTALL.md for the full guide and examples/showcase/README.md for the optional live showcase mount.
Compatibility
- Wheels 4.0+
- Lucee 7.0+ or Adobe ColdFusion 2021+
- basecoat-css 0.3.11 (bundled, pinned)
Migration from v1.x
The package API is mostly source-compatible across v1 → v3, but bundled-asset publishing is new and the helper output markup has been brought up to basecoat-css 0.3.x's semantic structure (cards now emit <header>/<section>/<footer>, alerts are flat children, tabs use ARIA roles, dropdown uses popover JS, sidebar uses semantic nav).
Apps that wrote custom CSS targeting the old class names (.card-header, .card-content, .card-footer, .tabs-list, .tabs-trigger, .sidebar-section) will need to re-target the semantic / ARIA selectors that basecoat-css 0.3.x uses.
The form-binding family (uiBound*) is purely additive — apps using uiField continue to work unchanged.
Acknowledgements
Built primarily during a single Wheels Tutorial fresh-VM bake session. The session surfaced the 14 footguns now documented in .ai/PITFALLS.md, drove the Wheels integration story (form binding, flash toasts, model introspection), and produced the showcase + AI/dev docs bundle that makes the package as approachable for AI assistants as it is for developers.
v1.0.4
wheels-basecoat 1.0.3
Fixed
Switch $uiBuildId and $uiLucideIcon from private → public on Basecoat.cfc.
Wheels' PackageLoader.$collectMixins integrates only PUBLIC methods of the package CFC into the target scope (controllers in our case). When the public sibling helpers — uiField, uiInput, uiCheckbox, uiButton(icon=...), uiAlert, uiPagination, etc. — invoked the $-prefixed helpers from the mixed-in scope, those calls had no resolution path and failed at runtime with:
No matching function [$UIBUILDID] found
(and the symmetrical [$UILUCIDEICON] variant from icon-rendering helpers). Hitting any page that rendered a form via uiField therefore 500'd on a fresh install of 1.0.2.
The leading $ keeps signalling "internal — don't call from app code"; the access-modifier change just lets PackageLoader carry the helpers across the mixin boundary alongside the public callers that depend on them.
Note on the 1.0.2 changelog
The 1.0.2 changelog asserted that Lucee 7 was rejecting the mixin="controller" component-level attribute outright, blocking compilation. That diagnosis was incorrect — verified post-release on Lucee 7.0.0.395, the package compiles fine and PackageLoader loads it successfully with the attribute in place. The actual blocker preventing helper rendering was always this private-helper visibility issue, fixed in 1.0.3.
Source
Discovered during a Wheels Tutorial fresh-VM bake; verified end-to-end on Lucee 7.0.0.395 — wheels packages add wheels-basecoat activates cleanly, form renders via uiField, /posts/1 returns 200 instead of the previous 500.
Install
Once wheels-dev/wheels-packages registry update merges and the mirror-tarball workflow completes:
wheels packages add wheels-basecoat
Existing 1.0.2 installs can upgrade with:
wheels packages update wheels-basecoat --yes
wheels stop && wheels start
(Note: wheels reload does not re-run onApplicationStart, so PackageLoader doesn't re-integrate the new public methods — a cold restart is required.)
wheels-basecoat 1.0.2
Fixed
Drop view from the component-level mixin attribute on Basecoat.cfc. Lucee 7 enforces native trait composition on component mixin="..." and tries to load each comma-separated value as a CFML component path; there is no view.cfc on the path, so the whole component failed to compile with a misleading can't find component [vendor.wheels-basecoat.Basecoat] error.
Net effect on Lucee 7: every wheels packages add wheels-basecoat install resulted in a successful extract but no helper activation. Helpers like #uiButton(...)#, #uiCard(...)#, #uiField(...)# all returned function not found. After this fix the package activates cleanly.
The package.json's provides.mixins: "controller" field remains the actual source of truth — the component-level attribute was a legacy convention obsolete on Lucee 7. Lucee 5/6 don't enforce native mixin composition the same way, which is why this went undetected until Wheels 4.0 made Lucee 7 the default.
Source
Install
Once wheels-dev/wheels-packages#8 (registry update) merges and the mirror-tarball workflow completes:
wheels packages add wheels-basecoat
wheels-basecoat 1.0.1
Documentation patch release. Clarifies controller-scope mixin inheritance in views. (See #1.)
wheels-basecoat v1.0.0
Initial standalone release, extracted from the Wheels monorepo at packages/basecoat with preserved git history.
Published to the wheels-dev/wheels-packages registry for installation via wheels packages install wheels-basecoat (coming in Wheels 4.1).
See CHANGELOG.md for details.