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
| Path | What it holds |
|---|---|
*.html | 449 pages, one file each; index.html is the dashboard |
emails/ | 22 email templates |
assets/js/config.js | The one configuration: the modules, the store, the demo’s date |
assets/js/charts-*.js | 5 starters for the chart-library pages |
assets/demo/products/ | 27 product photos |
system/tokens.css | The colours, in light and in dark |
system/system.css | Type, spacing, the components and the layouts |
system/system.js | The behaviours: menus, dialogs, tables, forms and the rest |
system/icons.js | The Tabler icons the pages use |
src/build.mjs | The generator: writes every page |
src/nav.json | The menu: sections, groups, pages and the module of each |
src/kit.mjs | The frame and the components |
src/data.mjs | The demo store, from which every figure is worked out |
src/patterns.mjs | Parts 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.
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
- Add it to
src/nav.json, in its group: a title, a slug and the module it belongs to. - Export a builder for the slug from a module in
src/pages. - Run the generator. It stops with a message if a page is missing from the menu, or uses an icon Tabler does not have.
// 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>' }),
}),
};{
"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.
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
…
}
};| Module | Pages | What it covers |
|---|---|---|
core | 344 | The product itself; always on |
restaurant | 5 | Tables, kitchen display, bar tabs, kitchen printer, dining options |
pharmacy | 1 | Batch and expiry at the counter |
grocery | 2 | Weighing at the counter, the scale |
fashion | 1 | Size and colour at the counter |
booking | 1 | Services and appointments |
kiosk | 2 | Self-checkout kiosk |
loyalty | 9 | Points, rewards, cards, campaigns |
multi-store | 2 | Several stores, transfers between them |
online | 15 | Online store and channels |
hr | 10 | Departments, attendance, leave, payroll |
integrations | 6 | App directory, API keys, webhooks, syncs |
saas | 6 | Companies, plans and subscriptions (multi-tenant) |
apps | 16 | Calendar, mail, chat, kanban and the other workspace apps |
cms | 10 | Pages, blog, places, FAQ |
chart-libraries | 6 | Chart libraries |
icons | 13 | Icons |
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.
<html lang="en" data-theme="dark">
Layouts
One attribute on the frame each
| Layout | What it is | Turn it on |
|---|---|---|
| Vertical | The sidebar on the left, the page on the right: the template’s default. | <div class="app"> |
| Horizontal | The 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"> |
| Detached | The sidebar as a card floating beside the page, with space around it. | <div class="app" data-layout="detached"> |
| Two column | A rail of the sections, and the pages of the current section beside it. | <div class="app" data-layout="two-column"> |
| Hover-expand rail | The sidebar folded to its icons; pointing at it or tabbing into it opens it over the page. | <div class="app" data-sidebar="rail"> |
| Boxed | The whole app in a centred box on a wide screen. | <div class="app" data-layout="boxed"> |
| Right to left | Mirrored for Arabic, Hebrew, Persian and Urdu: the sidebar on the right, the arrows turned. | <html lang="ar" dir="rtl"> |
| Dark | The 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.
<html lang="ar" dir="rtl">
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.
| Attribute | What 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-tabs | On a role="tablist": the arrow keys move between the tabs, each tab shows its own panel. |
data-radios | On a role="radiogroup": one Tab stop, the arrow keys choose. |
data-collapse | On a button with aria-expanded and aria-controls: shows or hides that panel. |
data-dismiss | Removes the alert it sits in and says so. |
data-confirm | In 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-validate | On 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-pad | A number pad that types into its field, as money or as a PIN. |
data-period | The Today, 7 days and 30 days switch: every figure marked data-by-period follows it. |
data-chart | A 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-wizard | Steps shown one at a time, with Back and Next and the step list. |
data-sortable | A list put in order by dragging, or by each row’s Move up and Move down. |
data-carousel | One slide at a time, with previous, next and a dot for each; nothing moves by itself. |
data-toc | An “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
| Library | Kind | Version | Licence | From | Used on |
|---|---|---|---|---|---|
| Geist Mono | Fonts | current | SIL OFL 1.1 | fonts.googleapis.com | Every page |
| Inter | Fonts | current | SIL OFL 1.1 | fonts.googleapis.com | Every page |
| Bootstrap Icons | Icons | 1.11.3 | MIT | cdnjs.cloudflare.com | Bootstrap |
| Feather | Icons | 4.29.2 | MIT | cdnjs.cloudflare.com | Feather |
| Flag Icons | Icons | 7.2.3 | MIT | cdnjs.cloudflare.com | Flags |
| Font Awesome | Icons | 6.5.2 | Icons CC BY 4.0, font SIL OFL 1.1, code MIT | cdnjs.cloudflare.com | Font Awesome |
| Ionicons | Icons | 7.4.0 | MIT | cdn.jsdelivr.net | Ionicons |
| Line Awesome | Icons | 1.3.0 | MIT | cdnjs.cloudflare.com | Line Awesome |
| Material Symbols | Icons | Outlined | Apache 2.0 | fonts.googleapis.com | Material |
| Remix Icon | Icons | 4.2.0 | Apache 2.0 | cdnjs.cloudflare.com | Remix |
| Simple Line Icons | Icons | 2.5.5 | MIT | cdnjs.cloudflare.com | Simple Line |
| Tabler Icons | Icons | 3.48.0 | MIT | system/icons.js, inline SVG | Every page |
| Themify Icons | Icons | 0.1.2 | Font SIL OFL 1.1, code MIT | cdn.jsdelivr.net | Themify |
| Typicons | Icons | 2.1.2 | Icons CC BY-SA 4.0, font SIL OFL 1.1 | cdn.jsdelivr.net | Typicons |
| Weather Icons | Icons | 2.0.12 | Font SIL OFL 1.1, code MIT | cdnjs.cloudflare.com | Weather |
| ApexCharts | Charts | 3.54.1 | MIT | cdnjs.cloudflare.com | 55 pages |
| C3 | Charts | 0.7.20 | MIT | cdnjs.cloudflare.com | C3 |
| Chart.js | Charts | 4.4.1 | MIT | cdn.jsdelivr.net | Chart.js |
| D3 | Charts | 5.16.0 | BSD-3-Clause | cdnjs.cloudflare.com | C3 |
| Flot | Charts | 0.8.3 | MIT | cdnjs.cloudflare.com | Flot |
| jQuery | Charts | 3.7.1 | MIT | cdnjs.cloudflare.com | Flot, Morris, Peity |
| Morris.js | Charts | 0.5.1 | BSD-2-Clause | cdnjs.cloudflare.com | Morris |
| Peity | Charts | 3.3.0 | MIT | cdnjs.cloudflare.com | Peity |
| Raphaël | Charts | 2.3.0 | MIT | cdnjs.cloudflare.com | Morris |
| QR Code Generator | Codes | 1.4.4 | MIT | cdnjs.cloudflare.com | 5 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.
| Photo | Photographer | From |
|---|---|---|
| Oat flat white | Jeremy Yap | Unsplash |
| Cappuccino | Anubhav Arora | Unsplash |
| Matcha latte | Jason Leung | Unsplash |
| Hot chocolate | Elena Leya | Unsplash |
| Espresso | Katt Galvan | Unsplash |
| Chai latte | Kamal Preet Kaur | Unsplash |
| Cold brew | pariwat pannium | Unsplash |
| Sparkling water | Sam Mai | Pexels |
| Orange juice | Greg Rosenke | Unsplash |
| Iced tea | Mae Mu | Unsplash |
| Lemonade | Laura Chouette | Unsplash |
| Kombucha | Geraud pfeiffer | Pexels |
| Butter croissant | Vicky Nguyen | Unsplash |
| Raspberry tart | Alexandra Kusper | Unsplash |
| Pain au chocolat | Skyler Ewing | Unsplash |
| Chocolate cookie | Ellie Ellien | Unsplash |
| Banana bread | Cody Chan | Unsplash |
| Blueberry muffin | Tayla Brand | Unsplash |
| Cinnamon roll | Yosep Sugiarto | Unsplash |
| Sourdough loaf | Monika Grabkowska | Unsplash |
| Whole milk, 1 L | Mary Skrynnikova | Unsplash |
| Free-range eggs, 12 | Morgane Perraud | Unsplash |
| Oat milk, 1 L | hello aesthe | Pexels |
| Granola, 500 g | Sollange Brenis | Unsplash |
| Wildflower honey | Art Rachen | Unsplash |
| Gift box | Baibhav Kumar | Unsplash |
| Tote bag | Mr. Mockup | Pexels |