@gemboss/ui 0.6.2 → 0.6.3

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
@@ -331,6 +331,7 @@ import { AppShell, BrandLockup, AccountMenu, AccountMenuItem, Icon, RailItem } f
331
331
  - `AccountMenu`: the admin's 32px initials chip and popover (same CSS). A `<details>`, so it opens
332
332
  without JavaScript on a page that never hydrates; with React it also closes on an outside click and
333
333
  on Escape. `AccountMenuItem` is a link (`href`), a POST form (`formAction`), or a button.
334
+ - `TopbarSearch` (0.6.3): see "Topbar search" below.
334
335
  - `Icon`: the GemBoss set, 39 icons on a 20px grid with 1.5px strokes, drawn to sit next to Polaris.
335
336
  `ICON_NAMES` lists them. Internal tools cannot use `@shopify/polaris-icons`: its licence only covers
336
337
  applications that integrate with Shopify. Draw a missing one HERE, not in the surface: every rail
@@ -342,6 +343,19 @@ import { AppShell, BrandLockup, AccountMenu, AccountMenuItem, Icon, RailItem } f
342
343
  (`href`) or a button; `disabled` on a link drops the href and sets `aria-disabled`; `className` is added
343
344
  to the row's class.
344
345
 
346
+ ## Topbar search (0.6.3)
347
+
348
+ The admin's "Search ⌘K" box for every surface, as a real form (works with no JavaScript):
349
+
350
+ ```tsx
351
+ <AppShell brand={…} center={<TopbarSearch action="/search" placeholder="Search by #ID, name or key" />} … />
352
+ ```
353
+
354
+ - Submits `GET action?q=…` (`name` changes the parameter). `defaultValue` keeps the current query on the results page.
355
+ - Same metrics as the admin's box: 36px under a mouse, 44px under a finger, ⌘K hint hidden on focus and below 460px.
356
+ - Shortcut on a page without React: `gembossUI.initSearch()` from `kit-plain.js` (⌘K / Ctrl+K and "/" focus it,
357
+ Escape leaves it). "/" is ignored while another field has focus.
358
+
345
359
  ## Report pages (0.4.0)
346
360
 
347
361
  Plain CSS in `kit-plain.css` for a numbers page written by hand (first user: gemnews `/admin`, 03/10/2026; that page moved to GemCommunity the same day, so gemnews no longer takes the package). The
@@ -27,7 +27,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
27
27
  @media (max-width:760px){.cat{grid-template-columns:1fr;padding:16px}.cat-nav{position:static;flex-direction:row;flex-wrap:wrap}}
28
28
  </style></head><body>
29
29
  <div class="cat"><nav class="cat-nav" aria-label="Mục"><a href="#tokens">Token</a><a href="#buttons">Nút</a><a href="#fields">Ô nhập</a><a href="#choice">Tab, nhóm nút, thẻ</a><a href="#surfaces">Thẻ, số liệu, bảng, trạng thái trống</a><a href="#report">Báo cáo</a><a href="#resizer">Thanh kéo giữa hai cột</a><a href="#modal">Hộp thoại, toast, menu (JS thuần)</a><a href="#shell">Khung app</a><a href="#auth">Đăng nhập</a><a href="#chat">Thân chat</a><a href="#assistant">Trợ lý: cột phiên</a><a href="#icons">Icon</a></nav>
