Skip to content
Brok UI

Loading…

No results

Sticky Cards

Open source

A scroll-stacking deck: direct-child cards stick at a top offset so later cards slide up and stack over earlier ones, with an optional scroll-driven depth scale-down on supporting browsers.

Version
v1.0.3
Stability
stable
License
MIT
Related
Layered Stack
Draggable Card Stack
Carousel

Preview

Scroll inside this box

First

Scroll to stack the cards.

Second

Each card sticks at the top offset.

Third

Later cards slide up and cover earlier ones.

Fourth

Covered cards scale down for depth where supported.

previews.components.sticky-cards.default.blade.php Blade
{{-- The stack needs scroll room: this stage scrolls on its own, so the cards
     stick and pile up inside the preview instead of needing the page to scroll. --}}
<div class="relative mx-auto h-96 w-full max-w-md overflow-y-auto rounded-lg border border-border bg-background p-4 [scrollbar-width:thin]">
    <p class="mb-4 text-center text-xs text-muted-foreground">{{ __('Scroll inside this box') }}</p>
    <x-ui.sticky-cards offset="1rem">
        <div class="rounded-xl border border-border bg-card p-6 text-card-foreground shadow-md">
            <h3 class="font-semibold">First</h3>
            <p class="text-sm text-muted-foreground">Scroll to stack the cards.</p>
        </div>
        <div class="rounded-xl border border-border bg-muted p-6 text-foreground shadow-md">
            <h3 class="font-semibold">Second</h3>
            <p class="text-sm text-muted-foreground">Each card sticks at the top offset.</p>
        </div>
        <div class="rounded-xl border border-border bg-secondary p-6 text-secondary-foreground shadow-md">
            <h3 class="font-semibold">Third</h3>
            <p class="text-sm text-muted-foreground">Later cards slide up and cover earlier ones.</p>
        </div>
        <div class="rounded-xl border border-border bg-accent p-6 text-accent-foreground shadow-md">
            <h3 class="font-semibold">Fourth</h3>
            <p class="text-sm text-muted-foreground">Covered cards scale down for depth where supported.</p>
        </div>
    </x-ui.sticky-cards>
    <div class="h-64" aria-hidden="true"></div>
</div>

Installation

terminal
php artisan ui:add sticky-cards

Registry contract

php artisan ui:add sticky-cards writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.

  • blade resources/views/components/ui/sticky-cards.blade.php
Registry dependencies
None — installs on its own.
Packages
composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.

sticky-cards.md
# Brok UI: Sticky Cards (`sticky-cards`)

A scroll-stacking deck: direct-child cards stick at a top offset so later cards slide up and stack over earlier ones, with an optional scroll-driven depth scale-down on supporting browsers.

Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.

## Install

```bash
php artisan ui:add sticky-cards
```

## Usage

```blade
{{-- The stack needs scroll room: this stage scrolls on its own, so the cards
     stick and pile up inside the preview instead of needing the page to scroll. --}}
<div class="relative mx-auto h-96 w-full max-w-md overflow-y-auto rounded-lg border border-border bg-background p-4 [scrollbar-width:thin]">
    <p class="mb-4 text-center text-xs text-muted-foreground">{{ __('Scroll inside this box') }}</p>
    <x-ui.sticky-cards offset="1rem">
        <div class="rounded-xl border border-border bg-card p-6 text-card-foreground shadow-md">
            <h3 class="font-semibold">First</h3>
            <p class="text-sm text-muted-foreground">Scroll to stack the cards.</p>
        </div>
        <div class="rounded-xl border border-border bg-muted p-6 text-foreground shadow-md">
            <h3 class="font-semibold">Second</h3>
            <p class="text-sm text-muted-foreground">Each card sticks at the top offset.</p>
        </div>
        <div class="rounded-xl border border-border bg-secondary p-6 text-secondary-foreground shadow-md">
            <h3 class="font-semibold">Third</h3>
            <p class="text-sm text-muted-foreground">Later cards slide up and cover earlier ones.</p>
        </div>
        <div class="rounded-xl border border-border bg-accent p-6 text-accent-foreground shadow-md">
            <h3 class="font-semibold">Fourth</h3>
            <p class="text-sm text-muted-foreground">Covered cards scale down for depth where supported.</p>
        </div>
    </x-ui.sticky-cards>
    <div class="h-64" aria-hidden="true"></div>
</div>
```

## Props

- `offset` (string, default `5rem`) — CSS length for how far from the top each card sticks.
- `gap` (string, default `1.5rem`) — CSS length for both the flex gap between cards and the scroll distance before the next one lands.
- `scale` (bool, default `true`) — Enables the optional scroll-driven depth scale-down on covered cards, on supporting browsers, when true.

## Use when

- Use only when visual expression supports storytelling, brand differentiation, or a clear showcase moment.
- Building a scroll-driven deck where each card should stick in place and be progressively covered by the next card as the visitor scrolls past it, such as a feature or step-by-step story section.
- Wanting an optional scroll-linked depth scale-down on covered cards for supporting browsers, with a clean fallback elsewhere.

