Register

Documentation

How the template is made, and how to make it yours

On this page

Getting started

Open it, or publish it

The template is plain HTML, CSS and JavaScript, with no framework. Open index.html in a browser to use it, or put the folder on any web server to publish it: every link is relative, so it works from any folder.

register.html, the counter, is written by hand; every other page is written by the generator in src/. Change a generated page through its builder, or edit its HTML directly if you will not run the generator again.

Pages
449
Groups in the menu
32
Modules you can turn off
16
Browsers
The current Chrome, Edge, Firefox and Safari: the CSS uses :has(), container queries, color-mix() and logical properties, which all four support
Measured in
Chrome, at 13 screen sizes from 320 to 1920 px wide, light and dark

Files

What is in the folder

Files
PathWhat it holds
*.html449 pages, one file each; index.html is the dashboard
emails/22 email templates
assets/js/config.jsThe one configuration: the modules, the store, the demo’s date
assets/js/charts-*.js5 starters for the chart-library pages
assets/demo/products/27 product photos
system/tokens.cssThe colours, in light and in dark
system/system.cssType, spacing, the components and the layouts
system/system.jsThe behaviours: menus, dialogs, tables, forms and the rest
system/icons.jsThe Tabler icons the pages use
src/build.mjsThe generator: writes every page
src/nav.jsonThe menu: sections, groups, pages and the module of each
src/kit.mjsThe frame and the components
src/data.mjsThe demo store, from which every figure is worked out
src/patterns.mjsParts several pages share
src/pages/27 page modules
src/vendor/Tabler’s icon outlines, for building icons.js

The generator

Pages written from one source

It needs Node.js 22 or newer, the version it is built with, and nothing else: there are no packages to install.

Every page, or only the pages named
node src/build.mjs
node src/build.mjs products-list sales

Each page is a function in a module of src/pages that returns the page’s body; kit.mjs puts it in the frame (the sidebar, the top bar, the footer) and loads what it needs: a page with a chart gets ApexCharts. data.mjs is the demo store, and every figure on every page is worked out from it, so totals always add up.

Add a page

  1. Add it to src/nav.json, in its group: a title, a slug and the module it belongs to.
  2. Export a builder for the slug from a module in src/pages.
  3. Run the generator. It stops with a message if a page is missing from the menu, or uses an icon Tabler does not have.
A page module
// src/pages/my-pages.mjs
import * as K from '../kit.mjs';

export default {
  'stock-note': () => ({
    body: K.pageHead({ title: 'Stock note', sub: 'What this page is for' })
      + K.card({ title: 'Today', body: '<p>Anything the kit can build.</p>' }),
  }),
};
A group in src/nav.json (the Help group)
{
  "label": "Help",
  "icon": "help",
  "pages": [
    {
      "title": "Documentation",
      "slug": "documentation",
      "module": "core"
    },
    {
      "title": "Changelog",
      "slug": "changelog",
      "module": "core"
    }
  ]
}

The one configuration

assets/js/config.js, read first by every page

Set a module to false and its pages, its menu groups and its widgets leave the menu and the screens; nothing else needs editing. A link elsewhere to one of its pages goes too, or keeps its words without the link, and a heading or a group it leaves empty goes with it. core is the product itself and is always on.

assets/js/config.js
window.POS_CONFIG = {
  store: 'Corner Store',
  currency: 'USD',
  locale: 'en-US',
  today: '2026-10-04',
  modules: {
    core: true,
    restaurant: false,   // tables, kitchen display, bar tabs… leave the menu and every screen
    …
  }
};
Modules
ModulePagesWhat it covers
core344The product itself; always on
restaurant5Tables, kitchen display, bar tabs, kitchen printer, dining options
pharmacy1Batch and expiry at the counter
grocery2Weighing at the counter, the scale
fashion1Size and colour at the counter
booking1Services and appointments
kiosk2Self-checkout kiosk
loyalty9Points, rewards, cards, campaigns
multi-store2Several stores, transfers between them
online15Online store and channels
hr10Departments, attendance, leave, payroll
integrations6App directory, API keys, webhooks, syncs
saas6Companies, plans and subscriptions (multi-tenant)
apps16Calendar, mail, chat, kanban and the other workspace apps
cms10Pages, blog, places, FAQ
chart-libraries6Chart libraries
icons13Icons

today is the demo’s “today”: date filters count back from the day the figures were made. Remove it and they count from the real date.

Colours and themes

Tokens, light and dark

system/tokens.css holds the colour ramps and what each colour is for, in light and in dark (180 tokens); system/system.css holds the type, the spacing, the corners and the components, all drawn from tokens.

