@the-portland-company/shell 0.92.0 → 0.93.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.93.0
2
+
3
+ Tab icons come from the Brand tab icon map (Reference/UI-Platform → Tabs, "Tab icon map (Brand `tabIcons`)", Ben 2026-10-24).
4
+
5
+ - **`NavTabStrip` `iconStrip` prop** (optional, e.g. `iconStrip="forms"`). Each tab reads `--tab-icon-<iconStrip>-<tab.key>` from `:root` (emitted by `/api/tokens?format=css`) and renders the named Lucide icon at the tab icon size (15px / 13px small, 1.75 stroke, `text-current`). Missing, empty or unknown names render the tab's own `icon`. One `<head>` MutationObserver per strip re-reads when the Brand stylesheet loads; no `window` access during render. Without `iconStrip` nothing changes.
6
+ - **New `resolveTabGlyph(name)` and `KNOWN_TAB_GLYPHS`** (main entry and `./native`): the 124-name tab icon vocabulary, kebab-case Lucide names mapped to `react-icons/lu` components. `resolveTabGlyph` trims, strips quotes and lowercases; it returns `null` for an unknown name. Also exports the `TabGlyphComponent` type.
7
+ - `react-icons/lu` is external in the build (like `react-icons/fi`); `./native` still has no Chakra, emotion or react-router dependency.
8
+ - Tests: `__tests__/tabIconMap.test.tsx`.
9
+
1
10
  ## 0.92.0
2
11
 
3
12
  Politogy ID gets its own registry row (Reference/UI-Platform: each app that renders the chrome has its own `POLITOGY_APPS` row).
package/README.md CHANGED
@@ -142,7 +142,7 @@ All `<ShellChrome>` props are optional — only supply what your app has. The ch
142
142
  | `<Card>` | Page-level layout primitive. |
143
143
  | `<PageHeader>` | The page header (Reference/UI-Platform → "Page header"): no filled surface, 22px below the top bar, 24px gutter (16px below `md`), spans its container. Eyebrow in the mode color, 30px bold Graphite `h1` title, 15px G4 subtitle, `actions` right, `tabBar` beneath. `breadcrumbs` and `maxW` are **deprecated** (accepted, ignored, warn once in dev); width comes from `FormColumn` (960px) or the fluid frame. Warns in dev if `title` / `subtitle` is a string holding an email address. |
144
144
  | `<FormColumn>` | The one constrained layout inside the frame's fluid `<main>`: a left-aligned column, `max-width: 960px`, for form and settings pages. Also exported from `./native`. |
145
- | `<NavTabStrip>` (from `./native`) | The canonical page tab strip: underlined text tabs with a 2px underline in the mode color. Render it in the page body, below the page header. |
145
+ | `<NavTabStrip>` (from `./native`) | The canonical page tab strip: underlined text tabs with a 2px underline in the mode color. Render it in the page body, below the page header. Pass `iconStrip` to take tab icons from the Brand tab icon map (see below). |
146
146
  | `<PageContainer>`, `<BreadcrumbBar>`, `<HeaderTabBar>` | **Deprecated for apps** (each warns in dev). The frame owns a fluid `<main>` (24px gutter, 16px below `md`, no max-width); there are no breadcrumbs; the tab strip is `NavTabStrip`. |
147
147
  | `<GlobalUiIdProvider>` | Assigns stable UI IDs (for testing/analytics). |
148
148
 
@@ -176,6 +176,29 @@ All `<ShellChrome>` props are optional — only supply what your app has. The ch
176
176
 
177
177
  `PageHeader`, `Card`, `FormColumn`, and the deprecated `BreadcrumbBar` and `HeaderTabBar` are also exported from `@the-portland-company/shell/native`, which needs no Chakra or react-router peer.
178
178
 
179
+ ## Tab icons from the Brand map (`iconStrip`)
180
+
181
+ Every tab icon comes from the Brand tab icon map (`tabIcons`, edited in the Brand editor's "Tab Icons" section; Reference/UI-Platform → Tabs). Pass your app's strip key and keep your current icons as the fallback:
182
+
183
+ ```tsx
184
+ import { NavTabStrip } from '@the-portland-company/shell/native'
185
+
186
+ <NavTabStrip
187
+ iconStrip="forms"
188
+ aria-label="Forms"
189
+ tabs={[
190
+ { key: 'forms', label: 'Forms', icon: LuFileText, href: '/forms', selected: true },
191
+ { key: 'canvas', label: 'Canvas', icon: LuLayoutTemplate, href: '/canvas' },
192
+ ]}
193
+ />
194
+ ```
195
+
196
+ - For each tab the strip reads `--tab-icon-<iconStrip>-<tab.key>` (emitted on `:root` by `/api/tokens?format=css`, e.g. `--tab-icon-forms-canvas: "layout-template"`) and renders that Lucide icon at the tab icon size (15px, 13px on `size="sm"`, 1.75 stroke, `currentColor`).
197
+ - One `<head>` observer per strip re-reads the variables when the Brand stylesheet loads, so icons upgrade from the fallback without a reload. Nothing reads `window` during render (SSR-safe).
198
+ - When the variable is missing, empty, or names an icon the shell does not know, the tab renders its own `icon` prop. Without `iconStrip` the strip behaves as before.
199
+ - The tab slug is the tab's `key`. The strip key is lowercase kebab-case, one per strip (`contacts`, `contacts-tribes`, `forms`, `surveys`, `settings`, …).
200
+ - The vocabulary is `KNOWN_TAB_GLYPHS` (124 Lucide kebab-case names); `resolveTabGlyph(name)` maps a name (trimmed, quotes stripped, lowercased) to its `react-icons/lu` component or `null`. Both are exported from the main entry and `./native`. Adding a name is a shell minor release; the Brand editor offers only these names.
201
+
179
202
  ## shell-version-check
180
203
 
181
204
  `shell-version-check` (a package `bin`, so `npx shell-version-check` works after install) exits 1 when the app's declared shell range cannot install the latest published version (`npm view`, 10s timeout; exit 2 if that lookup fails). `--offline` compares against the installed shell version instead; `--cwd=<dir>` checks another package.