@gemboss/ui 0.6.5 → 0.6.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
@@ -100,7 +100,8 @@ The host registers `allowSide` with the provider, and every way into the panel c
100
100
  a "side" remembered in storage: a host without it gets the popup.
101
101
 
102
102
  The rail's way in is a `RailItem` with `className="gnav-assistant"`, linking to your full-screen
103
- address (the admin puts it in `RailBottom`, above Inbox). That class draws the tinted pill, and the
103
+ address (the admin puts it in `RailBottom`, above Inbox). That class draws the iris-tinted pill (0.6.8; it
104
+ was a mix of `--gemboss-primary`, grey once primary went near-black, so it read as a second selected row), and the
104
105
  working / answer-waiting dot the provider switches on through `<body>` classes. Leave
105
106
  `bodyClassPrefix` at its default so those rules apply. If the popup is what the person last used, a
106
107
  plain click on that link opens the popup instead of navigating.
@@ -233,7 +234,8 @@ New primitives, Polaris' shapes on GemBoss tokens (`kit-plain.css`; the admin us
233
234
 
234
235
  And four targets that measured under 44px at phone width are now 44: a compact text button's width, the bare input
235
236
  inside `gemboss-field--group`, the brand mark alone (≤460px), a toggle chip. A popup opened by an Escape handler no