30
- <main><header class="cat-head"><h1>@gemboss/ui 0.6.2</h1><p>Ngôn ngữ thiết kế chung của các công cụ GemBoss. Markup dưới mỗi ví dụ là đúng thứ package render ra; bề mặt không dùng React (HTML tĩnh, Python) chép markup này và nạp <code>gemboss-ui.css</code>. Dựng từ gemboss <code>4397bdb795d6</code>.</p></header>
30
+ <main><header class="cat-head"><h1>@gemboss/ui 0.6.3</h1><p>Ngôn ngữ thiết kế chung của các công cụ GemBoss. Markup dưới mỗi ví dụ là đúng thứ package render ra; bề mặt không dùng React (HTML tĩnh, Python) chép markup này và nạp <code>gemboss-ui.css</code>. Dựng từ gemboss <code>b1b688827c14</code>.</p></header>
31
31
  <section id="tokens"><h2>Token</h2><p>Đổi giao diện một bề mặt bằng cách đặt lại token, không viết đè class. Tên và giá trị đọc thẳng từ gemboss-ui.css.</p><div class="cat-swatches"><div class="cat-swatch"><i style="background:var(--gemboss-primary)"></i><code>--gemboss-primary</code><span>#1b1c1e</span></div><div class="cat-swatch"><i style="background:var(--gemboss-primary-dark)"></i><code>--gemboss-primary-dark</code><span>#000000</span></div><div class="cat-swatch"><i style="background:var(--gemboss-primary-soft)"></i><code>--gemboss-primary-soft</code><span>#f2f3f5</span></div><div class="cat-swatch"><i style="background:var(--gemboss-accent)"></i><code>--gemboss-accent</code><span>#5661f5</span></div><div class="cat-swatch"><i style="background:var(--gemboss-accent-strong)"></i><code>--gemboss-accent-strong</code><span>#3a44de</span></div><div class="cat-swatch"><i style="background:var(--gemboss-accent-soft)"></i><code>--gemboss-accent-soft</code><span>#eef0fe</span></div><div class="cat-swatch"><i style="background:var(--gemboss-text-strong)"></i><code>--gemboss-text-strong</code><span>#1b1c1e</span></div><div class="cat-swatch"><i style="background:var(--gemboss-text)"></i><code>--gemboss-text</code><span>#1b1c1e</span></div><div class="cat-swatch"><i style="background:var(--gemboss-text-muted)"></i><code>--gemboss-text-muted</code><span>#686b71</span></div><div class="cat-swatch"><i style="background:var(--gemboss-surface)"></i><code>--gemboss-surface</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-canvas)"></i><code>--gemboss-canvas</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-preview-canvas)"></i><code>--gemboss-preview-canvas</code><span>#eceef1</span></div><div class="cat-swatch"><i style="background:var(--gemboss-rail-bg)"></i><code>--gemboss-rail-bg</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-panel-bg)"></i><code>--gemboss-panel-bg</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-divider)"></i><code>--gemboss-divider</code><span>#e5e7ea</span></div><div class="cat-swatch"><i style="background:var(--gemboss-divider-soft)"></i><code>--gemboss-divider-soft</code><span>#edeff2</span></div><div class="cat-swatch"><i style="background:var(--gemboss-hover)"></i><code>--gemboss-hover</code><span>#eceef2</span></div><div class="cat-swatch"><i style="background:var(--gemboss-active-bg)"></i><code>--gemboss-active-bg</code><span>#f0f1f4</span></div><div class="cat-swatch"><i style="background:var(--gemboss-active-fg)"></i><code>--gemboss-active-fg</code><span>#1b1c1e</span></div><div class="cat-swatch"><i style="background:var(--gemboss-danger)"></i><code>--gemboss-danger</code><span>#d72c0d</span></div><div class="cat-swatch"><i style="background:var(--gemboss-danger-strong)"></i><code>--gemboss-danger-strong</code><span>#b42318</span></div><div class="cat-swatch"><i style="background:var(--gemboss-danger-soft)"></i><code>--gemboss-danger-soft</code><span>#fff0e9</span></div><div class="cat-swatch"><i style="background:var(--gemboss-danger-border)"></i><code>--gemboss-danger-border</code><span>#f5c9bc</span></div><div class="cat-swatch"><i style="background:var(--gemboss-success)"></i><code>--gemboss-success</code><span>#007f5f</span></div><div class="cat-swatch"><i style="background:var(--gemboss-success-strong)"></i><code>--gemboss-success-strong</code><span>#0a6a4c</span></div><div class="cat-swatch"><i style="background:var(--gemboss-success-soft)"></i><code>--gemboss-success-soft</code><span>#e3f1df</span></div><div class="cat-swatch"><i style="background:var(--gemboss-success-border)"></i><code>--gemboss-success-border</code><span>#b7dfcd</span></div><div class="cat-swatch"><i style="background:var(--gemboss-warning)"></i><code>--gemboss-warning</code><span>#9a6700</span></div><div class="cat-swatch"><i style="background:var(--gemboss-warning-soft)"></i><code>--gemboss-warning-soft</code><span>#fff8e0</span></div><div class="cat-swatch"><i style="background:var(--gemboss-warning-border)"></i><code>--gemboss-warning-border</code><span>#f5d569</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-bg)"></i><code>--gemboss-dark-bg</code><span>#0a0a0c</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-rail)"></i><code>--gemboss-dark-rail</code><span>#0e0e11</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-surface)"></i><code>--gemboss-dark-surface</code><span>#16161a</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-surface-2)"></i><code>--gemboss-dark-surface-2</code><span>#1e1e24</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-divider)"></i><code>--gemboss-dark-divider</code><span>#2a2a31</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-text)"></i><code>--gemboss-dark-text</code><span>#ededf0</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-muted)"></i><code>--gemboss-dark-muted</code><span>#9a9aa4</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-bg)"></i><code>--gemboss-field-bg</code><span>#fdfdfd</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-bg-hover)"></i><code>--gemboss-field-bg-hover</code><span>#fafafa</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-ink)"></i><code>--gemboss-field-ink</code><span>#303030</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-placeholder)"></i><code>--gemboss-field-placeholder</code><span>#616161</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-line)"></i><code>--gemboss-field-line</code><span>#8a8a8a</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-line-hover)"></i><code>--gemboss-field-line-hover</code><span>#616161</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-line-active)"></i><code>--gemboss-field-line-active</code><span>#1a1a1a</span></div><div class="cat-swatch"><i style="background:var(--gemboss-chip-ink)"></i><code>--gemboss-chip-ink</code><span>#303633</span></div><div class="cat-swatch"><i style="background:var(--gemboss-chip-line)"></i><code>--gemboss-chip-line</code><span>#cfd6ce</span></div><div class="cat-swatch"><i style="background:var(--gemboss-chip-line-hover)"></i><code>--gemboss-chip-line-hover</code><span>#aab4ad</span></div><div class="cat-swatch"><i style="background:var(--gemboss-chip-bg-hover)"></i><code>--gemboss-chip-bg-hover</code><span>#f6f7f5</span></div><div class="cat-swatch"><i style="background:var(--gemboss-on-primary)"></i><code>--gemboss-on-primary</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-app-bg)"></i><code>--gemboss-app-bg</code><span>#ffffff</span></div></div><table class="ui-table cat-scale"><thead><tr><th>Token</th><th>Giá trị</th><th>Mẫu</th></tr></thead><tbody><tr><td><code>--gemboss-fs-micro</code></td><td>11px</td><td style="font-size:var(--gemboss-fs-micro)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-caption</code></td><td>12px</td><td style="font-size:var(--gemboss-fs-caption)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-body</code></td><td>14px</td><td style="font-size:var(--gemboss-fs-body)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-body-lg</code></td><td>16px</td><td style="font-size:var(--gemboss-fs-body-lg)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-h3</code></td><td>16px</td><td style="font-size:var(--gemboss-fs-h3)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-h2</code></td><td>20px</td><td style="font-size:var(--gemboss-fs-h2)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-h1</code></td><td>24px</td><td style="font-size:var(--gemboss-fs-h1)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-display</code></td><td>30px</td><td style="font-size:var(--gemboss-fs-display)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-radius-xs</code></td><td>4px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-xs)"></i></td></tr><tr><td><code>--gemboss-radius-sm</code></td><td>6px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-sm)"></i></td></tr><tr><td><code>--gemboss-radius-md</code></td><td>8px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-md)"></i></td></tr><tr><td><code>--gemboss-radius-lg</code></td><td>12px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-lg)"></i></td></tr><tr><td><code>--gemboss-radius-xl</code></td><td>16px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-xl)"></i></td></tr><tr><td><code>--gemboss-radius-2xl</code></td><td>20px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-2xl)"></i></td></tr><tr><td><code>--gemboss-radius-full</code></td><td>999px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-full)"></i></td></tr><tr><td><code>--gemboss-shadow-resting</code></td><td class="cat-small">0 1px 2px rgba(31, 30, 29, 0.04), 0 2px 6px rgba(31, 30, 29, 0.06)</td><td><i class="cat-radius" style="box-shadow:var(--gemboss-shadow-resting);border:0"></i></td></tr><tr><td><code>--gemboss-shadow-raised</code></td><td class="cat-small">0 4px 16px rgba(31, 30, 29, 0.10)</td><td><i class="cat-radius" style="box-shadow:var(--gemboss-shadow-raised);border:0"></i></td></tr><tr><td><code>--gemboss-shadow-overlay</code></td><td class="cat-small">0 16px 48px rgba(31, 30, 29, 0.18)</td><td><i class="cat-radius" style="box-shadow:var(--gemboss-shadow-overlay);border:0"></i></td></tr></tbody></table></section>
