@thebase/ui 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1662,13 +1662,13 @@
1662
1662
  "integrity": {
1663
1663
  "baseui.min.css": "sha384-hYFwlV6T/ONYzawtDGDFUoruqgNwYl9AVvLDHx24IWgKSMXqwACo/nX04I3gnyT9",
1664
1664
  "bootstrap.bundle.min.js": "sha384-RMbllTJcplkCRnELNTOc5Kz/3AvFDE/rNlJqWqfgmRWruobClBdfwvl10akQZkR1",
1665
- "baseui.min.js": "sha384-jzPn6U5dm/0jM9kcSMnDNOiDWLfdN4h28z+YRNYU/IbGmMf4nFTSVsTn6siJ+UHH",
1666
- "baseui.esm.js": "sha384-I61E5GCJymEsiX5xmIe5Ufd498zcuzGIWwu79WfKg1IKbtACkeC3LG1QXbIq/5cT",
1665
+ "baseui.min.js": "sha384-m91LivkZ3FsaSmngFXfmaGHxfBokU/tEXFAuYz3PIjeXuKMcJjdRmOEk596O8S8u",
1666
+ "baseui.esm.js": "sha384-v3JjyzSIh67awBXIDScUTR40lyCHyuOue1zwObQHjH4OjOj9Cg3mPMkto3LVcVO7",
1667
1667
  "baseui.icons.esm.js": "sha384-hj39VMhzcFvmYnjob2hlioMOAqwAXCURGreF6s9RitXFp/iYi7rUdqtfCI+ICQeS",
1668
1668
  "baseui.templates.xml": "sha384-XkqmfurKcEF6WuIQHWAbnZfW6oz19n7SDVRDU3g0hc3qOr4kPd/qfNGcTmcDDIgQ",
1669
1669
  "baseui.block-viewer.esm.js": "sha384-XlIb3jDxt6Q75dbC4rLctTdPU6u0vJFcbxp9Gl6VKOkhjpz3wRBe0E3dWd80425L",
1670
- "baseui.analytics.esm.js": "sha384-M7jEmmio32O8iquXxQjhCbfEAqZAkHG+a1+Wt6sajMFCubQDzEx7QQMMpek0QxY6",
1671
- "baseui.analytics.min.js": "sha384-aD3A1qm15yTTviPHRscI2enlOIHp7SaIdr+KBCBTgjChn23ZfEsOvY7izOB+hUZe",
1670
+ "baseui.analytics.esm.js": "sha384-M6TVmchmceTvpSzgI+DNA6zAn9xmz9a+YEexCLslSo/Ngxyp2kmzs9fm+PxWy+Da",
1671
+ "baseui.analytics.min.js": "sha384-B0hBkVDbT7gmDmUMazlN4Nc1KnYGSpL+IcW2hd8840knx73HGImPgGs84MvS3hKE",
1672
1672
  "icons/lucide.json": "sha384-mc5mzpmq9eOgZVa2IcjrL33Ty4nMXE8aoZMfy+lgkC/jMQ4aYnk2S58mFsSGFnW1",
1673
1673
  "themes/baseui-light.css": "sha384-/TosvycgVJ5aZ7EOWd4cr8Zen4nayAC/iA9BgGdXKH0LuHSnZk4guDaWM5WPC7yy",
1674
1674
  "themes/baseui-dark.css": "sha384-zF9Rhfjpv90sxuJ2WK7GBPCltN6mkebzK1w9EzQGY9DYHiX3eIX/fcE7fXzfIZVF"
@@ -6,6 +6,16 @@ export interface BaseUIAnalytics {
6
6
  track(eventName: string, options?: BaseUIAnalyticsEventOptions): void;
7
7
  /** File types tracked by default when `fileDownloads` is enabled. */
8
8
  DEFAULT_FILE_TYPES: string[];
9
+ /** Coarse visitor setup (timezone, language, screen bucket); pass as `customProperties`. */
10
+ visitorContext(): BaseUIVisitorContext;
11
+ }
12
+ export interface BaseUIVisitorContext {
13
+ /** IANA timezone, e.g. "Europe/Paris" (browsers may report legacy aliases like "Asia/Saigon"). */
14
+ tz: string;
15
+ /** `navigator.language`, e.g. "vi-VN". */
16
+ lang: string;
17
+ /** Viewport width bucket: < 576, < 992, < 1440, otherwise Desktop. */
18
+ screen: "Mobile" | "Tablet" | "Laptop" | "Desktop";
9
19
  }
10
20
 
11
21
  export interface BaseUIAnalyticsConfig {
@@ -63,3 +73,4 @@ export type BaseUIAnalyticsPayload = {
63
73
  export const DEFAULT_FILE_TYPES: string[];
64
74
  export function init(config: BaseUIAnalyticsConfig): void;
65
75
  export function track(eventName: string, options?: BaseUIAnalyticsEventOptions): void;
76
+ export function visitorContext(): BaseUIVisitorContext;
package/docs/analytics.md CHANGED
@@ -13,12 +13,12 @@ init({ backendUrl: "https://stats.example.com" });
13
13
  ```html
14
14
  <!-- ES module from the CDN (or map it as "@base/analytics" in an import map) -->
15
15
  <script type="module">
16
- import { init } from "https://cdn.jsdelivr.net/npm/@thebase/ui@1.1.0/dist/baseui.analytics.esm.js";
16
+ import { init } from "https://cdn.jsdelivr.net/npm/@thebase/ui@1.1.1/dist/baseui.analytics.esm.js";
17
17
  init({ backendUrl: "https://stats.example.com" });
18
18
  </script>
19
19
 
20
20
  <!-- Classic script tag: sets window.thebase.analytics (load order vs dist/baseui.min.js doesn't matter) -->
21
- <script src="https://cdn.jsdelivr.net/npm/@thebase/ui@1.1.0/dist/baseui.analytics.min.js"></script>
21
+ <script src="https://cdn.jsdelivr.net/npm/@thebase/ui@1.1.1/dist/baseui.analytics.min.js"></script>
22
22
  <script>
23
23
  thebase.analytics.init({ backendUrl: "https://stats.example.com" });
24
24
  </script>
@@ -68,6 +68,24 @@ Or tag markup with classes — no JavaScript needed:
68
68
 
69
69
  `+` in a class value becomes a space; `thebase-revenue-amount=10 thebase-revenue-currency=USD` attaches revenue.
70
70
 
71
+ ## Visitor context
72
+
73
+ `visitorContext()` returns coarse facts about the visitor's setup. Pass it as `customProperties` to attach them to every event (available from 1.1.1):
74
+
75
+ ```js
76
+ import { init, visitorContext } from "@thebase/ui/analytics";
77
+ init({ backendUrl: "https://stats.example.com", customProperties: visitorContext });
78
+ // every event's props now include { tz: "Asia/Ho_Chi_Minh", lang: "vi-VN", screen: "Mobile" }
79
+ ```
80
+
81
+ | Prop | Value |
82
+ | --- | --- |
83
+ | `tz` | `Intl.DateTimeFormat().resolvedOptions().timeZone`. Some browsers report legacy aliases (Chromium sends `Asia/Saigon`), so backends should canonicalize. |
84
+ | `lang` | `navigator.language`, e.g. `vi-VN`. |
85
+ | `screen` | Viewport width bucket: `Mobile` (< 576 px), `Tablet` (< 992), `Laptop` (< 1440), `Desktop`. |
86
+
87
+ The values are deliberately coarse: no exact pixels, nothing persistent. Backends can treat `tz`, `lang` and `screen` as visit dimensions instead of event props. To combine it with your own props, call it inside your function: `customProperties: (name) => ({ ...visitorContext(), plan: "pro" })`. The IP address and user agent never appear in the payload; the server reads them from the HTTP request (see [Receiving events](#receiving-events-backend)).
88
+
71
89
  ## Engagement
72
90
 
73
91
  After each pageview the service measures time on screen and maximum scroll depth, and sends an `engagement` event (`e` = ms, `sd` = %) when the tab is hidden, loses focus, or navigates — only if the visitor scrolled further or spent at least 3 seconds since the last report.
@@ -75,3 +93,175 @@ After each pageview the service measures time on screen and maximum scroll depth
75
93
  ## Ignored visits
76
94
 
77
95
  Events are dropped (and `callback()` gets `undefined`) on localhost unless `captureOnLocalhost` is set, in automated browsers (`navigator.webdriver`, Cypress, PhantomJS, Nightmare — unless `window.__thebase` is set), or when `localStorage.thebase_ignore === "true"` (set that on your own devices to exclude them).
96
+
97
+ ## Receiving events (backend)
98
+
99
+ Any HTTP server can collect events. It needs one route — `POST <backendUrl><apiPath>` (default `/api/event`) — that:
100
+
101
+ 1. Reads the **raw body** as JSON. The tracker sends `Content-Type: text/plain` to avoid a CORS preflight, so framework helpers that only parse `application/json` bodies won't decode it.
102
+ 2. Rejects bodies missing `n`, `u` or `d`, and caps the body size (events are well under 1 KB).
103
+ 3. Answers quickly (`202 Accepted`, no body) with `Access-Control-Allow-Origin` for your site. Store or queue the event after you reply, or send it to a queue, so the visitor's browser never waits on the database.
104
+
105
+ ### Payload
106
+
107
+ | Key | Type | Meaning |
108
+ | --- | --- | --- |
109
+ | `n` | string | Event name: `pageview`, `engagement`, or a custom name (`Signup`, `Outbound Link: Click`, `File Download`, `Form: Submission`, …). |
110
+ | `u` | string | Page URL. |
111
+ | `d` | string | Site `domain`. |
112
+ | `r` | string \| null | `document.referrer` (pageviews and custom events). |
113
+ | `p` | object | Custom properties (`props` + `customProperties`); values are usually strings. |
114
+ | `$` | object | Revenue, e.g. `{ "amount": 10, "currency": "USD" }` (`amount` is a string when it comes from a class tag). |
115
+ | `i` | `false` | Present only for non-interactive events (excluded from bounce rate). |
116
+ | `h` | `1` | Present only with `hashBasedRouting`. |
117
+ | `sd` | number | `engagement` only: max scroll depth, %. |
118
+ | `e` | number | `engagement` only: engaged time, ms. |
119
+ | `v` | number | Tracker version (currently `36`). |
120
+
121
+ The payload carries no visitor ID or cookie. To count unique visitors, derive one on the server, e.g. `sha256(dailySalt + domain + clientIP + userAgent)`. Rotate the salt every day and never store the raw IP. If the server sits behind a proxy or load balancer, configure it to trust the proxy's `X-Forwarded-For` header so it sees the visitor's IP instead of the proxy's.
122
+
123
+ ### Go — Fiber v3
124
+
125
+ Requires Go 1.25+ and `go get github.com/gofiber/fiber/v3` (tested with v3.5.0).
126
+
127
+ ```go
128
+ package main
129
+
130
+ import (
131
+ "encoding/json"
132
+ "log"
133
+
134
+ "github.com/gofiber/fiber/v3"
135
+ "github.com/gofiber/fiber/v3/middleware/cors"
136
+ )
137
+
138
+ // Event is the compact payload POSTed by @thebase/ui/analytics.
139
+ type Event struct {
140
+ Name string `json:"n"`
141
+ URL string `json:"u"`
142
+ Domain string `json:"d"`
143
+ Referrer *string `json:"r"`
144
+ Props map[string]any `json:"p"`
145
+ Revenue map[string]any `json:"$"`
146
+ Interactive *bool `json:"i"`
147
+ HashRouting int `json:"h"`
148
+ ScrollDepth *int `json:"sd"`
149
+ EngagedMs *int64 `json:"e"`
150
+ Version int `json:"v"`
151
+ }
152
+
153
+ func main() {
154
+ app := fiber.New(fiber.Config{BodyLimit: 16 * 1024})
155
+ app.Use(cors.New(cors.Config{AllowOrigins: []string{"https://www.example.com"}}))
156
+
157
+ app.Post("/api/event", func(c fiber.Ctx) error {
158
+ // Body is text/plain, so decode it directly instead of c.Bind().JSON().
159
+ var ev Event
160
+ if err := json.Unmarshal(c.Body(), &ev); err != nil || ev.Name == "" || ev.URL == "" || ev.Domain == "" {
161
+ return c.SendStatus(fiber.StatusBadRequest)
162
+ }
163
+ log.Printf("%s %s %s ip=%s ua=%q", ev.Domain, ev.Name, ev.URL, c.IP(), c.Get(fiber.HeaderUserAgent))
164
+ // TODO: enqueue/store ev.
165
+ return c.SendStatus(fiber.StatusAccepted)
166
+ })
167
+
168
+ log.Fatal(app.Listen(":8080"))
169
+ }
170
+ ```
171
+
172
+ ### Python — FastAPI
173
+
174
+ Requires Python 3.10+ and `pip install "fastapi[standard]"` (tested with FastAPI 0.142.2 / Pydantic 2.13). Run with `fastapi run main.py`.
175
+
176
+ ```python
177
+ from typing import Any
178
+
179
+ from fastapi import FastAPI, Request, Response
180
+ from fastapi.middleware.cors import CORSMiddleware
181
+ from pydantic import BaseModel, Field, ValidationError
182
+
183
+ app = FastAPI()
184
+ app.add_middleware(CORSMiddleware, allow_origins=["https://www.example.com"], allow_methods=["POST"])
185
+
186
+
187
+ class Event(BaseModel):
188
+ """Compact payload POSTed by @thebase/ui/analytics."""
189
+
190
+ name: str = Field(alias="n", min_length=1)
191
+ url: str = Field(alias="u", min_length=1)
192
+ domain: str = Field(alias="d", min_length=1)
193
+ referrer: str | None = Field(None, alias="r")
194
+ props: dict[str, Any] | None = Field(None, alias="p")
195
+ revenue: dict[str, Any] | None = Field(None, alias="$")
196
+ interactive: bool | None = Field(None, alias="i")
197
+ hash_routing: int | None = Field(None, alias="h")
198
+ scroll_depth: int | None = Field(None, alias="sd")
199
+ engaged_ms: int | None = Field(None, alias="e")
200
+ version: int = Field(alias="v")
201
+
202
+
203
+ @app.post("/api/event", status_code=202)
204
+ async def collect(request: Request) -> Response:
205
+ # Body is text/plain, so parse the raw bytes instead of declaring `event: Event` as a parameter.
206
+ body = await request.body()
207
+ if len(body) > 16 * 1024:
208
+ return Response(status_code=413)
209
+ try:
210
+ event = Event.model_validate_json(body)
211
+ except ValidationError:
212
+ return Response(status_code=400)
213
+ ip = request.client.host if request.client else None
214
+ print(event.domain, event.name, event.url, ip, request.headers.get("user-agent"))
215
+ # TODO: enqueue/store event.
216
+ return Response(status_code=202)
217
+ ```
218
+
219
+ Point the tracker at either server with `init({ backendUrl: "https://stats.example.com" })`. To smoke-test the server, run:
220
+
221
+ ```sh
222
+ curl -i -X POST http://localhost:8080/api/event -H 'Content-Type: text/plain' \
223
+ -d '{"n":"pageview","v":36,"u":"https://www.example.com/","d":"www.example.com","r":null}'
224
+ # HTTP/1.1 202 Accepted
225
+ ```
226
+
227
+ ## Sample dashboard
228
+
229
+ [`examples/analytics/`](../examples/analytics/dashboard/?backend=http://localhost:8080) contains a working dashboard plus two complete sample backends: [`server-go`](../examples/analytics/server-go/main.go) (Fiber v3) and [`server-fastapi`](../examples/analytics/server-fastapi/main.py). Each one extends the receivers above with an in-memory store, a daily-salted cookieless visitor hash, a `GET /api/stats` endpoint, and static hosting for the dashboard.
230
+
231
+ The dashboard ([`dashboard/`](../examples/analytics/dashboard/?backend=http://localhost:8080)) is a pure Owl app that loads `@thebase/ui@1.1.1` from jsDelivr: KPI `Card`s, an area `Chart`, top pages/sources/events `Table`s, site and period `NativeSelect`s, and a `ThemeButton`. It refreshes every 30 s and tracks its own visits through `@thebase/ui/analytics`, so a fresh backend has data straight away.
232
+
233
+ ```sh
234
+ # Go (1.25+)
235
+ cd examples/analytics/server-go && DEMO_SEED=1 go run .
236
+
237
+ # or Python (3.10+)
238
+ cd examples/analytics/server-fastapi && pip install -r requirements.txt && DEMO_SEED=1 fastapi run main.py --port 8080
239
+ ```
240
+
241
+ Open <http://localhost:8080/>, the backend serves the dashboard itself. If you are browsing these docs from `npm run dev` (port 5173), the [dashboard link](../examples/analytics/dashboard/?backend=http://localhost:8080) also works once a backend is running on port 8080; until then it shows a "Can't load stats" message. `DEMO_SEED=1` preloads 30 days of synthetic traffic for `demo.example.com`. Other environment variables:
242
+ - `ADDR`: the Go listen address.
243
+ - `ALLOWED_ORIGINS`: a comma-separated CORS allow-list (default `*`).
244
+ - `DASHBOARD_DIR`: the folder served at `/`.
245
+
246
+ To serve the dashboard from somewhere else, open it with `?backend=https://stats.example.com`.
247
+
248
+ `GET /api/stats?domain=<site>&days=<1..90>` (default: the busiest site, 7 days, UTC) returns:
249
+
250
+ ```json
251
+ {
252
+ "domain": "demo.example.com",
253
+ "domains": ["demo.example.com", "www.example.com"],
254
+ "days": 7,
255
+ "totals": { "visitors": 643, "pageviews": 1582, "events": 124, "avgTimeSec": 62.7, "avgScrollDepth": 63.4 },
256
+ "timeseries": [{ "date": "2026-09-28", "visitors": 105, "pageviews": 261 }],
257
+ "topPages": [{ "name": "/pricing", "visitors": 296, "count": 373 }],
258
+ "topSources": [{ "name": "google.com", "visitors": 186, "count": 186 }],
259
+ "topEvents": [{ "name": "Signup", "visitors": 40, "count": 40 }]
260
+ }
261
+ ```
262
+
263
+ The sample servers are demos, not production stores:
264
+ - Events live in memory only and are lost on restart.
265
+ - `/api/stats` has no authentication.
266
+
267
+ Before going live, persist events (e.g. PostgreSQL or ClickHouse), put `/api/stats` behind auth, and restrict `ALLOWED_ORIGINS`.
package/docs/blocks.md CHANGED
@@ -13,7 +13,7 @@ Blocks are not standalone runtime components. Paste the markup and load the norm
13
13
  <script src="https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.min.js" defer></script>
14
14
  ```
15
15
 
16
- `https://unpkg.com/@thebase/ui@latest/dist/...` mirrors the same files as an alternative CDN if jsDelivr is unreachable. Pin an exact version (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) instead of `@latest` for production. Blocks are static markup, so they only need the static `b-ui` runtime above — see [Usage Guide](usage-guide.md) if the surrounding page is itself an Owl app and you'd rather compose the equivalent layout from [Pure Owl Components](owl-components.md).
16
+ `https://unpkg.com/@thebase/ui@latest/dist/...` mirrors the same files as an alternative CDN if jsDelivr is unreachable. Pin an exact version (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) instead of `@latest` for production. Blocks are static markup, so they only need the static `b-ui` runtime above — see [Usage Guide](usage-guide.md) if the surrounding page is itself an Owl app and you'd rather compose the equivalent layout from [Pure Owl Components](owl-components.md).
17
17
 
18
18
  Use `b-block` to select the block style:
19
19
 
package/docs/icons.md CHANGED
@@ -22,7 +22,7 @@ Load the normal BaseUI assets. No separate icon script is required.
22
22
  <script src="https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.min.js" defer></script>
23
23
  ```
24
24
 
25
- `https://unpkg.com/@thebase/ui@latest/dist/...` mirrors the same files as an alternative CDN if jsDelivr is unreachable. Pin an exact version (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) instead of `@latest` for production. `b-icon` also works with the [pure Owl `Icon` component](components/icon.md) — the recommended API in an Owl app; the classic script above is for pages using the static `b-ui` API.
25
+ `https://unpkg.com/@thebase/ui@latest/dist/...` mirrors the same files as an alternative CDN if jsDelivr is unreachable. Pin an exact version (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) instead of `@latest` for production. `b-icon` also works with the [pure Owl `Icon` component](components/icon.md) — the recommended API in an Owl app; the classic script above is for pages using the static `b-ui` API.
26
26
 
27
27
  The build publishes every Lucide SVG to `dist/icons/lucide/` and writes the generated icon manifest to `dist/icons/lucide.json`.
28
28
 
@@ -4,7 +4,7 @@ See the [Usage Guide](usage-guide.md) for the end-to-end integration path (choos
4
4
 
5
5
  BaseUI is distributed as one CSS file and one JavaScript file for browser use. `dist/baseui.min.js` is fully self-contained: Bootstrap's JS bundle (Popper.js included) and the Owl runtime are embedded ahead of BaseUI's own code, in load order (Bootstrap, then Owl, then BaseUI) — nothing else needs to load first. Both install paths below load this same file; the pure Owl component API is the recommended way to consume BaseUI in an Owl app, with the static `b-ui` markup API available as a lighter-weight alternative for non-Owl pages.
6
6
 
7
- The snippets below use the `@latest` tag for readability. Pin an exact version instead (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) for any production page — `@latest` can silently change what your page loads the moment a new version is published.
7
+ The snippets below use the `@latest` tag for readability. Pin an exact version instead (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) for any production page — `@latest` can silently change what your page loads the moment a new version is published.
8
8
 
9
9
  ## Pure Owl component install (recommended)
10
10
 
@@ -43,7 +43,7 @@ For static HTML, a server-rendered page, or a Base HUB website page that has no
43
43
 
44
44
  Bootstrap 5 is the bundled baseline CSS framework, and its JS bundle now drives BaseUI's interactive components directly.
45
45
 
46
- `@thebase/ui` is published on npm, so [unpkg](https://unpkg.com/@thebase/ui@latest/) mirrors the same files as an alternative CDN (`https://unpkg.com/@thebase/ui@latest/dist/baseui.min.css` / `baseui.min.js`) if jsDelivr is unreachable in your environment. Pin an exact version (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) instead of `@latest` for production.
46
+ `@thebase/ui` is published on npm, so [unpkg](https://unpkg.com/@thebase/ui@latest/) mirrors the same files as an alternative CDN (`https://unpkg.com/@thebase/ui@latest/dist/baseui.min.css` / `baseui.min.js`) if jsDelivr is unreachable in your environment. Pin an exact version (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) instead of `@latest` for production.
47
47
 
48
48
  For npm/self-hosted usage:
49
49
 
package/docs/theming.md CHANGED
@@ -38,7 +38,7 @@ For a reusable theme toggle with persistence, URL override, button-label sync, a
38
38
  </script>
39
39
  ```
40
40
 
41
- `https://unpkg.com/@thebase/ui@latest/dist/baseui.esm.js` mirrors the same file as an alternative CDN if jsDelivr is unreachable. Pin an exact version (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) instead of `@latest` for production. Npm/bundler consumers can import the same API from `@thebase/ui`.
41
+ `https://unpkg.com/@thebase/ui@latest/dist/baseui.esm.js` mirrors the same file as an alternative CDN if jsDelivr is unreachable. Pin an exact version (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) instead of `@latest` for production. Npm/bundler consumers can import the same API from `@thebase/ui`.
42
42
 
43
43
  For a ready-made icon button, use the BaseUI component instead of wiring your own click handler:
44
44
 
@@ -32,7 +32,7 @@ Save this as an `.html` file and open it — no build step, no server required.
32
32
  </html>
33
33
  ```
34
34
 
35
- `https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/...` is the real jsDelivr URL for the published `@thebase/ui` npm package's `latest` tag, used here for a copy-paste-and-go quick start. Pin an exact version instead (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) for production, so a new release can't change what your page loads. See [Installation](installation.md) for jsDelivr/unpkg/npm/self-hosted paths.
35
+ `https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/...` is the real jsDelivr URL for the published `@thebase/ui` npm package's `latest` tag, used here for a copy-paste-and-go quick start. Pin an exact version instead (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) for production, so a new release can't change what your page loads. See [Installation](installation.md) for jsDelivr/unpkg/npm/self-hosted paths.
36
36
 
37
37
  The static bundle calls `thebase.mountAll()` automatically on `DOMContentLoaded`, so initial `[b-ui]` and `[b-icon]` markup mounts without an extra script. Call `thebase.mountAll(root)` yourself only after injecting new markup or when mounting a specific fragment. If the consumer is itself an Owl app, use [step 2](#2-install-pure-owl-api--recommended) instead — that's the recommended path.
38
38
 
@@ -63,7 +63,7 @@ Both APIs come from the same package and the same CSS file, and can be mixed on
63
63
  </script>
64
64
  ```
65
65
 
66
- `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. Pin an exact version (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) instead of `@latest` for production.
66
+ `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. Pin an exact version (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) instead of `@latest` for production.
67
67
 
68
68
  ```html
69
69
  <script type="module">
@@ -103,7 +103,7 @@ Two rules that account for most integration failures — see [Pure Owl Component
103
103
  <script src="https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.min.js" defer></script>
104
104
  ```
105
105
 
106
- `https://unpkg.com/@thebase/ui@latest/dist/...` mirrors the same files as an alternative CDN if jsDelivr is unreachable. Pin an exact version (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) instead of `@latest` for production.
106
+ `https://unpkg.com/@thebase/ui@latest/dist/...` mirrors the same files as an alternative CDN if jsDelivr is unreachable. Pin an exact version (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) instead of `@latest` for production.
107
107
 
108
108
  **Option B — npm (bundler-based apps: Vite, webpack, etc.):**
109
109
 
@@ -120,7 +120,7 @@ import "@thebase/ui"; // side-effect import: registers window.thebase and auto-m
120
120
 
121
121
  Rule that applies to both options:
122
122
 
123
- - Production pages must use a pinned version (e.g. `@thebase/ui@1.1.0` or `@thebase/ui@1`), never `@latest`.
123
+ - Production pages must use a pinned version (e.g. `@thebase/ui@1.1.1` or `@thebase/ui@1`), never `@latest`.
124
124
 
125
125
  Mount markup:
126
126
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thebase/ui",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "CDN-installable Owl and Bootstrap 5 UI component library.",
5
5
  "type": "module",
6
6
  "main": "./dist/baseui.min.js",