Skip to content

Layout

Tessera UI uses a systematic spacing scale with a comfortable default density. This ensures visual rhythm, consistent alignment, and predictable component composition.

All spacing — padding, margins, gaps — uses tokens from this scale. Never use arbitrary pixel values.

TokenValuePixelsCommon Usage
--ts-spacing-000Reset, no spacing
--ts-spacing-10.375rem6pxTight gaps (label-to-field, badge padding)
--ts-spacing-20.625rem10pxDefault inline gap, button padding (sm)
--ts-spacing-31rem16pxComponent internal padding (alert, modal sections)
--ts-spacing-41.25rem20pxStandard padding (md), section gaps
--ts-spacing-51.5rem24pxModal header/footer padding
--ts-spacing-62rem32pxCard padding (lg), modal body
--ts-spacing-82.5rem40pxLarge section spacing, button padding (xl)
--ts-spacing-103rem48pxSection separators
--ts-spacing-124rem64pxPage section spacing
--ts-spacing-165rem80pxLarge layout gaps, modal max-height offset

The spacing scale uses a comfortable progression (6/10/16/20/24/32px) that provides natural visual rhythm:

  • 6px — minimum spacing between closely related elements
  • 10px — default inline spacing (between icon and label, form field gap)
  • 16px — standard padding for medium-density components
  • 24px — comfortable padding for spacious components
  • 32px+ — layout-level spacing between sections
ComponentPaddingToken
Button (md)10px 20pxspacing-2 spacing-4
Input (md)10px 16pxspacing-2 spacing-3
Card (md)20pxspacing-4
Card (lg)32pxspacing-6
Alert16px 20pxspacing-3 spacing-4
Modal body16px 32pxspacing-3 spacing-6
ContextGapToken
Button icon ↔ label10pxspacing-2
Toggle track ↔ label10pxspacing-2
Alert icon ↔ message16pxspacing-3
Modal footer buttons10pxspacing-2
Label ↔ input field6pxspacing-1
Input ↔ help text6pxspacing-1

Tessera UI provides grid tokens for building consistent page layouts:

TokenValueUsage
--ts-grid-columns12Number of columns in the grid
--ts-grid-guttervar(--ts-spacing-6) (32px)Gap between columns
--ts-grid-marginvar(--ts-spacing-4) (20px)Outer margin of the grid container

These tokens are designed for use with CSS Grid:

.page-grid {
display: grid;
grid-template-columns: repeat(var(--ts-grid-columns), 1fr);
gap: var(--ts-grid-gutter);
padding-inline: var(--ts-grid-margin);
}

Grid values adapt automatically in density modes — compact density reduces the gutter to spacing-4 (16px), and spacious density increases it to spacing-8 (40px).

Breakpoint values are available as CSS custom properties for reference and JavaScript access. Because CSS custom properties cannot be used in @media queries, use the raw values in your media queries but reference these tokens as the source of truth.

TokenValueUsage
--ts-breakpoint-sm640pxMobile
--ts-breakpoint-md768pxTablet
--ts-breakpoint-lg1024pxDesktop
--ts-breakpoint-xl1280pxWide desktop
--ts-breakpoint-2xl1536pxUltra-wide

Tessera UI supports three density modes that remap spacing and font-size tokens globally. Apply a density by adding data-density to any ancestor element:

<body data-density="compact">
<!-- All components use tighter spacing -->
</body>
TokenCompactComfortable (default)Spacious
--ts-spacing-14px6px8px
--ts-spacing-28px10px12px
--ts-spacing-312px16px20px
--ts-spacing-416px20px24px
--ts-spacing-520px24px32px
--ts-spacing-624px32px40px
--ts-font-size-xs11px12px12px
--ts-font-size-sm12px14px16px
--ts-font-size-md14px16px18px
--ts-grid-gutterspacing-4spacing-6spacing-8

When to use each density:

  • Compact — Data-dense UIs (tables, dashboards, admin panels) where screen real estate is at a premium
  • Comfortable (default) — General-purpose applications with balanced readability and density
  • Spacious — Content-focused pages (marketing, documentation, onboarding) where breathing room aids comprehension

Density can be applied at any level of the DOM tree, so you can mix densities within a single page (e.g., a compact data table inside a comfortable page layout).