32
32
  <section id="buttons"><h2>Nút</h2><p>Một lớp <code>ui-btn</code>, ba biến thể, tông đỏ, một cỡ nhỏ. Cao 32px dưới chuột (bằng ô nhập và ô chọn), 44px dưới ngón tay và ở khổ điện thoại, cỡ nhỏ cũng vậy. Dùng được trên <code>&lt;button&gt;</code> lẫn <code>&lt;a&gt;</code> (căn giữa sẵn).</p><figure class="cat-demo"><figcaption>Mặc định · chính · ghost · nhỏ · tắt</figcaption><div class="cat-live"><div class="cat-row"><button type="button" class="ui-btn">Lưu nháp</button><button type="button" class="ui-btn ui-btn--primary">Xuất bản</button><button type="button" class="ui-btn ui-btn--ghost">Huỷ</button><button type="button" class="ui-btn ui-btn--sm">Nhỏ</button><button type="button" class="ui-btn ui-btn--primary" disabled>Đang lưu…</button><a class="ui-btn" href="#buttons">Link dạng nút</a></div></div><details class="cat-code"><summary>Markup</summary><pre><code>&lt;div class=&quot;cat-row&quot;&gt;
33
33
  &lt;button type=&quot;button&quot; class=&quot;ui-btn&quot;&gt;Lưu nháp&lt;/button&gt;
@@ -583,7 +583,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
583
583
  &lt;button type=&quot;button&quot; class=&quot;ui-btn ui-btn--primary&quot;&gt;Xoá&lt;/button&gt;
584
584
  &lt;/div&gt;
585
585
  &lt;/div&gt;</code></pre></details></figure></section>
