@thebase/ui
Version:
CDN-installable Owl and Bootstrap 5 UI component library.
106 lines (78 loc) • 8.23 kB
Markdown
# Pure Owl Components
See the [Usage Guide](usage-guide.md) for when to reach for this API versus the static `b-ui` one. This page covers the pure Owl API only.
BaseUI ships a second, additive public API alongside the static `b-ui` markers: real Owl component classes you import, compose with `static components`, and mount as part of your own Owl app. This page covers how to load and use it; see [Component Authoring](component-authoring.md) for how components implement both APIs from one source folder.
As of 1.1.0, every component in the registry (all 65) has a pure Owl export — there are no static-only components left. Compound components export a root class plus sub-components (e.g. `Dialog`/`DialogTrigger`/`DialogContent`/...); check a component's `owlComponents` entry in `dist/baseui.registry.json`, or its doc page under [`docs/components/`](components/), for the exact export list. A few components have a deliberately reshaped pure Owl API versus their static markup-scanning adapter — see [Registry](registry.md#component-export-policy) for which ones and why.
## What you need to load
Everything — the static `b-ui` runtime, the Owl framework, every pure Owl component class, and the theme helper — is one file: `dist/baseui.esm.js` (or `dist/baseui.min.js` as a classic script). There is no separate `baseui.owl.*`/`baseui.components.esm.js`/`baseui.theme.esm.js` to load.
| File | Purpose |
| --- | --- |
| `dist/baseui.min.css` | Bootstrap + BaseUI styling, shared by both APIs. |
| `/owl` (`dist/baseui.esm.js`) | The Owl framework — `Component`, `mount`, `xml`, `useState`, etc. Pinned to Owl `2.8.3`, embedded directly in this file. Import your app's own Owl usage from here too, not `/owl`. |
| `/component` (`dist/baseui.esm.js`) | The pure Owl component classes — same file as `/owl` above, just a different import-map key for readability. |
| `/theme` (`dist/baseui.esm.js`) | Same file again — the theme helper for persisted light/dark toggles, URL overrides, button-label sync, and iframe preview sync. |
| `/templates` (`dist/baseui.templates.xml`) | The Owl templates the component classes reference (`static template = "baseui.Button"`, etc). Must be loaded and passed to `mount()` before any BaseUI component tag renders — a component class alone does not carry its template. |
## Browser (import map) usage
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.min.css">
<script type="importmap">
{
"imports": {
"@base/owl": "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.esm.js",
"@base/component": "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.esm.js",
"@base/theme": "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.esm.js",
"@base/templates": "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.templates.xml"
}
}
</script>
<script type="module">
import { Component, mount, xml } from "@base/owl";
import { Button, Card, CardBody, CardHeader, CardTitle } from "@base/component";
// "@base/templates" is an import-map entry, not a JS module — resolve it
// to its mapped URL and fetch the text.
const templates = await fetch(import.meta.resolve("@base/templates")).then((r) => r.text());
class Root extends Component {
static components = { Button, Card, CardBody, CardHeader, CardTitle };
static template = xml`
<Card>
<CardHeader><CardTitle>Hello</CardTitle></CardHeader>
<CardBody>
<Button variant="'default'" label="'Save'" onClick="() => console.log('saved')"/>
</CardBody>
</Card>`;
}
await mount(Root, document.getElementById("app"), { templates });
</script>
```
`https://unpkg.com/@thebase/ui@latest/dist/...` mirrors the same files as an alternative CDN if jsDelivr is unreachable — swap the host in each URL above. For a self-hosted build, replace the CDN host with your own served path (e.g. `/dist/baseui.esm.js`). These snippets use `@latest` for readability; pin an exact version (e.g. `@0.0.4`) in production so a new release can't change what your page loads.
No `/owl` import appears anywhere in that snippet — `@base/owl` is the only Owl entry point BaseUI components and consumer apps need. Because `/owl`, `@base/component`, and `/theme` all point at the exact same `dist/baseui.esm.js` URL, the browser fetches and evaluates it once and every key shares the same module instance — no separate classic `<script>` needs to load first to set up a shared global.
## npm / bundler usage
```sh
npm install /ui
```
```js
import { Component, mount, xml, Button, Card } from "@thebase/ui";
import baseuiTemplates from "@thebase/ui/dist/baseui.templates.xml?raw"; // exact loader syntax depends on your bundler
class Root extends Component {
static components = { Button, Card };
static template = "Root";
}
```
There's only one package entry now — `/ui` exports the Owl framework (built from the exact `@odoo/owl@2.8.3` this BaseUI release vendors; you do not need to install `/owl` yourself), every pure Owl component class, and the theme helper together. Browser import-map examples above still use the `@base/owl`/`@base/component`/`@base/theme` keys for readability, but they're just local aliases to the same file.
When a bundler cannot import XML with `?raw`, load `dist/baseui.templates.xml` as a served asset and pass the fetched string to `mount(Root, target, { templates })`. The template file is part of the runtime contract, not optional documentation.
## Mixing with the static API
Both APIs come from the exact same bundle now, so there's nothing extra to load — just be clear about which mechanism owns which markup:
- `BaseUI.mountAll(root)` enhances static `[b-ui]` and `[b-icon]` markers.
- Owl `mount(Root, target, { templates })` renders component tags such as `<Button/>`.
- Importing the package always also runs `autoMount()` (its `[b-ui]`/`[b-icon]` DOM scan on `DOMContentLoaded`) — harmless if your page has none, but means there's no side-effect-free way to import just the component classes anymore.
- Both APIs share `dist/baseui.min.css`, `@base/theme`, and the same `b-theme` / `data-bs-theme` root attributes.
## Props, events, slots
- Props are camelCase (`variant`, `label`, `disabled`, `className`).
- Almost every component that renders its own root element also accepts `style` (a string or a plain object of CSS properties) alongside `className`. Components that only forward a default slot with no wrapping element of their own (`AlertDialog`, `Pwa`, `Sheet`, `ContextMenu`, `Drawer`) have neither prop. A handful of components that already compute an internal style (`Icon`, `AspectRatio`, `Progress`, `ContextMenuContent`, `SidebarMenuSkeleton`) merge your `style` with their own computed value instead of one overwriting the other.
- Events are callback props (`onClick`, `onChange`, `onOpenChange`), not DOM `CustomEvent`s — that's the static API's contract, not this one.
- Default slot content works as normal Owl children (`<Badge>Draft</Badge>`); compound components use named slots/sub-tags (`<CardHeader>`, `<DialogFooter>`).
See each component's page under [`docs/components/`](components/) for its specific props table and an Owl usage example alongside the static `b-ui` one.
## Common mistakes
- **Rendering a bare `<Button/>` with no template loaded.** `mount(Root, target)` without `{ templates }` (or `app.addTemplates(...)`) throws — the `.xml` file is a real build/runtime input, not documentation.
- **Registering `Button` under the wrong tag name.** `static components = { Button }` must match the tag used in the template (`<Button/>`), same as any other Owl component.
- **Importing from `/owl`.** Always import from `@base/owl` instead, even in your own app code — it keeps your app on the exact Owl version this BaseUI release was tested against.
- **Adding a `baseui.owl.min.js`/`baseui.owl.esm.js`/`baseui.components.esm.js` script or import-map entry.** None of those files are published anymore — point `@base/owl`/`@base/component`/`@base/theme` all at `dist/baseui.esm.js` instead.