Skip to content

ScrollFade API

Complete type definitions and prop reference for the ScrollFade component system.


ScrollFade

Low-level gradient fade overlay component.

Import

tsx
import { ScrollFade, type ScrollFadeProps } from '@/shared/ui/components/scroll/ScrollFade'

Props

ScrollFadeProps

PropTypeDefaultDescription
position'top' | 'bottom'RequiredPosition of the fade effect
size'sm' | 'md' | 'lg''md'Height of the fade gradient
visiblebooleantrueWhether to show the fade (controls opacity)
classNamestring-Additional CSS classes
zIndexnumber10Z-index for layering
gradientGradientConfig-Custom gradient configuration

GradientConfig

ts
interface GradientConfig {
  /** Starting color (e.g., 'rgb(255 255 255)', 'hsl(var(--card))') */
  from: string
  /** Middle color (optional, defaults to from with 80% opacity) */
  via?: string
  /** Ending color is always transparent */
}

Size Variants

SizeHeightUse Case
smh-8 (32px)Compact UIs, dropdowns
mdh-12 (48px)Standard lists, cards
lgh-16 (64px)Large containers, modals

Features

  • Sticky positioning: Stays at top/bottom of container
  • Gradient background: Smooth fade from solid to transparent
  • Backdrop blur: Subtle blur(2px) effect
  • Masked blur: Blur fades out with gradient
  • Framer Motion: Smooth opacity animations
  • Negative margins: Overlays content for seamless effect

Example

tsx
import { ScrollFade } from '@/shared/ui/components/scroll/ScrollFade'

// Basic usage
<ScrollFade position="top" visible={showTopFade} />

// Custom size
<ScrollFade position="bottom" size="lg" visible={showBottomFade} />

// Custom gradient for card background
<ScrollFade
  position="top"
  visible={showTopFade}
  gradient={{
    from: 'hsl(var(--card))',
    via: 'hsl(var(--card) / 0.8)',
  }}
/>

// Custom z-index
<ScrollFade
  position="bottom"
  visible={showBottomFade}
  zIndex={20}
/>

ScrollFadeContainer

All-in-one wrapper with automatic scroll detection.

Import

tsx
import { ScrollFadeContainer, type ScrollFadeContainerProps } from '@/shared/ui/components/scroll/ScrollFadeContainer'

Props

ScrollFadeContainerProps

PropTypeDefaultDescription
childrenReactNodeRequiredChild content (should include scrollable element)
fades'top' | 'bottom' | 'both''both'Which fades to show
fadeSize'sm' | 'md' | 'lg''md'Size of fade gradients
classNamestring-Additional CSS classes for container
scrollOptionsUseScrollFadeOptions-Options for scroll detection hook
fadeZIndexnumber10Z-index for fade overlays
fadeGradientGradientConfig-Custom gradient configuration
fadeClassNamestring-Additional CSS classes for fade elements

UseScrollFadeOptions

ts
interface UseScrollFadeOptions {
  /** Threshold in pixels before showing fade (default: 10) */
  threshold?: number
  /** Debounce delay in ms (default: 100) */
  debounce?: number
}

Features

  • Automatic detection: Uses useScrollFade hook internally
  • Ref management: Handles container ref automatically
  • Flexible fades: Show top, bottom, or both
  • Pass-through props: All fade customization options available

Examples

Basic Usage

tsx
import { ScrollFadeContainer } from '@/shared/ui/components/scroll/ScrollFadeContainer'
import SimpleBar from 'simplebar-react'

