# Layout Primitives

> Start here for layout: use Block as the general-purpose primitive, then compose Container and Text with exact published prop values.

Source: [https://askrjs.com/docs/components/application-layout](https://askrjs.com/docs/components/application-layout)

Status: stable. Packages: @askrjs/themes/components.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

```tsx
import { Block, Container, Text } from '@askrjs/themes/components';

<Container paddingY="lg">
  <Block direction="column" gap="md">
    <Text as="strong" size="lg">Projects</Text>
    <Text tone="muted">Active work in this workspace.</Text>
    <ProjectList />
  </Block>
</Container>
```

## Published props

Generated from the TypeScript declarations shipped by the installed package.

### `BlockOwnProps`

Import from `@askrjs/themes/components`.

- `align?: ResponsiveValue<BlockAlign> | undefined;`
- `as?: BlockElement | undefined;`
- `background?: ResponsiveValue<BlockBackground> | undefined;`
- `border?: ResponsiveValue<boolean> | undefined;`
- `borderBottom?: ResponsiveValue<boolean> | undefined;`
- `borderRight?: ResponsiveValue<boolean> | undefined;`
- `borderTop?: ResponsiveValue<boolean> | undefined;`
- `center?: ResponsiveValue<boolean> | undefined;`
- `children?: unknown;`
- `className?: string | undefined;`
- `direction?: ResponsiveValue<BlockDirection> | undefined;`
- `gap?: ResponsiveValue<BlockSpace> | undefined;`
- `grow?: ResponsiveValue<number | boolean> | undefined;`
- `height?: ResponsiveValue<BlockSize> | undefined;`
- `hide?: ResponsiveValue<boolean> | undefined;`
- `justify?: ResponsiveValue<BlockJustify> | undefined;`
- `margin?: ResponsiveValue<BlockMargin> | undefined;`
- `marginX?: ResponsiveValue<BlockMargin> | undefined;`
- `marginY?: ResponsiveValue<BlockMargin> | undefined;`
- `maxHeight?: ResponsiveValue<BlockSize> | undefined;`
- `maxWidth?: ResponsiveValue<BlockSize> | undefined;`
- `minHeight?: ResponsiveValue<BlockSize> | undefined;`
- `minWidth?: ResponsiveValue<BlockSize> | undefined;`
- `padding?: ResponsiveValue<BlockSpace> | undefined;`
- `paddingX?: ResponsiveValue<BlockSpace> | undefined;`
- `paddingY?: ResponsiveValue<BlockSpace> | undefined;`
- `radius?: ResponsiveValue<BlockRadius> | undefined;`
- `rowFrom?: BlockRowFrom | undefined;`
- `shadow?: ResponsiveValue<BlockShadow> | undefined;`
- `shrink?: ResponsiveValue<number | boolean> | undefined;`
- `sticky?: boolean | undefined;`
- `top?: ResponsiveValue<BlockSpace> | undefined;`
- `width?: ResponsiveValue<BlockSize> | undefined;`
- `wrap?: ResponsiveValue<boolean> | undefined;`
- `zIndex?: ResponsiveValue<BlockZIndex> | undefined;`

### `ContainerProps`

Import from `@askrjs/themes/components`.

- `align?: ResponsiveValue<BlockAlign> | undefined;`
- `as?: "div" | undefined;`
- `background?: ResponsiveValue<BlockBackground> | undefined;`
- `border?: ResponsiveValue<boolean> | undefined;`
- `borderBottom?: ResponsiveValue<boolean> | undefined;`
- `borderRight?: ResponsiveValue<boolean> | undefined;`
- `borderTop?: ResponsiveValue<boolean> | undefined;`
- `center?: ResponsiveValue<boolean> | undefined;`
- `children?: unknown;`
- `className?: string | undefined;`
- `direction?: ResponsiveValue<BlockDirection> | undefined;`
- `gap?: ResponsiveValue<BlockSpace> | undefined;`
- `grow?: ResponsiveValue<number | boolean> | undefined;`
- `height?: ResponsiveValue<BlockSize> | undefined;`
- `hide?: ResponsiveValue<boolean> | undefined;`
- `justify?: ResponsiveValue<BlockJustify> | undefined;`
- `margin?: ResponsiveValue<BlockMargin> | undefined;`
- `marginX?: ResponsiveValue<BlockMargin> | undefined;`
- `marginY?: ResponsiveValue<BlockMargin> | undefined;`
- `maxHeight?: ResponsiveValue<BlockSize> | undefined;`
- `minHeight?: ResponsiveValue<BlockSize> | undefined;`
- `minWidth?: ResponsiveValue<BlockSize> | undefined;`
- `padding?: ResponsiveValue<BlockSpace> | undefined;`
- `paddingX?: ResponsiveValue<BlockSpace> | undefined;`
- `paddingY?: ResponsiveValue<BlockSpace> | undefined;`
- `radius?: ResponsiveValue<BlockRadius> | undefined;`
- `rowFrom?: BlockRowFrom | undefined;`
- `shadow?: ResponsiveValue<BlockShadow> | undefined;`
- `shrink?: ResponsiveValue<number | boolean> | undefined;`
- `size?: BlockSize | undefined;`
- `sticky?: boolean | undefined;`
- `top?: ResponsiveValue<BlockSpace> | undefined;`
- `width?: ResponsiveValue<BlockSize> | undefined;`
- `wrap?: ResponsiveValue<boolean> | undefined;`
- `zIndex?: ResponsiveValue<BlockZIndex> | undefined;`

### `TextProps`

Import from `@askrjs/themes/components`.

- `as?: TextElement | undefined;`
- `children?: unknown;`
- `font?: TextFont | undefined;`
- `numeric?: TextNumeric | undefined;`
- `ref?: Ref<HTMLElement>;`
- `size?: TextSize | undefined;`
- `tone?: TextTone | undefined;`
- `truncate?: boolean | undefined;`
- `weight?: TextWeight | undefined;`
- `wrap?: TextWrap | undefined;`

## Purpose

There's no single `ApplicationLayout` component in Askr — this page is guidance on composing the layout primitives `@askrjs/themes` does ship (`Block`, `Container`, `Header`, `Main`, `Grid`, `Shell`, `ShellNav`, `ShellMain`) into the shell most apps need: a header, a main content area, and consistent page-level spacing. Treat it as a pattern to follow, not an API to import wholesale.

## Install and import

Pull the pieces you need from the `@askrjs/themes/components` barrel (there is no separate `@askrjs/themes/core` subpath — that name isn't a real entrypoint in the package): `Block` for general-purpose flex layout, `Container` for width-constrained content, `Header` and `Main` for semantic page regions, and `Shell`/`ShellNav`/`ShellMain` when you want a pre-composed app frame instead of assembling `Block`s by hand.

## Live examples

A common shell is a `Shell` containing a `ShellNav` for top-level navigation and a `ShellMain` for routed content, each internally built from `Block` with `direction` and `gap` props rather than custom flexbox CSS. For simpler pages, `Header` plus `Main` plus a `Container` wrapping the main content to cap line length is often enough without reaching for `Shell` at all.

## Anatomy

`Block` is the layout engine underneath nearly everything here — it takes `direction`, `align`, `justify`, `gap`, `padding`, and sizing props and renders as whatever native element you specify via `as`. `Header` and `Main` are semantic presets built on top of `Block` for their respective page landmarks; `Shell` composes `ShellNav` and `ShellMain` into the standard nav-plus-content frame.

## State model

Nothing at this level tracks an open/closed or collapsed/expanded flag — these components exist to arrange, not to remember. What looks like state is really configuration: responsiveness flows through `Block`'s `ResponsiveValue<T>` props, which take either a single value or a per-breakpoint object keyed by `base`, `sm`, `md`, `lg`, and `xl`, rather than anything you'd track at runtime.

## Keyboard and accessibility

Because `Header` and `Main` map to real landmark elements, screen reader users get correct page-region navigation for free as long as you don't override `as` to something non-semantic. Skip-link patterns and focus order are your responsibility to wire up at the app level — the layout primitives don't provide a built-in skip-to-content mechanism.

## Styling and tokens

Every spacing, sizing, and breakpoint prop on `Block` resolves through the shared token system rather than raw pixel values, so a layout built from `Block` props stays consistent with the rest of the theme automatically. Follow the mobile-first responsive convention documented in the theme's contract: base styles target narrow screens, and `sm`/`md`/`lg`/`xl` values layer on as `min-width` overrides.

## API

`Block` accepts `padding`, `paddingX`, `paddingY`, `margin`, `width`, `height`, `direction`, `align`, `justify`, `gap`, `grow`, `shrink`, `sticky`, `background`, `border`, `radius`, and `shadow`, most typed as `ResponsiveValue<T>` so they can vary per breakpoint. `rowFrom` (typed `BlockRowFrom`, one of `sm | md | lg | xl`) is specifically for switching a stacked mobile layout into a row at a given breakpoint — the standard tool for "stack on mobile, row on desktop" layouts.

## Edge cases

Nesting many `Block`s with conflicting `width`/`height` values can produce layouts that don't collapse the way you expect on narrow screens — test at the documented widths (320, 390, 768, 1024, and desktop) rather than assuming a layout that looks right at one size holds at another. `sticky` combined with `zIndex` needs a value from the fixed `BlockZIndex` set (`header`, `sticky`, `fixed`, `dropdown`, `modal`, `popover`, `toast`, `tooltip`) rather than an arbitrary number, so pick the token that matches what the element actually is.

## Related pages

Advanced Layout goes further into sidebar-driven and multi-region shells. Sidebar covers the specific collapsible side-navigation pattern that often pairs with this page's header-plus-main structure. Container and Grid are worth a look for content-width and multi-column needs that a single `Block` doesn't cover cleanly.

## Documentation navigation

[Previous](https://askrjs.com/docs/components/typography-and-display-primitives/index.md) | [Next](https://askrjs.com/docs/components/advanced-layout/index.md)