586
- <section id="shell"><h2>Khung app</h2><p>Topbar 56px, rail 240px, ≤760px thành ngăn kéo. Logo <code>BrandLockup</code>, tài khoản <code>AccountMenu</code> (bấm chữ viết tắt), mục Trợ lý ở chân rail có class <code>gnav-assistant</code>.</p><figure class="cat-demo cat-screen"><figcaption>AppShell + BrandLockup + AccountMenu + Icon</figcaption><iframe title="AppShell + BrandLockup + AccountMenu + Icon" style="height:460px" srcdoc="&lt;!doctype html&gt;&lt;html&gt;&lt;head&gt;&lt;meta charset=&quot;utf-8&quot;&gt;&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;../gemboss-ui.css&quot;&gt;&lt;/head&gt;&lt;body&gt;&lt;div class=&quot;gemboss-platform-shell&quot;&gt;&lt;header class=&quot;gemboss-platform-topbar&quot;&gt;&lt;div class=&quot;gemboss-topbar-start&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;gemboss-mobile-menu-btn&quot; aria-label=&quot;Mở menu&quot; aria-expanded=&quot;false&quot; data-label-open=&quot;Mở menu&quot; data-label-close=&quot;Đóng menu&quot; aria-controls=&quot;:R0:&quot;&gt;&lt;svg width=&quot;20&quot; height=&quot;20&quot; viewBox=&quot;0 0 20 20&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M3 5.5h14M3 10h14M3 14.5h14&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.7&quot; stroke-linecap=&quot;round&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/button&gt;&lt;a class=&quot;gb-brand&quot; href=&quot;#&quot; aria-label=&quot;GemFactory home&quot;&gt;&lt;svg class=&quot;gb-brand-gem&quot; viewBox=&quot;0 0 32 40&quot; width=&quot;20.8&quot; height=&quot;26&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g transform=&quot;translate(0,0.8) scale(0.31)&quot;&gt;&lt;polygon points=&quot;26,14 74,14 66,50 34,50&quot; fill=&quot;#5661F5&quot; fill-opacity=&quot;0.92&quot;&gt;&lt;/polygon&gt;&lt;path d=&quot;M26,14 74,14 96,50 50,110 4,50Z&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;8.75&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;5.45&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;span class=&quot;gb-brand-name&quot;&gt;GemFactory&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;gemboss-topbar-center&quot;&gt;&lt;/div&gt;&lt;div class=&quot;gemboss-topbar-end&quot;&gt;&lt;details class=&quot;gb-account&quot;&gt;&lt;summary class=&quot;gemboss-account-chip&quot; aria-label=&quot;Account · Minh Anh&quot; title=&quot;Minh Anh&quot;&gt;MA&lt;/summary&gt;&lt;div role=&quot;menu&quot; class=&quot;gemboss-popover-surface gemboss-account-menu&quot;&gt;&lt;div class=&quot;gb-account-head&quot;&gt;&lt;div class=&quot;gb-account-name&quot;&gt;Minh Anh&lt;/div&gt;&lt;div class=&quot;gb-account-email&quot;&gt;ma@gemboss.ai&lt;/div&gt;&lt;div class=&quot;gb-account-role&quot;&gt;Designer&lt;/div&gt;&lt;/div&gt;&lt;a role=&quot;menuitem&quot; class=&quot;gemboss-account-menu-item&quot; href=&quot;#&quot;&gt;Cài đặt&lt;/a&gt;&lt;form method=&quot;post&quot; action=&quot;#&quot; class=&quot;gb-account-form&quot;&gt;&lt;button role=&quot;menuitem&quot; type=&quot;submit&quot; class=&quot;gemboss-account-menu-item danger&quot;&gt;Đăng xuất&lt;/button&gt;&lt;/form&gt;&lt;/div&gt;&lt;/details&gt;&lt;/div&gt;&lt;/header&gt;&lt;nav class=&quot;icon-nav&quot; aria-label=&quot;Main navigation&quot; id=&quot;:R0:&quot;&gt;&lt;div class=&quot;gnav-scroll&quot;&gt;&lt;div class=&quot;gnav-heading&quot;&gt;Kho&lt;/div&gt;&lt;div class=&quot;gnav-group active&quot;&gt;&lt;a class=&quot;gnav-item active&quot; aria-current=&quot;page&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;gnav-ico&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; height=&quot;20&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;rect x=&quot;3&quot; y=&quot;3.5&quot; width=&quot;4&quot; height=&quot;13&quot; rx=&quot;1.2&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;8&quot; y=&quot;3.5&quot; width=&quot;4&quot; height=&quot;9&quot; rx=&quot;1.2&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;13&quot; y=&quot;3.5&quot; width=&quot;4&quot; height=&quot;6&quot; rx=&quot;1.2&quot;&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;gnav-label&quot;&gt;Board&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;gnav-group&quot;&gt;&lt;a class=&quot;gnav-item&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;gnav-ico&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; height=&quot;20&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;rect x=&quot;3.5&quot; y=&quot;3&quot; width=&quot;13&quot; height=&quot;3.5&quot; rx=&quot;1&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;3.5&quot; y=&quot;8.25&quot; width=&quot;13&quot; height=&quot;3.5&quot; rx=&quot;1&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;3.5&quot; y=&quot;13.5&quot; width=&quot;13&quot; height=&quot;3.5&quot; rx=&quot;1&quot;&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;gnav-label&quot;&gt;Ghép trang&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;gnav-group&quot;&gt;&lt;a class=&quot;gnav-item&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;gnav-ico&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; height=&quot;20&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;M10 3.2 17.3 16H2.7z&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M10 8.2v3.6M10 13.9v.1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;gnav-label&quot;&gt;Framework thiếu&lt;/span&gt;&lt;span class=&quot;gnav-count&quot; aria-label=&quot;3 mục đang mở&quot;&gt;3&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gnav-bottom&quot;&gt;&lt;div class=&quot;gnav-sep&quot;&gt;&lt;/div&gt;&lt;a class=&quot;gnav-item gnav-assistant&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;gnav-ico&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; height=&quot;20&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;M3 4.5h14v9H8.5L5 16.5v-3H3z&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;gnav-label&quot;&gt;Trợ lý&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/nav&gt;&lt;button type=&quot;button&quot; class=&quot;gemboss-mobile-nav-backdrop&quot; aria-label=&quot;Đóng menu&quot; tabindex=&quot;-1&quot;&gt;&lt;/button&gt;&lt;main class=&quot;gemboss-platform-main&quot;&gt;&lt;div style=&quot;padding:24px&quot;&gt;&lt;h1 style=&quot;margin:0&quot;&gt;Nội dung màn&lt;/h1&gt;&lt;/div&gt;&lt;/main&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;"></iframe><details class="cat-code"><summary>Markup</summary><pre><code>&lt;div class=&quot;gemboss-platform-shell&quot;&gt;
586
+ <section id="shell"><h2>Khung app</h2><p>Topbar 56px, rail 240px, ≤760px thành ngăn kéo. Logo <code>BrandLockup</code>, tài khoản <code>AccountMenu</code> (bấm chữ viết tắt), mục Trợ lý ở chân rail có class <code>gnav-assistant</code>.</p><figure class="cat-demo cat-screen"><figcaption>AppShell + BrandLockup + AccountMenu + Icon</figcaption><iframe title="AppShell + BrandLockup + AccountMenu + Icon" style="height:460px" srcdoc="&lt;!doctype html&gt;&lt;html&gt;&lt;head&gt;&lt;meta charset=&quot;utf-8&quot;&gt;&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;../gemboss-ui.css&quot;&gt;&lt;/head&gt;&lt;body&gt;&lt;div class=&quot;gemboss-platform-shell&quot;&gt;&lt;header class=&quot;gemboss-platform-topbar&quot;&gt;&lt;div class=&quot;gemboss-topbar-start&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;gemboss-mobile-menu-btn&quot; aria-label=&quot;Mở menu&quot; aria-expanded=&quot;false&quot; data-label-open=&quot;Mở menu&quot; data-label-close=&quot;Đóng menu&quot; aria-controls=&quot;:R0:&quot;&gt;&lt;svg width=&quot;20&quot; height=&quot;20&quot; viewBox=&quot;0 0 20 20&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M3 5.5h14M3 10h14M3 14.5h14&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.7&quot; stroke-linecap=&quot;round&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/button&gt;&lt;a class=&quot;gb-brand&quot; href=&quot;#&quot; aria-label=&quot;GemFactory home&quot;&gt;&lt;svg class=&quot;gb-brand-gem&quot; viewBox=&quot;0 0 32 40&quot; width=&quot;20.8&quot; height=&quot;26&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g transform=&quot;translate(0,0.8) scale(0.31)&quot;&gt;&lt;polygon points=&quot;26,14 74,14 66,50 34,50&quot; fill=&quot;#5661F5&quot; fill-opacity=&quot;0.92&quot;&gt;&lt;/polygon&gt;&lt;path d=&quot;M26,14 74,14 96,50 50,110 4,50Z&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;8.75&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;5.45&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;span class=&quot;gb-brand-name&quot;&gt;GemFactory&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;gemboss-topbar-center&quot;&gt;&lt;form role=&quot;search&quot; method=&quot;get&quot; action=&quot;#&quot; class=&quot;gemboss-topbar-search&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;18&quot; height=&quot;18&quot; class=&quot;gemboss-topbar-search-icon&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;circle cx=&quot;8.8&quot; cy=&quot;8.8&quot; r=&quot;5.3&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12.8 12.8 17 17&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;input class=&quot;gemboss-topbar-search-input&quot; type=&quot;search&quot; name=&quot;q&quot; placeholder=&quot;Search by #ID, name or key&quot; aria-label=&quot;Search by #ID, name or key&quot; autoComplete=&quot;off&quot; enterKeyHint=&quot;search&quot;/&gt;&lt;kbd class=&quot;gemboss-topbar-search-kbd&quot; aria-hidden=&quot;true&quot;&gt;⌘K&lt;/kbd&gt;&lt;/form&gt;&lt;/div&gt;&lt;div class=&quot;gemboss-topbar-end&quot;&gt;&lt;details class=&quot;gb-account&quot;&gt;&lt;summary class=&quot;gemboss-account-chip&quot; aria-label=&quot;Account · Minh Anh&quot; title=&quot;Minh Anh&quot;&gt;MA&lt;/summary&gt;&lt;div role=&quot;menu&quot; class=&quot;gemboss-popover-surface gemboss-account-menu&quot;&gt;&lt;div class=&quot;gb-account-head&quot;&gt;&lt;div class=&quot;gb-account-name&quot;&gt;Minh Anh&lt;/div&gt;&lt;div class=&quot;gb-account-email&quot;&gt;ma@gemboss.ai&lt;/div&gt;&lt;div class=&quot;gb-account-role&quot;&gt;Designer&lt;/div&gt;&lt;/div&gt;&lt;a role=&quot;menuitem&quot; class=&quot;gemboss-account-menu-item&quot; href=&quot;#&quot;&gt;Cài đặt&lt;/a&gt;&lt;form method=&quot;post&quot; action=&quot;#&quot; class=&quot;gb-account-form&quot;&gt;&lt;button role=&quot;menuitem&quot; type=&quot;submit&quot; class=&quot;gemboss-account-menu-item danger&quot;&gt;Đăng xuất&lt;/button&gt;&lt;/form&gt;&lt;/div&gt;&lt;/details&gt;&lt;/div&gt;&lt;/header&gt;&lt;nav class=&quot;icon-nav&quot; aria-label=&quot;Main navigation&quot; id=&quot;:R0:&quot;&gt;&lt;div class=&quot;gnav-scroll&quot;&gt;&lt;div class=&quot;gnav-heading&quot;&gt;Kho&lt;/div&gt;&lt;div class=&quot;gnav-group active&quot;&gt;&lt;a class=&quot;gnav-item active&quot; aria-current=&quot;page&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;gnav-ico&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; height=&quot;20&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;rect x=&quot;3&quot; y=&quot;3.5&quot; width=&quot;4&quot; height=&quot;13&quot; rx=&quot;1.2&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;8&quot; y=&quot;3.5&quot; width=&quot;4&quot; height=&quot;9&quot; rx=&quot;1.2&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;13&quot; y=&quot;3.5&quot; width=&quot;4&quot; height=&quot;6&quot; rx=&quot;1.2&quot;&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;gnav-label&quot;&gt;Board&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;gnav-group&quot;&gt;&lt;a class=&quot;gnav-item&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;gnav-ico&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; height=&quot;20&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;rect x=&quot;3.5&quot; y=&quot;3&quot; width=&quot;13&quot; height=&quot;3.5&quot; rx=&quot;1&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;3.5&quot; y=&quot;8.25&quot; width=&quot;13&quot; height=&quot;3.5&quot; rx=&quot;1&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;3.5&quot; y=&quot;13.5&quot; width=&quot;13&quot; height=&quot;3.5&quot; rx=&quot;1&quot;&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;gnav-label&quot;&gt;Ghép trang&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;gnav-group&quot;&gt;&lt;a class=&quot;gnav-item&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;gnav-ico&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; height=&quot;20&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;M10 3.2 17.3 16H2.7z&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M10 8.2v3.6M10 13.9v.1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;gnav-label&quot;&gt;Framework thiếu&lt;/span&gt;&lt;span class=&quot;gnav-count&quot; aria-label=&quot;3 mục đang mở&quot;&gt;3&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gnav-bottom&quot;&gt;&lt;div class=&quot;gnav-sep&quot;&gt;&lt;/div&gt;&lt;a class=&quot;gnav-item gnav-assistant&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;gnav-ico&quot;&gt;&lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; height=&quot;20&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;M3 4.5h14v9H8.5L5 16.5v-3H3z&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;gnav-label&quot;&gt;Trợ lý&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/nav&gt;&lt;button type=&quot;button&quot; class=&quot;gemboss-mobile-nav-backdrop&quot; aria-label=&quot;Đóng menu&quot; tabindex=&quot;-1&quot;&gt;&lt;/button&gt;&lt;main class=&quot;gemboss-platform-main&quot;&gt;&lt;div style=&quot;padding:24px&quot;&gt;&lt;h1 style=&quot;margin:0&quot;&gt;Nội dung màn&lt;/h1&gt;&lt;/div&gt;&lt;/main&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;"></iframe><details class="cat-code"><summary>Markup</summary><pre><code>&lt;div class=&quot;gemboss-platform-shell&quot;&gt;
587
587
  &lt;header class=&quot;gemboss-platform-topbar&quot;&gt;
