Skip to Content
glob.ui home
Layouts

Methodology

The methodology behind GlobUI layouts and templates: a layout owns the scroll, a template fixes part of it, and a page only fills the slots.

Most scroll bugs come from pages that decide their own heights and overflow. Two overflow-auto regions nest, a flex child lacks min-h-0, 100vh is wrong on phones, a sticky header stops sticking, or the sidebar scrolls the whole page. Every page solves this again, slightly differently.

GlobUI moves those decisions into the layout. A page only fills the slots.

Three layers

LayerOwnsNever does
LayoutStructure, slots, scroll, responsive behaviour, landmarksRender content
TemplateWhich slots are fixed, and with what; the props for the restSet heights or overflow
PageThe variable contentDecide structure or scroll
Layout, template and pageA layout holds slots. A template fills some slots with fixed content. A page fills the rest.Layout: AppShellLayout (owns the scroll)header · fixed · brand and account (template)sidebar · fixednavigation (template)main · scrollpage: props and content (variable)

Scroll rules

  1. A layout owns the scroll. Each slot declares data-scroll: fixed (never scrolls, stays in place) or scroll (the one region that scrolls for that part of the screen).
  2. Two modes. In app mode the viewport never scrolls (h-dvh), and only the scroll slots do. Use it for dashboards, chat and editors. In document mode the page scrolls as a document, and the fixed slots stick. Use it for docs, portals and landing pages.
  3. A page sets no heights, no overflow, no sticky, no 100vh, and no wrapper with page padding. It fills the slots. If it needs to scroll, it is inside the scroll slot. The shells own structure and scroll; PageLayout owns the page padding.
  4. One scroll region per part of the screen. Nested scroll regions are the cause of most bugs. A sidebar list may scroll inside its own fixed sidebar. Nothing else nests.

Accessibility comes with the structure

Every layout renders its landmarks (header, nav, main, aside, footer), one main, and a Skip to content link as its first stop. Below lg the navigation moves into a Sheet, opened by LayoutNavTrigger, with focus managed: focus moves in, Escape returns it to the trigger.

Two files: a template and a page

A template renders a layout and fixes what does not change between pages. The page sets only what varies.

The dashboard shows the three layers: the layout is the grid, the template fills its slots, and the page fills the template's props.

Dashboard template in AppShellLayout: what the template fills, and what comes in as props.
// templates/dashboard/dashboard-template.tsx (yours to edit)
export function DashboardTemplate({ title, actions, activeHref, children }: DashboardTemplateProps) {
  return (
    <AppShellLayout header={<AppHeader />} sidebar={<AppNav activeHref={activeHref} />}>
      <PageLayout header={<PageHeader title={title} actions={actions} />}>{children}</PageLayout>
    </AppShellLayout>
  )
}
// app/orders/page.tsx
export default function OrdersPage() {
  return (
    <DashboardTemplate title="Orders" activeHref="/orders" actions={<Button>New order</Button>}>
      <OrdersTable />
    </DashboardTemplate>
  )
}

Read more

Every layout and template page has a live preview. Anatomy shows the slots and their scroll behaviour, Fill with content shows which region scrolls, and the device buttons change the width so media queries are real.

On this page