236
- longer catches that same Escape (`openModal` listens from the next task). The rail item and segmented buttons draw the iris focus ring (they had
237
+ longer catches that same Escape (0.6.7: `openModal` listens at once and skips only the keydown that opened it; 0.5.1
238
+ listened from the next task, which on a busy page lost Escape or Tab pressed right after a popup opened). The rail item and segmented buttons draw the iris focus ring (they had
237
239
  none: the browser drew its own), the popup × too (it drew near-black); at phone width the chat composer is 16px (iOS
238
240
  zoomed it at 14) and the thread rail's "new chat" / collapse buttons are 44px under a fine pointer as well. Headings carry
239
241
  their own line-height (a wrapped 22px title overlapped itself on the 20px body line), a badge is a tint of the ink (it
@@ -353,7 +355,8 @@ The admin's "Search ⌘K" box for every surface, as a real form (works with no J
353
355
 
354
356
  - Submits `GET action?q=…` (`name` changes the parameter). `defaultValue` keeps the current query on the results page.
355
357
  - 36px under a mouse; 44px under a finger **and at phone width** (≤767px, 0.6.5: the kit's own controls use the same pair,
356
- and with `pointer: coarse` alone a narrow window measured 36 and failed `tap_target_size`). ⌘K hint hidden on focus and below 460px.
358
+ and with `pointer: coarse` alone a narrow window measured 36 and failed `tap_target_size`). The input itself spans the
359
+ full 44 (0.6.6): the box is a form, so the input is what a finger has to hit. ⌘K hint hidden on focus and below 460px.
357
360
  - Shortcut on a page without React: `gembossUI.initSearch()` from `kit-plain.js` (⌘K / Ctrl+K and "/" focus it,
358
361
  Escape leaves it). "/" is ignored while another field has focus.
359
362
 
@@ -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.5</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>682393d5cacd</code>.</p></header>
30
+ <main><header class="cat-head"><h1>@gemboss/ui 0.6.9</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>d82cf2553a58</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;
@@ -813,7 +813,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
813
813
  &lt;button type=&quot;submit&quot; aria-label=&quot;Gửi&quot;&gt;↑&lt;/button&gt;
814
814
  &lt;/form&gt;
815
815
  &lt;/div&gt;</code></pre></details></figure></section>
816
- <section id="assistant"><h2>Trợ lý: cột phiên</h2><p><code>SessionRail</code> (React). Khung toàn màn hình / cửa sổ nổi / cột phải cao hết màn (<code>allowSide</code>) / thanh là <code>AssistantHost</code>, cần React vì có trạng thái.</p><figure class="cat-demo"><figcaption>SessionRail</figcaption><div class="cat-live"><div style="height:360px;display:flex"><aside class="gemboss-assistant-rail"><div class="gemboss-assistant-rail-top"><button type="button" class="gemboss-assistant-newchat" title="Phiên mới"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H5.5A1.5 1.5 0 0 0 4 5.5v9A1.5 1.5 0 0 0 5.5 16h9a1.5 1.5 0 0 0 1.5-1.5V9"></path><path d="M14.5 3.5l2 2L10 12H8v-2z"></path></g></svg><span>Phiên mới</span></button><button type="button" class="gemboss-assistant-collapse" aria-label="Collapse sidebar" title="Collapse"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5l-5 5 5 5"></path></g></svg></button></div><div class="gemboss-assistant-search gemboss-field gemboss-field--group"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="5"></circle><path d="M13 13l3.5 3.5"></path></g></svg><input class="gemboss-field__bare" placeholder="Tìm phiên…" aria-label="Search history" value=""/><kbd class="gemboss-assistant-kbd">/</kbd></div><div class="gemboss-assistant-threads"><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Đã ghim</div><div class="gemboss-assistant-thread"><button type="button" class="gemboss-assistant-thread-main" title="So sánh advertorial với listicle"><span class="gemboss-assistant-thread-titlerow"><svg viewBox="0 0 20 20" class="gemboss-assistant-pin-dot" aria-hidden="true" focusable="false"><path d="M8 3.5h4l-.5 5 2.5 2.5H6l2.5-2.5z" fill="currentColor"></path><path d="M10 11v5.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"></path></svg><span class="gemboss-assistant-thread-title">So sánh advertorial với listicle</span></span><span class="gemboss-assistant-thread-meta">24/8/2026</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Hôm nay</div><div class="gemboss-assistant-thread active"><button type="button" class="gemboss-assistant-thread-main" title="Store nào đang dựng loạt landing?"><span class="gemboss-assistant-thread-titlerow"><span class="gemboss-assistant-thread-title">Store nào đang dựng loạt landing?</span></span><span class="gemboss-assistant-thread-meta">3 giờ trước</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Hôm qua</div><div class="gemboss-assistant-thread"><button type="button" class="gemboss-assistant-thread-main" title="Landing điểm cao nhất của corpus"><span class="gemboss-assistant-thread-titlerow"><span class="gemboss-assistant-thread-title">Landing điểm cao nhất của corpus</span></span><span class="gemboss-assistant-thread-meta">1 ngày trước</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div></div></aside></div></div><details class="cat-code"><summary>Markup</summary><pre><code>&lt;div style=&quot;height:360px;display:flex&quot;&gt;
816
+ <section id="assistant"><h2>Trợ lý: cột phiên</h2><p><code>SessionRail</code> (React). Khung toàn màn hình / cửa sổ nổi / cột phải cao hết màn (<code>allowSide</code>) / thanh là <code>AssistantHost</code>, cần React vì có trạng thái.</p><figure class="cat-demo"><figcaption>SessionRail</figcaption><div class="cat-live"><div style="height:360px;display:flex"><aside class="gemboss-assistant-rail"><div class="gemboss-assistant-rail-top"><button type="button" class="gemboss-assistant-newchat" title="Phiên mới"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H5.5A1.5 1.5 0 0 0 4 5.5v9A1.5 1.5 0 0 0 5.5 16h9a1.5 1.5 0 0 0 1.5-1.5V9"></path><path d="M14.5 3.5l2 2L10 12H8v-2z"></path></g></svg><span>Phiên mới</span></button><button type="button" class="gemboss-assistant-collapse" aria-label="Collapse sidebar" title="Collapse"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5l-5 5 5 5"></path></g></svg></button></div><div class="gemboss-assistant-search gemboss-field gemboss-field--group"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="5"></circle><path d="M13 13l3.5 3.5"></path></g></svg><input class="gemboss-field__bare" placeholder="Tìm phiên…" aria-label="Search history" value=""/><kbd class="gemboss-assistant-kbd">/</kbd></div><div class="gemboss-assistant-threads"><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Đã ghim</div><div class="gemboss-assistant-thread"><button type="button" class="gemboss-assistant-thread-main" title="So sánh advertorial với listicle"><span class="gemboss-assistant-thread-titlerow"><svg viewBox="0 0 20 20" class="gemboss-assistant-pin-dot" aria-hidden="true" focusable="false"><path d="M8 3.5h4l-.5 5 2.5 2.5H6l2.5-2.5z" fill="currentColor"></path><path d="M10 11v5.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"></path></svg><span class="gemboss-assistant-thread-title">So sánh advertorial với listicle</span></span><span class="gemboss-assistant-thread-meta">25/8/2026</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Hôm nay</div><div class="gemboss-assistant-thread active"><button type="button" class="gemboss-assistant-thread-main" title="Store nào đang dựng loạt landing?"><span class="gemboss-assistant-thread-titlerow"><span class="gemboss-assistant-thread-title">Store nào đang dựng loạt landing?</span></span><span class="gemboss-assistant-thread-meta">3 giờ trước</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Hôm qua</div><div class="gemboss-assistant-thread"><button type="button" class="gemboss-assistant-thread-main" title="Landing điểm cao nhất của corpus"><span class="gemboss-assistant-thread-titlerow"><span class="gemboss-assistant-thread-title">Landing điểm cao nhất của corpus</span></span><span class="gemboss-assistant-thread-meta">1 ngày trước</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div></div></aside></div></div><details class="cat-code"><summary>Markup</summary><pre><code>&lt;div style=&quot;height:360px;display:flex&quot;&gt;
817
817
  &lt;aside class=&quot;gemboss-assistant-rail&quot;&gt;
818
818
  &lt;div class=&quot;gemboss-assistant-rail-top&quot;&gt;
819
819
  &lt;button type=&quot;button&quot; class=&quot;gemboss-assistant-newchat&quot; title=&quot;Phiên mới&quot;&gt;
@@ -862,7 +862,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
862
862
  &lt;/svg&gt;
863
863
  &lt;span class=&quot;gemboss-assistant-thread-title&quot;&gt;So sánh advertorial với listicle&lt;/span&gt;
864
864
  &lt;/span&gt;
865
- &lt;span class=&quot;gemboss-assistant-thread-meta&quot;&gt;24/8/2026&lt;/span&gt;
865
+ &lt;span class=&quot;gemboss-assistant-thread-meta&quot;&gt;25/8/2026&lt;/span&gt;
866
866
  &lt;/button&gt;
867
867
  &lt;div class=&quot;gemboss-assistant-thread-actions&quot;&gt;
868
868
  &lt;button type=&quot;button&quot; class=&quot;gemboss-assistant-thread-act&quot; aria-label=&quot;Options&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: 682393d5cacdaa2f1f0650af237b6549efb0d142
5
- * sources sha256: d6e9fe1628015cfb2a7549549e7ab264d6fca5341dce08dea550e2beaf35c7a3
4
+ * gemboss commit: d82cf2553a585b741231a905e8845db7353c4726
5
+ * sources sha256: 67e1eb62e369a8fedf0d081820f8dffd9fd39801df6ca597060851c99cec3b42
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)
@@ -1305,15 +1305,15 @@ textarea.gemboss-field { height: auto; padding: 6px 12px; resize: vertical; }
1305
1305
  margin: 8px 6px;