588
588
  &lt;div class=&quot;gemboss-topbar-start&quot;&gt;
589
589
  &lt;button type=&quot;button&quot; class=&quot;gemboss-mobile-menu-btn&quot; aria-label=&quot;Mở menu&quot; aria-expanded=&quot;false&quot; data-label-open=&quot;Mở menu&quot; data-label-close=&quot;Đóng menu&quot; aria-controls=&quot;:R0:&quot;&gt;
@@ -607,6 +607,18 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
607
607
  &lt;/a&gt;
608
608
  &lt;/div&gt;
609
609
  &lt;div class=&quot;gemboss-topbar-center&quot;&gt;
610
+ &lt;form role=&quot;search&quot; method=&quot;get&quot; action=&quot;#&quot; class=&quot;gemboss-topbar-search&quot;&gt;
611
+ &lt;svg viewBox=&quot;0 0 20 20&quot; width=&quot;18&quot; height=&quot;18&quot; class=&quot;gemboss-topbar-search-icon&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;
612
+ &lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.5&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
613
+ &lt;circle cx=&quot;8.8&quot; cy=&quot;8.8&quot; r=&quot;5.3&quot;&gt;
614
+ &lt;/circle&gt;
615
+ &lt;path d=&quot;M12.8 12.8 17 17&quot;&gt;
616
+ &lt;/path&gt;
617
+ &lt;/g&gt;
618
+ &lt;/svg&gt;
619
+ &lt;input class=&quot;gemboss-topbar-search-input&quot; type=&quot;search&quot; name=&quot;q&quot; placeholder=&quot;Search by #ID, name or key&quot; aria-label=&quot;Search by #ID, name or key&quot; autoComplete=&quot;off&quot; enterKeyHint=&quot;search&quot;/&gt;
620
+ &lt;kbd class=&quot;gemboss-topbar-search-kbd&quot; aria-hidden=&quot;true&quot;&gt;⌘K&lt;/kbd&gt;
621
+ &lt;/form&gt;
610
622
  &lt;/div&gt;
