Skip to content

Because every npm datatable looks great until your designer opens Figma. Zero deps, framework-agnostic, CSS that actually listens to you.

License

Notifications You must be signed in to change notification settings

waga97/Mundane-UI

Repository files navigation

Mundane UI

Mundane UI

The boring UI library.
Framework-agnostic, zero-dependency, lightweight UI component library.
Written in TypeScript, works everywhere — React, Vue, Angular, Svelte, or plain HTML.

npm version npm downloads license zero dependencies bundle size

Documentation · Getting Started · Why This Exists


Install

npm install mundane-ui

Quick Start

<link rel="stylesheet" href="mundane-ui/css/plain.css">
<div id="my-table"></div>
import { DataTable } from 'mundane-ui'
import 'mundane-ui/css/plain.css'

const table = DataTable.create({
  el: '#my-table',
  mode: 'frontend',
  data: [
    { name: 'Alice', age: 30, email: 'alice@example.com' },
    { name: 'Bob', age: 25, email: 'bob@example.com' },
  ],
  columns: [
    { key: 'name', label: 'Name', type: 'string', sortable: true },
    { key: 'age', label: 'Age', type: 'number', sortable: true, thousandSeparator: true },
    { key: 'email', label: 'Email', type: 'string', sortable: true },
  ],
})

Features

  • Zero dependencies — pure vanilla TypeScript
  • Framework-agnostic — plain JS class, mounts to any DOM element
  • Two modes — frontend (client-side) and backend (server-side with QSP emission)
  • Sorting — single-column, per-type comparators (string, number, date, custom)
  • Search — global search, column filters, or both (debounced)
  • Pagination — configurable page sizes, ellipsis for large page counts
  • Column alignment — left/center/right per column (numbers default to right)
  • Number formatting — optional thousand separator (e.g. 1,000,000)
  • Date parsing — built-in parser for common date formats (YYYY, MM, DD, HH, mm, ss)
  • Custom columns — render any HTML with your own function
  • XSS safe — all data values are HTML-escaped
  • CSS customizable — CSS custom properties, class overrides, or Tailwind utility classes
  • Lightweight — ~9KB gzipped

Frontend Mode

All data is held in memory. Sorting, filtering, and pagination happen client-side.

const table = DataTable.create({
  el: '#table',
  mode: 'frontend',
  data: myData,
  pageSize: 25,
  search: { enabled: true, mode: 'both', debounce: 300 },
  columns: [
    { key: 'id', label: 'ID', type: 'number', sortable: true, align: 'center' },
    { key: 'name', label: 'Name', type: 'string', sortable: true },
    { key: 'salary', label: 'Salary', type: 'number', sortable: true, thousandSeparator: true },
    { key: 'joinDate', label: 'Joined', type: 'date', dateFormat: 'DD/MM/YYYY', sortable: true },
    {
      key: 'actions', label: 'Actions', type: 'custom',
      render: (row) => `<button onclick="edit(${row.id})">Edit</button>`,
    },
  ],
})

Backend Mode

The component does not sort/filter/paginate. It emits query string parameters on every state change, and you fetch the data however you want.

const table = DataTable.create({
  el: '#table',
  mode: 'backend',
  data: initialPageData,
  totalRows: 500,
  columns: [ /* ... */ ],
  onStateChange: async (queryString, state) => {
    // queryString: "?page=2&pageSize=10&sortBy=name&sortOrder=asc&search=john"
    const res = await fetch('/api/users' + queryString)
    const { data, total } = await res.json()
    table.setData(data, total)
  },
})

Column Types

Type Default Align Sortable Searchable Options
string left localeCompare substring match
number right numeric toString match thousandSeparator
date left Date comparison display string match dateFormat, displayFormat
custom left via comparator via filterFn render

CSS Customization

Layer 1 — Theme file:

import 'mundane-ui/css/plain.css'

Layer 2 — CSS custom properties:

.mu-datatable {
  --mu-color-border: #e2e8f0;
  --mu-color-bg-header: #f8fafc;
  --mu-border-radius: 8px;
}

Layer 3 — Direct class overrides:

.mu-datatable__header-cell {
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

Layer 4 — Tailwind via classes config:

DataTable.create({
  classes: {
    root: 'rounded-xl border border-gray-200 shadow-sm',
    headerCell: 'bg-gray-50 text-xs font-semibold uppercase',
    row: 'hover:bg-blue-50 transition-colors',
    pageButtonActive: 'bg-blue-600 text-white',
  },
  // ...
})

Public API

Method Description
setData(data, totalRows?) Replace dataset, triggers re-render
updateRow(index, rowData) Update a specific row
addRow(rowData, index?) Add a row
removeRow(index) Remove a row
setPageSize(size) Change page size (resets to page 1)
goToPage(page) Navigate to a page
setSearch(value) Set global search programmatically
setColumnFilter(key, value) Set a column filter
clearFilters() Clear all filters and search
setLoading(bool) Show/hide loading overlay
getState() Get current state object
getQueryString() Get current query string
setColumns(columns) Update column definitions
destroy() Remove DOM and event listeners
refresh() Force full re-render

License

MIT

About

Because every npm datatable looks great until your designer opens Figma. Zero deps, framework-agnostic, CSS that actually listens to you.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published