<ScrollFadeContainer>
  <SimpleBar className="h-[400px]">
    <div className="space-y-2 p-4">
      {items.map((item) => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  </SimpleBar>
</ScrollFadeContainer>

Custom Fade Size

tsx
<ScrollFadeContainer fadeSize="sm">
  <div className="h-[300px] overflow-y-auto">
    {content}
  </div>
</ScrollFadeContainer>

Top or Bottom Only

tsx
// Only show top fade
<ScrollFadeContainer fades="top">
  {content}
</ScrollFadeContainer>

// Only show bottom fade
<ScrollFadeContainer fades="bottom">
  {content}
</ScrollFadeContainer>

Custom Gradient

tsx
<ScrollFadeContainer
  fadeGradient={{
    from: 'hsl(var(--card))',
    via: 'hsl(var(--card) / 0.8)',
  }}
>
  <div className="bg-card rounded-lg">
    {content}
  </div>
</ScrollFadeContainer>

Custom Scroll Options

tsx
<ScrollFadeContainer
  scrollOptions={{
    threshold: 20,  // Show fade when 20px from edge
    debounce: 150,  // Debounce scroll events by 150ms
  }}
>
  {content}
</ScrollFadeContainer>

useScrollFade Hook

Hook for detecting scroll position and determining fade visibility.

Import

tsx
import { useScrollFade, type UseScrollFadeOptions } from '@/shared/ui/components/scroll/useScrollFade'

Signature

ts
function useScrollFade(
  ref: RefObject<HTMLElement>,
  options?: UseScrollFadeOptions
): {
  showTop: boolean
  showBottom: boolean
}

Parameters

ParameterTypeDescription
refRefObject<HTMLElement>Ref to scrollable container
optionsUseScrollFadeOptionsOptional configuration

Returns

ts
interface UseScrollFadeReturn {
  /** Whether to show top fade */
  showTop: boolean
  /** Whether to show bottom fade */
  showBottom: boolean
}

Options

ts
interface UseScrollFadeOptions {
  /** Threshold in pixels before showing fade (default: 10) */
  threshold?: number
  /** Debounce delay in ms (default: 100) */
  debounce?: number
}

Features

  • Automatic detection: Monitors scroll position and container size
  • Debounced: Prevents excessive updates
  • Resize aware: Updates on container resize
  • Threshold support: Configurable trigger distance

Example

tsx
import { useRef } from 'react'
import { useScrollFade } from '@/shared/ui/components/scroll/useScrollFade'
import { ScrollFade } from '@/shared/ui/components/scroll/ScrollFade'

function CustomScrollable() {
  const containerRef = useRef<HTMLDivElement>(null)
  const { showTop, showBottom } = useScrollFade(containerRef, {
    threshold: 20,
    debounce: 150,
  })

  return (
    <div ref={containerRef} className="relative">
      <ScrollFade position="top" visible={showTop} />
      
      <div className="h-[400px] overflow-y-auto">
        {/* Scrollable content */}
      </div>
      
      <ScrollFade position="bottom" visible={showBottom} />
    </div>
  )
}

Complete Example

tsx
import { ScrollFadeContainer } from '@/shared/ui/components/scroll/ScrollFadeContainer'
import { Card, CardHeader, CardTitle, CardContent } from '@/shadcn/components/ui/card'
import SimpleBar from 'simplebar-react'

function ActivityFeed({ activities }) {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Recent Activity</CardTitle>
      </CardHeader>
      <CardContent>
        <ScrollFadeContainer
          fadeSize="md"
          fadeGradient={{
            from: 'hsl(var(--card))',
            via: 'hsl(var(--card) / 0.8)',
          }}
          scrollOptions={{
            threshold: 15,
            debounce: 100,
          }}
        >
          <SimpleBar className="h-[400px]">
            <div className="space-y-3 pr-4">
              {activities.map((activity) => (
                <div
                  key={activity.id}
                  className="p-3 bg-muted rounded-md"
                >
                  <p className="text-sm font-medium">
                    {activity.user}
                  </p>
                  <p className="text-sm text-muted-foreground">
                    {activity.message}
                  </p>
                  <p className="text-xs text-muted-foreground mt-1">
                    {activity.timestamp}
                  </p>
                </div>
              ))}
            </div>
          </SimpleBar>
        </ScrollFadeContainer>
      </CardContent>
    </Card>
  )
}

Styling

Default Gradient

Top Fade:

css
background-image: linear-gradient(
  to bottom,
  hsl(var(--background)),
  hsl(var(--background) / 0.8),
  transparent
);

Bottom Fade:

css
background-image: linear-gradient(
  to top,
  hsl(var(--background)),
  hsl(var(--background) / 0.8),
  transparent
);

Custom Gradient

tsx
// For card backgrounds
<ScrollFade
  gradient={{
    from: 'hsl(var(--card))',
    via: 'hsl(var(--card) / 0.8)',
  }}
/>

// For custom colors
<ScrollFade
  gradient={{
    from: 'rgb(255 255 255)',
    via: 'rgb(255 255 255 / 0.8)',
  }}
/>

// For popovers
<ScrollFade
  gradient={{
    from: 'hsl(var(--popover))',
  }}
/>

Blur Effect

css
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);

/* Masked to fade out with gradient */
mask-image: linear-gradient(to bottom, black 20%, transparent 100%);
-webkit-mask-image: linear-gradient(to bottom, black 20%, transparent 100%);

Positioning

css
/* Sticky positioning for automatic scroll behavior */
position: sticky;
top: 0; /* or bottom: 0 */

/* Negative margins to overlay content */
margin-bottom: -48px; /* for top fade with size="md" */
margin-top: -48px;    /* for bottom fade with size="md" */

Animation

Framer Motion

tsx
<motion.div
  initial={{ opacity: 0 }}
  animate={{ opacity: visible ? 1 : 0 }}
  transition={{ duration: 0.4, ease: "easeOut" }}
>
  {/* Fade content */}
</motion.div>

Timing:

  • Duration: 400ms
  • Easing: easeOut
  • Property: opacity

Performance

Optimizations

  1. Sticky positioning: No JavaScript for positioning
  2. CSS animations: Hardware-accelerated
  3. Debounced scroll: Prevents excessive updates (default: 100ms)
  4. Conditional rendering: Only updates when visibility changes
  5. Passive event listeners: Improves scroll performance

Tips

  • Use smaller fadeSize for better performance
  • Increase debounce for less frequent updates
  • Use threshold to reduce unnecessary fade toggles

Accessibility

ARIA

tsx
<ScrollFade aria-hidden="true" />

Fades are purely decorative and hidden from screen readers.

Pointer Events

css
pointer-events: none;

Fades don't interfere with user interaction.


Type Definitions

ts
// ScrollFade
interface ScrollFadeProps {
  position: 'top' | 'bottom'
  size?: 'sm' | 'md' | 'lg'
  visible?: boolean
  className?: string
  zIndex?: number
  gradient?: {
    from: string
    via?: string
  }
}

// ScrollFadeContainer
interface ScrollFadeContainerProps {
  children: ReactNode
  fades?: 'top' | 'bottom' | 'both'
  fadeSize?: 'sm' | 'md' | 'lg'
  className?: string
  scrollOptions?: UseScrollFadeOptions
  fadeZIndex?: number
  fadeGradient?: ScrollFadeProps['gradient']
  fadeClassName?: string
}

// useScrollFade Hook
interface UseScrollFadeOptions {
  threshold?: number
  debounce?: number
}

interface UseScrollFadeReturn {
  showTop: boolean
  showBottom: boolean
}

Browser Support

  • Modern browsers: Full support
  • Safari: Requires -webkit- prefixes (included)
  • backdrop-filter: Supported in all modern browsers
  • Framer Motion: Requires JavaScript enabled

See Also