Layout Primitives
Start here for layout: use Block as the general-purpose primitive, then compose Container and Text with exact published prop values.
Example
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. Named types in the Type column define the accepted values.
BlockOwnProps
Import from @askrjs/themes/components.
| Prop | Type | Default | Description |
|---|---|---|---|
align | align?: ResponsiveValue<BlockAlign> | undefined; | — | — |
as | as?: BlockElement | undefined; | — | — |
background | background?: ResponsiveValue<BlockBackground> | undefined; | — | — |
border | border?: ResponsiveValue<boolean> | undefined; | — | — |
borderBottom | borderBottom?: ResponsiveValue<boolean> | undefined; | — | — |
borderRight | borderRight?: ResponsiveValue<boolean> | undefined; | — | — |
borderTop | borderTop?: ResponsiveValue<boolean> | undefined; | — | — |
center | center?: ResponsiveValue<boolean> | undefined; | — | — |
children | children?: unknown; | — | — |
className | className?: string | undefined; | — | — |
direction | direction?: ResponsiveValue<BlockDirection> | undefined; | — | — |
gap | gap?: ResponsiveValue<BlockSpace> | undefined; | — | — |
grow | grow?: ResponsiveValue<number | boolean> | undefined; | — | — |
height | height?: ResponsiveValue<BlockSize> | undefined; | — | — |
hide | hide?: ResponsiveValue<boolean> | undefined; | — | — |
justify | justify?: ResponsiveValue<BlockJustify> | undefined; | — | — |
margin | margin?: ResponsiveValue<BlockMargin> | undefined; | — | — |
marginX | marginX?: ResponsiveValue<BlockMargin> | undefined; | — | — |
marginY | marginY?: ResponsiveValue<BlockMargin> | undefined; | — | — |
maxHeight | maxHeight?: ResponsiveValue<BlockSize> | undefined; | — | — |
maxWidth | maxWidth?: ResponsiveValue<BlockSize> | undefined; | — | — |
minHeight | minHeight?: ResponsiveValue<BlockSize> | undefined; | — | — |
minWidth | minWidth?: ResponsiveValue<BlockSize> | undefined; | — | — |
padding | padding?: ResponsiveValue<BlockSpace> | undefined; | — | — |
paddingX | paddingX?: ResponsiveValue<BlockSpace> | undefined; | — | — |
paddingY | paddingY?: ResponsiveValue<BlockSpace> | undefined; | — | — |
radius | radius?: ResponsiveValue<BlockRadius> | undefined; | — | — |
rowFrom | rowFrom?: BlockRowFrom | undefined; | — | — |
shadow | shadow?: ResponsiveValue<BlockShadow> | undefined; | — | — |
shrink | shrink?: ResponsiveValue<number | boolean> | undefined; | — | — |
sticky | sticky?: boolean | undefined; | — | — |
top | top?: ResponsiveValue<BlockSpace> | undefined; | — | — |
width | width?: ResponsiveValue<BlockSize> | undefined; | — | — |
wrap | wrap?: ResponsiveValue<boolean> | undefined; | — | — |
zIndex | zIndex?: ResponsiveValue<BlockZIndex> | undefined; | — | — |
ContainerProps
Import from @askrjs/themes/components.
| Prop | Type | Default | Description |
|---|---|---|---|
align | align?: ResponsiveValue<BlockAlign> | undefined; | — | — |
as | as?: "div" | undefined; | — | — |
background | background?: ResponsiveValue<BlockBackground> | undefined; | — | — |
border | border?: ResponsiveValue<boolean> | undefined; | — | — |
borderBottom | borderBottom?: ResponsiveValue<boolean> | undefined; | — | — |
borderRight | borderRight?: ResponsiveValue<boolean> | undefined; | — | — |
borderTop | borderTop?: ResponsiveValue<boolean> | undefined; | — | — |
center | center?: ResponsiveValue<boolean> | undefined; | — | — |
children | children?: unknown; | — | — |
className | className?: string | undefined; | — | — |
direction | direction?: ResponsiveValue<BlockDirection> | undefined; | — | — |
gap | gap?: ResponsiveValue<BlockSpace> | undefined; | — | — |
grow | grow?: ResponsiveValue<number | boolean> | undefined; | — | — |
height | height?: ResponsiveValue<BlockSize> | undefined; | — | — |
hide | hide?: ResponsiveValue<boolean> | undefined; | — | — |
justify | justify?: ResponsiveValue<BlockJustify> | undefined; | — | — |
margin | margin?: ResponsiveValue<BlockMargin> | undefined; | — | — |
marginX | marginX?: ResponsiveValue<BlockMargin> | undefined; | — | — |
marginY | marginY?: ResponsiveValue<BlockMargin> | undefined; | — | — |
maxHeight | maxHeight?: ResponsiveValue<BlockSize> | undefined; | — | — |
minHeight | minHeight?: ResponsiveValue<BlockSize> | undefined; | — | — |
minWidth | minWidth?: ResponsiveValue<BlockSize> | undefined; | — | — |
padding | padding?: ResponsiveValue<BlockSpace> | undefined; | — | — |
paddingX | paddingX?: ResponsiveValue<BlockSpace> | undefined; | — | — |
paddingY | paddingY?: ResponsiveValue<BlockSpace> | undefined; | — | — |
radius | radius?: ResponsiveValue<BlockRadius> | undefined; | — | — |
rowFrom | rowFrom?: BlockRowFrom | undefined; | — | — |
shadow | shadow?: ResponsiveValue<BlockShadow> | undefined; | — | — |
shrink | shrink?: ResponsiveValue<number | boolean> | undefined; | — | — |
size | size?: BlockSize | undefined; | — | — |
sticky | sticky?: boolean | undefined; | — | — |
top | top?: ResponsiveValue<BlockSpace> | undefined; | — | — |
width | width?: ResponsiveValue<BlockSize> | undefined; | — | — |
wrap | wrap?: ResponsiveValue<boolean> | undefined; | — | — |
zIndex | zIndex?: ResponsiveValue<BlockZIndex> | undefined; | — | — |
TextProps
Import from @askrjs/themes/components.
| Prop | Type | Default | Description |
|---|---|---|---|
as | as?: TextElement | undefined; | — | — |
children | children?: unknown; | — | — |
font | font?: TextFont | undefined; | — | — |
numeric | numeric?: TextNumeric | undefined; | — | — |
ref | ref?: Ref<HTMLElement>; | — | — |
size | size?: TextSize | undefined; | — | — |
tone | tone?: TextTone | undefined; | — | — |
truncate | truncate?: boolean | undefined; | — | — |
weight | weight?: TextWeight | undefined; | — | — |
wrap | 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.