JavaScript & assets
How Atlas manages Alpine, the Atlas API, appearance and theme switching, and what to know when your application bundles its own Alpine.
Atlas JavaScript does four things: it provides an Alpine instance with the
collapse plugin, registers the Alpine behaviours its components need (accordion,
hamburger, tabs, swiper), exposes its API on window.Atlas, and wires up
appearance (dark/light/system) and theme switching with localStorage persistence.
With Vite
Import Atlas in your application's entry point and start it:
import { Atlas } from '../../vendor/webreact/atlas/dist/atlas.js'
Atlas.start()
That is all an application without its own Alpine instance needs. Atlas imports
Alpine, registers the collapse plugin, wires up its component behaviours, sets
window.Alpine and window.Atlas, and starts Alpine when you call
Atlas.start().
If your application bundles Alpine
Atlas is built to share: it reuses window.Alpine when it finds one, and starts
Alpine only if nobody else has. Two details decide whether that works.
Your bundle must register the collapse plugin Atlas needs, before starting
Alpine. A plugin registered after Alpine.start() never reaches DOM that
Alpine has already processed. Atlas registers collapse when its bundle runs —
if that happens after your Alpine.start(), x-collapse inside an accordion
silently does nothing. Registering it yourself, before you start, fixes it:
import Alpine from 'alpinejs' import collapse from '@alpinejs/collapse' window.Alpine = Alpine Alpine.plugin(collapse) // Register any project-level plugins here, then start. Alpine.start()
Then import Atlas after — it detects the existing window.Alpine, skips starting
it, and registers its own behaviours:
import { Atlas } from '../../vendor/webreact/atlas/dist/atlas.js'
// Atlas sees window.Alpine, so Atlas.start() does not call Alpine.start() again.
Atlas.start()
Load order has to be the same in every environment. Importing Atlas from your own entry point, as above, keeps the order fixed regardless of build mode.
<x-docs.note tone="warning">
This site does both, on purpose. The documentation shell owns Alpine and imports
the package bundle itself; the standalone preview pages use @atlasScripts,
because they have no Alpine of their own. That way both documented paths stay
exercised.
</x-docs.note>
Atlas API
window.Atlas exposes an API for controlling Atlas at runtime:
// Appearance (dark / light / system)
Atlas.appearance = 'dark' // set mode
Atlas.appearance // read current mode
Atlas.dark // true when dark mode is active (read/write)
// Theme switching
Atlas.theme = 'corporate' // switch theme (sets data-theme on <html>)
Atlas.theme // read current theme name
// Lifecycle
Atlas.start() // start Alpine and fire ready callbacks
Atlas.started // true after start() has been called
Atlas.ready(callback) // run when Atlas starts; returns unsubscribe fn
Atlas.refresh(root?) // re-run all ready callbacks (e.g. after navigation)
// Events
Atlas.on('appearance-changed', (detail) => { ... }) // subscribe
Atlas.dispatch('custom-event', { key: 'value' }) // dispatch
// Component registry
Atlas.register('swiper', 'hero', instance)
Atlas.get('swiper', 'hero')
Atlas.unregister('swiper', 'hero')
Alpine magic
Inside any Alpine component, $atlas gives access to the same API:
<div x-data>
<button @click="$atlas.appearance = 'dark'">Dark mode</button>
<span x-text="$atlas.dark ? 'Dark' : 'Light'"></span>
</div>
Appearance
The appearance feature manages dark/light/system mode with three moving parts:
- A reactive
Atlas.appearanceproperty that accepts'light','dark'or'system'. - A media query listener that tracks
prefers-color-schemewhen the mode issystem. - localStorage persistence under
atlas.appearance, so the choice survives page loads.
When the resolved mode is dark, Atlas adds the .dark class and
data-appearance="dark" to <html>. When it is light, both are removed.
The @atlasAppearance directive — placed in <head> — applies the stored mode
before the main bundle loads, preventing a flash of the wrong color scheme. It
reads the appearance value from the published config as the default for first
visits.
Configuration
Set the default in the published config:
// config/atlas.php 'appearance' => 'system', // 'system', 'light', or 'dark'
Theme switching
Atlas supports multiple themes through data-theme on <html>. Each theme is a
CSS file scoped under [data-theme="name"] — see
Scaffolding a theme in the theming guide.
Atlas.theme = 'corporate' // applies [data-theme="corporate"] Atlas.theme = '' // reverts to the base theme
The active theme persists in localStorage under atlas.theme. The
@atlasTheme directive applies the stored theme before the first paint.
Configuration
Set the default theme in the published config:
// config/atlas.php 'theme' => null, // null = base theme; 'corporate' = start with that theme
Livewire
Atlas listens for livewire:navigated and calls Atlas.refresh() automatically,
so components re-initialise after Livewire page navigations without any extra
wiring.
Swiper
The carousel reads its options from data-* attributes that the Blade component
writes, so there is no per-instance JavaScript to author. The runtime initialises
every carousel on the page once the DOM is ready, honours
prefers-reduced-motion, and registers the Pagination, Navigation, A11y and
Keyboard modules. See Swiper for the options.
Without Vite: asset routes
@atlasStyles and @atlasScripts serve files from the package's own dist
directory through Laravel routes:
| Route | Content |
| --- | --- |
| /atlas/atlas.css | Vendor CSS (swiper, hamburgers) |
| /atlas/atlas.js | ES module (development) |
| /atlas/atlas.min.js | ES module (production) |
| /atlas/atlas.csp.js | CSP-safe ES module (development) |
| /atlas/atlas.csp.min.js | CSP-safe ES module (production) |
| /atlas/atlas.umd.js | UMD bundle (classic script tag) |
| /atlas/atlas.umd.min.js | UMD bundle (production) |
| /atlas/atlas.umd.css | Standalone compiled CSS for UMD |
Each URL carries a content hash from the package's manifest, so a package upgrade
busts the cache. Remember that @atlasStyles serves vendor CSS only —
component styling comes from your Tailwind build, as described in
Installation.
Content Security Policy
Both directives accept a nonce:
@atlasStyles(['nonce' => $nonce]) @atlasScripts(['nonce' => $nonce]) @atlasAppearance(['nonce' => $nonce]) @atlasTheme(['nonce' => $nonce])
For Vite builds, import dist/atlas.csp.js instead of dist/atlas.js to get an Alpine build that avoids eval():
import { Atlas } from '../../vendor/webreact/atlas/dist/atlas.csp.js'
Atlas.start()
Or enable CSP-safe mode in the config so @atlasScripts serves the CSP variant
automatically:
// config/atlas.php 'csp_safe' => true,
Multisite and subdirectory installs
Set assets.base_url in the published config — a string or a closure — to
prefix the asset URLs with an absolute origin.