bzr-dial.ui Docs
A physics-based radial dial menu web component. Zero dependencies. 60 FPS. Shadow DOM encapsulated. Built by Web Works Systems.
rocket_launch Quick Start
<script src="/demo/bzr-dial-menu.js"></script>
<bzr-dial-menu>
<bzr-item label="Home" icon="/icons/home.svg" href="#home"></bzr-item>
<bzr-item label="Settings" icon="/icons/settings.svg" href="#settings"></bzr-item>
<bzr-item label="Profile" icon="/icons/profile.svg" href="#profile"></bzr-item>
</bzr-dial-menu>
No build step. No npm install. No framework dependency. The component registers two custom elements: <bzr-dial-menu> and <bzr-item>.
download Installation
CDN (Demo / Unlicensed)
<script src="https://czarui.game4real.us/demo/bzr-dial-menu.js"></script>
Includes a visible "UNLICENSED" watermark badge. For evaluation only.
Licensed Download
<script src="/component/bzr-dial-menu.js"></script>
Requires a valid license key. Purchase at bzzrr.link.
Self-Hosted
# Download and serve from your own domain
<script src="/your-path/bzr-dial-menu.js"></script>
# Or use as an ES module (future — see Coming Soon section)
tune <bzr-dial-menu> Attributes
| Attribute | Type | Default | Status | Description |
|---|---|---|---|---|
| radius | Number | 120 | Active | Radius of the dial circle in pixels |
| justify | String | right | Active | FAB position: left or right |
| top | String | 50% | Active | Vertical position of FAB (e.g. 200px) |
| bottom | String | — | Active | Bottom position override (mutually exclusive with top) |
| anchor | String | auto | Active | Active slot anchor: left, right, top, bottom. Auto-derives from justify. |
| license | String | — | Active | License key (format: BZRD-XXXX-XXXX-XXXX-XXXX). Removes watermark. |
| demo | Flag | — | Active | Marks component as demo (used on landing page) |
| snap | String | auto | Planned | Observed but not yet handled. Currently auto-calculated as 2π/item count. |
| sensitivity | String | — | Planned | Drag sensitivity control. Reserved for future use. |
widgets <bzr-item> Attributes
| Attribute | Type | Required | Status | Description |
|---|---|---|---|---|
| label | String | Yes | Active | Display name shown when item is active |
| icon | URL | No | Active | Path or URL to icon image (SVG, PNG). Shows placeholder if absent. |
| href | URL | No | Active | Navigation URL — triggered when active item is clicked |
| active | Flag | No | Active | Set automatically by the dial when an item reaches the active slot |
perm_media Inline Content Attributes
Display rich media and interactive content in a modal overlay instead of navigating away. Content attributes take priority over href.
data-audio
Audio player with WebAudio visualizer (80 radial bars, FFT 256) and custom media controls.
<bzr-item label="Podcast" icon="music.svg"
data-audio="episode.mp3"
data-autoplay>
</bzr-item>
data-video
Video player with canvas frame-copy render loop and aspect-ratio-aware fitting.
<bzr-item label="Tutorial" icon="video.svg"
data-video="tutorial.mp4">
</bzr-item>
data-image
Image viewer with contain-fit and max-height 70vh.
<bzr-item label="Gallery" icon="gallery.svg"
data-image="photo.png">
</bzr-item>
data-email
Contact form (to/subject/message) that opens a mailto: link on submit.
<bzr-item label="Contact" icon="email.svg"
data-email="hello@example.com">
</bzr-item>
data-phone
Phone card with large number display and tel: call button.
<bzr-item label="Call Us" icon="phone.svg"
data-phone="+1-555-123-4567">
</bzr-item>
data-map
OpenStreetMap embed via Leaflet + Nominatim geocoding. Lazily loads Leaflet CSS/JS.
<bzr-item label="Location" icon="map.svg"
data-map="Eiffel Tower, Paris">
</bzr-item>
data-iframe
Embed any external website in an iframe overlay.
<bzr-item label="Docs" icon="web.svg"
data-iframe="https://docs.example.com">
</bzr-item>
data-autoplay
Optional flag — auto-plays audio/video when the content modal opens. Works with data-audio and data-video only.
palette CSS Custom Properties
Override these CSS variables on the bzr-dial-menu host element to customize the theme.
bzr-dial-menu {
--primary: #2bee8c; /* Primary color: active items, glow, FAB */
--bg: #111; /* Background color */
--text: #fff; /* Text color */
--trigger-size: 80px; /* FAB button diameter */
--trigger-inset: 20px; /* FAB inset from viewport edge */
}
Custom Theme Example
<style>
#custom-dial {
--primary: #ff6b6b; /* Red theme */
--bg: #2c3e50;
--text: #ecf0f1;
--trigger-size: 60px; /* Smaller FAB */
}
</style>
<bzr-dial-menu id="custom-dial" radius="150" justify="right">
<div slot="trigger-content" style="font-size:28px; font-weight:bold;">⚡</div>
<bzr-item label="Dashboard" icon="dashboard.svg" href="/dashboard"></bzr-item>
<bzr-item label="Analytics" icon="chart.svg" href="/analytics"></bzr-item>
</bzr-dial-menu>
bolt Events
bzr-change
Active
Fired when the active item changes (an item rotates into the active slot).
{
index: Number, // Index of the active item (0-based)
item: HTMLElement // The bzr-item element
}
const dial = document.querySelector('bzr-dial-menu');
dial.addEventListener('bzr-change', (event) => {
const { index, item } = event.detail;
const label = item.getAttribute('label');
console.log(`Active item: ${label} (index: ${index})`);
// Track analytics
analytics.track('dial_item_selected', { item: label });
});
code JavaScript API
Properties
| Property | Type | Description |
|---|---|---|
| dial.isOpen | Boolean | Is the dial currently open? |
| dial.rotation | Number | Current rotation in radians |
| dial.activeIndex | Number | Index of active item (0-based) |
| dial.items | Array | Array of all slotted bzr-item elements |
| dial.radius | Number | Dial radius in pixels |
Methods
const dial = document.querySelector('bzr-dial-menu');
// Toggle open/close
dial.toggle();
// Programmatically open/close
dial.isOpen = true; // Open
dial.isOpen = false; // Close
Dynamic Item Management
// Add items dynamically
const newItem = document.createElement('bzr-item');
newItem.setAttribute('label', 'New Item');
newItem.setAttribute('icon', 'new-icon.svg');
newItem.setAttribute('href', '#new');
dial.appendChild(newItem);
// Remove items
const items = dial.querySelectorAll('bzr-item');
items[2].remove(); // Remove third item
// Update item attributes
const item = dial.querySelector('bzr-item[label="Home"]');
item.setAttribute('label', 'Dashboard');
item.setAttribute('icon', 'dashboard.svg');
The slotchange event auto-triggers updateItems() when children change.
Custom Trigger Content
Use the trigger-content slot to customize the FAB button:
<bzr-dial-menu>
<!-- Custom trigger -->
<div slot="trigger-content" style="font-size:24px;">☰</div>
<!-- Items -->
<bzr-item label="Home" icon="home.svg"></bzr-item>
</bzr-dial-menu>
touch_app Interaction Guide
Single click the FAB button to toggle the dial open/closed.
Drag anywhere on the overlay — outer ring, icons, or inner ring area. Rotation pivots around the FAB center.
Swipe up/down on any icon to snap to the next/previous item. 10px threshold locks the direction.
Click the active (highlighted) item to trigger its action — navigate to href or open inline content.
Double-click the FAB to enable slide mode. Drag vertically to reposition the FAB along the viewport edge. Click backdrop to exit.
Vibration API feedback on snap, slide-mode toggle, and item activation (mobile only).
Physics Model
Inertial scrolling with velocity decay, spring-snapping to slot positions, and a target-rotation lerp for programmatic navigation. The rAF loop runs continuously at 60 FPS.
collections Examples
Navigation Menu
<bzr-dial-menu justify="right">
<div slot="trigger-content">☰</div>
<bzr-item label="Home" icon="home.svg" href="/"></bzr-item>
<bzr-item label="About" icon="info.svg" href="/about"></bzr-item>
<bzr-item label="Services" icon="services.svg" href="/services"></bzr-item>
<bzr-item label="Contact" icon="contact.svg" href="/contact"></bzr-item>
</bzr-dial-menu>
App Actions with Event Handling
<bzr-dial-menu radius="150" justify="left">
<div slot="trigger-content">+</div>
<bzr-item label="New Post" icon="edit.svg"></bzr-item>
<bzr-item label="Upload Photo" icon="camera.svg"></bzr-item>
<bzr-item label="Create Event" icon="calendar.svg"></bzr-item>
<bzr-item label="Start Chat" icon="message.svg"></bzr-item>
</bzr-dial-menu>
<script>
const dial = document.querySelector('bzr-dial-menu');
dial.addEventListener('bzr-change', (e) => {
const label = e.detail.item.getAttribute('label');
switch(label) {
case 'New Post': openPostEditor(); break;
case 'Upload Photo': openPhotoUploader(); break;
}
});
</script>
Media Showcase
<bzr-dial-menu justify="right" radius="140">
<bzr-item label="Music" icon="music.svg"
data-audio="track.mp3" data-autoplay></bzr-item>
<bzr-item label="Video" icon="video.svg"
data-video="demo.mp4"></bzr-item>
<bzr-item label="Gallery" icon="image.svg"
data-image="photo.jpg"></bzr-item>
<bzr-item label="Map" icon="map.svg"
data-map="350 Fifth Avenue, New York"></bzr-item>
</bzr-dial-menu>
Using Iconify Icons
<bzr-dial-menu>
<bzr-item label="Home"
icon="https://api.iconify.design/mdi:home.svg?color=%23ffffff"></bzr-item>
<bzr-item label="Search"
icon="https://api.iconify.design/mdi:magnify.svg?color=%23ffffff"></bzr-item>
<bzr-item label="Settings"
icon="https://api.iconify.design/mdi:cog.svg?color=%23ffffff"></bzr-item>
</bzr-dial-menu>
Coming Soon
The following features are planned for future releases. Attributes may be observed but not yet functional, or methods are stubbed with TODOs. These will be activated in upcoming versions.
snap
Custom snap angle control. Currently auto-calculated as 2π/item count. Will allow manual override (e.g. 30deg) for half-dial and custom layouts.
sensitivity
Adjustable drag sensitivity multiplier. Will control how much physical drag translates to rotational movement. Currently fixed at 1:1 with a 5px movement threshold.
Snap Feedback Audio
Sophisticated tick/haptic feedback on snap. The checkSnapFeedback() method exists as a TODO stub — will produce audio ticks and refined vibration patterns when crossing slot boundaries.
Inner Jog Dial
Secondary inner-ring jog dial for fine-grained navigation. The drawInnerControls() method is reserved — currently empty to keep the icon path clean.
Keyboard & A11y
Focus management, keyboard navigation (arrow keys for rotation, Enter to select), ARIA roles, and focus trapping within the dial overlay. Not yet implemented.
Server License Check
Server-side license validation. Currently format-only regex check (BZRD-XXXX-XXXX-XXXX-XXXX). Will validate against the Web Works Systems license server.
Active Label Overlay
The #active-label element exists in the template and CSS but the label text is not yet populated at runtime. Will display the active item's label prominently at the bottom of the viewport.
Fullscreen Media Mode
CSS class fullscreen-mode is defined for the content container but not yet applied by JS. Will allow video/image content to expand to full viewport.
data-pdf
Inline PDF viewer attribute. Will render PDFs in the content overlay modal with page navigation controls.
Image Gallery
Lightbox-style image gallery mode. Will support multiple images with swipe navigation, zoom, and captions.
Calendar / Date Picker
Inline calendar/date picker interface accessible from a dial item. Will integrate with form workflows.
Form Builder
Declarative form configuration via data attributes. Will render custom forms in the content overlay with field types, validation, and submission.
public Browser Support
Requirements
- check Custom Elements v1 (Web Components)
- check Shadow DOM
- check ES6 JavaScript
- check Canvas 2D API
- check Pointer / Touch Events
build Troubleshooting
Icons not showing?
- Verify icon URLs resolve (check Network tab for 404s)
- Ensure icon files have correct permissions (644, www-data readable)
- Check CORS headers if loading icons from a different domain
- Use 40×40px SVG icons for optimal rendering
Dial not rotating correctly?
- Ensure the
justifyattribute matches your layout direction - Check that the FAB has enough viewport space — don't trap it behind other fixed elements
- The rotation pivot is the FAB center, not the screen center
Component not mounting?
- Ensure
bzr-dial-menu.jsloads before injecting<bzr-dial-menu>DOM - Use
customElements.whenDefined('bzr-dial-menu')before DOM injection - Check browser console for registration errors
Audio visualizer not working?
- WebAudio
createMediaElementSourcerequires same-origin audio files or proper CORS headers - Set
crossOrigin="anonymous"on cross-origin audio (already done by component)
Performance issues?
- Keep item count under 10 (4–8 is optimal)
- Use optimized SVG icons (under 1KB each)
- The rAF loop runs continuously — ensure the page isn't running heavy JS elsewhere
history Changelog
Initial MVP Release
- Physics-based radial dial with inertial scrolling and spring snapping
- Shadow DOM encapsulated — zero CSS leakage
- Canvas-rendered rail with velocity-warp arc visualization
- Inline content modal system: audio (with visualizer), video, image, email, phone, map, iframe
- Slide-to-reposition FAB via double-click
- Icon swipe-to-snap quick navigation
- Custom
bzr-changeevent - License key validation (format-only)
- Haptic feedback (Vibration API)
- Responsive resize handling
Built by Web Works Systems. Part of The Conglomerate Group. The code executes.