611
623
  &lt;div class=&quot;gemboss-topbar-end&quot;&gt;
612
624
  &lt;details class=&quot;gb-account&quot;&gt;
package/gemboss-ui.css CHANGED
@@ -1,8 +1,8 @@
1
1
  /*
2
2
  * GemBoss admin design system — GENERATED — do not edit; regenerate from gemboss:
3
3
  * node apps/admin/scripts/export-ui-css.mjs --out <path> (npm run export:ui-css)
4
- * gemboss commit: 4397bdb795d6d122d2384e14f9538dceceb82e9e
5
- * sources sha256: bea19a3043c91b979b8550aa0844ef1f9b09ec145cf1a037be3a9e66ce7f516b
4
+ * gemboss commit: b1b688827c1444a994a2e477ead056c4816f5dd2
5
+ * sources sha256: 6914d1ac2ece84f2132dce7c9cb1dc69b24fbd81dce5c961749a2c12ae56a9c9
6
6
  * contents: 1 allow-listed rule from apps/admin/src/app/globals.css + packages/ui/src/gemboss-tokens.css
7
7
  * (verbatim) + packages/ui/src/ui.css (verbatim) + packages/ui/src/gemboss-field.css (verbatim)
8
8
  * + 181 rules from packages/ui/src/shell.css (the shared shell)
@@ -2097,6 +2097,44 @@ body.gemboss-assistant-unread .gnav-item.gnav-assistant::after {
2097
2097
  /* ── AccountMenu (./account.tsx): its rules live in shell.css since 29/09/2026, because the admin
2098
2098
  * renders AccountMenu too now and loads shell.css, not this file. */
2099
2099
 