## Avoid when

- Do not place heavy effects in task-dense workflows where clarity, speed, and readability matter more.
- The cards should be user-navigable via clicks, drag or buttons rather than driven purely by page scroll; use layered-stack, draggable-card-stack or one of the carousels instead.

## Anti-patterns

- Heavy effects in forms or task-dense workflows
- Motion that interferes with reading or focus

## Rules

- Use the `<brok:sticky-cards>` tag (or `<x-ui.sticky-cards>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.

## Links

- Docs: https://brokui.dev/docs/components/sticky-cards
- Registry JSON (files, props, contract): https://brokui.dev/r/open/sticky-cards.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
offset string 5rem CSS length for how far from the top each card sticks.
gap string 1.5rem CSS length for both the flex gap between cards and the scroll distance before the next one lands.
scale bool true Enables the optional scroll-driven depth scale-down on covered cards, on supporting browsers, when true.

Slots

  • default — The direct-child cards to stack; each becomes sticky in turn.

Data slots

Stable hooks for CSS overrides and browser tests.

sticky-cards

Behavior

  • Every direct child becomes position: sticky at a configurable top offset, so as the page scrolls each later card slides up and covers the earlier ones; gap sets both the flex gap and the scroll distance travelled before the next card lands.
  • When scale is true, covered cards optionally shrink slightly via a scroll-driven animation-timeline: view() rule, gated behind @supports and motion-safe.
  • Browsers without scroll-driven animations, and anyone with reduced motion, get a clean, fully readable stack of sticky cards with no scaling.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized, reducedMotion.

Guidance

Expressive and showcase effect

Support brand expression or a showcase moment.

Use when

  • Use only when visual expression supports storytelling, brand differentiation, or a clear showcase moment.
  • Building a scroll-driven deck where each card should stick in place and be progressively covered by the next card as the visitor scrolls past it, such as a feature or step-by-step story section.
  • Wanting an optional scroll-linked depth scale-down on covered cards for supporting browsers, with a clean fallback elsewhere.

Avoid when

  • Do not place heavy effects in task-dense workflows where clarity, speed, and readability matter more.
  • The cards should be user-navigable via clicks, drag or buttons rather than driven purely by page scroll; use layered-stack, draggable-card-stack or one of the carousels instead.

Use instead

  • Static content-first presentation

Anti-patterns

  • Heavy effects in forms or task-dense workflows
  • Motion that interferes with reading or focus
Anatomy
root
Theming hooks
sticky-cards

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
No component-owned keyboard interaction; native element behavior applies.
Focus
none
  • All card content stays in normal document flow and fully readable at any scroll position; the scale effect is purely decorative and never hides or reorders content.
  • No custom keyboard handling is needed since navigation is native page scroll.
  • Semantic HTML and a stable data-slot attribute for styling and scripting hooks.
  • Focus-visible rings use the ring token, so keyboard focus is always visible.
  • Disabled and invalid states are conveyed to assistive tech, not by color alone.
  • Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
  • Labels go through __() and layout uses logical properties (ms-*, text-start), so it mirrors under dir="rtl" — flip the preview to RTL to confirm.
  • Dark mode uses the same semantic tokens under the dark class; high contrast follows forced-color system tokens.

Livewire

Safe

Livewire can update this component through forwarded wire:* attributes.

Source

The exact, editable file ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/sticky-cards.blade.php Blade
@props([
    'offset' => '5rem',
    'gap' => '1.5rem',
    'scale' => true,
])

@php
    // A scroll-stacking deck. Every direct child becomes `position: sticky` at
    // `--sc-offset` (rule lives in ui.css, keyed on `[data-slot="sticky-cards"] > *`),
    // so as the page scrolls each later card slides up and covers the earlier
    // ones. `--sc-gap` is the flex gap between cards, which is also the scroll
    // distance you travel before the next card lands.
    //
    // The optional depth scale-down (covered cards shrink slightly) is purely
    // decorative: it rides an `animation-timeline: view()` rule gated behind
    // both `@supports` and motion-safe in ui.css. Browsers without scroll-driven
    // animations — and anyone with reduced-motion — simply get a clean, fully
    // readable stack of sticky cards with no scaling.
@endphp

<div
    data-slot="sticky-cards"
    data-scale="{{ $scale ? 'true' : 'false' }}"
    style="--sc-offset:{{ $offset }}; --sc-gap:{{ $gap }};"
    {{ $attributes->merge(['class' => 'flex flex-col gap-[var(--sc-gap)]']) }}
>
    {{ $slot }}
</div>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.0.3
Status
Stable
License
open
Accessibility reviewed
No review date recorded
Last breaking change
No date recorded
Deprecation
Not deprecated
Contract
v6
Foundation
≥ 1.0.0