@hotosm/ui
v3.0.0
Published
Shared UI components with HOT theming.
Keywords
Readme
HOT Shared UI
Overview
Shared Web Components built with Lit and themed for HOTOSM tools using WebAwesome primitives.
Goals:
- Reduce code duplication across HOT tools.
- Provide a consistent HOT look-and-feel out of the box.
- Keep the component set small and focused (header, sidebar, footer, etc.).
WebAwesome version: 3.11.0
Installation
pnpm install @hotosm/ui @awesome.me/[email protected][!NOTE]
@awesome.me/webawesomeis a peer dependency. Always pin it to the version shown above to avoid conflicts between HOT components and WebAwesome internals.
Loading Styles
Use the single self-contained stylesheet (WebAwesome base styles + HOT theme inlined):
import "@hotosm/ui/dist/style.css";Or from CDN:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@hotosm/[email protected]/dist/style.css" />style-core.css (HOT theme only, no WebAwesome) exists for pages that
load the WebAwesome base stylesheets themselves - most apps don't need it.
Fonts
Nothing to add. style.css and style-core.css include self-hosted
@font-face rules for the HOT brand fonts: Archivo (--hot-font-sans),
Barlow (--hot-font-sans-variant) and Barlow Condensed
(--hot-font-sans-variant-condensed). The woff2 files ship in
dist/assets/fonts as latin / latin-ext / vietnamese subsets with
unicode-range, so browsers only download what a page renders. Remove any
Google Fonts <link> for these families.
Scripts these fonts don't cover fall back to Helvetica (Apple) or Noto
(Android / ChromeOS), then the system sans. Set lang on <html> and on any
content in a different language from the page (e.g. user-created project
names) - browsers use it to pick the right fallback, and it is what future
per-language :lang() tweaks will hook onto.
See MADR 0014 and the font test page.
HTML Setup
Add the required WebAwesome classes to your <html> element:
<!DOCTYPE html>
<html class="wa-theme-default wa-palette-hotosm">
<head>
...
</head>
<body>
...
</body>
</html>[!IMPORTANT] These classes activate the HOT colour palette and light theme for every WebAwesome component on the page. Do not remove them.
Semantic variants
brand, danger, warning and neutral follow the HOT palette with no extra
classes. Since 3.0.0 brand follows HOT primary, which is grey (see
hotosm/ui-design).
success stays green rather than the HOT teal, which reads poorly against a
red danger.
Any variant can take any hue via WebAwesome's variant classes, which override the theme - e.g. to opt in to the teal:
<html class="wa-theme-default wa-palette-hotosm wa-success-cyan"></html>Using Components
Via Bundler (Vite, Webpack, etc.)
Recommended: register all WebAwesome elements once, up front, so every custom element is defined before first render - this eliminates flash of undefined custom elements (FOUCE) without maintaining per-page import lists:
import "@hotosm/ui/dist/style.css";
import "@hotosm/ui/dist/webawesome-all.js"; // registers every wa-* element
import "@hotosm/ui/dist/components/header/header.js"; // hot-* components you use<hot-header title="My App"></hot-header>Build setup
@hotosm/ui/vite provides build helpers for Vite 5, 7, and 8.
Add the circular chunk guard to catch chunk cycles that can cause a blank page at runtime:
// vite.config.ts
import { circularChunkGuard } from "@hotosm/ui/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [circularChunkGuard()],
});Keep the guard enabled even if you do not split chunks manually. Dependency updates can also introduce cycles.
You can also place WebAwesome in its own chunk. This lets browsers keep it cached when the rest of your app changes:
For Vite 8:
// vite.config.ts
import { WEBAWESOME_CHUNK } from "@hotosm/ui/vite";
import { defineConfig } from "vite";
export default defineConfig({
build: {
rolldownOptions: {
output: {
codeSplitting: {
groups: [WEBAWESOME_CHUNK],
},
},
},
},
});For Vite 5-7:
// vite.config.ts
import { matchWebawesome } from "@hotosm/ui/vite";
import { defineConfig } from "vite";
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: (id) => matchWebawesome(id),
},
},
},
});This cache is per origin, so it is reused across deployments of the same app, not across different HOT tools.
Via CDN / Plain HTML / HTMX
Load the raw dist files, with an import map to resolve the bare
@awesome.me/webawesome/... imports in webawesome-all.js. The map's
WebAwesome pin must match the version this @hotosm/ui release was
built against. Do not use jsDelivr's /+esm URLs - they duplicate
element registrations and most wa-* elements never register.
<!DOCTYPE html>
<!-- wa-cloak: hides the page until all custom elements are defined (2s max) -->
<html class="wa-theme-default wa-palette-hotosm wa-cloak">
<head>
<!-- Fonts: see "Fonts" above -->
<!-- WebAwesome base styles + HOT theme, self-contained -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@hotosm/[email protected]/dist/style.css" />
<script type="importmap">
{
"imports": {
"@awesome.me/webawesome/dist/components/": "https://cdn.jsdelivr.net/npm/@awesome.me/[email protected]/dist-cdn/components/"
}
}
</script>
<!-- Register every wa-* element, then the hot-* components -->
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@hotosm/[email protected]/dist/webawesome-all.js"
></script>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@hotosm/[email protected]/dist/hotosm-ui.js"
></script>
</head>
<body>
<hot-header id="hdr" title="My App" size="s" show-login></hot-header>
<script>
// Boolean props default to false.
// Include the attribute to make it true.
const hdr = document.getElementById("hdr");
hdr.drawer = true; // enable the hamburger drawer
</script>
</body>
</html>For multi-page (HTMX) apps, also inline a few critical theme tokens in
<head> so first paint looks right before the CDN CSS arrives - see
Loading Strategies.
React
Web Components work in React with a small caveat - use ref callbacks
for custom events if React's synthetic event system doesn't forward them:
import "@hotosm/ui/dist/components/header/header.js";
function App() {
return (
<hot-header
title="My App"
ref={(el) => {
if (el) el.addEventListener("login", () => console.log("logged in"));
}}
/>
);
}Using HOT Design Tokens
The HOT theme exposes CSS custom properties you can reference in your own stylesheets:
.my-card {
background: var(--hot-color-primary-50);
color: var(--hot-color-neutral-900);
font-family: var(--hot-font-sans);
padding: var(--hot-spacing-medium);
border-radius: var(--hot-border-radius-large);
}See all available tokens in
src/themes/hot.css.
Component Guides
- Header: see
header.mdfor detailed integration, styling, and framework examples.
Contributing
git clone [email protected]:hotosm/ui.git
cd ui
pnpm install
pnpm run dev # starts Storybook on localhost:3001Styling files (under src/themes/):
| File | Purpose |
| ------------ | ------------------------------------------------------ |
| hot.css | HOT design tokens (colours, typography, spacing) |
| hot-wa.css | WebAwesome variable overrides to apply the HOT palette |
License
HOT UI is free and open source software. You may use any HOT UI project under the terms of the GNU General Public License (GPL) Version 3.
