Frontend

Server-rendered BXM views, small Alpine.js components, and a Vite-compiled SCSS/JS pipeline.

On this page

Frontend

How it fits together

The frontend is a hybrid server-rendered + Alpine.js application - no SPA, no client-side router:

1
ColdBox layouts provide the shell

Admin.bxm, AuthSplit.bxm, and friends in app/layouts/ render the HTML frame.

2
BXM templates render server-side

Views in app/views/ render with rc/prc data already resolved by the handler.

3
Alpine.js adds interactivity

Small x-data components handle forms, modals, drawers, and toggles - no build step needed per-component.

4
Vite compiles the assets

SCSS + JS from resources/assets/ compile into public/includes/, served at the ASSET_URL prefix.

Alpine.js architecture

App.js (entry)
  ├── Registers all Alpine stores + components
  ├── Imports Bootstrap JS + Phosphor icons + Tippy.js
  │
  ├── Stores ($store.*)
  │   ├── theme.js     → dark/light mode, syncs data-bs-theme + localStorage
  │   └── sidebar.js   → collapse/open, mobile overlay, localStorage persistence
  │
  └── Components (x-data)
      ├── auth/        → AuthForm, RegisterForm, ForgotPasswordForm, PasswordResetForm
    ├── security/     → AuditLogForm, PermissionsForm, RolesForm, UserDetailForm, UsersForm
    ├── profile/      → PasskeyOnboarding, PreferencesForm, ProfileForm
    ├── settings/     → SettingsForm, SettingsRegistryForm
    └── ui/           → Drawer, GlobalProgress, GlobalToast, Logo, MessageBox, PasswordMeter, PasswordStrength, Switch

Each component is a standalone module returning an Alpine x-data object:

export default () => ( {
    visible: true,
    init() {
        setTimeout( () => this.visible = false, 5000 );
    }
} );
<div x-data="messageBox" x-show="visible" x-transition>
    <!-- alert content -->
</div>

SCSS structure

app.scss
  ├── _variables.scss   Bootstrap variable overrides
  ├── bootstrap          Full Bootstrap 5.3 import
  ├── _base.scss         CSS custom properties (light/dark theme)
  ├── components/        9 component partials
  ├── layouts/            Admin + Auth layout partials
  └── views/              Page-specific styles

Vite configuration

vite.config.mjs uses coldbox-vite-plugin's coldbox() plugin:

  • Entry points: resources/assets/scss/app.scss and resources/assets/js/App.js
  • refresh: appRefreshPaths — auto full-reload on handler/view changes
  • publicDirectory: "public/includes" — where built assets land
  • SCSS preprocessor with silenceDeprecations flags for newer Dart Sass (import, global-builtin, color-functions, if-function)
npm run dev        # Vite dev server with HMR
npm run build      # Production build → public/includes/
npm run lint       # ESLint check on resources/assets/js
npm run lint:fix   # ESLint auto-fix
npm run lint:scss  # Stylelint on resources/assets/scss
ASSET_URL

In production, compiled asset URLs are prefixed with the ASSET_URL environment variable (.env.example defaults it to /includes) - see Configuration.

Server-rendered view components

These BXM partials live under app/views/_components/ and are rendered with ColdBox's view() helper. They are intentionally presentation-focused: pass values through the args struct and keep business logic in handlers or services.

Application shell

PartialPurpose and inputs
_components/app/includesDocument metadata, theme/sidebar FOUC prevention, passkey script, and Vite CSS/JS. Optional title. Include once in <head>.
_components/app/sidebarAdmin navigation, permission-aware Users/Roles/Permissions/Audit Log links, settings submenu, and sidebar footer. Reads prc.authUser; include from Admin.bxm.
_components/app/sidebar-brandApplication logo/name link used by the sidebar.
_components/app/sidebar-footerAuthenticated user summary and profile/sign-out actions used by the sidebar.
_components/app/topbarSidebar toggle, theme toggle, breadcrumbs, user menu, and sign-out action. Reads prc.authUser and prc.title.
_components/app/topbar-breadcrumbsDashboard breadcrumb rendered inside the topbar. Extend when adding deeper navigation.
_components/app/topbar-notificationsTopbar notification slot/component for application notifications.
_components/app/footerCopyright and footer links. Optional classes. Reads prc.settings.cbCopyrightNotice.

