Quizzman Modular Framework v3 — A modular, plugin-based frontend framework for the entire Quizzman ecosystem.
echo '@qmf:registry=https://ui.quizzman.com/registry/' >> .npmrc && pnpm add @qmf/core @qmf/tokens
4-layer Architecture · Plugin system · Error isolation · DevTools
Register modules as plugins with automatic dependency resolution. Supports eager & lazy loading.
Token-based dependency injection — register and resolve services anywhere in your app.
Each module has its own error boundary. One module crashing won't affect others.
Global + isolated module store with Zod schema validation. Selectors, watchers, immutable updates.
Code-split modules, loaded only when needed. CSS auto-injected when plugin mounts.
Event tracing, state inspection, plugin graph — all via window.__QMF_DEVTOOLS__.
Pub/sub with wildcards, scoped bus, and tracing. Inter-module communication without coupling.
Manage module lifecycle: onInit → onMount → onReady → onDestroy. Sync and async.
Fetch wrapper with interceptors, retry logic, auth injection, and automatic error mapping.
Each layer can only import from lower layers — no circular dependencies.
Choose the integration method that fits your project.
<!-- 1. Load QMF -->
<link rel="stylesheet" href="https://ui.quizzman.com/qmf/dist/tokens.css">
<script src="https://ui.quizzman.com/qmf/dist/qmf.iife.js"></script>
<!-- 2. Init -->
<script>
QMF.init({
appId: 'my-app',
debug: true
}).then(() => {
console.log('QMF Ready!');
});
</script>
// 1. Add to .npmrc (one-time)
// echo '@qmf:registry=https://ui.quizzman.com/registry/' >> .npmrc
// 2. Install
// pnpm add @qmf/core @qmf/tokens @qmf/header
import { QMF, lazy } from '@qmf/core';
import { createHeaderPlugin } from '@qmf/header';
import '@qmf/tokens/css';
// Register plugins
QMF.use(createHeaderPlugin({ brandName: 'My App' }));
QMF.use(lazy(() => import('@qmf/chat-embed')));
// Initialize
await QMF.init({
appId: 'my-app',
channel: 'production',
debug: false
});
import { QMF, lazy, getGlobalEventBus } from '@qmf/core';
import { createHeaderPlugin } from '@qmf/header';
import { createFooterPlugin } from '@qmf/footer';
import '@qmf/tokens/css';
// 1. Register plugins (eager)
QMF.use(createHeaderPlugin({ brandName: 'Quizzman LMS' }));
QMF.use(createFooterPlugin());
// 2. Register plugins (lazy — loaded on demand)
QMF.use(lazy(() => import('@qmf/chat-embed')));
QMF.use(lazy(() => import('@qmf/noti-embed')));
// 3. Init framework
await QMF.init({
appId: 'quizzman-lms',
channel: 'production',
debug: import.meta.env.DEV,
});
// 4. Use event bus
const bus = getGlobalEventBus();
bus.on('auth:login', (user) => console.log('Logged in:', user));
bus.emit('auth:login', { id: 1, name: 'Admin' });
// 5. Access store
const state = QMF.store.getState();
QMF.store.watch(s => s.user, (newUser) => {
console.log('User changed:', newUser);
});
Core APIs of @qmf/core
| Module | API | Description |
|---|---|---|
| EventBus | on · off · emit · once |
Pub/sub with wildcard * and EventBus.scoped(prefix) |
| Store | getState · setState · select · watch |
Immutable state container, Zod validation, selector + watcher |
| Lifecycle | hook · transition |
Module lifecycle: onInit → onMount → onReady → onDestroy |
| Config | validateConfig · resolveEnvUrls |
Zod schema validation + environment URL resolution |
| HttpClient | get · post · put · delete |
Fetch wrapper: interceptor, retry, auth injection |
| DI | register · resolve · createToken |
Token-based dependency injection |
| PluginLoader | QMF.use · lazy |
Plugin registration with dependency graph + lazy loading |
| ErrorBoundary | protect · onError |
Per-module error isolation, fallback UI |
Notification pill — System notifications displayed as a "pill" at the top of the page, expands on hover/click.
Click buttons to see Dynamic Island. Hover to expand, click close button or wait for auto-dismiss.
<!-- 1. Load CSS + JS -->
<link rel="stylesheet"
href="https://ui.quizzman.com/qmf/dist/modules/header/dynamic-island.css">
<script src="https://ui.quizzman.com/qmf/dist/modules/header/dynamic-island.js"></script>
<!-- 2. Use -->
<script>
const island = new QMFDynamicIsland();
// Push a notification
island.push({
title: 'System Update',
message: 'Version 3.0 is now available!',
icon: 'fas fa-rocket',
priority: 'info', // info | success | warning | error | promo
duration: 6000, // ms, 0 = sticky
actions: [
{ label: 'View Details', href: 'https://quizzman.com', primary: true },
{ label: 'Dismiss' }
]
});
</script>
// Constructor
const island = new QMFDynamicIsland({
autoDismiss: 6000, // Default auto-dismiss (ms)
maxQueue: 10, // Max queued notifications
hoverExpand: true, // Expand on hover (desktop)
container: document.body
});
// Methods
island.push(data) // Add notification to queue
island.dismiss() // Dismiss current, show next
island.expand() // Expand to full view
island.collapse() // Collapse to compact pill
island.clear() // Clear all notifications
// Notification data shape
{
title: 'string', // Required — Heading
message: 'string', // Required — Body text
icon: 'fas fa-bell', // FontAwesome class
priority: 'info', // info | success | warning | error | promo
duration: 6000, // Auto-dismiss ms (0 = sticky)
actions: [ // Optional buttons
{ label: 'Click', href: '...', primary: true },
{ label: 'Action', onClick: () => {} }
]
}
/* Override these CSS variables to customize */
:root {
--qmf-island-bg: rgba(15, 15, 30, 0.92); /* Background */
--qmf-island-border: rgba(255, 255, 255, 0.1);
--qmf-island-text: #ffffff;
--qmf-island-text-muted: rgba(255, 255, 255, 0.7);
--qmf-island-radius: 28px; /* Compact pill radius */
--qmf-island-z: 10000; /* z-index */
/* Priority colors */
--qmf-island-info: #4facfe;
--qmf-island-success: #43e97b;
--qmf-island-warning: #f5b942;
--qmf-island-error: #ff6b6b;
--qmf-island-promo: linear-gradient(135deg, #667eea, #764ba2);
}
/* BEM classes */
.qmf-island /* Container */
.qmf-island--visible /* Shown */
.qmf-island--expanded /* Full view */
.qmf-island--has-queue /* Queue dots visible */
.qmf-island__compact /* Pill row */
.qmf-island__icon /* Icon circle */
.qmf-island__icon--info /* Priority modifier */
.qmf-island__label /* Title text */
.qmf-island__body /* Expanded content */
.qmf-island__actions /* Action buttons row */
.qmf-island__action--primary /* Primary CTA */
.qmf-island__queue /* Queue dots container */
Push multiple notifications in sequence — they're queued and displayed one by one with queue dots indicator.
info · success · warning · error · promo — each level has its own gradient color.
Responsive on all devices. ARIA role="status", aria-live="polite", prefers-reduced-motion.
Auto-detects [data-theme="light"]. All colors via CSS custom properties, easy to override.
Each notification can have action buttons: link (href) or callback (onClick).
Just 1 CSS + 1 JS file. No framework or library dependencies. <10KB combined.
https://ui.quizzman.com/qmf/dist/modules/header/dynamic-island.css
https://ui.quizzman.com/qmf/dist/modules/header/dynamic-island.js
Read detailed documentation for each part of QMF.
Basic usage guide: init, plugin, state, event bus, config.
Detailed API docs for EventBus, Store, Lifecycle, HttpClient, DI, Config.
4-layer architecture, import rules, plugin system, module structure.
Guide for migrating from share-ui (v2) to QMF v3. Includes compat layer.