@gemboss/ui 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -1
- package/catalog/index.html +196 -5
- package/gemboss-ui.css +28 -2
- package/package.json +1 -1
- package/src/app-shell.css +26 -0
- package/src/index.ts +3 -0
- package/src/page.tsx +35 -0
package/README.md
CHANGED
|
@@ -40,6 +40,55 @@ Surfaces with no React (gemcare, gempress) use the CSS alone and write the same
|
|
|
40
40
|
`nav.icon-nav > .gnav-scroll > .gnav-group > a.gnav-item > span.gnav-ico + span.gnav-label`.
|
|
41
41
|
`packages/ui/src/shell.tsx` is the reference.
|
|
42
42
|
|
|
43
|
+
## Page width (0.8.1)
|
|
44
|
+
|
|
45
|
+
Chris, 04/10/2026: the rail on the left, and on the right a content column with the same max width on every
|
|
46
|
+
service, not edge to edge. The tiers are the admin's (`gemboss-shell.css`, `.gemboss-w-*` around a Polaris Page),
|
|
47
|
+
same numbers; `page-width.test.tsx` fails if the admin and the package disagree.
|
|
48
|
+
|
|
49
|
+
| Tier | Class | Max width | For | Admin |
|
|
50
|
+
|---|---|---|---|---|
|
|
51
|
+
| `default` | `ui-page` | `--ui-content-max` (900px) | lists, tables, card grids, dashboards, detail pages | bare Page, `.gemboss-w-gallery`, `.gemboss-w-dashboard` |
|
|
52
|
+
| `feed` | `ui-page ui-page--feed` | 720px | read top to bottom: activity, logs, timelines, a thread | `.gemboss-w-feed` |
|
|
53
|
+
| `form` | `ui-page ui-page--form` | 662px | one column of fields: settings, a create form | `.gemboss-w-form` (Polaris narrowWidth) |
|
|
54
|
+
| `canvas` | `ui-page ui-page--canvas` | 1440px | content measured to be wider than a column of text: a horizontal path, a wide comparison table | `.gemboss-w-canvas` (funnel path, 864px needed) |
|
|
55
|
+
| `full` | `ui-page ui-page--full` | none | a board or an editor that IS the screen. Write the reason next to it. | the editor (fixed, its own frame) |
|
|
56
|
+
|
|
57
|
+
React: one `PageContainer` directly inside `AppShell` caps every screen. Pass the CURRENT route's tier; a page
|
|
58
|
+
cannot widen a cap its parent set, so a screen that needs `canvas` or `full` gets it here, not from a second
|
|
59
|
+
wrapper of its own.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<AppShell rail={rail}>
|
|
63
|
+
<PageContainer width={route === "settings" ? "form" : route === "board" ? "full" : "default"}>{page}</PageContainer>
|
|
64
|
+
</AppShell>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`as="section"` changes the tag. `pageWidthClass("form")` returns the class string for a server template.
|
|
68
|
+
`PageContainer` lives in its own plain module (`packages/ui/src/page.tsx`), not in `shell.tsx`, and `AppShell` has no width
|
|
69
|
+
prop: the admin imports the shell pieces from `shell.tsx`, a client module its bundler keeps whole, and the
|
|
70
|
+
admin's first-load shell budget had no room for even this much JS (240.0 / 240 KB, 04/10/2026).
|
|
71
|
+
|
|
72
|
+
Plain HTML (GemFactory, gemcare, gempress): one wrapper as the first child of main.
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<main class="gemboss-platform-main">
|
|
76
|
+
<div class="ui-page ui-page--form">…</div>
|
|
77
|
+
</main>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Delete the surface's own cap when it moves onto a tier (`maxWidth: 1160` on each gemwatcher screen, a
|
|
81
|
+
`max-width` on a `.container`): two caps on one column means the narrower one wins and nobody can tell which.
|
|
82
|
+
|
|
83
|
+
**A cap only exists where it is SMALLER than the room it sits in.** Right of the 240px rail, inside main's 24px
|
|
84
|
+
padding, the room is `viewport − 288`: 992 at 1280, 1152 at 1440, 1632 at 1920. So 900 bites at all three, and
|
|
85
|
+
1440 (`canvas`) bites only from about 1730px; below that the column is just the room. Before you quote a width,
|
|
86
|
+
check `cap < viewport − rail − padding` at the width people actually use (the admin shipped two caps, 1040 then
|
|
87
|
+
998, that were wider than the room at 1277 and did nothing there, 10/09/2026). The column is never wider than
|
|
88
|
+
the room, so no tier makes a page scroll sideways; at ≤760px there is no rail and the column is `viewport − 32`.
|
|
89
|
+
|
|
90
|
+
The page must reset `body { margin: 0 }` itself: `gemboss-ui.css` does not (the admin gets it from Polaris).
|
|
91
|
+
|
|
43
92
|
## The sign-in screen
|
|
44
93
|
|
|
45
94
|
```tsx
|
|
@@ -547,7 +596,7 @@ something the base cannot express, add a token or a prop here, and every surface
|
|
|
547
596
|
| `packages/ui/src/shell.css` | the SHARED SHELL (rail, topbar, main, mobile drawer, the assistant's thread rail, popover surface, account chip). The admin's `gemboss-shell.css` @imports it on line 2; the export refuses a shared-shell rule in the admin's sheet or an admin-only rule here (one owner). |
|
|
548
597
|
| `packages/ui/src/kit-plain.css` | primitives for surfaces that render plain HTML, not React (0.2.1): menu, key/value list, icon button; 0.3.0 adds `ui-link` / `ui-prose`, the media card (`ui-card--media`, `__media`, `__body`, `__foot`) and the popup's `ui-modal__body` / `__foot`. Shipped inside `gemboss-ui.css`; the admin does not import it (its first-load CSS budget has ~0.1 KB headroom).; (0.4.0) report pages: delta, large KPI grid, bar list, column chart, legend, funnel, card grid, section head. |
|
|
549
598
|
| `packages/ui/src/kit-plain.js` | 0.3.0: the behaviour those surfaces each wrote by hand: `gembossUI.openModal` (Escape, backdrop, Tab trap, stacking, ask-before-close, focus back), `toast` (text, error tone), `initMenus` (outside click, item, blur, Escape closes the menu before the popup); 0.5.0: `confirm`, `initShell`, `initPasswordFields` / `resetPasswordFields`. A classic script: inline it or `<script src>`; see "Plain-HTML behaviour" above. |
|
|
550
|
-
| `packages/ui/src/chat.css`, `chat-composer.css`, `assistant.css`, `auth.css`, `app-shell.css` | the chat body, the composer, the assistant frame, the sign-in screen, the topbar slots + base text. All inside `gemboss-ui.css`; the admin @imports `chat.css` and `chat-composer.css` itself. |
|
|
599
|
+
| `packages/ui/src/chat.css`, `chat-composer.css`, `assistant.css`, `auth.css`, `app-shell.css` | the chat body, the composer, the assistant frame, the sign-in screen, the topbar slots + base text + the page width tiers (`ui-page`, 0.8.1). All inside `gemboss-ui.css`; the admin @imports `chat.css` and `chat-composer.css` itself. |
|
|
551
600
|
| `packages/ui/src/icon.svg` | 0.5.0: the GemBoss app icon, for `<link rel="icon" type="image/svg+xml">`. Must stay byte-identical to `apps/admin/src/app/icon.svg` (`kit-look.test.ts` checks). |
|
|
552
601
|
| `packages/ui/src/**/*.tsx` | the components, as TypeScript source. Next: add `@gemboss/ui` to `transpilePackages`. Vite compiles it as is. |
|
|
553
602
|
|
package/catalog/index.html
CHANGED
|
@@ -10,7 +10,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
10
10
|
.cat-demo{margin:0 0 16px;background:var(--gemboss-surface);border:1px solid var(--gemboss-divider);border-radius:12px;overflow:hidden}
|
|
11
11
|
.cat-demo figcaption{padding:10px 16px;font-size:13px;font-weight:600;border-bottom:1px solid var(--gemboss-divider)}
|
|
12
12
|
.cat-demo figcaption span{font-weight:400;color:var(--gemboss-text-muted)}
|
|
13
|
-
.cat-live{padding:20px 16px}.cat-screen iframe{display:block;width:100%;border:0}
|
|
13
|
+
.cat-live{padding:20px 16px}.cat-screen iframe{display:block;width:100%;border:0}.cat-wide{overflow-x:auto}
|
|
14
14
|
.cat-code summary{padding:10px 16px;font-size:13px;cursor:pointer;color:var(--gemboss-text-muted);border-top:1px solid var(--gemboss-divider);min-height:24px}
|
|
15
15
|
.cat-code pre{margin:0;padding:12px 16px;overflow:auto;background:#0f1012;color:#e8e8e6;font-size:12px;line-height:1.5}
|
|
16
16
|
.cat-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.cat-stack{display:grid;gap:10px;max-width:420px}
|
|
@@ -26,8 +26,8 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
26
26
|
|
|
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
|
-
<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="#portal">Cổng công khai: tab trên topbar, tìm kiếm, sáng / tối, người dẫn đầu</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.8.
|
|
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="#page">Bề rộng nội dung</a><a href="#portal">Cổng công khai: tab trên topbar, tìm kiếm, sáng / tối, người dẫn đầu</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.8.1</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>22b5d0daf43a</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><button></code> lẫn <code><a></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><div class="cat-row">
|
|
33
33
|
<button type="button" class="ui-btn">Lưu nháp</button>
|
|
@@ -752,6 +752,197 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
752
752
|
</div>
|
|
753
753
|
</main>
|
|
754
754
|
</div></code></pre></details></figure></section>
|
|
755
|
+
<section id="page"><h2>Bề rộng nội dung</h2><p>Rail bên trái, bên phải là một cột có bề rộng tối đa theo bậc của admin: <code>ui-page</code> 900px (<code>--ui-content-max</code>: danh sách, bảng, lưới card, dashboard), <code>--feed</code> 720, <code>--form</code> 662, <code>--canvas</code> 1440 (chỉ khi nội dung đo được là rộng hơn một cột chữ), <code>--full</code> không giới hạn (board, editor; ghi lý do). React: một <code>PageContainer width="form"</code> ngay trong <code>AppShell</code>. Một cap chỉ có tác dụng khi nó NHỎ HƠN chỗ trống thật: ở 1280 chỗ trống là 992. Hai khung dưới vẽ ở 1440, kéo ngang để xem hết.</p><figure class="cat-demo cat-screen"><figcaption>PageContainer width="default" ở 1440: cột 900</figcaption><div class="cat-wide"><iframe title="PageContainer width="default" ở 1440: cột 900" style="height:300px;width:1440px" srcdoc="<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="../gemboss-ui.css"></head><body><div class="gemboss-platform-shell"><header class="gemboss-platform-topbar"><div class="gemboss-topbar-start"><button type="button" class="gemboss-mobile-menu-btn" aria-label="Open menu" aria-expanded="false" data-label-open="Open menu" data-label-close="Close menu" aria-controls=":R0:"><svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path d="M3 5.5h14M3 10h14M3 14.5h14" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"></path></svg></button><a class="gb-brand" href="#" aria-label="GemFactory home"><svg class="gb-brand-gem" viewBox="0 0 32 40" width="20.8" height="26" aria-hidden="true" focusable="false"><g transform="translate(0,0.8) scale(0.31)"><polygon points="26,14 74,14 66,50 34,50" fill="#5661F5" fill-opacity="0.92"></polygon><path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" stroke-width="8.75" stroke-linejoin="round"></path><path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" stroke-width="5.45" stroke-linejoin="round"></path></g></svg><span class="gb-brand-name">GemFactory</span></a></div><div class="gemboss-topbar-center"></div><div class="gemboss-topbar-end"></div></header><nav class="icon-nav" aria-label="Main navigation" id=":R0:"><div class="gnav-scroll"><div class="gnav-heading">Kho</div><div class="gnav-group active"><a class="gnav-item active" aria-current="page" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3.5" width="4" height="13" rx="1.2"></rect><rect x="8" y="3.5" width="4" height="9" rx="1.2"></rect><rect x="13" y="3.5" width="4" height="6" rx="1.2"></rect></g></svg></span><span class="gnav-label">Board</span></a></div><div class="gnav-group"><a class="gnav-item" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="3" width="13" height="3.5" rx="1"></rect><rect x="3.5" y="8.25" width="13" height="3.5" rx="1"></rect><rect x="3.5" y="13.5" width="13" height="3.5" rx="1"></rect></g></svg></span><span class="gnav-label">Ghép trang</span></a></div><div class="gnav-group"><a class="gnav-item" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3.2 17.3 16H2.7z"></path><path d="M10 8.2v3.6M10 13.9v.1"></path></g></svg></span><span class="gnav-label">Framework thiếu</span><span class="gnav-count" aria-label="3 mục đang mở">3</span></a></div></div></nav><button type="button" class="gemboss-mobile-nav-backdrop" aria-label="Close menu" tabindex="-1"></button><main class="gemboss-platform-main"><div class="ui-page"><h1 style="margin:0 0 16px">Board</h1><div class="ui-card"><div class="ui-card__title">#412 Hero split</div><p class="ui-muted">Cột 900px, căn giữa trong chỗ trống bên phải rail.</p></div></div></main></div></body></html>"></iframe></div><details class="cat-code"><summary>Markup</summary><pre><code><div class="gemboss-platform-shell">
|
|
756
|
+
<header class="gemboss-platform-topbar">
|
|
757
|
+
<div class="gemboss-topbar-start">
|
|
758
|
+
<button type="button" class="gemboss-mobile-menu-btn" aria-label="Open menu" aria-expanded="false" data-label-open="Open menu" data-label-close="Close menu" aria-controls=":R0:">
|
|
759
|
+
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true">
|
|
760
|
+
<path d="M3 5.5h14M3 10h14M3 14.5h14" stroke="currentColor" stroke-width="1.7" stroke-linecap="round">
|
|
761
|
+
</path>
|
|
762
|
+
</svg>
|
|
763
|
+
</button>
|
|
764
|
+
<a class="gb-brand" href="#" aria-label="GemFactory home">
|
|
765
|
+
<svg class="gb-brand-gem" viewBox="0 0 32 40" width="20.8" height="26" aria-hidden="true" focusable="false">
|
|
766
|
+
<g transform="translate(0,0.8) scale(0.31)">
|
|
767
|
+
<polygon points="26,14 74,14 66,50 34,50" fill="#5661F5" fill-opacity="0.92">
|
|
768
|
+
</polygon>
|
|
769
|
+
<path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" stroke-width="8.75" stroke-linejoin="round">
|
|
770
|
+
</path>
|
|
771
|
+
<path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" stroke-width="5.45" stroke-linejoin="round">
|
|
772
|
+
</path>
|
|
773
|
+
</g>
|
|
774
|
+
</svg>
|
|
775
|
+
<span class="gb-brand-name">GemFactory</span>
|
|
776
|
+
</a>
|
|
777
|
+
</div>
|
|
778
|
+
<div class="gemboss-topbar-center">
|
|
779
|
+
</div>
|
|
780
|
+
<div class="gemboss-topbar-end">
|
|
781
|
+
</div>
|
|
782
|
+
</header>
|
|
783
|
+
<nav class="icon-nav" aria-label="Main navigation" id=":R0:">
|
|
784
|
+
<div class="gnav-scroll">
|
|
785
|
+
<div class="gnav-heading">Kho</div>
|
|
786
|
+
<div class="gnav-group active">
|
|
787
|
+
<a class="gnav-item active" aria-current="page" href="#">
|
|
788
|
+
<span class="gnav-ico">
|
|
789
|
+
<svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false">
|
|
790
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
791
|
+
<rect x="3" y="3.5" width="4" height="13" rx="1.2">
|
|
792
|
+
</rect>
|
|
793
|
+
<rect x="8" y="3.5" width="4" height="9" rx="1.2">
|
|
794
|
+
</rect>
|
|
795
|
+
<rect x="13" y="3.5" width="4" height="6" rx="1.2">
|
|
796
|
+
</rect>
|
|
797
|
+
</g>
|
|
798
|
+
</svg>
|
|
799
|
+
</span>
|
|
800
|
+
<span class="gnav-label">Board</span>
|
|
801
|
+
</a>
|
|
802
|
+
</div>
|
|
803
|
+
<div class="gnav-group">
|
|
804
|
+
<a class="gnav-item" href="#">
|
|
805
|
+
<span class="gnav-ico">
|
|
806
|
+
<svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false">
|
|
807
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
808
|
+
<rect x="3.5" y="3" width="13" height="3.5" rx="1">
|
|
809
|
+
</rect>
|
|
810
|
+
<rect x="3.5" y="8.25" width="13" height="3.5" rx="1">
|
|
811
|
+
</rect>
|
|
812
|
+
<rect x="3.5" y="13.5" width="13" height="3.5" rx="1">
|
|
813
|
+
</rect>
|
|
814
|
+
</g>
|
|
815
|
+
</svg>
|
|
816
|
+
</span>
|
|
817
|
+
<span class="gnav-label">Ghép trang</span>
|
|
818
|
+
</a>
|
|
819
|
+
</div>
|
|
820
|
+
<div class="gnav-group">
|
|
821
|
+
<a class="gnav-item" href="#">
|
|
822
|
+
<span class="gnav-ico">
|
|
823
|
+
<svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false">
|
|
824
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
825
|
+
<path d="M10 3.2 17.3 16H2.7z">
|
|
826
|
+
</path>
|
|
827
|
+
<path d="M10 8.2v3.6M10 13.9v.1">
|
|
828
|
+
</path>
|
|
829
|
+
</g>
|
|
830
|
+
</svg>
|
|
831
|
+
</span>
|
|
832
|
+
<span class="gnav-label">Framework thiếu</span>
|
|
833
|
+
<span class="gnav-count" aria-label="3 mục đang mở">3</span>
|
|
834
|
+
</a>
|
|
835
|
+
</div>
|
|
836
|
+
</div>
|
|
837
|
+
</nav>
|
|
838
|
+
<button type="button" class="gemboss-mobile-nav-backdrop" aria-label="Close menu" tabindex="-1">
|
|
839
|
+
</button>
|
|
840
|
+
<main class="gemboss-platform-main">
|
|
841
|
+
<div class="ui-page">
|
|
842
|
+
<h1 style="margin:0 0 16px">Board</h1>
|
|
843
|
+
<div class="ui-card">
|
|
844
|
+
<div class="ui-card__title">#412 Hero split</div>
|
|
845
|
+
<p class="ui-muted">Cột 900px, căn giữa trong chỗ trống bên phải rail.</p>
|
|
846
|
+
</div>
|
|
847
|
+
</div>
|
|
848
|
+
</main>
|
|
849
|
+
</div></code></pre></details></figure><figure class="cat-demo cat-screen"><figcaption>PageContainer width="form" ở 1440: cột 662</figcaption><div class="cat-wide"><iframe title="PageContainer width="form" ở 1440: cột 662" style="height:300px;width:1440px" srcdoc="<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="../gemboss-ui.css"></head><body><div class="gemboss-platform-shell"><header class="gemboss-platform-topbar"><div class="gemboss-topbar-start"><button type="button" class="gemboss-mobile-menu-btn" aria-label="Open menu" aria-expanded="false" data-label-open="Open menu" data-label-close="Close menu" aria-controls=":R0:"><svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path d="M3 5.5h14M3 10h14M3 14.5h14" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"></path></svg></button><a class="gb-brand" href="#" aria-label="GemFactory home"><svg class="gb-brand-gem" viewBox="0 0 32 40" width="20.8" height="26" aria-hidden="true" focusable="false"><g transform="translate(0,0.8) scale(0.31)"><polygon points="26,14 74,14 66,50 34,50" fill="#5661F5" fill-opacity="0.92"></polygon><path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" stroke-width="8.75" stroke-linejoin="round"></path><path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" stroke-width="5.45" stroke-linejoin="round"></path></g></svg><span class="gb-brand-name">GemFactory</span></a></div><div class="gemboss-topbar-center"></div><div class="gemboss-topbar-end"></div></header><nav class="icon-nav" aria-label="Main navigation" id=":R0:"><div class="gnav-scroll"><div class="gnav-heading">Kho</div><div class="gnav-group active"><a class="gnav-item active" aria-current="page" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3.5" width="4" height="13" rx="1.2"></rect><rect x="8" y="3.5" width="4" height="9" rx="1.2"></rect><rect x="13" y="3.5" width="4" height="6" rx="1.2"></rect></g></svg></span><span class="gnav-label">Board</span></a></div><div class="gnav-group"><a class="gnav-item" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="3" width="13" height="3.5" rx="1"></rect><rect x="3.5" y="8.25" width="13" height="3.5" rx="1"></rect><rect x="3.5" y="13.5" width="13" height="3.5" rx="1"></rect></g></svg></span><span class="gnav-label">Ghép trang</span></a></div><div class="gnav-group"><a class="gnav-item" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3.2 17.3 16H2.7z"></path><path d="M10 8.2v3.6M10 13.9v.1"></path></g></svg></span><span class="gnav-label">Framework thiếu</span><span class="gnav-count" aria-label="3 mục đang mở">3</span></a></div></div></nav><button type="button" class="gemboss-mobile-nav-backdrop" aria-label="Close menu" tabindex="-1"></button><main class="gemboss-platform-main"><div class="ui-page ui-page--form"><h1 style="margin:0 0 16px">Settings</h1><div class="ui-card" style="display:grid;gap:6px"><label for="cat-pw-name">Tên hiển thị</label><input id="cat-pw-name" class="gemboss-field" value="Minh Anh"></div></div></main></div></body></html>"></iframe></div><details class="cat-code"><summary>Markup</summary><pre><code><div class="gemboss-platform-shell">
|
|
850
|
+
<header class="gemboss-platform-topbar">
|
|
851
|
+
<div class="gemboss-topbar-start">
|
|
852
|
+
<button type="button" class="gemboss-mobile-menu-btn" aria-label="Open menu" aria-expanded="false" data-label-open="Open menu" data-label-close="Close menu" aria-controls=":R0:">
|
|
853
|
+
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true">
|
|
854
|
+
<path d="M3 5.5h14M3 10h14M3 14.5h14" stroke="currentColor" stroke-width="1.7" stroke-linecap="round">
|
|
855
|
+
</path>
|
|
856
|
+
</svg>
|
|
857
|
+
</button>
|
|
858
|
+
<a class="gb-brand" href="#" aria-label="GemFactory home">
|
|
859
|
+
<svg class="gb-brand-gem" viewBox="0 0 32 40" width="20.8" height="26" aria-hidden="true" focusable="false">
|
|
860
|
+
<g transform="translate(0,0.8) scale(0.31)">
|
|
861
|
+
<polygon points="26,14 74,14 66,50 34,50" fill="#5661F5" fill-opacity="0.92">
|
|
862
|
+
</polygon>
|
|
863
|
+
<path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" stroke-width="8.75" stroke-linejoin="round">
|
|
864
|
+
</path>
|
|
865
|
+
<path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" stroke-width="5.45" stroke-linejoin="round">
|
|
866
|
+
</path>
|
|
867
|
+
</g>
|
|
868
|
+
</svg>
|
|
869
|
+
<span class="gb-brand-name">GemFactory</span>
|
|
870
|
+
</a>
|
|
871
|
+
</div>
|
|
872
|
+
<div class="gemboss-topbar-center">
|
|
873
|
+
</div>
|
|
874
|
+
<div class="gemboss-topbar-end">
|
|
875
|
+
</div>
|
|
876
|
+
</header>
|
|
877
|
+
<nav class="icon-nav" aria-label="Main navigation" id=":R0:">
|
|
878
|
+
<div class="gnav-scroll">
|
|
879
|
+
<div class="gnav-heading">Kho</div>
|
|
880
|
+
<div class="gnav-group active">
|
|
881
|
+
<a class="gnav-item active" aria-current="page" href="#">
|
|
882
|
+
<span class="gnav-ico">
|
|
883
|
+
<svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false">
|
|
884
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
885
|
+
<rect x="3" y="3.5" width="4" height="13" rx="1.2">
|
|
886
|
+
</rect>
|
|
887
|
+
<rect x="8" y="3.5" width="4" height="9" rx="1.2">
|
|
888
|
+
</rect>
|
|
889
|
+
<rect x="13" y="3.5" width="4" height="6" rx="1.2">
|
|
890
|
+
</rect>
|
|
891
|
+
</g>
|
|
892
|
+
</svg>
|
|
893
|
+
</span>
|
|
894
|
+
<span class="gnav-label">Board</span>
|
|
895
|
+
</a>
|
|
896
|
+
</div>
|
|
897
|
+
<div class="gnav-group">
|
|
898
|
+
<a class="gnav-item" href="#">
|
|
899
|
+
<span class="gnav-ico">
|
|
900
|
+
<svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false">
|
|
901
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
902
|
+
<rect x="3.5" y="3" width="13" height="3.5" rx="1">
|
|
903
|
+
</rect>
|
|
904
|
+
<rect x="3.5" y="8.25" width="13" height="3.5" rx="1">
|
|
905
|
+
</rect>
|
|
906
|
+
<rect x="3.5" y="13.5" width="13" height="3.5" rx="1">
|
|
907
|
+
</rect>
|
|
908
|
+
</g>
|
|
909
|
+
</svg>
|
|
910
|
+
</span>
|
|
911
|
+
<span class="gnav-label">Ghép trang</span>
|
|
912
|
+
</a>
|
|
913
|
+
</div>
|
|
914
|
+
<div class="gnav-group">
|
|
915
|
+
<a class="gnav-item" href="#">
|
|
916
|
+
<span class="gnav-ico">
|
|
917
|
+
<svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false">
|
|
918
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
919
|
+
<path d="M10 3.2 17.3 16H2.7z">
|
|
920
|
+
</path>
|
|
921
|
+
<path d="M10 8.2v3.6M10 13.9v.1">
|
|
922
|
+
</path>
|
|
923
|
+
</g>
|
|
924
|
+
</svg>
|
|
925
|
+
</span>
|
|
926
|
+
<span class="gnav-label">Framework thiếu</span>
|
|
927
|
+
<span class="gnav-count" aria-label="3 mục đang mở">3</span>
|
|
928
|
+
</a>
|
|
929
|
+
</div>
|
|
930
|
+
</div>
|
|
931
|
+
</nav>
|
|
932
|
+
<button type="button" class="gemboss-mobile-nav-backdrop" aria-label="Close menu" tabindex="-1">
|
|
933
|
+
</button>
|
|
934
|
+
<main class="gemboss-platform-main">
|
|
935
|
+
<div class="ui-page ui-page--form">
|
|
936
|
+
<h1 style="margin:0 0 16px">Settings</h1>
|
|
937
|
+
<div class="ui-card" style="display:grid;gap:6px">
|
|
938
|
+
<label for="cat-pw-name">Tên hiển thị</label>
|
|
939
|
+
<input id="cat-pw-name" class="gemboss-field" value="Minh Anh">
|
|
940
|
+
</div>
|
|
941
|
+
</div>
|
|
942
|
+
</main>
|
|
943
|
+
</div></code></pre></details></figure><figure class="cat-demo"><figcaption>HTML thuần: một lớp bọc là con đầu tiên của main</figcaption><div class="cat-live"><div class="ui-page ui-page--feed"><div class="ui-card">Một mục trong feed, cột 720px.</div></div></div><details class="cat-code"><summary>Markup</summary><pre><code><div class="ui-page ui-page--feed">
|
|
944
|
+
<div class="ui-card">Một mục trong feed, cột 720px.</div>
|
|
945
|
+
</div></code></pre></details></figure></section>
|
|
755
946
|
<section id="portal"><h2>Cổng công khai: tab trên topbar, tìm kiếm, sáng / tối, người dẫn đầu</h2><p>Cho bề mặt công khai kiểu bảng Featurebase (GemFeedback) và cộng đồng (GemCommunity). <code>TopbarNav</code> đặt tab mục ngay trên topbar cạnh logo; <code>SearchDialog</code> là popup tìm (⌘K, gõ là ra); <code>ThemeToggle</code> + <code>themeScript</code> bật bảng màu tối (chỉ đổi token, mọi class theo); <code>Avatar</code> ảnh hoặc chữ viết tắt trên một tông lấy theo tên; <code>Leaderboard</code> huy chương 1–3 rồi thứ hạng; <code>Podium</code> ba người đầu.</p><figure class="cat-demo cat-screen"><figcaption>TopbarNav + SearchDialog + ThemeToggle, sáng</figcaption><iframe title="TopbarNav + SearchDialog + ThemeToggle, sáng" style="height:360px" srcdoc="<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="../gemboss-ui.css"></head><body><div class="gemboss-platform-shell"><header class="gemboss-platform-topbar"><div class="gemboss-topbar-start"><a class="gb-brand" href="/" aria-label="GemFeedback home"><svg class="gb-brand-gem" viewBox="0 0 32 40" width="20.8" height="26" aria-hidden="true" focusable="false"><g transform="translate(0,0.8) scale(0.31)"><polygon points="26,14 74,14 66,50 34,50" fill="#5661F5" fill-opacity="0.92"></polygon><path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" stroke-width="8.75" stroke-linejoin="round"></path><path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" stroke-width="5.45" stroke-linejoin="round"></path></g></svg><span class="gb-brand-name">GemFeedback</span></a><nav class="gb-topnav" aria-label="Sections"><div class="gb-topnav-row"><a href="#portal" class="gb-topnav-link" aria-current="page">Feedback</a><a href="#portal" class="gb-topnav-link">Roadmap</a><a href="#portal" class="gb-topnav-link">Changelog</a></div><details class="gb-topnav-fold"><summary class="gb-topnav-link gb-topnav-fold-btn" aria-current="page">Feedback<svg class="gb-topnav-chevron" width="12" height="12" viewBox="0 0 12 12" aria-hidden="true" focusable="false"><path d="M3 4.5 6 7.5 9 4.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"></path></svg></summary><div class="gb-topnav-pop gemboss-popover-surface"><a href="#portal" class="gb-topnav-item" aria-current="page">Feedback</a><a href="#portal" class="gb-topnav-item">Roadmap</a><a href="#portal" class="gb-topnav-item">Changelog</a></div></details></nav></div><div class="gemboss-topbar-center"></div><div class="gemboss-topbar-end"><a class="gb-search-trigger" href="#portal" aria-label="Search" aria-haspopup="dialog"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8.8" cy="8.8" r="5.3"></circle><path d="M12.8 12.8 17 17"></path></g></svg></a><dialog class="ui-modal gb-search" aria-label="Search"><div class="gb-search-box"><svg viewBox="0 0 20 20" width="18" height="18" class="gb-search-icon" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8.8" cy="8.8" r="5.3"></circle><path d="M12.8 12.8 17 17"></path></g></svg><input class="gb-search-input" type="search" placeholder="Search…" aria-label="Search" role="combobox" aria-expanded="true" aria-controls=":Rt:" autoComplete="off" value=""/></div><p class="gb-search-empty" aria-live="polite"></p><div class="gb-search-results" id=":Rt:" role="listbox" aria-busy="false"></div><div class="gb-search-foot" aria-hidden="true"><span>↵ open</span><span>↑↓ move</span><span>esc</span></div></dialog><button type="button" class="gb-theme-toggle" aria-label="Switch to dark mode" title="Switch to dark mode"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15.6 12.4A6.2 6.2 0 0 1 7.6 4.4a6.2 6.2 0 1 0 8 8Z"></path></g></svg></button><a class="ui-btn ui-btn--primary" href="#portal">Sign in</a></div></header><div><div style="padding:72px 24px 24px;display:grid;grid-template-columns:minmax(0,1fr) 260px;gap:24px"><div class="ui-card"><b>Have something to say?</b><p class="ui-muted">Tell us what to build next.</p></div><section class="gb-leaderboard" aria-labelledby="gb-lb-most-helpful-1wqxo9r"><h2 class="gb-leaderboard-title" id="gb-lb-most-helpful-1wqxo9r">Most helpful</h2><p class="gb-leaderboard-caption">By votes on their comments.</p><ol class="gb-leaderboard-list"><li class="gb-leaderboard-row is-first"><a href="#portal" class="gb-leaderboard-link"><span class="gb-leaderboard-rank"><span aria-hidden="true">🥇</span><span class="gb-sr-only">Rank 1: </span></span><span class="gb-avatar gb-avatar--24" data-tone="2" aria-hidden="true">LN</span><span class="gb-leaderboard-name">Lucas Nguyen</span><span class="gb-leaderboard-value">163<span class="gb-sr-only"> votes</span></span></a></li><li class="gb-leaderboard-row"><a href="#portal" class="gb-leaderboard-link"><span class="gb-leaderboard-rank"><span aria-hidden="true">🥈</span><span class="gb-sr-only">Rank 2: </span></span><span class="gb-avatar gb-avatar--24" data-tone="0" aria-hidden="true">DC</span><span class="gb-leaderboard-name">Dima Chernyshov</span><span class="gb-leaderboard-value">56<span class="gb-sr-only"> votes</span></span></a></li><li class="gb-leaderboard-row"><a href="#portal" class="gb-leaderboard-link"><span class="gb-leaderboard-rank"><span aria-hidden="true">🥉</span><span class="gb-sr-only">Rank 3: </span></span><span class="gb-avatar gb-avatar--24" data-tone="5" aria-hidden="true">AR</span><span class="gb-leaderboard-name">Artjom</span><span class="gb-leaderboard-value">55<span class="gb-sr-only"> votes</span></span></a></li><li class="gb-leaderboard-row"><a href="#portal" class="gb-leaderboard-link"><span class="gb-leaderboard-rank is-number"><span aria-hidden="true">4</span><span class="gb-sr-only">Rank 4: </span></span><span class="gb-avatar gb-avatar--24" data-tone="4" aria-hidden="true">GU</span><span class="gb-leaderboard-name">GutBrush</span><span class="gb-leaderboard-value">34<span class="gb-sr-only"> votes</span></span></a></li><li class="gb-leaderboard-row"><a href="#portal" class="gb-leaderboard-link"><span class="gb-leaderboard-rank is-number"><span aria-hidden="true">5</span><span class="gb-sr-only">Rank 5: </span></span><span class="gb-avatar gb-avatar--24" data-tone="5" aria-hidden="true">WO</span><span class="gb-leaderboard-name">Wondercraft</span><span class="gb-leaderboard-value">30<span class="gb-sr-only"> votes</span></span></a></li></ol></section></div></div></div></body></html>"></iframe><details class="cat-code"><summary>Markup</summary><pre><code><div class="gemboss-platform-shell">
|
|
756
947
|
<header class="gemboss-platform-topbar">
|
|
757
948
|
<div class="gemboss-topbar-start">
|
|
@@ -1266,7 +1457,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
1266
1457
|
<button type="submit" aria-label="Gửi">↑</button>
|
|
1267
1458
|
</form>
|
|
1268
1459
|
</div></code></pre></details></figure></section>
|
|
1269
|
-
<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">
|
|
1460
|
+
<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">26/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><div style="height:360px;display:flex">
|
|
1270
1461
|
<aside class="gemboss-assistant-rail">
|
|
1271
1462
|
<div class="gemboss-assistant-rail-top">
|
|
1272
1463
|
<button type="button" class="gemboss-assistant-newchat" title="Phiên mới">
|
|
@@ -1315,7 +1506,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
1315
1506
|
</svg>
|
|
1316
1507
|
<span class="gemboss-assistant-thread-title">So sánh advertorial với listicle</span>
|
|
1317
1508
|
</span>
|
|
1318
|
-
<span class="gemboss-assistant-thread-meta">
|
|
1509
|
+
<span class="gemboss-assistant-thread-meta">26/8/2026</span>
|
|
1319
1510
|
</button>
|
|
1320
1511
|
<div class="gemboss-assistant-thread-actions">
|
|
1321
1512
|
<button type="button" class="gemboss-assistant-thread-act" aria-label="Options">
|
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:
|
|
5
|
-
* sources sha256:
|
|
4
|
+
* gemboss commit: 22b5d0daf43ab644a6a9bffef08352a93cc5ca1c
|
|
5
|
+
* sources sha256: 7d836f2fc37705cee220b28af995f1e1006b2cb1a41e0ac162c29fa9276f70d3
|
|
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)
|
|
@@ -2154,6 +2154,32 @@ body.gemboss-assistant-unread .gnav-item.gnav-assistant::after {
|
|
|
2154
2154
|
/* A phone has no ⌘ key, and the box needs every pixel the bar has left. */
|
|
2155
2155
|
@media (max-width: 460px) { .gemboss-topbar-search-kbd { display: none; } }
|
|
2156
2156
|
|
|
2157
|
+
/* ── Page width (0.8.1) ──────────────────────────────────────────────────────────────────────────────
|
|
2158
|
+
* Chris 04/10/2026: the rail on the left, and on the right a content column with ONE max width across every
|
|
2159
|
+
* service, not edge to edge. The admin's tiers (gemboss-shell.css, `.gemboss-w-*` around a Polaris Page), same
|
|
2160
|
+
* numbers; `page-width.test.tsx` fails if the two disagree:
|
|
2161
|
+
* .ui-page → --ui-content-max (900px, ui.css): lists, tables, card grids, dashboards
|
|
2162
|
+
* .ui-page--feed → 720px: read top to bottom (activity, logs, timelines)
|
|
2163
|
+
* .ui-page--form → 662px: one column of fields (Polaris narrowWidth)
|
|
2164
|
+
* .ui-page--canvas → 1440px: content measured to be wider than a reading column (admin: the funnel path)
|
|
2165
|
+
* .ui-page--full → no cap: a board or an editor that is the screen. Say why where you use it.
|
|
2166
|
+
* A cap only exists where it is SMALLER than the room it sits in. Right of the 240px rail with main's 24px padding,
|
|
2167
|
+
* 900 bites from 1188px up (1280 → 992 of room); 1440 bites only from ~1730px. Below that the column is simply the
|
|
2168
|
+
* room, so the tier never causes a horizontal scroll.
|
|
2169
|
+
* Here, not in ui.css: ui.css is exported verbatim into the admin under a 90 KB budget (89.8 KB at 0.7.3), and the
|
|
2170
|
+
* admin draws its tiers on Polaris' Page instead. Fixed px, not %: a % cap grows the line length with the monitor. */
|
|
2171
|
+
.ui-page {
|
|
2172
|
+
box-sizing: border-box;
|
|
2173
|
+
width: 100%;
|
|
2174
|
+
min-width: 0;
|
|
2175
|
+
max-width: var(--ui-content-max, 900px);
|
|
2176
|
+
margin-inline: auto;
|
|
2177
|
+
}
|
|
2178
|
+
.ui-page--feed { max-width: 720px; }
|
|
2179
|
+
.ui-page--form { max-width: 662px; }
|
|
2180
|
+
.ui-page--canvas { max-width: 1440px; }
|
|
2181
|
+
.ui-page--full { max-width: none; }
|
|
2182
|
+
|
|
2157
2183
|
/* ═══ @gemboss/ui src/auth.css ═══ */
|
|
2158
2184
|
/* The GemBoss sign-in screen — @gemboss/ui (28/09/2026).
|
|
2159
2185
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gemboss/ui",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
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
|
@@ -139,3 +139,29 @@
|
|
|
139
139
|
@media (pointer: coarse), (max-width: 47.9975em) { .gemboss-topbar-search { height: 44px; } }
|
|
140
140
|
/* A phone has no ⌘ key, and the box needs every pixel the bar has left. */
|
|
141
141
|
@media (max-width: 460px) { .gemboss-topbar-search-kbd { display: none; } }
|
|
142
|
+
|
|
143
|
+
/* ── Page width (0.8.1) ──────────────────────────────────────────────────────────────────────────────
|
|
144
|
+
* Chris 04/10/2026: the rail on the left, and on the right a content column with ONE max width across every
|
|
145
|
+
* service, not edge to edge. The admin's tiers (gemboss-shell.css, `.gemboss-w-*` around a Polaris Page), same
|
|
146
|
+
* numbers; `page-width.test.tsx` fails if the two disagree:
|
|
147
|
+
* .ui-page → --ui-content-max (900px, ui.css): lists, tables, card grids, dashboards
|
|
148
|
+
* .ui-page--feed → 720px: read top to bottom (activity, logs, timelines)
|
|
149
|
+
* .ui-page--form → 662px: one column of fields (Polaris narrowWidth)
|
|
150
|
+
* .ui-page--canvas → 1440px: content measured to be wider than a reading column (admin: the funnel path)
|
|
151
|
+
* .ui-page--full → no cap: a board or an editor that is the screen. Say why where you use it.
|
|
152
|
+
* A cap only exists where it is SMALLER than the room it sits in. Right of the 240px rail with main's 24px padding,
|
|
153
|
+
* 900 bites from 1188px up (1280 → 992 of room); 1440 bites only from ~1730px. Below that the column is simply the
|
|
154
|
+
* room, so the tier never causes a horizontal scroll.
|
|
155
|
+
* Here, not in ui.css: ui.css is exported verbatim into the admin under a 90 KB budget (89.8 KB at 0.7.3), and the
|
|
156
|
+
* admin draws its tiers on Polaris' Page instead. Fixed px, not %: a % cap grows the line length with the monitor. */
|
|
157
|
+
.ui-page {
|
|
158
|
+
box-sizing: border-box;
|
|
159
|
+
width: 100%;
|
|
160
|
+
min-width: 0;
|
|
161
|
+
max-width: var(--ui-content-max, 900px);
|
|
162
|
+
margin-inline: auto;
|
|
163
|
+
}
|
|
164
|
+
.ui-page--feed { max-width: 720px; }
|
|
165
|
+
.ui-page--form { max-width: 662px; }
|
|
166
|
+
.ui-page--canvas { max-width: 1440px; }
|
|
167
|
+
.ui-page--full { max-width: none; }
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,9 @@ export {
|
|
|
2
2
|
AppShell, MobileMenuButton, MobileNavBackdrop, Rail, RailBottom, RailCount, RailGroup, RailHeading,
|
|
3
3
|
RailItem, RailScroll, RailSection, RailSep, RailSub, RailSubItem, Shell, ShellMain, Topbar,
|
|
4
4
|
} from "./shell";
|
|
5
|
+
// Page width (0.8.1): its own plain module, so nothing the admin imports from shell.tsx carries it (first-load budget).
|
|
6
|
+
export { PageContainer, pageWidthClass } from "./page";
|
|
7
|
+
export type { PageWidth } from "./page";
|
|
5
8
|
export { GemMark } from "./GemMark";
|
|
6
9
|
export { AuthError, AuthField, AuthFoot, AuthForm, AuthHelp, AuthLayout, PasswordField, AuthSubmit } from "./auth";
|
|
7
10
|
export { BrandLockup } from "./brand";
|
package/src/page.tsx
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How wide a page's content may grow right of the rail (0.8.1). The admin's tiers, same numbers:
|
|
3
|
+
* default → `--ui-content-max` (900px): lists, tables, card grids, dashboards. Admin `.gemboss-w-gallery` / `-dashboard`.
|
|
4
|
+
* feed → 720px: something read top to bottom (activity, a log, a timeline). Admin `.gemboss-w-feed`.
|
|
5
|
+
* form → 662px: one column of fields (settings). Admin `.gemboss-w-form`, Polaris' narrowWidth.
|
|
6
|
+
* canvas → 1440px: content that is WIDER than a column of text and was measured to need it (a horizontal path,
|
|
7
|
+
* a wide comparison table). Admin `.gemboss-w-canvas`.
|
|
8
|
+
* full → no cap: a board or an editor that is the whole screen. Say why at the call site.
|
|
9
|
+
* The CSS is `.ui-page` + `.ui-page--<tier>` in app-shell.css; HTML surfaces write the class themselves.
|
|
10
|
+
*
|
|
11
|
+
* Its OWN module, not shell.tsx, and not "use client": the admin imports Shell / ShellMain / Rail* from shell.tsx, a
|
|
12
|
+
* client module the bundler keeps whole, and its first-load shell budget had 0.0 KB of room (240.0 / 240, 04/10/2026).
|
|
13
|
+
* In shell.tsx this code reached the admin's first load; here nothing the admin imports pulls it in. No state, so a
|
|
14
|
+
* server component can render it too.
|
|
15
|
+
*/
|
|
16
|
+
import React from "react";
|
|
17
|
+
|
|
18
|
+
export type PageWidth = "default" | "feed" | "form" | "canvas" | "full";
|
|
19
|
+
|
|
20
|
+
/** `ui-page` plus the tier's modifier: for a surface that renders the wrapper itself (a server template). */
|
|
21
|
+
export function pageWidthClass(width: PageWidth = "default"): string {
|
|
22
|
+
return width === "default" ? "ui-page" : `ui-page ui-page--${width}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `div.ui-page`: the content column, capped at `width`'s tier and centred in the space right of the rail. Put ONE
|
|
27
|
+
* directly inside `AppShell`, with the CURRENT route's tier, so every screen is capped in one place:
|
|
28
|
+
* `<AppShell …><PageContainer width={route === "settings" ? "form" : "default"}>{page}</PageContainer></AppShell>`.
|
|
29
|
+
* A page cannot widen a cap its parent set, so do not nest a second one to get `canvas` or `full`.
|
|
30
|
+
*/
|
|
31
|
+
export function PageContainer({ as: Comp = "div", width = "default", className, children, ...rest }:
|
|
32
|
+
{ as?: React.ElementType; width?: PageWidth; className?: string; children?: React.ReactNode }
|
|
33
|
+
& Omit<React.HTMLAttributes<HTMLElement>, "className" | "children">) {
|
|
34
|
+
return <Comp className={className ? `${pageWidthClass(width)} ${className}` : pageWidthClass(width)} {...rest}>{children}</Comp>;
|
|
35
|
+
}
|