The accent is the blue ramp, --blue-50 to --blue-950, at the top of tokens.css: give it your brand’s ramp and the buttons, links, focus rings and selected states follow in both themes. Keep --accent-text at 4.5:1 or more against --bg-surface.

The theme button in the top bar sets Light, Dark or the device’s own setting, and the device remembers it (pos.theme in local storage). data-theme on <html> fixes one theme; data-theme-default="dark" opens a page dark, as the Dark layout does. Charts redraw in the new colours.

A page that is always dark
<html lang="en" data-theme="dark">

Layouts

One attribute on the frame each

Layouts
LayoutWhat it isTurn it on
VerticalThe sidebar on the left, the page on the right: the template’s default.<div class="app">
HorizontalThe sections across the top under the top bar; each opens its groups and pages. On a phone the menu is the drawer again.<div class="app" data-layout="horizontal">
DetachedThe sidebar as a card floating beside the page, with space around it.<div class="app" data-layout="detached">
Two columnA rail of the sections, and the pages of the current section beside it.<div class="app" data-layout="two-column">
Hover-expand railThe sidebar folded to its icons; pointing at it or tabbing into it opens it over the page.<div class="app" data-sidebar="rail">
BoxedThe whole app in a centred box on a wide screen.<div class="app" data-layout="boxed">
Right to leftMirrored for Arabic, Hebrew, Persian and Urdu: the sidebar on the right, the arrows turned.<html lang="ar" dir="rtl">
DarkThe dark theme, as the page opens. The theme button in the top bar still switches it.<html data-theme="dark">

Right to left

Arabic, Hebrew, Persian and Urdu

Set dir="rtl", with your language, on <html>. The layout mirrors: the sidebar moves to the right, arrows turn, and every margin and padding is logical, so it follows. Text takes its direction from its own words, so an English line keeps its full stop at its end and figures read left to right; code blocks stay left to right.

ApexCharts draws left to right in either direction (its canvas is fixed to it); Chart.js legends and tooltips read right to left.

A right-to-left page
<html lang="ar" dir="rtl">

See the right-to-left layout

Behaviours

Added by attribute, no script of your own

system.js wires every behaviour by attribute: add the attribute and the behaviour comes with it, with its keyboard support. The UI kit pages show each one working, with its markup to copy.

Behaviours
AttributeWhat it does
data-dialog="id"Opens the dialog with that id: focus moves in and stays there, Escape closes it, focus comes back to the button.
data-drawer="id"The same for a drawer from the side.
data-edit="name"On a row’s Edit button that opens its list’s Add form: the form opens as the edit form for that row.
data-popover="id"Opens a menu or popover under the button; Escape or a press outside closes it.
data-tabsOn a role="tablist": the arrow keys move between the tabs, each tab shows its own panel.
data-radiosOn a role="radiogroup": one Tab stop, the arrow keys choose.
data-collapseOn a button with aria-expanded and aria-controls: shows or hides that panel.
data-dismissRemoves the alert it sits in and says so.
data-confirmIn a confirm dialog: does what the dialog asks (removes the row) and says so.
data-copy="id"Copies the text of the element with that id and says it was copied.
data-toast="text"Shows a short message at the corner of the screen.
data-validateOn a form: checks each field when it is sent, names what is wrong under it and moves focus to the first.
data-mask="phone"Formats what is typed: phone, date, card, expiry, zip, money or sku.
data-padA number pad that types into its field, as money or as a PIN.
data-periodThe Today, 7 days and 30 days switch: every figure marked data-by-period follows it.
data-chartA chart drawn by ApexCharts from the JSON in the attribute, in the theme’s colours.
data-scroller="name"A box that scrolls sideways becomes a named region and a Tab stop while it scrolls.
data-wizardSteps shown one at a time, with Back and Next and the step list.
data-sortableA list put in order by dragging, or by each row’s Move up and Move down.
data-carouselOne slide at a time, with previous, next and a dot for each; nothing moves by itself.
data-tocAn “On this page” list that marks the section being read.
data-module="name"Hidden when that module is off in assets/js/config.js.
data-fallback="page.html"On a link to a page of a module: where it goes instead when that module is off.

Charts

ApexCharts 3.54.1

A chart is described in JSON in data-chart and drawn by ApexCharts in the theme’s colours, so it follows a change of theme. In the generator, K.chart({ type, categories, series, money }) writes it, with the chart’s figures in a table for screen readers.

The Charts pages show 20 kinds on the store’s figures; the Chart libraries pages draw the same figures with 6 libraries, for a team that already uses one.

Icons

Tabler Icons 3.48.0

The template draws Tabler Icons as inline SVG: write <i class="ti ti-NAME"></i> and system/icons.js puts the icon in its place. icons.js holds only the icons the pages use, so after you use a new one, run the generator and it is added.