2100
+ /* ── Topbar search (TopbarSearch, search.tsx; 0.6.3) ────────────────────────────────────────────────────────
2101
+ The admin's "Search ⌘K" box as a real form for AppShell's `center` slot. Same metrics as the admin's
2102
+ .gemboss-command-search (gemboss-shell.css): 36px under a mouse, 44px under a finger (pinned in the admin's
2103
+ kit-controls-reach-44.test.ts), 18px icon, 13px text, the shortcut hint at the right. Field tokens, so it
2104
+ wears the same box as every other text field in the kit. */
2105
+ .gemboss-topbar-search {
2106
+ width: 100%;
2107
+ max-width: 560px;
2108
+ height: 36px;
2109
+ min-width: 0;
2110
+ margin: 0 auto;
2111
+ border: 1px solid var(--gemboss-field-line);
2112
+ border-radius: var(--gemboss-radius-md);
2113
+ background: var(--gemboss-field-bg);
2114
+ padding: 0 8px 0 12px;
2115
+ display: grid;
2116
+ grid-template-columns: 18px minmax(0, 1fr) auto;
2117
+ gap: 8px;
2118
+ align-items: center;
2119
+ color: var(--gemboss-text-muted);
2120
+ }
2121
+ .gemboss-topbar-search:hover { border-color: var(--gemboss-field-line-hover); background: var(--gemboss-field-bg-hover); }
2122
+ .gemboss-topbar-search:focus-within { border-color: var(--gemboss-field-line-active); background: var(--gemboss-surface); }
2123
+ .gemboss-topbar-search-icon { width: 18px; height: 18px; color: currentColor; }
2124
+ .gemboss-topbar-search-input {
2125
+ min-width: 0; height: 100%; border: 0; background: transparent; padding: 0; margin: 0;
2126
+ font: inherit; font-size: var(--gemboss-fs-body); line-height: 18px; color: var(--gemboss-field-ink); outline: none;
2127
+ }
2128
+ .gemboss-topbar-search-input::placeholder { color: var(--gemboss-field-placeholder); opacity: 1; }
2129
+ .gemboss-topbar-search-kbd {
2130
+ border: 1px solid var(--gemboss-field-line); border-radius: var(--gemboss-radius-sm); background: var(--gemboss-surface);
2131
+ padding: 1px 6px; color: var(--gemboss-text-muted); font-family: inherit; font-size: var(--gemboss-fs-micro); line-height: 16px;
2132
+ }
2133
+ .gemboss-topbar-search:focus-within .gemboss-topbar-search-kbd { display: none; }
2134
+ @media (pointer: coarse) { .gemboss-topbar-search { height: 44px; } }
2135
+ /* A phone has no ⌘ key, and the box needs every pixel the bar has left. */
2136
+ @media (max-width: 460px) { .gemboss-topbar-search-kbd { display: none; } }
2137
+
2100
2138
  /* ═══ @gemboss/ui src/auth.css ═══ */
2101
2139
  /* The GemBoss sign-in screen — @gemboss/ui (28/09/2026).
2102
2140
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gemboss/ui",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "GemBoss design system for every surface in the fleet: the admin shell (topbar + rail + drawer) as React components, and the admin stylesheet exported as one CSS file.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
package/src/app-shell.css CHANGED
@@ -95,3 +95,41 @@
95
95
 
96
96
  /* ── AccountMenu (./account.tsx): its rules live in shell.css since 29/09/2026, because the admin
97
97
  * renders AccountMenu too now and loads shell.css, not this file. */