1306
1306
  }
1307
1307
  .gnav-item.gnav-assistant {
1308
- color: var(--gemboss-primary-dark);
1308
+ color: var(--gemboss-accent-strong);
1309
1309
  font-weight: 600;
1310
- background: color-mix(in srgb, var(--gemboss-primary) 8%, #fff);
1310
+ background: var(--gemboss-accent-soft);
1311
1311
  }
1312
1312
  .gnav-item.gnav-assistant .gnav-ico svg {
1313
- fill: var(--gemboss-primary);
1313
+ fill: var(--gemboss-accent);
1314
1314
  }
1315
1315
  .gnav-item.gnav-assistant:hover {
1316
- background: color-mix(in srgb, var(--gemboss-primary) 15%, #fff);
1316
+ background: color-mix(in srgb, var(--gemboss-accent) 16%, var(--gemboss-surface));
1317
1317
  }
1318
1318
  .gnav-item.gnav-assistant.active {
1319
1319
  background: var(--gemboss-active-bg);
@@ -2124,8 +2124,11 @@ body.gemboss-assistant-unread .gnav-item.gnav-assistant::after {
2124
2124
  .gemboss-topbar-search:hover { border-color: var(--gemboss-field-line-hover); background: var(--gemboss-field-bg-hover); }
2125
2125
  .gemboss-topbar-search:focus-within { border-color: var(--gemboss-field-line-active); background: var(--gemboss-surface); }
2126
2126
  .gemboss-topbar-search-icon { width: 18px; height: 18px; color: currentColor; }
2127
+ /* The INPUT is the tap target, not the box: the box is a <form>, and a tap on its icon or border does not focus
2128
+ anything. Inside a 1px border, `height: 100%` left the input 42 of the box's 44 (visual-gate tap_target_size at
2129
+ 320/390/768 on GemFactory, 03/10/2026). It reaches over both border lines; transparent, so the border still shows. */
2127
2130
  .gemboss-topbar-search-input {
2128
- min-width: 0; height: 100%; border: 0; background: transparent; padding: 0; margin: 0;
2131
+ min-width: 0; height: calc(100% + 2px); border: 0; background: transparent; padding: 0; margin: -1px 0;
2129
2132
  font: inherit; font-size: var(--gemboss-fs-body); line-height: 18px; color: var(--gemboss-field-ink); outline: none;
2130
2133
  }
2131
2134
  .gemboss-topbar-search-input::placeholder { color: var(--gemboss-field-placeholder); opacity: 1; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gemboss/ui",
3
- "version": "0.6.5",
3
+ "version": "0.6.9",
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
@@ -120,8 +120,11 @@
120
120
  .gemboss-topbar-search:hover { border-color: var(--gemboss-field-line-hover); background: var(--gemboss-field-bg-hover); }
121
121
  .gemboss-topbar-search:focus-within { border-color: var(--gemboss-field-line-active); background: var(--gemboss-surface); }
122
122
  .gemboss-topbar-search-icon { width: 18px; height: 18px; color: currentColor; }
123
+ /* The INPUT is the tap target, not the box: the box is a <form>, and a tap on its icon or border does not focus
124
+ anything. Inside a 1px border, `height: 100%` left the input 42 of the box's 44 (visual-gate tap_target_size at
125
+ 320/390/768 on GemFactory, 03/10/2026). It reaches over both border lines; transparent, so the border still shows. */
123
126
  .gemboss-topbar-search-input {
124
- min-width: 0; height: 100%; border: 0; background: transparent; padding: 0; margin: 0;
127
+ min-width: 0; height: calc(100% + 2px); border: 0; background: transparent; padding: 0; margin: -1px 0;
125
128
  font: inherit; font-size: var(--gemboss-fs-body); line-height: 18px; color: var(--gemboss-field-ink); outline: none;
126
129
  }
127
130
  .gemboss-topbar-search-input::placeholder { color: var(--gemboss-field-placeholder); opacity: 1; }
package/src/kit-plain.js CHANGED
@@ -85,7 +85,7 @@
85
85
  if (o.onClose) o.onClose(value);
86
86
  }
87
87
  function onKey(e) {
88
- if (MODALS[MODALS.length - 1] !== m) return;
88
+ if (e === opening || MODALS[MODALS.length - 1] !== m) return;
89
89
  // Escape while an IME is composing (zh, ja, vi telex) cancels the composition, not the dialog.
90
90
  if (e.key === "Escape") { if (e.isComposing || e.keyCode === 229) return; e.preventDefault(); m.close(null); return; }
91
91
  if (e.key !== "Tab") return;
@@ -103,13 +103,26 @@
103
103
  overlay.addEventListener("click", function (e) { if (e.target === overlay && downOnBackdrop) m.close(null); downOnBackdrop = false; });
104
104
  var x = panel.querySelector(".ui-modal__head .ui-modal__close");
105
105
  if (x) x.onclick = function () { m.close(null); };
106
- // On the NEXT task, not now: a popup opened from a keydown handler (content-radar's composer asks "close without
107
- // saving?" on Escape) would otherwise receive that same Escape when it bubbles up to window, and close itself at
108
- // once (0.5.1). A listener added during a dispatch still runs if its target has not been reached yet.
109
- setTimeout(function () { if (MODALS.indexOf(m) >= 0) addEventListener("keydown", onKey); }, 0);
106
+ // Keys belong to the popup from the moment it opens (0.6.4). 0.5.1 attached this listener a task later, and on a busy
107
+ // page the browser runs input before timers: Escape or Tab pressed right after opening was lost, the popup stayed open
108
+ // or Tab left the trap (CI lost "stacked popups" three runs in a row, 03/10/2026). The one key it must NOT take is the
109
+ // keydown that is opening it: a popup opened from a keydown handler (content-radar's composer asks "close without
110
+ // saving?" on Escape) would receive that same Escape when it bubbles up to window — a listener added during a dispatch
111
+ // still runs if its target has not been reached yet — and close at once. `window.event` is that event while it runs.
112
+ var opening = window.event;
113
+ addEventListener("keydown", onKey);
110
114
  if (!MODALS.length) savedOverflow = document.body.style.overflow;
111
115
  MODALS.push(m);
112
- document.body.append(overlay);
116
+ // Inside the top open MODAL <dialog>, not <body> (0.6.9, 03/10/2026). A `<dialog class="ui-modal">` opened with
117
+ // showModal() sits in the top layer and makes the rest of the page inert, so a confirm appended to <body> drew
118
+ // BEHIND it and could not be clicked: GemCommunity's lesson editor asked "Discard changes?" into a dead layer and
119
+ // had to move the overlay by hand. Appended inside that dialog, the overlay rides the top layer with it.
120
+ var host = document.body;
121
+ try {
122
+ var open = document.querySelectorAll("dialog[open]");
123
+ for (var di = open.length - 1; di >= 0; di--) { if (open[di].matches(":modal")) { host = open[di]; break; } }
124
+ } catch (_) { /* a browser without :modal keeps <body> */ }
125
+ host.append(overlay);
113
126
  document.body.style.overflow = "hidden";
114
127
  setTimeout(function () { (panel.querySelector("[autofocus]") || panel).focus(); }, 0);
115
128
  return m;
package/src/shell.css CHANGED
@@ -406,8 +406,8 @@
406
406
  .gnav-item:focus-visible { outline: 2px solid var(--gemboss-accent); outline-offset: -2px; } /* the one iris ring (0.5.1): had none, the browser drew its own */
407
407
 
408
408
  /* neutral gray on white (Polaris-like) */
409
- /* Active hub: a full coral pill when you're ON the hub's own page. But when a deeper
410
- LEAF is the destination, the hub quiets to coral text only — so the leaf below carries
409
+ /* Active hub: the neutral selected pill (--gemboss-active-bg) when you're ON the hub's own page. But when a deeper
410
+ LEAF is the destination, the hub quiets to bold text only — so the leaf below carries
411
411
  the strong "you are here" and the two don't compete. */
412
412
  .gnav-item.active { color: var(--gemboss-active-fg); font-weight: 600; }
413
413
 
@@ -450,7 +450,7 @@
450
450
 
451
451
  .gnav-subitem:hover { background: var(--gemboss-nav-hover); color: var(--gemboss-text); }
452
452
 
453
- /* Active leaf — the strong "you are here": coral pill + a coral accent bar on the tree line. */
453
+ /* Active leaf — the strong "you are here": the neutral selected pill + a near-black bar on the tree line. */
454
454
  .gnav-subitem.active { color: var(--gemboss-active-fg); font-weight: 600; background: var(--gemboss-active-bg); }
455
455
 
456
456
  .gnav-subitem.active:hover { background: var(--gemboss-active-bg); color: var(--gemboss-active-fg); }
@@ -470,19 +470,21 @@
470
470
  /* section separator (Assistant · Settings) */
471
471
  .gnav-sep { height: 1px; background: var(--gemboss-divider); margin: 8px 6px; }
472
472
 
473
- /* Assistant = the AI front door, not just another row. A coral-tinted pill that stands
473
+ /* Assistant = the AI front door, not just another row. An iris-tinted pill that stands
474
474
  apart from the neutral hubs above; deepens on hover; becomes the standard active pill
475
- when it's the current page. */
475
+ when it's the current page. It was a --gemboss-primary mix, which turned grey (#ececed)
476
+ once primary went near-black and read as a second selected row next to #f0f1f4 (0.6.8).
477
+ Contrast on --gemboss-accent-soft: label 6.05:1, icon and the iris focus ring 4.19:1 (16% hover: 5.56 / 3.84). */
476
478
  .gnav-item.gnav-assistant {
477
- /* Label uses the DARKER coral for WCAG AA (4.77:1 on the 8% pill; the lighter
478
- --gemboss-primary was 2.87, failed). Icon + pill keep --gemboss-primary as the accent. */
479
- color: var(--gemboss-primary-dark); font-weight: 600;
480
- background: color-mix(in srgb, var(--gemboss-primary) 8%, #fff);
479
+ color: var(--gemboss-accent-strong); font-weight: 600;
480
+ /* The kit's iris tint token, not a mix with #fff: a dark surface remaps -soft and -strong together, so the label keeps
481
+ its contrast there too (review #3710: a #fff mix under gempress's dark -strong would be 1.8:1). */
482
+ background: var(--gemboss-accent-soft);
481
483
  }
482
484
 
483
- .gnav-item.gnav-assistant .gnav-ico svg { fill: var(--gemboss-primary); }
485
+ .gnav-item.gnav-assistant .gnav-ico svg { fill: var(--gemboss-accent); }
484
486
 
485
- .gnav-item.gnav-assistant:hover { background: color-mix(in srgb, var(--gemboss-primary) 15%, #fff); }
487
+ .gnav-item.gnav-assistant:hover { background: color-mix(in srgb, var(--gemboss-accent) 16%, var(--gemboss-surface)); }
486
488
 
487
489
  .gnav-item.gnav-assistant.active { background: var(--gemboss-active-bg); color: var(--gemboss-active-fg); }
488
490