zedkit
@zedtech/zedkit-ui

StickyAside

alpha

Main column scrolls; aside sticks under the header until the section ends. Pure CSS, server components, no scroll hijacking.

  1. Step 1

    Install the package

    Add the UI kit to your app.

  2. Step 2

    Import the styles

    Pull in the kit CSS once in your globals.

  3. Step 3

    Render a control

    Use a primitive in your page.

Rendered example

View props table
Who is it for?
Developers building docs, onboarding, or pricing pages where steps scroll on one side and a summary or preview should stay visible until the section ends.
When should I use it?
Long step lists beside a short preview, install instructions beside a rendered example, or any section where reading order is main then aside.
When should I pick something else?
Pinning a single stage that crossfades through scenes — use `ScrollScenes`. A sticky bar that spans the whole viewport outside a section — use your shell header. Tall aside content without `scrollable` when you need the full block visible at once on small viewports — stack on mobile or shorten the aside.

Install or copy

This ships on npm only; there is no copy-source brick for it.

You get
Every component from one import, with the theme tokens included. Updates arrive with your package manager.
You need
  • React 19 and Tailwind CSS 4, for example a Next.js app created with the Tailwind option.
  1. Install

    Terminal
    pnpm add @zedtech/zedkit-ui
  2. Load the styles

    Tailwind does not scan node_modules, so the @source line points it at the kit's class names. The path is relative to this file.

    src/app/globals.cssReplace the contents
    @import "tailwindcss";
    @import "@zedtech/zedkit-ui/styles.css";
    @source "../../node_modules/@zedtech/zedkit-ui/src/**/*.{ts,tsx}";
    
  3. Import and render

    src/app/page.tsxReplace the contents
    import { StickyAside, Stack, Text } from "@zedtech/zedkit-ui";
    import { GlassSurface } from "@zedtech/zedkit-ui/marketing";
    
    export default function Page() {
      return (
        <main className="p-8">
          <StickyAside side="right" ratio="1/3" className="[--zk-sticky-top:7rem]">
            <StickyAside.Main>
              <Stack gap={4}>{/* numbered steps */}</Stack>
            </StickyAside.Main>
            <StickyAside.Aside asChild>
              <GlassSurface padding="md">
                <Text>Rendered example</Text>
              </GlassSurface>
            </StickyAside.Aside>
          </StickyAside>
        </main>
      );
    }
    

Package version 1.6.2.

Usage and states

Get started steps with live preview
import { StickyAside, Stack, Text } from "@zedtech/zedkit-ui";
import { GlassSurface } from "@zedtech/zedkit-ui/marketing";

<StickyAside side="right" ratio="1/3" className="[--zk-sticky-top:7rem]">
  <StickyAside.Main>
    <Stack gap={4}>{/* numbered steps */}</Stack>
  </StickyAside.Main>
  <StickyAside.Aside asChild>
    <GlassSurface padding="md">
      <Text>Rendered example</Text>
    </GlassSurface>
  </StickyAside.Aside>
</StickyAside>

Doc page with summary card

Write the guide

Steps stay in the main column for reading order.

import { Card, CardContent, StickyAside, Text } from "@zedtech/zedkit-ui";

// Aside on the left from the md breakpoint; stack main first on phones.
<StickyAside side="left" from="md" mobile="stack-after">
  <StickyAside.Main>
    <Text>Form fields…</Text>
  </StickyAside.Main>
  <StickyAside.Aside scrollable>
    <Card>
      <CardContent>Order summary</CardContent>
    </Card>
  </StickyAside.Aside>
</StickyAside>

Wraps: position: sticky

Props

Prop

Type

Show inherited HTML attributes

Standard attributes from the underlying element. See MDN.

Prop

Type

Accessibility

DOM order is always main then aside; use mobile="before" only when the aside should appear first visually on phones. Put headings and steps in the main column; label specimen previews (for example with role="group" and aria-label). In-section links in the main column get scroll-margin-top from --zk-pin-top. With scrollable, the aside scrolls internally — keep focusable controls reachable.

Troubleshooting

Aside never sticks — check that `data-from` matches your breakpoint, the section is tall enough to scroll, and no ancestor between this root and the viewport uses `overflow: hidden|auto|scroll` or a transform. Aside hides under the site header — set `--zk-sticky-top` (or `--zk-pin-top` globally) to the header offset. Columns too narrow — pass `ratio="1/2"` or `--zk-sticky-cols`. Aside taller than the window — pass `scrollable` so it keeps sticky with an internal scroll area. Snap fights a long section — parent sections that contain `StickyAside` disable scroll-snap when `SectionScroll` is active.


title: Keep a side panel in view description: StickyAside for steps plus a sticky preview — pure CSS, no scroll hijacking.

Every UI snippet on this page is type-checked exactly as written.

Who this is for

You ship docs, onboarding, or pricing pages where one column scrolls (steps, form fields) and a short preview or summary should stay visible until the reader leaves the section.

Prerequisites

  • React 19 and @zedtech/zedkit-ui styles imported (see Theme an admin app).
  • A fixed or sticky site header — set --zk-sticky-top on the layout root if the default --zk-pin-top is too short.

Steps

1. Wrap main and aside

Put scrolling content in StickyAside.Main and the panel that should stick in StickyAside.Aside. DOM order stays main then aside for screen readers.

  1. Step 1

    Install the package

    Add the UI kit to your app.

  2. Step 2

    Import the styles

    Pull in the kit CSS once in your globals.

  3. Step 3

    Render a control

    Use a primitive in your page.

Rendered example

steps-with-preview.tsx
import { Button, Card, CardContent, Heading, StickyAside, Stack, Text } from "@zedtech/zedkit-ui";

<StickyAside className="[--zk-sticky-top:4.5rem]" side="right">
  <StickyAside.Main>{/* steps */}</StickyAside.Main>
  <StickyAside.Aside asChild>
    <Card>
      <CardContent>
        <Button>Save</Button>
      </CardContent>
    </Card>
  </StickyAside.Aside>
</StickyAside>

Visible result: on wide viewports the card stays under the header while you scroll the steps; on phones the columns stack (aside after main by default).

2. Tune columns and mobile order

Use ratio="1/3" or side="left" when the sticky column should sit on the other side. Pass mobile="stack-before" when the preview should appear above the steps on narrow screens without changing DOM order.

Write the guide

Steps stay in the main column for reading order.

left-summary.tsx
import { Card, CardContent, StickyAside, Text } from "@zedtech/zedkit-ui";

<StickyAside side="left" from="md" ratio="2/5" scrollable>
  <StickyAside.Main>…</StickyAside.Main>
  <StickyAside.Aside>
    <Card>
      <CardContent>
        <Text variant="muted">Summary</Text>
      </CardContent>
    </Card>
  </StickyAside.Aside>
</StickyAside>

Final result

One section scrolls natively: the aside uses position: sticky inside the grid and stops at the section edge. No JavaScript runs in the layout primitive.

Troubleshooting

Aside never sticks — A parent with overflow: hidden, auto, or scroll, or a transform, sits between the layout and the viewport. Move StickyAside to the normal page flow or remove that ancestor constraint.

Panel hides under the header — Set className="[--zk-sticky-top:7rem]" (or your header offset) on StickyAside.

Tall summary clipped — Pass scrollable so the aside keeps sticky with an internal scroll area.

Next steps

On this page