Authentication partials

PartialPurpose and inputs
_components/auth/footerFooter used by authentication layouts.
_components/auth/passwordInputReusable password field with visibility toggle and password-strength affordances.

UI partials

PartialPurpose and inputs
_components/ui/modalGeneric Alpine dialog that renders an optional nested view. Required id should be unique; supports title, openExpression, closeExpression, contentView, and contentArgs.
_components/ui/drawerRight-side focus-trapped dialog with backdrop/Escape closing and optional contentView/contentArgs; also initializes drawer().
_components/ui/confirmConfirmation dialog with static or Alpine-bound message, confirm/cancel expressions, labels, icon, button class, and disabled expression.
_components/ui/messageboxDismissible info/success/warning/error alert. Supports static message/title or dynamic messageExpression/typeExpression/dismissAction, plus autoDismiss and classes.
_components/ui/globalProgressGlobal accessible progress bar. Include once per layout; controlled by $progress.start()`, `$progress.set(), and $progress.stop().
_components/ui/globalToastGlobal toast stack. Include once per layout; accepts duration, position, and maxVisible, and receives notifications from $toast().
_components/ui/avatarRenders a user's avatar image when hasAvatar is true, falling back to initials otherwise. Read-only display used by the sidebar, topbar, Users listing, and Users detail page — see Avatars & branding logo.
_components/ui/logoReusable application logo/branding partial.
_components/ui/passwordMeterPassword policy meter used beside password fields.
_components/ui/progressbarInline progress bar partial for a local numeric value.
_components/ui/switchAccessible switch control partial for boolean settings.

Alpine components and stores

resources/assets/js/App.js registers the following names globally with Alpine. Use them as x-data="name" or x-data="name(...)" in BXM views. Form components make remote requests to the matching handler routes and expect the CSRF token supplied by their view.

Application shell and authentication

Alpine nameSourceResponsibility
adminBodycomponents/app/AdminBody.jsAdmin page shell behavior and global layout events.
sidebarBrandcomponents/app/SidebarBrand.jsSidebar brand interactions.
footercomponents/app/Footer.jsFooter state and current-year behavior.
authFormcomponents/auth/AuthForm.jsLogin submission, validation, remember-me, and errors.
registerFormcomponents/auth/RegisterForm.jsRegistration validation, email availability, and submission.
forgotPasswordFormcomponents/auth/ForgotPasswordForm.jsForgot-password request state and feedback.
passwordResetFormcomponents/auth/PasswordResetForm.jsPassword reset token submission and validation.

Admin and profile forms

Alpine nameSourceResponsibility
usersFormcomponents/security/UsersForm.jsUser listing, search, pagination, invitation, status, and admin actions.
userDetailFormcomponents/security/UserDetailForm.jsUser profile, role, permission, preference, token, and verification actions.
rolesFormcomponents/security/RolesForm.jsRole CRUD and assigning/removing users and permissions.
permissionsFormcomponents/security/PermissionsForm.jsPermission listing and CRUD operations.
auditLogFormcomponents/security/AuditLogForm.jsAudit filtering, pagination, detail drawer, CSV export, purge, and clear actions.
settingsFormcomponents/settings/SettingsForm.jsCore application settings editing and cache-related feedback.
logoUploadercomponents/settings/LogoUploader.jsBranding logo upload/remove for the "App Logo Path" field, alongside its existing manual URL input and live preview — see Avatars & branding logo.
settingsRegistryFormcomponents/settings/SettingsRegistryForm.jsRegistry search, pagination, create/update, enable/disable, and delete actions.
profileFormcomponents/profile/ProfileForm.jsProfile fields, password policy, API token management, the email-change request/cancel sub-form, and avatar upload/remove.
preferencesFormcomponents/profile/PreferencesForm.jsPersisting user preferences.
passkeyOnboardingcomponents/profile/PasskeyOnboarding.jsPasskey registration and required-passkey onboarding.

