Skip to content

Commit

The page sits flat against the sidebar. On a computer it fills the rest of the window with no inset, no rounded corners and no frame around it, and its top bar always draws the line under itself, so a page's own header, a conversation's or a project's, sits beneath it as its own band instead of merging with it; the scroll watcher that used to decide when the line appeared is gone. A phone keeps its rounded panel above the bottom bar. The workspaces guide says so.

syntaqxcommitted Parenteb8a4c4Browse files
3 files+25−370/3 viewed
+1−1
539539 | --- | --- | --- |
540540 | **The rail** | A narrow column of icons down the left edge | g1t's mark, which goes to [Home](/guides/home/); the built-in apps; the apps you pinned; **Apps**; and at its foot **People**, **Workspace** and your account. Hold the pointer over an icon for its name. |
541541 | **The sidebar** | Flush against the rail | The workspace and its switcher at the top, **Search or jump to**, then the lists of the app you are in, in named groups. Home and Apps have none. |
542−| **The page** | A rounded panel, the rest of the window | Its header: the button that shows or hides the sidebar, where you are, **Search or jump to** (<kbd>Ctrl</kbd> <kbd>K</kbd>, or <kbd>⌘</kbd> <kbd>K</kbd> on a Mac) when the sidebar is hidden or has a search of its own, [your spend this month](/guides/spend/#in-the-top-bar), **Ask g1t**, the [notifications](/guides/notifications/) bell and **Create new**. Then the page itself. |
542+| **The page** | The rest of the window, flat against the sidebar | Its header: the button that shows or hides the sidebar, where you are, **Search or jump to** (<kbd>Ctrl</kbd> <kbd>K</kbd>, or <kbd>⌘</kbd> <kbd>K</kbd> on a Mac) when the sidebar is hidden or has a search of its own, [your spend this month](/guides/spend/#in-the-top-bar), **Ask g1t**, the [notifications](/guides/notifications/) bell and **Create new**. Then the page itself. |
543543
544544 ### The rail
545545
+9−11
2121 --color-bg: light-dark(var(--g1t-light-bg), var(--g1t-bg));
2222 /*
2323 * The app's frame (components/shell.tsx, components/ui/sidebar.tsx):
24− * the page is a rounded panel on the shell; the sidebar sits flat on the
25− * same surface beside it, and the rail, the narrow column of icons at
24+ * the page sits flat against the sidebar, a hairline between; the sidebar
25+ * is the shell's surface, and the rail, the narrow column of icons at
2626 * the window's edge, is a step apart: white in the light theme, near
2727 * black in the dark one. The current row or icon is a muted fill.
2828 */
353353
354354 /*
355355 * The app's frame (components/shell.tsx), set on <body data-frame="app">:
356− * the page is a rounded panel inset from the window, with the rail and the
357− * sidebar beside it and, on a phone, the bottom bar under it. What sticks to the top or
358− * the bottom of a page reads these:
359− * --frame-top the gap above the panel
360− * --topbar-h the panel's top edge plus its header: where sticky things sit
361− * --tabbar-h what the bottom takes: the gap under the panel, and the phone's bar
362− * --page-h the height left for a page that fills the panel (Chat)
356+ * the page fills the window beside the rail and the sidebar; on a phone
357+ * it is a rounded panel inset above the bottom bar. What sticks to the top
358+ * or the bottom of a page reads these:
359+ * --frame-top the gap above the page (none on a computer)
360+ * --topbar-h that gap plus the top bar: where sticky things sit
361+ * --tabbar-h what the bottom takes: on a phone, the gap under the panel and the bar
362+ * --page-h the height left for a page that fills the window (Chat)
363363 * The public frame keeps a plain header 3.5rem tall and nothing at the bottom.
364364 */
365365 :root {
369369 --page-h: calc(100dvh - var(--topbar-h) - var(--tabbar-h));
370370 }
371371 body[data-frame="app"] {
372− --frame-top: 0.5rem;
373372 --topbar-h: calc(var(--frame-top) + 3.5rem);
374− --tabbar-h: 0.5rem;
375373 --page-h: calc(100dvh - var(--topbar-h) - var(--tabbar-h));
376374 }
377375 @media (max-width: 767px) {
+15−25
19401940 * The app: three surfaces that never blur together. The rail, a narrow
19411941 * column of icons down the window's left edge; the mode's sidebar, flat
19421942 * against it with a hairline between, which folds away with Ctrl B and is
1943− * a drawer below 1024px; and the page, a rounded panel inset from the
1944− * window, with its header across the top: the sidebar's toggle, where you
1945− * are, search, Ask g1t and Create new. On a phone the rail is a bar along
1946− * the bottom. Only for someone signed in; without a workspace yet, there
1943+ * a drawer below 1024px; and the page, flat against the sidebar and
1944+ * filling the rest of the window, with its top bar across the top and a
1945+ * line under it: the sidebar's toggle, where you are, search, Ask g1t and
1946+ * Create new. On a phone the rail is a bar along the bottom and the page
1947+ * a rounded panel above it. Only for someone signed in; without a workspace yet, there
19471948 * is no rail. The rail and the sidebar are drawn from the sidebar kit
19481949 * (components/ui/sidebar.tsx): one provider holds whether the sidebar is
19491950 * open on a computer and whether the drawer is.
19761977 usePaletteShortcut(() => setPalette((open) => !open));
19771978 const leaving = useLeaving();
19781979 useVisualViewport();
1979− // Whether the page has scrolled under the top bar, which then draws its line (see data-page-head).
1980− const [scrolled, setScrolled] = useState(false);
1981− useEffect(() => {
1982− const onScroll = () => setScrolled(window.scrollY > 2);
1983− onScroll();
1984− window.addEventListener("scroll", onScroll, { passive: true });
1985− return () => window.removeEventListener("scroll", onScroll);
1986− }, []);
1987−
19881980 const going = useNavigation().location?.pathname;
19891981 const ws = shell.workspace;
19901982 const mode = modeOf(going ?? pathname, ws?.slug ?? null);
20702062 // On a phone (below 768px): the bottom bar, and a conversation full screen.
20712063 const conversation = isConversation(going ?? pathname);
20722064 // Where the page panel starts: 8px past the rail (56px), or 8px past the sidebar (56 + 240px) when it is open.
2073− const left = ws ? (inline ? "md:left-16 lg:left-[19rem]" : "md:left-16") : inline ? "md:left-2 lg:left-[15.5rem]" : "md:left-2";
2074− const pad = ws ? (inline ? "md:pl-16 lg:pl-[19rem]" : "md:pl-16") : inline ? "md:pl-2 lg:pl-[15.5rem]" : "md:pl-2";
2065+ // The page starts where the rail (3.5rem) and, open, the sidebar (15rem) end.
2066+ const pad = ws ? (inline ? "md:pl-14 lg:pl-[18.5rem]" : "md:pl-14") : inline ? "lg:pl-[15rem]" : "";
20752067
20762068 return (
20772069 <SidebarProvider open={!closed} onOpenChange={(open) => setClosed(!open)} openMobile={drawer} onOpenMobileChange={setDrawer} toggleSidebar={toggleSidebar}>
21002092 </SidebarMobile>
21012093 )}
21022094
2103− <div className={`min-h-dvh px-1.5 pt-(--frame-top) pb-(--tabbar-h) md:pr-2 ${pad}`}>
2104− {/* A page whose own header sits right under this bar (data-page-head: a conversation's, a project's) reads as one
2105− block with it: no line between them until the page scrolls under the bar. A sticky one (data-page-head="sticky",
2106− an open artifact's) stays under the bar, so the line is always its. */}
2107− <div className="flex min-h-[calc(100dvh-var(--frame-top)-var(--tabbar-h))] min-w-0 flex-col rounded-[14px] bg-bg [&:has([data-page-head])>header:not([data-scrolled])]:border-b-transparent [&:has([data-page-head=sticky])>header]:border-b-transparent">
2095+ <div className={`min-h-dvh pt-(--frame-top) pb-(--tabbar-h) max-md:px-1.5 ${pad}`}>
2096+ {/* The top bar always has its line, so a page's own header (data-page-head: a conversation's, a project's,
2097+ a sticky one under an open artifact) sits under it as its own band. */}
2098+ <div className="flex min-h-[calc(100dvh-var(--frame-top)-var(--tabbar-h))] min-w-0 flex-col bg-bg max-md:rounded-[14px]">
21082099 <header
2109− data-scrolled={scrolled || undefined}
2110− className={`sticky top-(--frame-top) z-30 flex h-14 items-center gap-1 rounded-t-[14px] border-b border-line bg-bg pr-2 pl-2 transition-[border-color] duration-150 sm:pr-3 ${inline ? "lg:pl-3.5" : ""} ${conversation ? "max-md:hidden" : ""}`}
2100+ className={`sticky top-(--frame-top) z-30 flex h-14 items-center gap-1 border-b border-line bg-bg pr-2 pl-2 max-md:rounded-t-[14px] sm:pr-3 ${inline ? "lg:pl-3.5" : ""} ${conversation ? "max-md:hidden" : ""}`}
21112101 >
21122102 {panel && (
21132103 <SidebarTrigger
21552145 </main>
21562146 </div>
21572147 </div>
2158− {/* The panel's edge: a rounded frame over the page, with the background beyond it, so what scrolls stays inside.
2159− Every page has it, a conversation too; only a phone's conversation, full screen over everything, goes without. */}
2148+ {/* On a phone the page is a rounded panel above the bottom bar: a frame over it, with the background beyond,
2149+ so what scrolls stays inside. A phone's conversation, full screen over everything, goes without. */}
21602150 <div
21612151 aria-hidden="true"
2162− className={`pointer-events-none fixed top-(--frame-top) right-1.5 bottom-(--tabbar-h) left-1.5 z-[35] rounded-[14px] shadow-[0_0_0_1px_var(--color-line),0_0_0_100vmax_var(--color-shell)] md:right-2 ${left} ${conversation ? "max-md:hidden" : ""}`}
2152+ className={`pointer-events-none fixed top-(--frame-top) right-1.5 bottom-(--tabbar-h) left-1.5 z-[35] rounded-[14px] shadow-[0_0_0_1px_var(--color-line),0_0_0_100vmax_var(--color-shell)] md:hidden ${conversation ? "max-md:hidden" : ""}`}
21632153 />
21642154 {ws && <BottomBar user={user} workspace={ws} pins={shell.pins ?? []} unread={unread} onReselect={panel ? () => setDrawer(true) : undefined} />}
21652155 <CommandPalette open={palette} onOpenChange={setPalette} commands={commands} repo={shell.repo ? `${shell.repo.namespace}/${shell.repo.name}` : null} />