Interactive elements must meet minimum size requirements for accessible touch input:

TokenValueUsage
--ts-touch-target-min44pxDefault minimum (WCAG 2.5.5 AAA)
--ts-touch-target-compact32pxCompact mode minimum (WCAG 2.5.8 AA)

In compact density mode, --ts-touch-target-min is automatically remapped to the compact value. Even in compact mode, ensure touch targets are never smaller than 32px.

Modals provide five size options for different content needs:

SizeMax WidthUsage
xs320pxConfirmations, simple prompts
sm440pxForms, notifications
md560pxStandard dialogs
lg720pxComplex forms, data entry
xl960pxTables, rich content

Tessera UI provides five layout primitives that eliminate the need for manual display: flex / display: grid styling. Default to these components for all structural layout.

Start with <ts-stack> for vertical flow and <ts-row> for horizontal flow. These cover the majority of layout needs with built-in gap support using the spacing scale.

PropTypeDefaultDescription
gap0–164Spacing between children (maps to --ts-spacing-{n})
alignstart | center | end | stretchstretchCross-axis alignment
PropTypeDefaultDescription
gap0–162Spacing between children
alignstart | center | end | stretch | baselinecenterCross-axis alignment
justifystart | center | end | between | around | evenlystartMain-axis distribution
wrapbooleantrueWhether items wrap to the next line
stack-atsm | md | lg | xl | 2xlBreakpoint at which the row stacks vertically
PropTypeDefaultDescription
columns1–12 | auto3Number of columns, or auto for auto-fill
gap0–164Spacing between grid items
min-column-widthCSS length200pxMinimum column width when columns="auto"

<ts-container> — Centered max-width wrapper

Section titled “<ts-container> — Centered max-width wrapper”
PropTypeDefaultDescription
sizesm | md | lg | xl | fulllgMax-width preset (sm=640px, md=768px, lg=1024px, xl=1280px)
paddingbooleantrueWhether to add horizontal padding
PropTypeDefaultDescription
size0–164Vertical space (maps to --ts-spacing-{n})

Use the stack-at prop on <ts-row> to collapse a horizontal row into a vertical stack at a given breakpoint:

<ts-row gap="4" stack-at="md">
<ts-card>Sidebar</ts-card>
<ts-card>Main content</ts-card>
</ts-row>

Below the md breakpoint (768px), the row becomes a stack automatically.

Set columns="auto" with a min-column-width to create a responsive grid that fills columns based on available space — no media queries needed:

<ts-grid columns="auto" min-column-width="280px" gap="4">
<ts-card>Item 1</ts-card>
<ts-card>Item 2</ts-card>
<ts-card>Item 3</ts-card>
<ts-card>Item 4</ts-card>
</ts-grid>

Use <ts-container> to center page content with a maximum width:

<ts-container size="lg">
<p>This content is constrained to 1024px and centered on the page.</p>
</ts-container>

Combine all layout primitives to build complete page structures:

<ts-container size="lg">
<ts-stack gap="6">
<h1>Dashboard</h1>
<ts-grid columns="auto" min-column-width="280px" gap="4">
<ts-card>...</ts-card>
<ts-card>...</ts-card>
<ts-card>...</ts-card>
</ts-grid>
<ts-row gap="2" justify="end">
<ts-button>Cancel</ts-button>
<ts-button variant="primary">Save</ts-button>
</ts-row>
</ts-stack>
</ts-container>

Tessera UI components are Web Components — they adapt to their container rather than the viewport. Components do not ship with built-in responsive breakpoints. Instead:

  • Use block prop on buttons to make them full-width on small screens
  • Use CSS container queries in your layout to adjust component sizing
  • Modal widths use max-width — they naturally shrink on narrow viewports
  • Spacing tokens are rem-based — they scale if the user changes their browser font size

Because Tessera UI components are Web Components with Shadow DOM, container queries are a natural fit for responsive component behavior. Wrap components in a container-type element to adapt layout based on available space rather than viewport width:

.component-wrapper {
container-type: inline-size;
}
@container (max-width: 400px) {
/* Stack buttons vertically in narrow containers */
.button-group {
flex-direction: column;
}
}

Container queries are preferred over media queries for component-level responsiveness because they respond to the component’s actual space, not the page viewport.