98
+
99
+ /* ── Topbar search (TopbarSearch, search.tsx; 0.6.3) ────────────────────────────────────────────────────────
100
+ The admin's "Search ⌘K" box as a real form for AppShell's `center` slot. Same metrics as the admin's
101
+ .gemboss-command-search (gemboss-shell.css): 36px under a mouse, 44px under a finger (pinned in the admin's
102
+ kit-controls-reach-44.test.ts), 18px icon, 13px text, the shortcut hint at the right. Field tokens, so it
103
+ wears the same box as every other text field in the kit. */
104
+ .gemboss-topbar-search {
105
+ width: 100%;
106
+ max-width: 560px;
107
+ height: 36px;
108
+ min-width: 0;
109
+ margin: 0 auto;
110
+ border: 1px solid var(--gemboss-field-line);
111
+ border-radius: var(--gemboss-radius-md);
112
+ background: var(--gemboss-field-bg);
113
+ padding: 0 8px 0 12px;
114
+ display: grid;
115
+ grid-template-columns: 18px minmax(0, 1fr) auto;
116
+ gap: 8px;
117
+ align-items: center;
118
+ color: var(--gemboss-text-muted);
119
+ }
120
+ .gemboss-topbar-search:hover { border-color: var(--gemboss-field-line-hover); background: var(--gemboss-field-bg-hover); }
121
+ .gemboss-topbar-search:focus-within { border-color: var(--gemboss-field-line-active); background: var(--gemboss-surface); }
122
+ .gemboss-topbar-search-icon { width: 18px; height: 18px; color: currentColor; }
123
+ .gemboss-topbar-search-input {
124
+ min-width: 0; height: 100%; border: 0; background: transparent; padding: 0; margin: 0;
125
+ font: inherit; font-size: var(--gemboss-fs-body); line-height: 18px; color: var(--gemboss-field-ink); outline: none;
126
+ }
127
+ .gemboss-topbar-search-input::placeholder { color: var(--gemboss-field-placeholder); opacity: 1; }
128
+ .gemboss-topbar-search-kbd {
129
+ border: 1px solid var(--gemboss-field-line); border-radius: var(--gemboss-radius-sm); background: var(--gemboss-surface);
130
+ padding: 1px 6px; color: var(--gemboss-text-muted); font-family: inherit; font-size: var(--gemboss-fs-micro); line-height: 16px;
131
+ }
132
+ .gemboss-topbar-search:focus-within .gemboss-topbar-search-kbd { display: none; }
133
+ @media (pointer: coarse) { .gemboss-topbar-search { height: 44px; } }
134
+ /* A phone has no ⌘ key, and the box needs every pixel the bar has left. */
135
+ @media (max-width: 460px) { .gemboss-topbar-search-kbd { display: none; } }
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export {
5
5
  export { GemMark } from "./GemMark";
6
6
  export { AuthError, AuthField, AuthFoot, AuthForm, AuthHelp, AuthLayout, PasswordField, AuthSubmit } from "./auth";
7
7
  export { BrandLockup } from "./brand";
8
+ export { TopbarSearch } from "./search";
8
9
  export { AccountMenu, AccountMenuItem, initialsOf } from "./account";
9
10
  export { Menu, MenuItem, MenuSeparator } from "./menu";
10
11
  export { Icon, ICON_NAMES, type IconName } from "./icons";
package/src/kit-plain.js CHANGED
@@ -15,6 +15,7 @@
15
15
  if (await confirm({ title: "Delete #12?", body: "It leaves the board.", confirmLabel: "Delete", tone: "critical" })) …
16
16
  initShell(); // a hand-written AppShell: ☰ drawer, backdrop, Escape, followed link
17
17
  initPasswordFields(); // .gb-auth-pw-toggle reveal; resetPasswordFields() on sign-out
18
+ initSearch(); // TopbarSearch: ⌘K / Ctrl+K and "/" focus the box, Escape leaves it
18
19
  */
19
20
  (function (g) {
20
21
  "use strict";
@@ -287,7 +288,23 @@
287
288
  document.querySelectorAll(".gb-auth-pw-toggle").forEach(function (b) { pwSet(b, false); });
288
289
  }
289
290
 
291
+ // TopbarSearch (search.tsx, 0.6.3): the admin's ⌘K, for a page without React. "/" only when no field has focus, so it
292
+ // never eats a slash typed into a form; Escape blurs the box and gives the page its keys back.
293
+ var searchBound = false;
294
+ function initSearch() {
295
+ if (searchBound) return;
296
+ searchBound = true;
297
+ document.addEventListener("keydown", function (e) {
298
+ var box = document.querySelector(".gemboss-topbar-search-input");
299
+ if (!box) return;
300
+ var t = e.target, typing = t && (t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName));
301
+ if ((e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey) && !e.altKey) { e.preventDefault(); box.focus(); box.select(); }
302
+ else if (e.key === "/" && !typing && !e.metaKey && !e.ctrlKey && !e.altKey) { e.preventDefault(); box.focus(); }
303
+ else if (e.key === "Escape" && t === box) box.blur();
304
+ });
305
+ }
306
+
290
307
  g.gembossUI = { openModal: openModal, toast: toast, confirm: confirm, initMenus: initMenus, closeMenus: closeMenus,
291
- initShell: initShell, initPasswordFields: initPasswordFields, resetPasswordFields: resetPasswordFields,
308
+ initSearch: initSearch, initShell: initShell, initPasswordFields: initPasswordFields, resetPasswordFields: resetPasswordFields,
292
309
  openModals: function () { return MODALS.length; } };
293
310
  })(globalThis);
package/src/search.tsx ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The topbar search: a REAL search box in the topbar's centre column, for every surface — @gemboss/ui (03/10/2026).
3
+ *
4
+ * The admin's topbar has had "Search ⌘K" since the shell was drawn, as admin-only CSS (`.gemboss-command-search`
5
+ * in apps/admin gemboss-shell.css) on a button that opens its command palette. Other surfaces had no search in the
6
+ * frame at all: GemFactory's board had a filter box inside the page and nothing in the topbar (Chris, 03/10/2026:
7
+ * "bổ sung ô search theo gemboss ui chuẩn … ở topbar"). This is the same box, as a form: it works with no
8
+ * JavaScript (GET `action?q=…`), on a server-rendered page that never hydrates.
9
+ *
10
+ * Same look as the admin's (36px box, 18px icon, 13px text, the ⌘K hint), and the same two-tier target rule:
11
+ * 36px under a mouse, 44px under a finger (`pointer: coarse`), the size its own test pins in the admin.
12
+ * `kit-plain.js` `initSearch()` gives it the shortcut (⌘K / Ctrl+K and "/") on a page without React.
13
+ */
14
+ import React from "react";
15
+ import { Icon } from "./icons";
16
+
17
+ type FormProps = React.FormHTMLAttributes<HTMLFormElement>;
18
+
19
+ export function TopbarSearch({ action, name = "q", placeholder = "Search", label, defaultValue, shortcut = "⌘K", className, ...rest }: {
20
+ /** Where the query goes: `GET action?{name}=…`. */
21
+ action: string;
22
+ name?: string;
23
+ placeholder?: string;
24
+ /** Accessible name of the box; defaults to the placeholder. */
25
+ label?: string;
26
+ defaultValue?: string;
27
+ /** The hint shown at the right edge; null hides it. */
28
+ shortcut?: string | null;
29
+ } & Omit<FormProps, "action">) {
30
+ return (
31
+ <form role="search" method="get" action={action} className={className ? `gemboss-topbar-search ${className}` : "gemboss-topbar-search"} {...rest}>
32
+ <Icon name="search" size={18} className="gemboss-topbar-search-icon" />
33
+ <input className="gemboss-topbar-search-input" type="search" name={name} placeholder={placeholder}
34
+ aria-label={label ?? placeholder} defaultValue={defaultValue} autoComplete="off" enterKeyHint="search" />
35
+ {shortcut ? <kbd className="gemboss-topbar-search-kbd" aria-hidden="true">{shortcut}</kbd> : null}
36
+ </form>
37
+ );
38
+ }