Installation
kob-ui ships one JavaScript file and one stylesheet. There is nothing to install alongside it and no build step unless you want one.
npm i kob-ui
Script tag
Bundler
What is in the package
| File | Format | Use it for |
|---|---|---|
| dist/kob-ui.min.js | IIFE | A plain <script> tag. Defines window.Kob. |
| dist/kob-ui.js | IIFE | The same, unminified, for debugging. |
| dist/kob-ui.esm.js | ESM | import in a bundler. |
| dist/kob-ui.cjs | CommonJS | require(). |
| dist/kob-ui.css | CSS | The whole stylesheet, icons included. |
| types/index.d.ts | TypeScript | Picked up automatically. |
Browsers. Chrome/Edge 80+, Firefox 78+, Safari 14+. No polyfills required.
Creating widgets
Every widget has one accessor. Call it with options to build the widget; call it again with nothing to get the same instance back.
Methods return the instance where there is nothing else to return, so
they chain: grid.search('bangkok').gotoPage(1).
Events
Callbacks are options, named onSomething. Inside one,
this is the widget's element.
Cleanup
destroy() unbinds listeners and puts the DOM back roughly
as it was. Kob.unparse(root) does it for everything under
an element — worth calling before you tear a screen out of the page.
Markup and data-options
A widget can also be declared in HTML. Kob.parse(root)
walks the markup and builds anything whose class names a widget.
Parsing twice is safe: an element that already carries a widget is skipped, so you can call it again after injecting HTML.
The two can be combined. Declare the layout in
markup, then hand the real configuration over from JavaScript — the
widget merges it and rebuilds. That is how a datagrid declared as
class="kob-datagrid" picks up its columns later.
What data-options accepts
Quoted strings, numbers, true, false and
null. Functions, formatters and column arrays have no
sensible text form, so pass those from JavaScript.
Loading remote data
combobox and datagrid both take a
url. Everything they fetch goes through one function, so
authentication and response shapes are configured once for the whole
library.
A single widget can still reshape its own payload with
loadFilter.
Theming
Nothing in the stylesheet hard-codes a colour. Redefine the tokens on
:root — or on any container — and everything inside
follows.
| Token | Used for |
|---|---|
| --kob-accent | Primary buttons, selection, focus rings, active tabs |
| --kob-accent-dark | Header text, sorted column, emphasis on the accent |
| --kob-accent-soft | Headers, hovers, striped rows, chip backgrounds |
| --kob-accent-hover | The stronger hover state |
| --kob-surface / --kob-surface-2 | Panels, windows, dropdowns |
| --kob-bg | The ground the widgets sit on |
| --kob-text / --kob-muted | Body text, secondary text |
| --kob-border / --kob-border-soft | Outlines, table rules |
| --kob-danger / --kob-success / --kob-warn / --kob-info | Status colours and badges |
| --kob-radius / --kob-radius-sm / --kob-radius-xs | Corner rounding |
| --kob-shadow-1 / -2 / -3 | Cards, dropdowns, windows |
| --kob-scrollbar-thumb / -hover / -track / -size | Scrollbars — see below |
Scrollbars
The browser's own scrollbars are a fixed grey that reads as a foreign object on a dark surface, so every box the library scrolls gets one painted from the tokens instead — vertical, horizontal, and the corner where the two meet. Anything scrolling inside a window, panel or layout matches too.
To carry the same treatment to the rest of the page, put the class on the root element:
A region that keeps its own colours — a dark code panel on a light page, say — just redefines the tokens locally:
The three themes
Three palettes ship. Leave the attribute off and the library follows
the operating system, choosing between light and
dark; set it and that choice is pinned.
| Value | What it looks like |
|---|---|
| light | The default. Cool greys on white. |
| dark | Blue-grey night theme, surfaces from #0b1e27 up. |
| black |
Neutral near-black with no colour cast — surfaces from
#0f1011, and the header and hover washes are white at
low alpha rather than a tinted accent, so nothing goes blue.
Deliberately not #000: pure black under near-white
text is the highest contrast a panel can make, which reads as
glare in a dim room.
|
Switching at runtime is one attribute. Remembering the choice is on you — the library never writes to storage:
A fourth theme is a block of your own. Only the tokens that differ need redefining — everything else falls through to the light values:
Localisation
Every string the widgets show, plus the date format and calendar era, comes from the active locale. English and Thai are built in.
Dates stay ISO. A Thai locale shows
09/03/2567 on screen, but datebox.getValue()
still returns '2024-03-09'. The calendar in use never
changes what you send to the server.
The datebox example further down switches between
English and Thai live, and reports both what is on screen and what
getValue() returns.
Icons
181 single-colour SVGs applied as a CSS mask. They take the element's
color, live inside the stylesheet, and cost no requests.
The full set — click any icon to copy its class name:
Your own artwork works the same way, without touching the library — point the mask at your SVG:
textbox, numberbox, passwordbox
Three thin wrappers over <input>: an optional label,
a required flag, and for numbers, clamping and formatting.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| label | string | '' | Draws a label next to the field. Omit for a bare input. |
| labelWidth | number | 110 | Label width in pixels when it sits to the left. |
| labelPosition | 'left' | 'top' | auto | Defaults to top on touch screens, left otherwise. |
| prompt | string | '' | Placeholder text. |
| value | any | — | Initial value. |
| required | boolean | false | Checked by isValid() and by the containing form. |
| readonly / disabled | boolean | — | Applied to the underlying input. |
| min / max | number | — | numberbox: clamped on blur. |
| precision | number | — | numberbox: decimal places applied on blur. |
| groupSeparator | string | '' | numberbox: thousands separator shown while unfocused. |
| onChange | function(value) | — | Fires on the input's native change. |
Methods
| Method | Returns | Description |
|---|---|---|
| getValue() | string | number | numberbox returns a number, or '' when empty. |
| setValue(v) | this | numberbox also clamps and formats. |
| clear() | this | Empties the field and drops the invalid mark. |
| isValid() | boolean | Whether required is satisfied. |
| enable() / disable() | this | |
| readonly(mode) | this | |
| focus() | this |
combobox
A text input backed by a list. The value the form submits and the text the user reads are kept apart, so an id column never leaks onto screen.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| data | object[] | null | Rows to show. |
| url | string | '' | Fetched through Kob.config.request. |
| queryParams | object | null | Sent with the request. |
| valueField | string | 'id' | Field getValue() returns. |
| textField | string | 'text' | Field shown in the box. |
| formatter | function(row) | — | HTML for one list item. The box shows its plain text. |
| editable | boolean | true | false means pick from the list only. |
| panelHeight | number | 300 | Maximum dropdown height in pixels. |
| limit | number | 300 | Items rendered at once; the rest are reachable by typing. |
| label / labelWidth / prompt / required | — | — | As for textbox. |
| onChange | function(value, row) | — | After a pick. |
| onSelect | function(row) | — | The row that was clicked. |
| onLoadSuccess | function(rows) | — | After loadData or a fetch. |
Methods
| Method | Returns | Description |
|---|---|---|
| getValue() / setValue(v) | any / this | The valueField value. |
| getText() | string | What the user reads. |
| loadData(rows) | this | Swap the list without touching the value. |
| reload(url?) | Promise | Fetch again, optionally from a new URL. |
| getData() | object[] | The current list. |
| clear() | this |
Keyboard: arrows move through the list, Enter picks, Escape closes.
datebox
A text field with a month calendar. It reads and writes ISO dates whatever the locale displays.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| value | string | Date | — | ISO, a Date, or a string in the display format. |
| format | string | locale | Overrides the locale's format, from yyyy, yy, mm, dd. |
| min / max | string | '' | ISO bounds; days outside them are disabled. |
| showToday | boolean | true | The shortcut under the calendar. |
| label / labelWidth / prompt / required | — | — | As for textbox. |
| onChange / onSelect | function(iso) | — | Both receive the ISO date. |
Methods
| Method | Returns | Description |
|---|---|---|
| getValue() | string | ISO yyyy-mm-dd, or ''. |
| setValue(v) | this | Accepts ISO, a Date, or the display format. |
| getText() | string | What is on screen, e.g. 09/03/2567. |
| clear() | this |
checkbox
A real <input type="checkbox"> drawn as a switch, so
forms submit it and getValue() is simply its checked state.
| Option | Type | Default | Description |
|---|---|---|---|
| label | string | '' | Caption beside the switch; also its accessible name. |
| checked | boolean | — | Initial state. |
| disabled | boolean | — | |
| onChange | function(checked) | — |
Methods: getValue(), setValue(v), check(), uncheck(), toggle(), enable(), disable().
searchbox
A text input that looks and behaves like a search field: a magnifier
on the left, a clear button that appears once there is something to
clear, Escape to empty it, and onSearch debounced so
holding a key down costs one search rather than twenty.
| Option | Type | Default | What it does |
|---|---|---|---|
| delay | number | 300 | Milliseconds of quiet before onSearch fires. 0 fires on every keystroke. |
| searchOnEnter | boolean | false | Never search while typing — only on Enter or the clear button. |
| clearable | boolean | true | Show the clear button once the field has a value. |
| onSearch | function | null | Receives the query, already trimmed. |
| onClear | function | null | Fires when the field is emptied by the button or by Escape. |
It inherits label, labelWidth,
prompt and the rest from
textbox. Methods:
getValue() (trimmed), setValue(),
clear(), search(value?) — which runs
onSearch immediately, ignoring both the debounce and the
guard that skips an unchanged query.
filebox
A drop zone and a file list. Drag files onto it or click to browse;
it checks them against accept, maxSize and
maxFiles, lists what survived, and — if you give it a
url — uploads each one with a progress bar.
url out and the widget never touches the network:
it collects files and getFiles() hands them to your own
FormData, which is what you want when the files ride
along with the rest of a form.
| Option | Type | Default | What it does |
|---|---|---|---|
| multiple | boolean | true | Accept more than one file. |
| accept | string | '' | Extensions and MIME patterns: '.pdf,image/*'. |
| maxSize | number | 0 | Bytes per file. 0 means no limit. |
| maxFiles | number | 0 | How many may be queued at once. |
| url | string | '' | POST target. Empty leaves it a picker. |
| fieldName | string | 'file' | Form field name each file is sent under. |
| data | object | null | Extra form fields sent with every file. |
| headers | object | null | Request headers, e.g. an Authorization token. |
| autoUpload | boolean | false | Upload as soon as a file is added. |
| text / hint | string | '' | Drop-zone caption and sub-line. Both default to the locale strings. |
| onAdd / onRemove | function | null | (file). |
| onProgress | function | null | (file, percent) while uploading. |
| onSuccess | function | null | (file, response) — parsed as JSON when it is JSON. |
| onError | function | null | (file, message) — also fires for a file the rules turned away. |
| onComplete | function | null | (ok, failed) once every queued file has finished. |
Methods: add(files), remove(index),
clear(), getFiles(),
getItems() (each with its status and percent),
upload(), enable() / disable().
Uploads go through XMLHttpRequest rather than
fetch, because fetch still cannot report upload progress
— and the progress bar is most of the point of a file field. One
request per file, so a single failure does not lose the batch.
form
Collects every kob-ui field inside a <form> so you
can read, fill, validate and clear them in one call — whatever mix of
widgets they are.
| Method | Returns | Description |
|---|---|---|
| getValues() | object | Keyed by each field's name, read through its widget. |
| setValues(obj) | this | Fills matching fields; unknown keys are ignored. |
| validate() | boolean | Marks offenders with .kob-invalid and focuses the first. |
| clear() | this | Empties every field and drops the marks. |
| Option | Type | Description |
|---|---|---|
| markInvalid | boolean | Add .kob-invalid to failing fields. Default true. |
| onValidate | function(ok, values) | Return false to fail validation with your own rule. |
| onSubmit | function(event, values) | Runs after validation passes on a real submit. |
panel
A titled content box that can fold away or load its body from a URL — and parse the widgets in whatever comes back.
| Option | Type | Default | Description |
|---|---|---|---|
| title | string | '' | Header caption. Omit for a headerless panel. |
| iconCls | string | '' | Icon in the header. |
| collapsible | boolean | false | Shows the fold caret. |
| collapsed | boolean | false | Start folded. |
| closable | boolean | false | Shows a close button that removes the panel. |
| href | string | '' | Fetch this URL into the body on init. |
| fit | boolean | false | Fill the parent box. |
| border | boolean | true | |
| loadingMessage | string | locale | Shown while href is in flight. |
| onLoad / onCollapse / onExpand / onClose | function | — |
Methods: getBody(), setTitle(s), load(url), setContent(html), collapse(), expand(), toggleCollapse(), close().
layout
The classic five-region frame. Regions are declared on the children; the widget restacks them once so flexbox can do the sizing.
Region options
Set these in each child's data-options.
| Option | Type | Description |
|---|---|---|
| region | string | north, south, west, east or center. Required. |
| width | number | west and east, in pixels. |
| height | number | north and south, in pixels. |
| split | boolean | Adds a draggable splitter on the inner edge. |
| minSize / maxSize | number | Drag limits. |
| collapsible | boolean | Puts a fold button on the splitter. |
Methods: panel(region) returns the region element, plus resize(region, px), collapse(region), expand(region), toggle(region), remove(region).
Events: onResize(region, size), onCollapse(region), onExpand(region).
A layout fills its parent, so give the parent a height — height:100% or a fixed value — or it collapses to nothing.
tabs
Every direct child carrying a title attribute becomes a
page.
| Option | Type | Default | Description |
|---|---|---|---|
| selected | number | 0 | Index shown first. |
| closable | boolean | false | Close buttons on every tab. A page can opt in on its own with data-options="closable:true". |
| onSelect | function(index, title) | — | |
| onBeforeClose | function(index, title) | — | Return false to keep the tab open. |
| onClose / onAdd | function | — |
Methods: select(i), selectByTitle(s), getSelected(), getPanel(i), getPages(), add({ title, content, closable }), close(i).
dialog and modal
Built around an element that is already on the page: it keeps its content and becomes the window body, with the frame added around it.
| Option | Type | Default | Description |
|---|---|---|---|
| title | string | '' | |
| width | number | 460 | |
| height | number | 'auto' | 'auto' | |
| modal | boolean | true | false makes it a draggable, resizable window. |
| closed | boolean | true | Start hidden; call open(). |
| buttons | array | null | { text, iconCls, primary, handler(dialog) }. |
| draggable / resizable | boolean | true | Non-modal windows only. |
| minimizable / maximizable | boolean | true | Non-modal windows only. |
| sheet | boolean | false | Docks to the bottom edge — the shape phones expect. |
| minWidth / minHeight | number | 300 / 120 | Resize limits. |
| onOpen / onClose / onResize / onMove / onMinimize / onMaximize | function | — | onMinimize returning false keeps the window open. |
Methods: open(), close(), isOpen(), setTitle(s), setButtons(list), maximize(on?), restore(), minimize(), center(), resize({width,height}), move({left,top}), window(), getBody(), destroy().
Two spellings
The modal flag decides everything, so it has a name at each
end and you never have to remember which way round it goes:
Dialog markup is usually hidden with style="display:none"
so it cannot flash before the widget is built. The widget clears that
when it takes the element over — the frame owns visibility from then on.
card
CSS only — there is no behaviour to attach, so there is no
Kob.card(). Compose the parts you need.
| Class | Description |
|---|---|
| kob-card | The box. A flex column, so the body stretches. |
| kob-card-header | Title row. kob-card-tools inside pushes to the right. |
| kob-card-media | Full-bleed image at the top. |
| kob-card-body | Content. First and last child margins are collapsed away. |
| kob-card-footer | Action row on the accent tint. |
| kob-card-hover | Lifts on hover — for cards that are links. |
ribbon
Tabs of grouped commands. One large button, or a stack of small ones;
a button with a menu opens it instead of firing.
| Option | Type | Default | Description |
|---|---|---|---|
| tabs | array | null | [{ title, groups: [{ title, items: [...] }] }] |
| selected | number | 0 | Tab shown first. |
| collapsible | boolean | true | Clicking the open tab folds the ribbon away. |
| collapsed | boolean | false | Start folded. |
| onSelect / onClick | function | — | (index, tab) / (item) |
Item keys: text, iconCls,
size: 'large' | 'small', disabled,
menu, onClick. Consecutive small buttons stack
three to a column.
Methods: select(i), collapse(), expand(), toggleCollapse(), setTabs(tabs).
tree
Nodes are plain objects. Children may be an array, or left out with
state: 'closed' to be fetched the first time the node
opens.
Node shape
| Option | Type | Default | Description |
|---|---|---|---|
| data | TreeNode[] | null | The root nodes. |
| url | string | '' | Root through Kob.config.request; lazy children are fetched from the same URL with id. |
| idField / textField / childrenField | string | id / text / children | Read your own shape without remapping. |
| checkbox | boolean | false | A tick box on every node. |
| cascadeCheck | boolean | true | Folders tick their children; partly-ticked folders show a dash. |
| lines | boolean | false | Guide lines down each branch. |
| expandDepth | number | 1 | How deep to start open. |
| formatter | function(node) | — | HTML for one label. |
| onSelect / onClick / onDblClick | function(node) | — | |
| onExpand / onCollapse | function(node) | — | |
| onCheck | function(node, checked) | — | |
| onContextMenu | function(event, node) | — | Pairs with menu.showAt(). |
Methods: loadData, reload,
expand(node), collapse(node),
toggle(node), expandAll(),
collapseAll(), select(node),
getSelected(), find(id),
check(node, on), getChecked(),
walk(nodes, fn), getData().
desktop
A windowed shell: wallpaper with launcher icons, a taskbar, a start menu, and one window per running app. Each window is a non-modal dialog, so dragging, resizing and maximising come from that widget — what the desktop adds is knowing what is running, which window has focus, and how to get a minimised one back.
Apps
An app says what its window contains, in one of three ways.
| Key | Type | Description |
|---|---|---|
| id | string | Required. What open() and close() take. |
| title | string | Window title, icon caption and taskbar label. |
| iconCls | string | Icon from the built-in set. |
| icon | string | An image URL instead, for a real app icon. |
| content | string | HTML for the window body. Widgets in it are parsed. |
| href | string | Fetch a fragment into the body instead. |
| onOpen | function(win, body) | Or build the contents yourself. |
| width / height | number | Window size. |
| single | boolean | Default true — opening again focuses the existing window. |
| hidden | boolean | Reachable through open(), but not shown as an icon. |
Options
| Option | Type | Default | Description |
|---|---|---|---|
| apps | App[] | null | As above. |
| icons | boolean | true | Launcher icons on the wallpaper. |
| taskbar | boolean | true | The bar along the bottom. |
| startLabel | string | false | 'Start' | Start button text; false hides it. |
| startExtras | MenuItem[] | — | Entries under the app list. |
| clock | boolean | true | Clock at the right. |
| user | object | — | { name, iconCls, onLogout } |
| wallpaper | string | — | Any CSS background. |
| onOpen / onClose / onFocus | function(app, win) | — |
Methods: open(id) returns the window's
Dialog, plus close(id), closeAll(),
minimize(id), getRunning(),
getWindow(id), setApps(apps),
setWallpaper(css).
The desktop fills its parent, so give that parent a height. On a full
page that means html, body { height: 100% }.
datagrid
The whole result set lives in memory, and searching, filtering, sorting and paging all happen locally — so changing a filter never costs another round trip.
Filter chips
Above the header sits one chip per column. Each decides what to offer by looking at the data: a pick-list for categories, a lower bound for numbers, a contains box for free text.
They filter what the reader sees. A column with a
formatter is flattened to text once per row and cached, so a status
column rendering 1 as a green "Paid" badge filters and
searches as Paid.
Inline editing
Options
| Option | Type | Default | Description |
|---|---|---|---|
| columns | array | null | See below. A nested [[…]] array is accepted for EasyUI-shaped configs. |
| data | array | object | null | Rows, { rows } or { data }. |
| url | string | '' | Fetched through Kob.config.request. |
| queryParams | object | null | Sent with the request. |
| loadFilter | function(payload) | — | Reshape the payload before it is read. |
| pagination | boolean | true | The footer with page controls. |
| pageSize | number | 50 | |
| pageList | number[] | [20,50,100] | Choices in the rows-per-page menu. |
| searchable | boolean | true | The free-text box in the footer. |
| filterable | boolean | true | The chip row above the header. |
| rownumbers | boolean | false | |
| showHeader | boolean | true | |
| striped | boolean | true | |
| singleSelect | boolean | true | false lets clicks accumulate a selection. |
| fit | boolean | false | Fill the parent's height. |
| emptyMsg | string | locale | Shown when nothing matches. |
Column definition
| Key | Type | Description |
|---|---|---|
| field | string | Property on the row. |
| title | string | Header text. |
| width | number | Pixels. Columns that fit are stretched to fill the grid. |
| align | 'left' | 'center' | 'right' | |
| sortable | boolean | false makes the header inert. |
| formatter | function(value, row, index) | Returns the cell's HTML. |
| editor | string | object | 'text', 'numberbox', or { type, options: { precision } }. |
| filter | false | string | false drops the chip; a string forces its kind: text, number, enum, bool. |
Methods
| Method | Returns | Description |
|---|---|---|
| loadData(rows) | this | Replace the rows. |
| reload(url?) | Promise | Fetch again. Accepts { url, queryParams }. |
| getRows() | Row[] | Everything loaded. |
| getVisibleRows() | Row[] | After search, filters and sort. |
| getSelected() | Row | null | |
| getSelections() | Row[] | For singleSelect: false. |
| selectRow(i) / clearSelections() | this | |
| sort(field, order?) | this | Omit the order to flip it. |
| setFilter(field, value) | this | { in: [...] }, { ge: n }, { q: 'text' }, { eq: v }, or null to drop it. |
| getFilters() / clearFilters() | object / this | |
| search(text) | this | As if typed into the footer box. |
| gotoPage(n) / pageCount() | this / number | |
| beginEdit(i) / endEdit(i) / cancelEdit() | this | |
| loading() / loaded() | this | Show or hide the mask by hand. |
| resize() | this | Re-measure columns. Rarely needed — it watches its own box. |
Events
| Event | Arguments |
|---|---|
| onClickRow / onDblClickRow / onSelect | (index, row) |
| onLoadSuccess / onLoadError | (data) / (error) |
| onSortChange | (field, order) |
| onFilterChange | (filters) |
| onEndEdit | (index, row) |
Editing and deleting from the row
Not every table has room for a column of buttons.
rowActions gives you the actions without one: a strip
that appears over a row when the pointer is on it, and parks on the
row the user clicked so it stays reachable from the keyboard.
| Key | Type | What it does |
|---|---|---|
| text | string | Button caption. Leave it out for an icon-only button; the label then comes from title. |
| iconCls | string | e.g. 'icon-remove'. |
| color | string | One of the five button colours. |
| soft | boolean | Defaults to true — a strip of solid buttons over a row is too loud. |
| disabled | boolean | function | (row, index). Re-evaluated for every row the strip lands on. |
| handler | function | (row, index), with the grid element as this. |
rowActionsOn: 'selected' drops the hover behaviour and
shows the strip only on the selected row. Methods
showRowActions(tr, hovering) and
hideRowActions() drive it by hand.
messager and toasts
Each helper returns a promise, and still calls an old-style
fn callback if you pass one.
| Call | Resolves to |
|---|---|
| Kob.alert({ title, msg, html, icon }) | true |
| Kob.confirm({ title, msg }) | true or false |
| Kob.prompt({ title, msg, value }) | the typed string, or null |
| Kob.toast({ msg, type, timeout }) | { close(), element } |
icon takes 'info', 'warn' or
'error'; toast type takes
'success', 'error', 'warn' or
'info'. Pass html: true to let
msg carry markup.
sweet
The big centred confirmation. Kob.alert and
Kob.confirm draw a titled window, which is right for the
dozens of small confirmations a data-entry app asks for. This is the
other shape: one decision, front and centre, with a coloured mark
that says at a glance whether the news is good.
true for confirm and false for
everything else — cancel, Escape, a click on the backdrop, the timer
running out — so if (await Kob.sweet(…)) is always safe
to write. It never resolves undefined.
| Option | Type | Default | What it does |
|---|---|---|---|
| type | string | 'info' | success, error, warn, info, question, or '' for no mark. |
| title / msg | string | '' | Heading and body. |
| html | boolean | false | Treat msg as markup. Only pass markup you produced yourself. |
| showCancel | boolean | false | Add the second button. |
| confirmText / cancelText | string | locale | Name the buttons after what they do, not OK and Cancel. |
| danger | boolean | false | Paint the confirm button as destructive. |
| timer | number | 0 | Auto-dismiss after n ms, resolving false. |
| closeOnBackdrop | boolean | true | A click on the dimmed page dismisses. |
Kob.sweet.success, .error,
.warn, .info and .question are
shorthands taking (title, msg) or the same options
object.
statusbar
The strip along the bottom of a screen: what the app is doing, how many rows there are, who is signed in. CSS only — there is no widget to build and nothing to destroy.
| Class | What it is |
|---|---|
| kob-statusbar | The strip. Put it last inside a bordered box, or in a layout's south region. |
| kob-status-item | One entry. Aligns an icon, a dot and text on one line. |
| kob-status-dot | A coloured dot inside an item. |
| kob-status-sep | A vertical rule between groups. |
| kob-spacer | Pushes everything after it to the right. |
| kob-status-ok / -busy / -bad | On an item: colours its text and its dot green, amber or red. |
React, Vue and Angular
kob-ui is not a component library — it builds DOM imperatively. That works inside any framework, on one condition: give it an element the framework will leave alone, and destroy the widget when that element goes away.
The rule. Render an empty container, hand it to kob-ui, and never render children into it yourself. Everything inside belongs to the widget; everything outside belongs to your framework.
destroy() takes back every node the widget added and
returns the element as it found it, so mounting again on the same
element is safe — which is what React's StrictMode does in
development, and what any router does when you revisit a screen.
React
Vue 3
Angular
Widget callbacks fire outside Angular's zone by default. If a change
detection cycle needs to follow one, wrap it:
this.zone.run(() => …).
Things to watch
| Situation | What to do |
|---|---|
| Data changed | Call grid.loadData(rows) from an effect or watcher. Do not rebuild the widget. |
| Options changed | Kob.datagrid(el, { pageSize: 100 }) merges and refreshes in place. |
| A cell needs a framework component | A formatter returns an HTML string, not JSX. For rich cells, render into the row element from onLoadSuccess, or keep the cell simple and open a dialog on click. |
| Server rendering | Widgets touch the DOM, so build them in a client-only lifecycle hook — useEffect, onMounted, ngOnInit in the browser. |
| Strict mode double mount | Handled: destroy() restores the element, so the second mount produces exactly one widget. |
alert
The banner that stays on the page — a validation summary above a form, a warning across the top of a screen.
Two different things share the word.
Kob.alert() opens a modal dialog.
This inline banner is Kob.alertbox(), or
class="kob-alert" in markup.
| Option | Type | Default | Description |
|---|---|---|---|
| type | 'info' | 'success' | 'warn' | 'danger' | 'info' | Colour and icon. |
| title | string | '' | Bold line above the message. |
| msg | string | — | Body. Omit to keep what is already in the markup. |
| html | boolean | false | Treat title and message as HTML. |
| iconCls | string | false | by type | Override or remove the icon. |
| closable | boolean | false | Adds a dismiss button. |
| timeout | number | 0 | Dismiss by itself after this many ms. |
| onClose | function | — |
Methods: setMessage(s), setType(t), close().
badge
Static badges are markup. The widget is for the ones that change — an unread count on a toolbar button, a status that flips as data arrives.
| Option | Type | Default | Description |
|---|---|---|---|
| text | string | number | markup | What is shown. |
| type | string | 'muted' | muted, accent, success, warn, danger, info. |
| dot | boolean | false | A coloured dot with no text. |
| max | number | 99 | Counts above this show as "99+". |
| hideEmpty | boolean | true | Hide at 0 or empty. |
Methods: setText(v), getText(), setType(t), increment(by).
progressbar
A measured fill, or a sweeping stripe while the length of the job is unknown.
| Option | Type | Default | Description |
|---|---|---|---|
| value | number | 0 | 0-100, clamped. |
| indeterminate | boolean | false | Sweeping stripe; no percentage shown. |
| text | string | false | '{value}%' | Label template; false for none. |
| height | number | 20 | Bar height in pixels. |
| type | string | 'accent' | accent, success, warn, danger. |
| striped | boolean | false | Moving stripes on the fill. |
| onChange / onComplete | function | — | onComplete fires once, when the value first reaches 100. |
Methods: setValue(n), getValue(), indeterminate(on).
preloader and spinner
Over an element it is a busy mask; over <body> it is
the splash the page shows before the application has drawn anything.
The spinner on its own is a class, with three sizes:
| Option | Type | Default | Description |
|---|---|---|---|
| msg | string | '' | Caption under the spinner. |
| size | 'sm' | 'md' | 'lg' | 'md' | |
| visible | boolean | true | false builds it hidden. |
| backdrop | boolean | true | Dim what is underneath. |
| timeout | number | 0 | Hide by itself after this many ms. |
Methods: show(msg), hide(), isVisible(), setMessage(s).
A static element gets position: relative so the cover has
something to sit against; destroy() puts that back.
Grid, flex and button groups
Enough layout CSS to lay out a form or a dashboard without reaching for a whole framework. Deliberately small.
The 12-column row
Below 720px every column takes the full width — one honest breakpoint
rather than a matrix of per-size classes. For cards and tiles,
kob-autogrid fits as many columns as it can:
Flex helpers
| Class | Does |
|---|---|
| kob-flex / kob-inline-flex | Flex container with the standard gap. |
| kob-flex-col / kob-flex-wrap | Direction and wrapping. |
| kob-items-start / -center / -end / -baseline / -stretch | Cross-axis alignment. |
| kob-justify-start / -center / -end / -between / -around | Main-axis alignment. |
| kob-grow / kob-none | Take the slack, or refuse to. |
| kob-spacer | An empty element that pushes what follows to the far end. |
| kob-gap-0 … kob-gap-5 | Gap scale: 0, 6, 10, 14, 20, 28px. |
| kob-p-0 … kob-p-4, kob-mt-*, kob-mb-* | Padding and vertical margin on the same scale. |
| kob-fill / kob-w-full / kob-h-full / kob-scroll | Sizing and overflow. |
| kob-truncate / kob-muted / kob-small / kob-strong / kob-text-* | Text. |
Button groups and toolbars
Keep selected on one member and the group reads as a
segmented control. kob-btn-group-vertical stacks it.
Custom widgets
Extend Kob.Widget and register it. From then on it behaves
like a built-in: Kob.parse() builds it from markup,
data-options works, and the accessor returns the instance.
| Member | Description |
|---|---|
| static widgetName | Drives the class name: 'rating' → class="kob-rating". |
| static defaults | Merged under data-options and the constructor argument. |
| this.el | The element the widget is mounted on. |
| this.options | The resolved options. |
| init() | Build the DOM. Called by the constructor. |
| refresh() | Called when new options arrive after construction. |
| track(off) | Register a cleanup function — pass the unbinder Kob.dom.on returns. |
| emit(hook, ...args) | Fire an option callback with the element as this. |
| destroy() | Runs every tracked cleanup. Extend it, then call super.destroy(). |
Kob reference
| Member | Description |
|---|---|
| Kob.version | The library version. |
| Kob.parse(root) / unparse(root) / autoParse() | Build or tear down the widgets declared in markup. |
| Kob.parseOptions(str) | Read the inline data-options syntax. |
| Kob.instance(el, name) | The widget of that name on an element, if any. |
| Kob.config | request, onRequestError, prefix, zIndex, locale. |
| Kob.locale(name?) / addLocale(name, bundle) / locales / t(key, vars) | Localisation. |
| Kob.zIndex() | Next z-index for a floating layer, shared by every widget. |
| Kob.Widget / register(Class) / registry | Extension points. |
| Kob.dom | The internal DOM helpers: on, el, append, css, escapeHtml, … |
| Kob.date | parseDate, formatDate, toISO, today. |
| Kob.classes | The widget classes themselves, for extends. |