flet-box 1.0.8 → 1.0.9

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.
package/README.md CHANGED
@@ -23,6 +23,7 @@ Real DOM · No Virtual DOM · No build config to learn.
23
23
  <br><br>
24
24
 
25
25
  <a href="https://kuko53348.github.io/flet-box-docs/">📖 Docs</a> •
26
+ <a href="https://kuko53348.github.io/flet-box-widgets-exemples/">🧪 Widget gallery</a> •
26
27
  <a href="#quick-start">Quick start</a> •
27
28
  <a href="https://www.youtube.com/@flet-box">▶ YouTube</a> •
28
29
  <a href="#support--sponsorship">❤️ Sponsor</a>
@@ -157,6 +158,8 @@ Read the docs online: **[The FletBox Book — kuko53348.github.io/flet-box-docs]
157
158
 
158
159
  They are **one continuous book** — the cover maps every page: [The FletBox Book source](docs/README.md).
159
160
 
161
+ Rather than read, click: **[the live widget gallery](https://kuko53348.github.io/flet-box-widgets-exemples/)** gives every widget its own page, renders each example in the browser it was written for, and prints the exact call underneath it. The theme toggle in the corner shows the same screen in light and dark, which is the honest way to judge a default color.
162
+
160
163
  - [Your first app](docs/guides/first-app.md) — build a working todo app in 20 minutes
161
164
  - [The widget book](docs/widget/README.md) — every widget with property tables and examples
162
165
  - [State, Router & Services](docs/guides/state.md) — shared state, routing, storage, and HTTP
@@ -1,5 +1,21 @@
1
1
  // bin/utils/templates.js
2
2
 
3
+ /**
4
+ * The plate behind everything a reader sees before the app paints: the PWA
5
+ * splash (`manifest.json`), the Android and iOS launch screens, the adaptive
6
+ * icon background, and the static `<meta name="theme-color">`.
7
+ *
8
+ * It is the dark palette's `surface` — the zinc scale the 2026-10 design rebuild
9
+ * moved to, replacing the indigo shell color that stayed in the scaffold and kept
10
+ * showing on installed PWAs long after the app itself had changed color. One
11
+ * value because a native splash cannot follow a theme; `applyPalette()` writes
12
+ * the live palette onto `<meta name="theme-color">` at runtime, so the bars do.
13
+ */
14
+ export const SHELL_BRAND = "#18181b";
15
+
16
+ /** `SHELL_BRAND` as the channels the icon and splash scripts composite with. */
17
+ export const SHELL_BRAND_RGB = { r: 24, g: 24, b: 27 };
18
+
3
19
  export const indexHtml = (appName = "FletBox App") => `<!DOCTYPE html>
4
20
  <html lang="en">
5
21
  <head>
@@ -19,7 +35,7 @@ export const indexHtml = (appName = "FletBox App") => `<!DOCTYPE html>
19
35
  <meta property="og:description" content="${appName} built with Flet-Box.">
20
36
  <meta property="og:type" content="website">
21
37
 
22
- <meta name="theme-color" content="#1a1a2e">
38
+ <meta name="theme-color" content="${SHELL_BRAND}">
23
39
 
24
40
  <!-- ============================================================
25
41
  GOOGLE ADSENSE (optional — disabled by default)
@@ -904,7 +920,7 @@ export const capacitorConfig = (appName, appId) => `${JSON.stringify(
904
920
  appId,
905
921
  appName,
906
922
  webDir: "www",
907
- backgroundColor: "#1a1a2e",
923
+ backgroundColor: SHELL_BRAND,
908
924
  server: { androidScheme: "https", iosScheme: "https" },
909
925
  },
910
926
  null,
@@ -960,7 +976,7 @@ const densities = [
960
976
  ["xxhdpi", 144],
961
977
  ["xxxhdpi", 192],
962
978
  ];
963
- const background = { r: 26, g: 26, b: 46 };
979
+ const background = { r: ${SHELL_BRAND_RGB.r}, g: ${SHELL_BRAND_RGB.g}, b: ${SHELL_BRAND_RGB.b} };
964
980
 
965
981
  for (const [density, size] of densities) {
966
982
  const directory = path.join(androidRes, \`mipmap-\${density}\`);
@@ -998,7 +1014,7 @@ const backgroundColor = /(<color name="ic_launcher_background">)#(?:[A-Fa-f0-9]{
998
1014
  if (!backgroundColor.test(xml)) {
999
1015
  throw new Error(\`Invalid adaptive icon background resource: \${backgroundPath}\`);
1000
1016
  }
1001
- await writeFile(backgroundPath, xml.replace(backgroundColor, "$1#1a1a2e$2"));
1017
+ await writeFile(backgroundPath, xml.replace(backgroundColor, "$1${SHELL_BRAND}$2"));
1002
1018
 
1003
1019
  // SPLASH SCREENS
1004
1020
  // The launch theme (AppTheme.NoActionBarLaunch) shows @drawable/splash while the
@@ -1066,8 +1082,8 @@ const infoPlistPath = path.join(nativeAppDir, "Info.plist");
1066
1082
  const launchScreenPath = path.join(nativeAppDir, "Base.lproj", "LaunchScreen.storyboard");
1067
1083
  const logoPath = path.join(projectRoot, "assets", "logo.png");
1068
1084
 
1069
- // Brand background, the same #1a1a2e as theme-color in index.html and manifest.json.
1070
- const background = { r: 26, g: 26, b: 46 };
1085
+ // Brand background, the same value as theme-color in index.html and manifest.json.
1086
+ const background = { r: ${SHELL_BRAND_RGB.r}, g: ${SHELL_BRAND_RGB.g}, b: ${SHELL_BRAND_RGB.b} };
1071
1087
  const backgroundComponent = (value) => Number((value / 255).toFixed(6));
1072
1088
 
1073
1089
  const requireExists = async (target) => {
@@ -1245,8 +1261,8 @@ export const manifest = (appName = "FletBox App") => `{
1245
1261
  "scope": "./",
1246
1262
  "display": "standalone",
1247
1263
  "display_override": ["window-controls-overlay"],
1248
- "theme_color": "#1a1a2e",
1249
- "background_color": "#1a1a2e",
1264
+ "theme_color": "${SHELL_BRAND}",
1265
+ "background_color": "${SHELL_BRAND}",
1250
1266
  "orientation": "any",
1251
1267
  "icons": [
1252
1268
  {
@@ -1739,7 +1755,7 @@ run \`npm run ios:open\`.
1739
1755
 
1740
1756
  Replace \`assets/logo.png\` with a 1024×1024 logo before distributing; the icon,
1741
1757
  the launch images and the launch background are all generated from it. The
1742
- launch background is the app brand color \`#1a1a2e\`, which lives in
1758
+ launch background is the app brand color \`${SHELL_BRAND}\`, which lives in
1743
1759
  \`scripts/sync-ios-assets.mjs\`.
1744
1760
 
1745
1761
  Capacitor uses a local HTTPS origin (\`https://localhost\`) in the Android and
package/docs/README.md CHANGED
@@ -4,6 +4,8 @@ FletBox is a tiny UI framework: you describe your screen with plain JavaScript *
4
4
 
5
5
  This documentation is one continuous book. Start at **Chapter 0** and follow the "Continue reading" links at the bottom of every page; each page teaches you something real and links to the next.
6
6
 
7
+ > **Want to see before you read?** The [live widget gallery](https://kuko53348.github.io/flet-box-widgets-exemples/) runs in the browser: one page per widget, every example rendered, with the exact call printed under it and a light/dark toggle. It is the same code the chapters describe, so it doubles as the answer to "what does this look like when it is real?".
8
+
7
9
  ## The reading path
8
10
 
9
11
  | # | Chapter | What you learn | Level |
@@ -448,7 +448,7 @@ Capacitor template with different slots keeps working. It:
448
448
  status bar style ignored.
449
449
 
450
450
  The script is idempotent: running it twice leaves identical files. The brand
451
- color `#1a1a2e` is defined at the top of the script.
451
+ color `#18181b` is defined at the top of the script.
452
452
 
453
453
  ## Convert asset images to PNG
454
454
 
@@ -331,6 +331,37 @@ A PWA needs:
331
331
 
332
332
  PWA installation depends on browser and platform support.
333
333
 
334
+ ### The browser chrome follows the theme
335
+
336
+ Once installed, Android and iOS paint two more surfaces you do not control from
337
+ CSS: the status bar at the top and the navigation bar at the bottom. Chrome takes
338
+ both from `<meta name="theme-color">` in the document — a value it reads once per
339
+ page, unless the page rewrites it.
340
+
341
+ FletBox rewrites it. Every `setTheme`, `registerPalette` and system-preference
342
+ change updates that single meta tag with the live `colors.surface`, the color the
343
+ `AppBar` and the `BottomNavigation` are painted with, so the bars meet the app
344
+ instead of sitting against it. It also sets `color-scheme` on the root element,
345
+ which is what tells the operating system to draw dark scrollbars, dark form
346
+ controls and light status-bar glyphs on a dark page.
347
+
348
+ That means the colors in `manifest.json` are only the **splash screen** — the
349
+ plate shown while the app boots:
350
+
351
+ ```json
352
+ {
353
+ "theme_color": "#18181b",
354
+ "background_color": "#18181b"
355
+ }
356
+ ```
357
+
358
+ Leave them at the brand color generated by `flet-box create` (the dark palette's
359
+ surface) and let the runtime handle the rest. There is no manifest field for a
360
+ per-theme bar color, and an installed app keeps the manifest copy it was
361
+ installed with, so editing these two lines does not recolor a home-screen icon
362
+ that is already there — reinstall, or let the first paint of the new version
363
+ update the document.
364
+
334
365
  ## Desktop options
335
366
 
336
367
  Capacitor targets Android and iOS. For desktop packaging, consider a desktop shell such as Electron or Tauri around the same `dist` output.
@@ -122,6 +122,7 @@ runApp(ThemeProvider({ children: ThemedApp() }), "root");
122
122
  ## Notes
123
123
 
124
124
  - The shared palette tokens are updated across all widgets when you switch themes; widgets referencing `colors.*` at build time pick up the active palette.
125
+ - Switching a theme also repaints the browser's own chrome: the `<meta name="theme-color">` the installed PWA takes for its status and navigation bars, and the root `color-scheme` that decides how the OS draws scrollbars and form controls. Both read the live palette, so an app that changes `colors.surface` changes its system bars with it — see [Mobile and platforms](../guides/mobile-and-platforms.md#the-browser-chrome-follows-the-theme).
125
126
  - `colors.glass` is genuinely see-through: its contrast depends on whatever is behind it, so use it over photography or gradients with a scrim, and keep body copy off the most colourful part of the backdrop. The dark token is a dark pane, not white glass.
126
127
  - `watchSystemTheme()` and `applySystemTheme()` read `prefers-color-scheme`; both return/need cleanup to release listeners.
127
128
  - `subscribeTheme(callback)` fires with the new palette object — remember to unsubscribe when a feature is destroyed.
@@ -13,6 +13,8 @@ If you have never used FletBox, begin with [Start here](START_HERE.md), then fol
13
13
 
14
14
  Related guides: [Build your first FletBox app](../guides/app-templates.md) · [Routing with FletBox](../guides/router.md) · [FletBox CLI](../cli/README.md).
15
15
 
16
+ Every page here has a live twin: **[the widget gallery](https://kuko53348.github.io/flet-box-widgets-exemples/)** renders each example in the browser and prints the call that made it under the result, so you can read a property table and see the same prop move something.
17
+
16
18
  ## The reading path
17
19
 
18
20
  ### Chapter 1 · First steps: the core mental model
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flet-box",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Declarative, zero-dependency UI framework in vanilla JS — ship web, PWA, Android and iOS from one codebase",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,4 +1,5 @@
1
1
  // src/components/flet-box/utils/themes.js
2
+ import { readableInk } from "./readableInk.js";
2
3
 
3
4
  // ========== DEFAULT COLORS (static, safe) ==========
4
5
  const DEFAULT_COLORS = {
@@ -247,6 +248,41 @@ const notifyThemeChange = () => {
247
248
  };
248
249
 
249
250
  // ========== THEME FUNCTIONS ==========
251
+ /**
252
+ * Moves the browser's own chrome — the PWA status bar and navigation bar, the
253
+ * scrollbar and form-control canvases — onto the palette that was just painted.
254
+ *
255
+ * An installed web app paints those bars from `<meta name="theme-color">`, which
256
+ * is a document-level value the page must rewrite itself: nothing in CSS reaches
257
+ * it. Left alone, the app keeps whatever literal the scaffold wrote — the indigo
258
+ * every FletBox PWA shipped before the zinc rebuild — and the reader sees a
259
+ * system bar from the old palette hanging over the new one. `color-scheme` is the
260
+ * same problem in the other direction: without it the OS draws light-mode
261
+ * scrollbars and form controls on a dark page.
262
+ *
263
+ * @private
264
+ */
265
+ const syncBrowserChrome = () => {
266
+ try {
267
+ let meta = document.querySelector('meta[name="theme-color"]');
268
+ if (!meta) {
269
+ meta = document.createElement("meta");
270
+ meta.name = "theme-color";
271
+ document.head.appendChild(meta);
272
+ }
273
+ // The bars sit directly against the AppBar and the BottomNavigation, and
274
+ // both paint `surface` — matching `background` would put a band of the page
275
+ // color between two panels and look like a gap in the chrome.
276
+ meta.setAttribute("content", colors.surface);
277
+ // Whether the page is dark is read from its own background, not from the
278
+ // palette's name: `registerPalette` lets an app call a dark scale anything.
279
+ document.documentElement.style.colorScheme =
280
+ readableInk(colors.background, "#ffffff") === "#ffffff" ? "dark" : "light";
281
+ } catch (e) {
282
+ // Non-browser environments have no chrome to sync.
283
+ }
284
+ };
285
+
250
286
  /**
251
287
  * Copies a registered palette onto the live `colors` object and refreshes the
252
288
  * CSS custom properties. Shared by `setTheme` and `registerPalette`; it does not
@@ -275,6 +311,8 @@ const applyPalette = (themeName) => {
275
311
  // Silently fail in non-browser environments
276
312
  }
277
313
 
314
+ syncBrowserChrome();
315
+
278
316
  notifyThemeChange();
279
317
  return colors;
280
318
  };
@@ -291,10 +329,16 @@ export const setTheme = (themeName) => {
291
329
  // and a subscriber that reads `getTheme()` while it rebuilds (a toggle button
292
330
  // labelling itself, a theme-aware screen) has to see the theme it is being
293
331
  // rebuilt for, not the one being replaced.
294
- try {
295
- localStorage.setItem("fletbox-theme", themeName);
296
- } catch (e) {
297
- // Silently fail
332
+ //
333
+ // `window` is the test, not a try/catch around the call: Node 22+ defines a
334
+ // global `localStorage` that only answers with a warning and then throws, so
335
+ // an import-time theme would print an ExperimentalWarning on every server run.
336
+ if (typeof window !== "undefined") {
337
+ try {
338
+ localStorage.setItem("fletbox-theme", themeName);
339
+ } catch (e) {
340
+ // Silently fail
341
+ }
298
342
  }
299
343
 
300
344
  applyPalette(themeName);