Page container
One element that centres content at a named width (prose, narrow, default, wide or full) and keeps a 16, 24 or 32 px gutter from the screen edge. No vertical space, no background.
cmp_page_container_01 Preview
Fit to the available width. The frame follows the height of its content; previews taller than the maximum auto-height scroll inside it.
Give this component to your coding agent Copy a prompt that fetches this exact version and palette through the PageSugar MCP server.
Using the PageSugar MCP server, fetch component cmp_page_container_01 version 1.0.0 with variant "default", first inspect its requirements and license status and confirm this project uses Svelte 5 and Tailwind CSS 4. Retrieve every manifest file, including binary assets and any manifest-only response files, preserving relative paths. Then integrate the source and follow its usage notes. Run project checks, review the browser result and report anything unverified. Do not substitute another version or invent missing files.Not connected yet? Set up the MCP server
Code
- Palette
- Default
- Version
- 1.0.0
- Digest
Full digest
sha256-125f140e2d30dffe1765e42925e64c98bff9e0e1f002ab26b0b26208ff815fb2
<!--
Page container: one element that centres content at a named width and keeps a gutter between
it and the screen edge. It adds no vertical space and paints nothing.
The named width is the maximum width of the content, not of the box. The box is content-box, so
the gutter sits outside the measure: `default` holds up to 72rem of content, the same edge a
PageSugar section's inner column uses, and `prose` up to 65ch.
-->
<script lang="ts" module>
export type ContainerSize = 'prose' | 'narrow' | 'default' | 'wide' | 'full';
export type ContainerElement = 'div' | 'section' | 'header' | 'footer' | 'main' | 'article';
</script>
<script lang="ts">
import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
interface Props extends Omit<HTMLAttributes<HTMLElement>, 'class' | 'children'> {
/** Content width: prose 65ch, narrow 48rem, default 72rem, wide 80rem, full none. */
size?: ContainerSize;
/** The element to render. Name a section with aria-labelledby if it should be a landmark. */
as?: ContainerElement;
/** Extra classes on the root. Added after the container's own, not a guaranteed override. */
class?: string;
/** The content. */
children: Snippet;
}
let { size = 'default', as = 'div', class: className, children, ...rest }: Props = $props();
/* Complete class strings, so Tailwind finds every one of them in this file. */
const widths: Record<ContainerSize, string> = {
prose: 'max-w-prose',
narrow: 'max-w-3xl',
default: 'max-w-6xl',
wide: 'max-w-7xl',
full: 'max-w-none'
};
</script>
<!--
--_inline is the gutter in force at this breakpoint. The width takes both gutters off the
parent, so the box fills it inside a flex or grid parent too, where auto margins would
otherwise shrink it to its content. Keep the parentheses round var(--_inline)*2: without them
Tailwind reads the underscore as a space.
-->
<svelte:element
this={as}
{...rest}
class={[
'page-container mx-auto box-content w-[calc(100%-(var(--_inline)*2))] px-[var(--_inline)]',
'[--_inline:var(--_gutter)] sm:[--_inline:var(--_gutter-sm)] lg:[--_inline:var(--_gutter-lg)]',
widths[size],
className
]}
>
{@render children?.()}
</svelte:element>
<style>
/* One public variable sets the gutter at every width. Unset, it steps 16, 24 and 32 px. */
.page-container {
--_gutter: var(--page-container-gutter, 1rem);
--_gutter-sm: var(--page-container-gutter, 1.5rem);
--_gutter-lg: var(--page-container-gutter, 2rem);
}
/* A container anywhere inside another is already clear of the screen edge; its gutter would
double. Close the outer container before a band that breaks out to the viewport edge. */
.page-container :global(.page-container) {
--_inline: 0px;
}
</style>
Usage#
On this pageWrap content and pick a size; the container centres it and holds the gutter. It adds no vertical padding or margin, paints no background and sets no type, so vertical rhythm, backgrounds and full-bleed bands belong to the section around it. Other HTML attributes (id, aria-labelledby, style) pass through to the element.
- Suggested location
src/lib/components/page-container-01- Required props
children
Limitations
- The maximum width is the content width: once the maximum is reached, a default container measures 72rem plus two gutters. To cap the outer box instead, change box-content to box-border and the width class to
w-fulltogether; changing only one of them insets the content twice. - The gutter follows viewport breakpoints (sm, lg), not the width of the parent. Inside a narrow column, set
--page-container-gutteror nest the container so its gutter drops to zero. - A container anywhere inside another .page-container has no gutter of its own, and
--page-container-gutteron it cannot restore one. A band that breaks out of a container to the viewport edge should close the outer container instead of nesting in it. A section of your own with side padding does not clear the gutter; set--page-container-gutter: 0px on the inner container there. - Give
--page-container-guttera length or percentage with a unit: 0px, not 0. A unitless zero makes the width calculation invalid, and inside a flex or grid parent the container can then shrink to its content. - Borders on the root are not subtracted from its width and overflow the parent by their thickness; put a border on an element inside, or use a ring or outline.
- No safe-area insets. If your page sets
viewport-fit=cover, wrap the gutter in max() with env(safe-area-inset-left) and env(safe-area-inset-right). - The class prop is appended after the container's classes; a conflicting width or padding utility is not guaranteed to win.
Example
<script lang="ts">
import Container from '$lib/components/page-container-01/Container.svelte';
</script>
<Container as="section" size="prose" aria-labelledby="returns-title" class="py-16 sm:py-24">
<h2 id="returns-title" class="text-3xl font-semibold tracking-tight">Returns</h2>
<p class="mt-4 text-zinc-600">Send anything back within 30 days of delivery for a full refund.</p>
</Container>Page container#
A width primitive. Use one per band of content: the section around it owns vertical space and background, the container owns the measure and the gutter.
Sizes#
| Size | Content width | Use |
|---|---|---|
prose |
65ch | Articles, help pages, policies |
narrow |
48rem | Forms, sign-in, checkout |
default |
72rem | Marketing sections |
wide |
80rem | Headers, footers, dense grids |
full |
none | Boards, tables, dashboards |
The width is the content width. The box is box-content, so the gutter (16 px, 24 px from
sm, 32 px from lg) sits outside it. A default container therefore starts on the same x as
the inner column of every PageSugar section.
A full-bleed band#
<div class="bg-zinc-950 py-24 text-zinc-50">
<Container>
<h2 class="text-3xl font-semibold tracking-tight">Plan the next quarter in one afternoon</h2>
</Container>
</div>An article inside a page layout#
<Container as="main" size="wide">
<Container as="article" size="prose">
<!-- 65ch, centred, no second gutter -->
</Container>
</Container>Changing the gutter#
.docs-page {
--page-container-gutter: clamp(1.5rem, 6vw, 5rem);
}Props and content inputs#
On this page| Name | Type | Required | Default | Description |
|---|---|---|---|---|
size | 'prose' | 'narrow' | 'default' | 'wide' | 'full' | No | 'default' | Content width: prose 65ch (max-w-prose), narrow 48rem, default 72rem (the edge PageSugar sections use), wide 80rem, full no maximum. |
as | 'div' | 'section' | 'header' | 'footer' | 'main' | 'article' | No | 'div' | The element to render. A section becomes a region landmark only when it is named, for example with aria-labelledby. |
class | string | No | None | Extra classes on the root, such as vertical padding. Appended after the container's own; not a guaranteed override for width or gutter. |
children | Snippet | Yes | None | The content to constrain. |
Customization#
On this pagePick a size per use, set one CSS variable to change the gutter, and edit the widths lookup in the source to change what each size means.
- Gutter: set
--page-container-gutteron the container or any ancestor to one length with a unit, such as 0px, 1.25rem or clamp(1.5rem, 6vw, 5rem). It replaces all three steps (16, 24 and 32 px). - Widths: the widths object at the top of the script maps each size to a complete Tailwind class. Change
max-w-6xltomax-w-5xlto make default 64rem; keep whole class names so Tailwind finds them. - Vertical space and backgrounds: add them on a section around the container, or pass padding through class (class="
py-16sm:py-24"). The container never adds its own. - Full-bleed bands: put the background on an outer element that spans the page and the container inside it; the content still lines up with every other container of the same size.
- Nesting: a prose container inside a default one keeps its 65ch measure and takes no second gutter, so an article can sit inside a page layout.
- Container queries: add @container through class when children should respond to the container's width rather than the viewport's.
Public CSS variables
| Variable | Token |
|---|---|
--page-container-gutter | gutter |
Accessibility#
On this page- Renders the element named by as and adds no role or ARIA; the element's own semantics apply.
- A section is a region landmark only when it has an accessible name; pass
aria-labelledbypointing at its heading. - Render main once per page. header and footer are banner and contentinfo landmarks only when they are not inside an article, aside, main, nav or section, or an element with the matching role (article, complementary, main, navigation, region).
- The gutter uses padding-inline and centring uses margin-inline, so the layout mirrors under dir="rtl" without changes.
Known limitations
- The container does not break long words or wrap wide children; content wider than the measure (a long URL, a wide table) needs its own overflow-wrap or scrolling.
Release details#
On this page- Integration
- Presentational
- Works without client-side JavaScript
- Server-side rendering supported
- Dependencies
- No additional runtime packages beyond Svelte and Tailwind CSS
- License
MIT. Default license approval is pending; see the license status before adopting the source.
- Version history
- 1.0.0 (Published) Current release · 30 September 2026
Only the current release is available. Keep downloaded source and its receipt if you need to use it again later.