LibMui
LibMui is a lightweight, high-performance widget library for modern websites and JavaScript applications. It is built on Mithril and provides responsive controls, selectors, dialogs, color tools, file pickers, parameter editors, and reusable layout components.
The package ships its widget styles and Font Awesome icon definitions with the JavaScript entry point, so importing LibMui also loads its visual system.
Installation
npm install @nebularstreams/libmui mithril
import m from "mithril";
import {
ButtonNew,
InputWidget,
RangeWidget,
SwitchWidget
} from "@nebularstreams/libmui";
LibMui widgets are Mithril components. Render them with m(Component, attrs, children) and keep application values in your own state.
Quick start
import m from "mithril";
import {
ButtonNew,
InputWidget,
RangeWidget,
SwitchWidget
} from "@nebularstreams/libmui";
const state = {
name: "Nebular",
volume: 0.5,
enabled: true
};
const Demo = {
view: () => m("main.pad-3", [
m(InputWidget, {
label: "Display name",
value: state.name,
hint: "Enter a name",
onchange: value => state.name = value
}),
m(RangeWidget, {
title: "Volume",
value: state.volume,
min: 0,
max: 1,
step: 0.01,
onchange: value => state.volume = value
}),
m(SwitchWidget, {
text: "Enabled",
checked: state.enabled,
onchange: checked => state.enabled = checked
}),
m(ButtonNew, {
icon: "check",
text: "Save",
onClick: () => saveSettings(state)
})
])
};
m.mount(document.body, Demo);
Buttonuses anonclickcallback, while the newerButtonNewusesonClick.
Buttons and icons
Icon
Renders a Font Awesome icon.
m(Icon, {
icon: "gear",
size: 2,
icontitle: "Settings",
onclick: openSettings
});
| Attribute | Description |
|---|---|
icon |
Font Awesome icon name without the fa- prefix. Defaults to cog. |
pack |
Icon pack class. Defaults to fas. |
size |
Font Awesome size multiplier. |
icontitle |
Native tooltip text. |
iconClass |
Additional classes for the icon element. |
onclick |
Optional icon click handler. |
ButtonNew
Renders a button, link, or download action. Promise-returning handlers automatically apply a busy state and expose rejected errors through data-error.
m(ButtonNew, {
icon: "cloud-upload-alt",
text: "Publish",
title: "Publish changes",
class: "accentbutton",
onClick: () => publishProject()
});
Important attributes are icon, text, title, class, textClass, right, onClick, link, target, and download. When link or download is present, the widget renders an anchor instead of a button.
Button offers the same basic presentation but uses onclick.
ButtonChoiceWidget
Displays a set of choices as a row of buttons.
m(ButtonChoiceWidget, {
label: "Quality",
value: state.quality,
choices: {
low: {value: "low", caption: "Low"},
high: {value: "high", caption: "High", icon: "star"}
},
onchange: choice => state.quality = choice.value
});
Use choose: ["one", "two"] for a simple list. Use choices for captions, icons, classes, and distinct values. Set toggle to allow the active choice to be cleared, field to compare a property of each choice, or render(choice) for custom button content.
Text and numeric input
InputWidget
Provides single-line, multiline, numeric, validated, read-only, and editable-static inputs.
m(InputWidget, {
label: "Retries",
type: "number",
value: state.retries,
min: 0,
max: 10,
step: 1,
onchange: value => state.retries = value
});
| Attribute | Description |
|---|---|
value |
Current controlled value. |
onchange(value, event, multiline) |
Called when the value is committed. |
oninput(value, event) |
Optional live-input callback. |
type |
Native input type. Use number for parsed numeric values. |
float |
Parses numeric input with parseFloat instead of parseInt. |
hint |
Placeholder text. |
label |
Accessible label applied to the container. |
multi |
Adds a control for switching between input and textarea. |
alwaysmulti |
Always renders a textarea. |
validator(value) |
Returns whether the current value is valid. |
readonly |
Prevents editing. |
maxlength, min, max, step |
Native input constraints. |
action, actions |
One or more ButtonNew configurations shown beside the input. |
onenter(value, event) |
Called when Enter is pressed. |
staticField |
Displays a static value until the user activates it. |
draginc |
Enables vertical drag adjustment by this increment on a static numeric field. |
RangeWidget
Renders an accessible slider with a live value display.
m(RangeWidget, {
title: "Opacity",
value: state.opacity,
min: 0,
max: 1,
step: 0.01,
parser: value => `${Math.round(value * 100)}%`,
onchange: (value, dragging) => state.opacity = value
});
Use multiplier to let the range expand beyond its initial limits, relative for relative movement, and factor to scale interaction. The second onchange argument indicates whether the slider is being dragged.
PlusMinus
Provides decrement and increment buttons around a displayed value.
m(PlusMinus, {
title: "Copies",
value: state.copies,
min: 1,
max: 20,
step: 1,
onchange: value => state.copies = value
});
Customize the display with parser(value) and the buttons with iconMinus, iconPlus, and buttonClass.
SwitchWidget
Displays a controlled toggle.
m(SwitchWidget, {
text: "Show guides",
subline: "Display alignment helpers",
checked: state.guides,
right: true,
onchange: checked => state.guides = checked
});
Selection widgets
ComboWidget
Renders a native select from {name, value} items.
m(ComboWidget, {
caption: "Renderer",
value: state.renderer,
items: [
{name: "WebGL", value: "webgl"},
{name: "WebGPU", value: "webgpu"}
],
onchange: (value, item, index) => state.renderer = value
});
Pass groups instead of items to create named option groups. parser(value) can normalize values before matching the controlled selection.
ComboPrompt
Shows the current choice as a compact button and opens a modal choice menu.
m(ComboPrompt, {
label: "Theme",
value: state.theme,
items: themes,
field: "name",
emptyText: "Choose a theme",
onchange: value => state.theme = value
});
Useful attributes include items, field, itemsText, icon, emptyIcon, emptyText, locked, nodelete, confirm, textNew, and textHint. Shift-clicking the main button clears the value.
SmileysButton
Opens LibMui's application-level icon and emoji selector. The selected entry is returned through onchange.
m(SmileysButton, {
icon: state.markerIcon,
icons: availableIcons,
onchange: icon => state.markerIcon = icon
});
The current icon may contain an icon field for a Font Awesome symbol, a glyph field for an emoji, or both. This widget uses MainState to open the emoji application panel, so that panel must be registered by the host application.
Optional emoji picker package
Install @nebularstreams/libmui-extensions when your application needs a ready-made emoji picker:
npm install @nebularstreams/libmui-extensions
The optional package provides SmileysWidget, a searchable picker organized into emoji categories. It can also include Font Awesome symbols and remembers selected emoji as favorites during the current session.
import m from "mithril";
import {SmileysWidget} from "@nebularstreams/libmui-extensions";
m(SmileysWidget, {
icons: true,
onClick: (glyph, item) => {
state.marker = {
glyph,
icon: item.icon,
name: item.name
};
}
});
| Attribute | Description |
|---|---|
onClick(glyph, item) |
Called with the selected character and its metadata. |
icons |
Includes Font Awesome symbols alongside emoji. |
noemojis |
Hides emoji and shows only the optional icon set. |
horizontal |
Places the category navigation above the picker instead of beside it. |
mainclass |
Replaces the picker's default root class. |
The package also exports SmileysPanel, which wraps the picker in a horizontal panel with a title bar and optional back action:
import {SmileysPanel} from "@nebularstreams/libmui-extensions";
m(SmileysPanel, {
icons: true,
onClick: (glyph, item) => selectMarker(glyph, item),
onClose: closePicker
});
Use SmileysWidget when embedding the picker in your own layout. Use SmileysPanel when you want a complete application panel. The extension package is optional; the rest of LibMui does not require it.
Selector
Builds a toolbar that switches between complete Mithril components.
const panels = [
{text: "General", icon: "sliders-h", widget: GeneralPanel},
{text: "Advanced", icon: "cogs", widget: AdvancedPanel}
];
m(Selector, {
title: "Settings",
items: panels,
selected: 0,
toggle: true,
closeButton: true,
onSelected: (item, params, changed) => {
if (changed) console.log("Selected", item);
}
});
Each item accepts text, icon, title, class, widget, attrs, and children. Widget attrs may be an object or a function receiving the selector's own attributes. Pass a shared state object when another component needs to call state.setItem(index, ...params).
Layout and status
CardieWidget
Creates a collapsible card whose body can be supplied as children or through expandedView(attrs, state).
m(CardieWidget, {
cardid: "settings-audio",
title: "Audio settings",
collapsed: true,
expandedView: () => m(AudioSettings)
});
Cards sharing the prefix before the first - in cardid behave like an accordion. Use nofold for a permanently expanded card, action and collapsedAction for title actions, and alwaysView for content that remains outside the folding body.
TitleBarWidget
Creates an application or panel title bar with optional back navigation, logo, editable title, actions, and overflow menu.
m(TitleBarWidget, {
title: "Project settings",
onClick: goBack,
action: {icon: "save", title: "Save", onClick: save},
overflow: {
items: [
{caption: "Duplicate", icon: "copy", onClick: duplicate},
{caption: "Delete", icon: "trash", onClick: remove}
]
}
});
Separator
Renders a labelled section divider and optional child actions.
m(Separator, {text: "Appearance"});
NoticeWidget
Renders inline informational content with an optional icon, click handler, and child content.
m(NoticeWidget, {
icon: "info-circle",
text: "Changes are saved automatically."
});
Empty states
Empty renders a centered icon, message, and optional children. EmptyButton adds a primary action, while CoolerEmpty supports a header and one or more action definitions.
m(CoolerEmpty, {
icon: "folder-open",
header: "No projects",
message: "Create your first project to begin.",
action: {caption: "Create project", onclick: createProject}
});
Color widgets
ColorJoeWidget
Displays a color swatch that opens an RGB or HSL picker in a modal.
m(ColorJoeWidget, {
caption: "Background",
color: state.color,
onchange: (cssColor, colorObject) => state.color = cssColor
});
Set hsl for an HSL picker, noalpha to remove alpha controls, and onshiftclick to provide an alternate swatch action. Passing null from the picker indicates removal.
ColorJoePack
Edits an array of RGB(A) or HSL(A) colors and supports up to eight entries by default.
m(ColorJoePack, {
colors: state.palette,
max: 8,
onchange: colors => state.palette = colors
});
Click a swatch to edit it, Shift-click one to remove it, click + to add a color, or Shift-click + to clear the collection.
File and image widgets
FileSelectButton
m(FileSelectButton, {
text: "Choose JSON",
icon: "upload",
accept: "application/json",
onFile: file => importFile(file)
});
Image helpers
ImageSelectButtonreturns selected files throughonImageFiles(files).ImageProcessButtonloads the selected image and passes anHTMLImageElementtoonImage(image).ImageDataButtonreads the image as a data URL and callsonImageUrl({name, url}).MediaSelectButtonopens LibMui's configured media-selection application state.processImage(file)provides the image-loading behavior as a standalone promise.
All image buttons accept the presentation attributes class, icon, iconClass, text, textClass, and accept. Processing buttons also support onError(error).
Notifications and dialogs
Add these containers once near the root of the document when using notifications, prompts, color pickers, or modal utilities:
<div id="notification-container"></div>
<div id="modal-container" style="display: none"></div>
Notifications
Notification.add("Saved", "success");
Notification.add("Could not connect", "error");
Notification.clear();
Notifications are queued and dismissed automatically. Supported semantic types include info, success, and error; clicking the current notification advances the queue.
Confirmations
const result = await ConfirmPromise(
"Delete project?",
"This action cannot be undone.",
["CANCEL", "DELETE"],
true
);
if (result.name === "DELETE") deleteProject();
ConfirmPromise resolves to {idx, name}. The lowercase confirmPromise export provides the library's alternative confirmation helper. popupMessage(content, cancelable, contentClass) can render custom Mithril content in the modal container, while clearModal() closes the active modal.
OverflowMenu
const {action} = await OverflowMenu(
document.body,
{title: "Project actions"},
[
{caption: "Duplicate", icon: "copy"},
{caption: "Archive", icon: "archive"}
]
);
console.log(action.caption);
Tickers and collections
GenericButtonTicker renders a compact collection of selectable buttons with add, edit, update, rename, and delete behavior. CoolGenericTicker switches large collections to previous/next navigation. ThematicWrapper provides their themed frame.
m(GenericButtonTicker, {
items: state.presets,
current: state.currentPreset,
emptyText: "No presets",
addText: "Add preset",
onSelect: (item, index) => state.currentPreset = index,
onAdd: () => addPreset(),
onDelete: index => deletePreset(index)
});
CoolContentTicker is the application-integrated variant. It expects a layer containing items and a global.storyWrapper implementing LibMui's ticker state methods.
Metadata-driven forms
ParameterTable and TemplatedInputWidget generate complete control panels from metadata and a mutable parameter object. They are intended for applications with many dynamic properties, presets, or schema-defined editors.
const parameters = {
title: "Scene one",
visible: true,
opacity: 0.8
};
const meta = {
title: {type: "text", title: "Title"},
visible: {type: "boolean", title: "Visible"},
opacity: {type: "number", title: "Opacity", min: 0, max: 1, step: 0.01}
};
m(ParameterTable, {
meta,
parameters,
onchange: (changeCode, target, key) => {
console.log("Changed", key, target[key]);
}
});
Related widgets include:
ParameterCardie— groups generated parameter controls in a collapsible card.MultiToggleWidget— edits a keyed set of toggle values.RangeToggleWidget— edits a keyed set of numeric range values.TemplatedInputWidget— combines a value editor with presets and optional property controls.
Metadata forms support standard strings, numbers, booleans, ranges, choices, colors, JSON, nested groups, separators, custom widgets, and injected asset or code editors. For application-specific editors, pass AssetButton or CodeMirror through the table attributes.
Application routing
startApp(target, appMetadata) starts a Mithril router on an element ID:
startApp("app", {
default: "/home",
routes: {
"/home": HomePage,
"/settings": SettingsPage
}
});
Public exports
The package exports the following public widgets:
- Foundations:
Button,ButtonNew,Icon,Empty,Selector,Notification - Inputs:
InputWidget,RangeWidget,PlusMinus,SwitchWidget - Choices:
ButtonChoiceWidget,ComboWidget,ComboPrompt,SmileysButton - Layout:
CardieWidget,Separator,TitleBarWidget,NoticeWidget,EmptyButton,CoolerEmpty - Colors:
ColorJoeWidget,ColorJoePack - Files:
FileSelectButton,ImageSelectButton,ImageProcessButton,ImageDataButton,MediaSelectButton - Collections:
GenericButtonTicker,CoolGenericTicker,CoolContentTicker,ThematicWrapper - Dynamic forms:
TemplatedInputWidget,ParameterTable,ParameterCardie,MultiToggleWidget,RangeToggleWidget
LibMui also exports modal, cloning, tokenization, file-path, routing, application-state, and widget-definition utilities. These support the widgets but are outside this widget-focused guide.
Browser support
LibMui is an ES module intended for modern browsers. Widgets that read local files require the FileReader API, while image processing also uses the browser Image API. The core rendering dependency is Mithril 2.x.