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
| Layer | Owns | Never does |
|---|---|---|
| Layout | Structure, slots, scroll, responsive behaviour, landmarks | Render content |
| Template | Which slots are fixed, and with what; the props for the rest | Set heights or overflow |
| Page | The variable content | Decide structure or scroll |
Scroll rules
- A layout owns the scroll. Each slot declares
data-scroll:fixed(never scrolls, stays in place) orscroll(the one region that scrolls for that part of the screen). - 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. - A page sets no heights, no overflow, no
sticky, no100vh, 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;PageLayoutowns the page padding. - 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.
// 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
- Layouts: AppShellLayout, PageLayout, StackedLayout.
- Templates: Dashboard, List page, Detail page.
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.