A Wheels package that ships CFML view helpers for Basecoat UI — shadcn/ui-quality components rendered as plain HTML + Tailwind CSS classes. No React, no build step. Works with or without wheels-hotwire.
- Wheels 3.0+
- Lucee 5+ or Adobe ColdFusion 2018+
- Basecoat CSS (served from your app — see Serving Basecoat CSS below)
# Activate the package
cp -r packages/basecoat vendor/basecoat
# Restart or reload your app
wheels reloadAll ui* helpers become available in views and controllers via the package mixin system.
Basecoat has no set()-style application settings. Configuration is passed to helpers directly — the only helper with knobs is basecoatIncludes():
#basecoatIncludes(
alpine = true,
alpineVersion = "3",
basecoatCSSPath = "/plugins/basecoat/assets/basecoat/basecoat.min.css",
turboAware = true
)#| Argument | Default | Description |
|---|---|---|
alpine |
true |
Include the Alpine.js CDN script (needed for dropdowns, dialogs with transitions, tabs). |
alpineVersion |
"3" |
Alpine.js major version. |
basecoatCSSPath |
"/plugins/basecoat/assets/basecoat/basecoat.min.css" |
Path to the Basecoat CSS file your app serves. |
turboAware |
true |
Emit the <meta name="turbo-cache-control" content="no-preview"> tag — safe default when paired with wheels-hotwire. |
This package ships helpers, not CSS assets. Grab basecoat.min.css from the Basecoat UI releases and serve it from your app — for example under public/assets/basecoat.min.css — then point basecoatCSSPath at it.
<!-- app/views/layout.cfm -->
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
#basecoatIncludes(basecoatCSSPath="/assets/basecoat.min.css")#
</head>
<body>
#includeContent()#
</body>
</html><!-- Buttons -->
#uiButton(text="Save", variant="primary")#
#uiButton(text="Cancel", variant="outline", href="/users")#
#uiButton(text="Delete", variant="destructive", turboConfirm="Are you sure?")#
<!-- Badges -->
#uiBadge(text="Active", variant="default")#
#uiBadge(text="Archived", variant="secondary")#
<!-- Alert -->
#uiAlert(title="Heads up", description="Your trial expires in 3 days.", variant="default")#
<!-- Card (block component — remember uiCardEnd()) -->
#uiCard()#
#uiCardHeader(title="Order ##1234", description="Placed 2 days ago")#
#uiCardContent()#
<p>Order details go here.</p>
#uiCardContentEnd()#
#uiCardFooter()#
#uiButton(text="View", href="/orders/1234")#
#uiCardFooterEnd()#
#uiCardEnd()#
<!-- Form field (handles label + input + error) -->
#uiField(label="Email", name="user[email]", type="email", required=true)#
#uiField(label="Bio", name="user[bio]", type="textarea", rows=4)#
#uiField(label="Role", name="user[role]", type="select", options="admin:Admin,user:User")#All helpers live on Basecoat.cfc and are injected into the controller scope via the package mixin system; they surface in views because Wheels views execute in the controller's variables scope.
| Category | Helpers |
|---|---|
| Includes | basecoatIncludes |
| Buttons & icons | uiButton, uiBadge, uiIcon, uiSpinner, uiSkeleton, uiProgress, uiSeparator |
| Tooltip | uiTooltip / uiTooltipEnd |
| Alert | uiAlert |
| Card | uiCard / uiCardHeader / uiCardContent / uiCardContentEnd / uiCardFooter / uiCardFooterEnd / uiCardEnd |
| Dialog | uiDialog / uiDialogFooter / uiDialogEnd |
| Forms | uiField (text, email, textarea, select, checkbox, switch, …) |
| Table | uiTable / uiTableHeader / uiTableBody / uiTableRow / uiTableHead / uiTableCell + matching *End helpers |
| Tabs | uiTabs / uiTabList / uiTabTrigger / uiTabContent + matching *End helpers |
| Dropdown | uiDropdown / uiDropdownItem / uiDropdownSeparator / uiDropdownEnd |
| Pagination | uiPagination |
| Layout | uiBreadcrumb / uiBreadcrumbItem / uiBreadcrumbSeparator / uiBreadcrumbEnd, uiSidebar / uiSidebarSection / uiSidebarItem + matching *End helpers |
Basecoat does not depend on wheels-hotwire, but its helpers are Turbo-friendly:
uiButton()acceptsturboConfirmandturboMethod— emitted asdata-turbo-confirm/data-turbo-methodattributes and ignored when Turbo is absent.uiButton(close=true)closes the enclosing<dialog>via the nativeHTMLDialogElement.close()API — no JS library required.- All block components generate markup that renders correctly inside
<turbo-frame>elements.
rm -rf vendor/basecoat
wheels reloadpackages/basecoat/CLAUDE.md— markup reference, naming conventions, component categoriespackages/basecoat/.ai/ARCHITECTURE.md— design principles, phased component inventory- Basecoat UI — upstream CSS component library
MIT