The rail on the left is the deliverable
The SISO CRM GroupedRail (product-app GroupedRail.tsx + GroupedRail.css + tokens.css) as plain HTML, CSS and JS. Sizes, fonts, spacing, gradients, shadows and transitions are the CRM's values through the CRM's token names. Press [ to collapse it to icons with tooltips, ⌘K to search; click a group heading and nothing disappears.
Why one shell
his words · the reading-
His words 1
verbatim, from
00_AGENT_ZERO/source/— never paraphrasedlook at the size of crm one it's just so much nicer okay look at the size of crm and you'll understand that's the exact side map that i want like sizing fonts everything spacing the way it all fits together. And I click on certain shit and it disappears now. Templated page on the side now doesn't fully wire up.Shaan · 6 Sep 18:41
-
Reading authored by the seat
His 13:09 to LIBRARY-ZERO: 'you're not using our size of crm side nav, which is Specifically what I told you to do'; 16:50: 'not all of them have side nav so they're connected'; 18:19: 'the side nav is a bit shit'. One shell is what makes 60+ pages across 10 repos and 6 pages.dev sites feel like one place.
Readers. Human: orient, jump between families, collapse it and read. Agent: the rail's link data is the site map; Cmd-K search is the same map as text.
- Where am I in the ecosystem?
- What are the sibling pages?
- Which section of this page do I want?
-
What this page may claim
Claim ceilingMay assert only what the link data says exists; every destination must return 200 or be marked private.Data it rendersA link JSON per site (groups → destinations → status chip); built at publish; the rail reads it, never hand-edited HTML.PrivacyPublic; private destinations show a lock chip, never a payload.Empty stateNo JS: the page still reads; the rail is absent, nav.top remains.Agent entrysiso-shell/shell.mjs rail({title, groups, page, current}) · shell/rail.js · shell/rail.css · shell/tokens.css
Every value, and where the CRM sets it
if a number was chosen here it is wrong; these are read from tokens.css| Thing | CRM token | Value | Where the CRM sets it |
|---|---|---|---|
| Expanded width | --crm-rail-width | 232px | tokens.css · Component sizing |
| Collapsed width | --crm-rail-collapsed-width | 52px | tokens.css |
| Screen inset (top/left/bottom) | --crm-rail-screen-inset | 16px (space-4) | GroupedRail.css .siso-sidebar |
| Rail padding | --crm-rail-padding | 16px | tokens.css |
| Rail gap | --crm-rail-gap | 10px | tokens.css |
| Rail radius / inner | --crm-radius-rail / -inner | 27px / 26px | tokens.css · Radius roles |
| Shell background | --crm-gradient-rail-shell | 2 radial + 1 linear, rail-top/mid/bottom rgb | tokens.css · Effects |
| Shell border | --crm-gradient-rail-border | conic-gradient 0→360° | tokens.css |
| Glass | --crm-blur-glass | blur(32px) saturate(124%) | GroupedRail.css |
| Shell shadow | --crm-shadow-rail-shell | 7 layers | tokens.css |
| Header grid | .siso-sidebar__header | 40px 1fr 36px · min-height 48px · gap 6px | GroupedRail.css |
| Brand tile | .siso-sidebar__brand | 40×40 · radius 12px · rail-brand-tile gradient | GroupedRail.css |
| Product name | --crm-text-sm | 12px · weight 660 · -0.01em · lh 1.08 | GroupedRail.css |
| Toggle | .siso-sidebar__toggle | 36×36 · radius 11px · svg 17px | GroupedRail.css |
| Search row | --crm-control-touch / --crm-radius-control-lg | 44px high · radius 13px · gap 10px · pad 0 12px | GroupedRail.css |
| Search label | --crm-text-sm | 12px · -0.005em | GroupedRail.css |
| Search kbd | --crm-text-2xs | 9px mono · 31×24 · radius 7px | GroupedRail.css |
| Group heading | --crm-text-2xs / --crm-tracking-label | 9px · weight 650 · 0.17em · uppercase · 17px high · white/0.47 | GroupedRail.css .siso-sidebar__group-heading |
| Group gap | --crm-nav-section-gap | 16px | tokens.css |
| Heading inset | --crm-nav-button-gutter + --crm-nav-inline-inset | 4px + 12px | tokens.css |
| Nav row | --crm-nav-row-height | 44px (control-touch) | tokens.css |
| Nav label | --crm-text-md | 13.5px · weight 510 · white/0.7 | GroupedRail.css .siso-sidebar__nav-link |
| Nav icon | --crm-icon-md / --crm-icon-stroke | 17px · stroke 1.7 · white/0.56 | GroupedRail.css |
| Icon–label gap | --crm-nav-icon-label-gap | 12px | tokens.css |
| Row gap | --crm-space-0-5 | 2px | GroupedRail.css .siso-sidebar__nav-items |
| Row radius | --crm-radius-control | 11px | tokens.css |
| Active row | --crm-gradient-rail-active / --crm-shadow-active | brand-hover text · radial+linear brand/0.13 · 4-layer shadow | GroupedRail.css .is-active |
| Hover row | rgb(white/0.035) | text → crm-color-text | GroupedRail.css |
| Compact strip | .siso-sidebar__compact-navigation | 44px wide · radius 13px · capsule gradient · gap 2px | GroupedRail.css |
| Compact link | .siso-sidebar__compact-link | 44×44 · radius 11px · active-icon gradient | GroupedRail.css |
| Collapsed shell | .siso-sidebar.is-collapsed | width 52px · pad 4px · gap 8px · radius 20px · brand/toggle/search 42px | GroupedRail.css |
| Transitions | — | width 200ms ease · colour/background 160ms ease | GroupedRail.css |
| Font | --crm-font-sans | 'Geist', ui-sans-serif, system-ui, -apple-system, … | tokens.css · Typography |
| Workspace offset | app.css .crm-workspace | margin-left = inset×2 + rail width | SISOCRM src/styles/app.css:17-18 |
| Preference key | App.tsx RAIL_STORAGE_KEY | 'siso.crm.rail.collapsed' | SISOCRM src/App.tsx:14 |
Rule: every pixel, font-size, letter-spacing, gap, radius, gradient and transition in shell/rail.css is the CRM's value, resolved through the same token names. Section 1 of that file is GroupedRail.css unchanged; section 2 adds only what a static page needs, from the same tokens.
Behaviour, from GroupedRail.tsx and App.tsx
Grouped labels: a heading per group (<h2>), the destinations under it with icon + label. Headings are headings; a click does nothing and nothing disappears.
GroupedRail.tsx LabeledNavigation
The same destinations as 44px icon links with title tooltips, in the capsule strip. Collapsing never changes what the navigation means.
GroupedRail.tsx CompactNavigation
The 36px header button (PanelLeftClose / PanelLeftOpen) flips is-collapsed on the rail and rail-collapsed on body; the workspace margin follows.
GroupedRail.tsx · App.tsx useRailState
Outside editable fields, [ collapses/expands.
Library adaptation (briefs/LIBRARY-UI-FABLE.md)
Focuses the search row (expanding the rail if needed). Typing filters destinations by label; Esc clears; Enter opens the first match.
App.tsx onSearchShortcut · SidebarSearch
Collapsed state persists in localStorage["siso.crm.rail.collapsed"], the CRM's key. Below 980px it starts collapsed.
App.tsx:14,44-56 · app.css:101
The one folding group: a <details> (Action Model lanebox) listing the page's sections, built by bin/compose from <section class="siso-section" id>; the current section is marked as you scroll.
blueprint/index.html:185-190 · compose.mjs sectionsOf
The rail renders expanded with every group; toggle and search are inert. Navigation never depends on JS.
shell/rail.css .no-js
The two bugs this port does not repeat
v1/v2 atlas ports, supersededdisplay rule beat [hidden], so the search trigger and the search box rendered at once. Fixed here: .siso-sidebar [hidden] { display: none !important } in section 2 of rail.css, and one search row (a real input) instead of two.OWNERS.md 2026-09-06T17:13 · atlas/crm-rail.css:10,83<details>, so clicking a heading collapsed the group. Fixed here: groups are <section> + <h2> like the CRM; only "On this page" folds.his 18:41 'I click on certain shit and it disappears now' · atlas/crm-rail.js:103-116- The CRM brand tile and profile orb show an
<img>; the static shell shows letters in the same tile. - The CRM search button dispatches a global palette; here the row is the search input itself.
Use it on your page
import { rail, head } from "siso-shell/shell.mjs";const html = `<!doctype html><html class="no-js"><head>${head("https://siso-shell.pages.dev/shell")}</head><body class="rail-expanded">${rail({ title: "My site", groups, current: "/x/", page: [{label:"Why", href:"#why"}] })}<div class="siso-shell-page">…</div></body></html>`;// or, without node: copy shell/ next to your HTML and paste the <nav> from any published page
- Read first
shell.mjs- Owner
- TEMPLATE-BANK-ZERO
- Machine
https://siso-shell.pages.dev/nav.json- Done when
- Open your page: the rail at the left has the CRM's sizes; [ collapses to icons with tooltips; ⌘K focuses search; "On this page" lists your sections.
Sources
src/components/GroupedRail.tsx (306 lines), src/styles/GroupedRail.css (677), src/styles/tokens.css (303), src/App.tsx (rail state, ⌘K), src/styles/app.css (workspace offset). Presentation robbed; no client routes, auth or data.shell/ (tokens, rail.css, rail.js, icons), shell.mjs, parts/, templates/, bin/compose.plan/templates.json, the 24 template issues, atlas/crm-rail.{css,js} (v1/v2 ports, superseded), receipts.