Askr documentation
UI & Components

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.

PropTypeDefaultDescription
alignalign?: ResponsiveValue<BlockAlign> | undefined;
asas?: BlockElement | undefined;
backgroundbackground?: ResponsiveValue<BlockBackground> | undefined;
borderborder?: ResponsiveValue<boolean> | undefined;
borderBottomborderBottom?: ResponsiveValue<boolean> | undefined;
borderRightborderRight?: ResponsiveValue<boolean> | undefined;
borderTopborderTop?: ResponsiveValue<boolean> | undefined;
centercenter?: ResponsiveValue<boolean> | undefined;
childrenchildren?: unknown;
classNameclassName?: string | undefined;
directiondirection?: ResponsiveValue<BlockDirection> | undefined;
gapgap?: ResponsiveValue<BlockSpace> | undefined;
growgrow?: ResponsiveValue<number | boolean> | undefined;
heightheight?: ResponsiveValue<BlockSize> | undefined;
hidehide?: ResponsiveValue<boolean> | undefined;
justifyjustify?: ResponsiveValue<BlockJustify> | undefined;
marginmargin?: ResponsiveValue<BlockMargin> | undefined;
marginXmarginX?: ResponsiveValue<BlockMargin> | undefined;
marginYmarginY?: ResponsiveValue<BlockMargin> | undefined;
maxHeightmaxHeight?: ResponsiveValue<BlockSize> | undefined;
maxWidthmaxWidth?: ResponsiveValue<BlockSize> | undefined;
minHeightminHeight?: ResponsiveValue<BlockSize> | undefined;
minWidthminWidth?: ResponsiveValue<BlockSize> | undefined;
paddingpadding?: ResponsiveValue<BlockSpace> | undefined;
paddingXpaddingX?: ResponsiveValue<BlockSpace> | undefined;
paddingYpaddingY?: ResponsiveValue<BlockSpace> | undefined;
radiusradius?: ResponsiveValue<BlockRadius> | undefined;
rowFromrowFrom?: BlockRowFrom | undefined;
shadowshadow?: ResponsiveValue<BlockShadow> | undefined;
shrinkshrink?: ResponsiveValue<number | boolean> | undefined;
stickysticky?: boolean | undefined;
toptop?: ResponsiveValue<BlockSpace> | undefined;
widthwidth?: ResponsiveValue<BlockSize> | undefined;
wrapwrap?: ResponsiveValue<boolean> | undefined;
zIndexzIndex?: ResponsiveValue<BlockZIndex> | undefined;

ContainerProps

Import from @askrjs/themes/components.

PropTypeDefaultDescription
alignalign?: ResponsiveValue<BlockAlign> | undefined;
asas?: "div" | undefined;
backgroundbackground?: ResponsiveValue<BlockBackground> | undefined;
borderborder?: ResponsiveValue<boolean> | undefined;
borderBottomborderBottom?: ResponsiveValue<boolean> | undefined;
borderRightborderRight?: ResponsiveValue<boolean> | undefined;
borderTopborderTop?: ResponsiveValue<boolean> | undefined;
centercenter?: ResponsiveValue<boolean> | undefined;
childrenchildren?: unknown;
classNameclassName?: string | undefined;
directiondirection?: ResponsiveValue<BlockDirection> | undefined;
gapgap?: ResponsiveValue<BlockSpace> | undefined;
growgrow?: ResponsiveValue<number | boolean> | undefined;
heightheight?: ResponsiveValue<BlockSize> | undefined;
hidehide?: ResponsiveValue<boolean> | undefined;
justifyjustify?: ResponsiveValue<BlockJustify> | undefined;
marginmargin?: ResponsiveValue<BlockMargin> | undefined;
marginXmarginX?: ResponsiveValue<BlockMargin> | undefined;
marginYmarginY?: ResponsiveValue<BlockMargin> | undefined;
maxHeightmaxHeight?: ResponsiveValue<BlockSize> | undefined;
minHeightminHeight?: ResponsiveValue<BlockSize> | undefined;
minWidthminWidth?: ResponsiveValue<BlockSize> | undefined;
paddingpadding?: ResponsiveValue<BlockSpace> | undefined;
paddingXpaddingX?: ResponsiveValue<BlockSpace> | undefined;
paddingYpaddingY?: ResponsiveValue<BlockSpace> | undefined;
radiusradius?: ResponsiveValue<BlockRadius> | undefined;
rowFromrowFrom?: BlockRowFrom | undefined;
shadowshadow?: ResponsiveValue<BlockShadow> | undefined;
shrinkshrink?: ResponsiveValue<number | boolean> | undefined;
sizesize?: BlockSize | undefined;
stickysticky?: boolean | undefined;
toptop?: ResponsiveValue<BlockSpace> | undefined;
widthwidth?: ResponsiveValue<BlockSize> | undefined;
wrapwrap?: ResponsiveValue<boolean> | undefined;
zIndexzIndex?: ResponsiveValue<BlockZIndex> | undefined;

TextProps

Import from @askrjs/themes/components.

PropTypeDefaultDescription
asas?: TextElement | undefined;
childrenchildren?: unknown;
fontfont?: TextFont | undefined;
numericnumeric?: TextNumeric | undefined;
refref?: Ref<HTMLElement>;
sizesize?: TextSize | undefined;
tonetone?: TextTone | undefined;
truncatetruncate?: boolean | undefined;
weightweight?: TextWeight | undefined;
wrapwrap?: 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.

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.