The Icons pages show 13 sets; each one loads from a CDN on its own page only.

Accessibility

WCAG 2.2 AA, measured

  • Text, controls and their edges meet the AA contrast ratios in both themes.
  • Everything works from the keyboard: menus, dialogs, tabs and radio groups follow the WAI-ARIA patterns, and focus can always be seen.
  • Targets are at least 24 px; at the counter they are larger, for a touchscreen.
  • A page reflows to 320 px wide, and keeps working with text spacing increased (WCAG 1.4.10 and 1.4.12).
  • Movement stops when the device asks for reduced motion.
  • Every chart has its figures in a table for screen readers.

Libraries and credits

What the pages load, and its licence

Libraries
LibraryKindVersionLicenceFromUsed on
Geist MonoFontscurrentSIL OFL 1.1fonts.googleapis.comEvery page
InterFontscurrentSIL OFL 1.1fonts.googleapis.comEvery page
Bootstrap IconsIcons1.11.3MITcdnjs.cloudflare.comBootstrap
FeatherIcons4.29.2MITcdnjs.cloudflare.comFeather
Flag IconsIcons7.2.3MITcdnjs.cloudflare.comFlags
Font AwesomeIcons6.5.2Icons CC BY 4.0, font SIL OFL 1.1, code MITcdnjs.cloudflare.comFont Awesome
IoniconsIcons7.4.0MITcdn.jsdelivr.netIonicons
Line AwesomeIcons1.3.0MITcdnjs.cloudflare.comLine Awesome
Material SymbolsIconsOutlinedApache 2.0fonts.googleapis.comMaterial
Remix IconIcons4.2.0Apache 2.0cdnjs.cloudflare.comRemix
Simple Line IconsIcons2.5.5MITcdnjs.cloudflare.comSimple Line
Tabler IconsIcons3.48.0MITsystem/icons.js, inline SVGEvery page
Themify IconsIcons0.1.2Font SIL OFL 1.1, code MITcdn.jsdelivr.netThemify
TypiconsIcons2.1.2Icons CC BY-SA 4.0, font SIL OFL 1.1cdn.jsdelivr.netTypicons
Weather IconsIcons2.0.12Font SIL OFL 1.1, code MITcdnjs.cloudflare.comWeather
ApexChartsCharts3.54.1MITcdnjs.cloudflare.com55 pages
C3Charts0.7.20MITcdnjs.cloudflare.comC3
Chart.jsCharts4.4.1MITcdn.jsdelivr.netChart.js
D3Charts5.16.0BSD-3-Clausecdnjs.cloudflare.comC3
FlotCharts0.8.3MITcdnjs.cloudflare.comFlot
jQueryCharts3.7.1MITcdnjs.cloudflare.comFlot, Morris, Peity
Morris.jsCharts0.5.1BSD-2-Clausecdnjs.cloudflare.comMorris
PeityCharts3.3.0MITcdnjs.cloudflare.comPeity
RaphaëlCharts2.3.0MITcdnjs.cloudflare.comMorris
QR Code GeneratorCodes1.4.4MITcdnjs.cloudflare.com5 pages

Product photos in the demo: 23 from Unsplash and 4 from Pexels, under the Unsplash and Pexels licences, which allow commercial use without credit. The download carries placeholders in their place.

Product photos
PhotoPhotographerFrom
Oat flat whiteJeremy YapUnsplash
CappuccinoAnubhav AroraUnsplash
Matcha latteJason LeungUnsplash
Hot chocolateElena LeyaUnsplash
EspressoKatt GalvanUnsplash
Chai latteKamal Preet KaurUnsplash
Cold brewpariwat panniumUnsplash
Sparkling waterSam MaiPexels
Orange juiceGreg RosenkeUnsplash
Iced teaMae MuUnsplash
LemonadeLaura ChouetteUnsplash
KombuchaGeraud pfeifferPexels
Butter croissantVicky NguyenUnsplash
Raspberry tartAlexandra KusperUnsplash
Pain au chocolatSkyler EwingUnsplash
Chocolate cookieEllie EllienUnsplash
Banana breadCody ChanUnsplash
Blueberry muffinTayla BrandUnsplash
Cinnamon rollYosep SugiartoUnsplash
Sourdough loafMonika GrabkowskaUnsplash
Whole milk, 1 LMary SkrynnikovaUnsplash
Free-range eggs, 12Morgane PerraudUnsplash
Oat milk, 1 Lhello aesthePexels
Granola, 500 gSollange BrenisUnsplash
Wildflower honeyArt RachenUnsplash
Gift boxBaibhav KumarUnsplash
Tote bagMr. MockupPexels