UI components and global APIs

Alpine nameSourceResponsibility
messageBoxcomponents/ui/MessageBox.jsAlert visibility and optional timed dismissal.
passwordMetercomponents/ui/PasswordMeter.jsPassword requirement and strength display.
passwordStrengthcomponents/ui/PasswordStrength.jsPassword strength calculation and labels.
switchComponentcomponents/ui/Switch.jsToggle state and change handling.
drawercomponents/ui/Drawer.jsDrawer lifecycle and focus behavior.
globalProgresscomponents/ui/GlobalProgress.jsProgress events and current progress value.
globalToastcomponents/ui/GlobalToast.jsToast queue, dismissal, type mapping, and stack limits.

The source also contains Header.js, Sidebar.js, TopBarNotifications.js, and Logo.js. Their exports are available for local imports, but they are not currently registered by App.js; register them with Alpine.data() before using them as global x-data components.

Stores, utilities, and magic properties

APISourceUsage
$store.themestores/theme.jsLight/dark mode, data-bs-theme, and localStorage persistence.
$store.sidebarstores/sidebar.jsDesktop collapse, mobile open/close, and localStorage persistence.
$formatDate`, `$formatDateTime, $relativeDateutils/dateFormat.jsConsistent date display with fallbacks.
$countLabelutils/countLabel.jsSingular/plural count labels.
$sortClass`, `$sortIconutils/sort.jsSortable table headers and indicators.
$passwordMeetsPolicyutils/passwordPolicy.jsChecks the configured password requirements.
$isEmailApp.jsLightweight email-format check.
$toast` / `$progresscomponents/ui/GlobalToast.js, GlobalProgress.jsGlobal notification and progress APIs.
$focus` / `$copyApp.jsFocus a descendant after Alpine updates; copy text through the browser clipboard API.
createRemoteListing()utils/listing.jsShared remote listing state, loading, pagination, and error handling.

AlpinePlugins.js installs Collapse, Focus, Mask, and Persist. passkeys.js provides the browser-side WebAuthn integration. Keep new reusable browser APIs documented here and add their registration/import to App.js when they are global.

User avatars and the application branding logo are stored on the private cbfs assets disk (see Configuration) and streamed out by Assets.bx (see Handlers & Routing) rather than served as static files.

The Profile page, showing the avatar upload and assigned role

  • Display goes through the _components/ui/avatar partial: it renders <img src="/avatars/:userId/:size"> when hasAvatar is true, and falls back to an initials <span> otherwise. It is wired into the sidebar, topbar, and Users listing table (server-projected hasAvatar field), and inline in the Users detail page (x-show/x-cloak toggling on user.hasAvatar, since that page's avatar sits inside an Alpine-driven summary card rather than a static partial).
  • Upload/remove for the current user's own avatar lives on the Profile page, owned by profileForm (ProfileForm.js): a hidden file input reads the selected image as a base64 data URI (readFileAsDataUrl()) and posts it to POST /profile/avatar; DELETE /profile/avatar removes it. Both bump a version counter used as a cache-busting query param on the streamed URL, since the file path itself does not change between uploads.
  • The branding logo gets the same upload/remove treatment on the Settings page, via the logoUploader component (LogoUploader.js) against POST/DELETE /settings/logo. It replaces the cbAppLogo setting's text input value with the streamed path (/branding/logo/lg) on upload, and restores the configured default on removal — the manual URL text input and live <img> preview keep working exactly as before for anyone who wants to point cbAppLogo at an external URL instead.
  • Both upload endpoints accept the same shapes: images are decoded server-side with BaseSecureHandler.decodeDataUri(), then resized/cropped into sm/lg JPEG (avatar) or PNG (logo) variants by ImageService (app/models/system/ImageService.bx).
Edit this page Download Markdown Last updated Sep 16, 2026, 5:50:56 PM