Scroll Progress Overview

The scroll progress component displays a visual progress bar that tracks scroll position, providing users with feedback about their reading or navigation progress. It supports both page-level (global) and container-level scroll tracking, making it versatile for various use cases from article reading progress to long-form content navigation.

When to Use

Use scroll progress when:

  • Displaying reading progress through long articles or blog posts
  • Tracking navigation through lengthy documentation pages
  • Providing visual feedback in scrollable containers (modals, sidebars, content areas)
  • Enhancing user experience in single-page applications with long content
  • Creating a sense of progress in onboarding flows or tutorials

Don't use scroll progress for:

  • Short content that doesn't require scrolling
  • Replacing proper navigation or table of contents
  • Critical information that must be visible without scrolling
  • Mobile-first experiences where space is extremely limited
Examples Default (Global Scroll)

The default scroll progress bar tracks the entire page scroll and is fixed at the top of the viewport:

<div>
<dap-ds-scroll-progress></dap-ds-scroll-progress>
<h1>Scroll Down to See Progress on the top of this page</h1>
</div>
Container Scroll

When a target attribute is provided, the progress bar tracks scroll within a specific container instead of the entire page:

<div style={{ padding: '20px' }}>
<h2>Container Scroll Progress</h2>
<p>The progress bar below tracks scroll within the container.</p>
<div
  id="scroll-container"
  style={{
    height: '300px',
    overflowY: 'auto',
    border: '1px solid var(--dds-neutral-300)',
    borderRadius: 'var(--dds-radius-md)',
    position: 'relative',
    background: 'var(--dds-neutral-50)',
    marginTop: '20px'
  }}>
  <dap-ds-scroll-progress target="#scroll-container"></dap-ds-scroll-progress>
  <div style={{ padding: '20px' }}>
    <p>Scroll within this container to see the progress bar update.</p>
    {Array(15).fill(0).map((_, i) => (
      <p key={i} style={{ margin: '20px 0' }}>
        Paragraph {i + 1} - Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
      </p>
    ))}
  </div>
</div>
</div>
Variant Options

The scroll progress component supports five color variants to match different contexts and themes:

<>
<dap-ds-stack direction="column" style={{ gap: '20px' }}>
  <div>
    <dap-ds-typography variant="h4">Neutral Variant</dap-ds-typography>
    <div
      id="variant-neutral"
      style={{
        height: '150px',
        overflowY: 'auto',
        border: '1px solid var(--dds-neutral-300)',
        borderRadius: 'var(--dds-radius-md)',
        position: 'relative',
        background: 'var(--dds-neutral-50)'
      }}>
      <dap-ds-scroll-progress target="#variant-neutral" variant="neutral"></dap-ds-scroll-progress>
      <div style={{ padding: '20px' }}>
        <p>Scroll content 1</p>
        <p>Scroll content 2</p>
        <p>Scroll content 3</p>
        <p>Scroll content 4</p>
        <p>Scroll content 5</p>
      </div>
    </div>
  </div>

  <div>
    <dap-ds-typography variant="h4">Brand Variant (Default)</dap-ds-typography>
    <div
      id="variant-brand"
      style={{
        height: '150px',
        overflowY: 'auto',
        border: '1px solid var(--dds-neutral-300)',
        borderRadius: 'var(--dds-radius-md)',
        position: 'relative',
        background: 'var(--dds-neutral-50)'
      }}>
      <dap-ds-scroll-progress target="#variant-brand" variant="brand"></dap-ds-scroll-progress>
      <div style={{ padding: '20px' }}>
        <p>Scroll content 1</p>
        <p>Scroll content 2</p>
        <p>Scroll content 3</p>
        <p>Scroll content 4</p>
        <p>Scroll content 5</p>
      </div>
    </div>
  </div>

  <div>
    <dap-ds-typography variant="h4">Positive Variant</dap-ds-typography>
    <div
      id="variant-positive"
      style={{
        height: '150px',
        overflowY: 'auto',
        border: '1px solid var(--dds-neutral-300)',
        borderRadius: 'var(--dds-radius-md)',
        position: 'relative',
        background: 'var(--dds-neutral-50)'
      }}>
      <dap-ds-scroll-progress target="#variant-positive" variant="positive"></dap-ds-scroll-progress>
      <div style={{ padding: '20px' }}>
        <p>Scroll content 1</p>
        <p>Scroll content 2</p>
        <p>Scroll content 3</p>
        <p>Scroll content 4</p>
        <p>Scroll content 5</p>
      </div>
    </div>
  </div>

  <div>
    <dap-ds-typography variant="h4">Negative Variant</dap-ds-typography>
    <div
      id="variant-negative"
      style={{
        height: '150px',
        overflowY: 'auto',
        border: '1px solid var(--dds-neutral-300)',
        borderRadius: 'var(--dds-radius-md)',
        position: 'relative',
        background: 'var(--dds-neutral-50)'
      }}>
      <dap-ds-scroll-progress target="#variant-negative" variant="negative"></dap-ds-scroll-progress>
      <div style={{ padding: '20px' }}>
        <p>Scroll content 1</p>
        <p>Scroll content 2</p>
        <p>Scroll content 3</p>
        <p>Scroll content 4</p>
        <p>Scroll content 5</p>
      </div>
    </div>
  </div>
</dap-ds-stack>
</>
Inverted Variant

The inverted variant is designed for use on dark backgrounds:

<div
style={{
  backgroundColor: 'var(--dds-indigo-1000)',
  color: 'white',
  padding: '20px',
  borderRadius: 'var(--dds-radius-md)'
}}>
<h2 style={{ color: 'white' }}>Scroll Progress - Inverted Variant</h2>
<p style={{ color: 'white' }}>For use on dark backgrounds.</p>
<div
  id="variant-inverted"
  style={{
    height: '150px',
    overflowY: 'auto',
    border: '1px solid var(--dds-indigo-700)',
    borderRadius: 'var(--dds-radius-md)',
    position: 'relative',
    background: 'var(--dds-indigo-900)',
    marginTop: '20px'
  }}>
  <dap-ds-scroll-progress target="#variant-inverted" variant="inverted"></dap-ds-scroll-progress>
  <div style={{ padding: '20px', color: 'white' }}>
    <p>Scroll content 1</p>
    <p>Scroll content 2</p>
    <p>Scroll content 3</p>
    <p>Scroll content 4</p>
    <p>Scroll content 5</p>
  </div>
</div>
</div>
Size Variants

The scroll progress component comes in three sizes: extra small (2px), small (4px - default), and medium (6px):

<>
<dap-ds-stack direction="column" style={{ gap: '20px' }}>
  <div>
    <dap-ds-typography variant="h4">Extra Small (2px)</dap-ds-typography>
    <div
      id="size-xs"
      style={{
        height: '150px',
        overflowY: 'auto',
        border: '1px solid var(--dds-neutral-300)',
        borderRadius: 'var(--dds-radius-md)',
        position: 'relative',
        background: 'var(--dds-neutral-50)'
      }}>
      <dap-ds-scroll-progress target="#size-xs" size="xs"></dap-ds-scroll-progress>
      <div style={{ padding: '20px' }}>
        <p>Scroll content 1</p>
        <p>Scroll content 2</p>
        <p>Scroll content 3</p>
        <p>Scroll content 4</p>
        <p>Scroll content 5</p>
      </div>
    </div>
  </div>

  <div>
    <dap-ds-typography variant="h4">Small (4px - Default)</dap-ds-typography>
    <div
      id="size-sm"
      style={{
        height: '150px',
        overflowY: 'auto',
        border: '1px solid var(--dds-neutral-300)',
        borderRadius: 'var(--dds-radius-md)',
        position: 'relative',
        background: 'var(--dds-neutral-50)'
      }}>
      <dap-ds-scroll-progress target="#size-sm" size="sm"></dap-ds-scroll-progress>
      <div style={{ padding: '20px' }}>
        <p>Scroll content 1</p>
        <p>Scroll content 2</p>
        <p>Scroll content 3</p>
        <p>Scroll content 4</p>
        <p>Scroll content 5</p>
      </div>
    </div>
  </div>

  <div>
    <dap-ds-typography variant="h4">Medium (6px)</dap-ds-typography>
    <div
      id="size-md"
      style={{
        height: '150px',
        overflowY: 'auto',
        border: '1px solid var(--dds-neutral-300)',
        borderRadius: 'var(--dds-radius-md)',
        position: 'relative',
        background: 'var(--dds-neutral-50)'
      }}>
      <dap-ds-scroll-progress target="#size-md" size="md"></dap-ds-scroll-progress>
      <div style={{ padding: '20px' }}>
        <p>Scroll content 1</p>
        <p>Scroll content 2</p>
        <p>Scroll content 3</p>
        <p>Scroll content 4</p>
        <p>Scroll content 5</p>
      </div>
    </div>
  </div>
</dap-ds-stack>
</>
Custom Styling

The scroll progress component supports customization through CSS custom properties and parts.

Quick Customization with CSS Custom Properties
<div>
<dap-ds-scroll-progress
  style={{
    '--dds-scroll-progress-fill-color-brand': 'var(--dds-violet-600)',
    '--dds-scroll-progress-track-color': 'var(--dds-neutral-100)',
    '--dds-scroll-progress-transition': 'width 0.2s ease-out'
  }}></dap-ds-scroll-progress>
<h1>Custom Styled Progress</h1>
<p>Scroll to see the custom-colored progress bar.</p>
</div>
Importing
import { DapDSScrollProgress } from 'dap-design-system'
Importing React
import { DapDSScrollProgressReact } from 'dap-design-system/react'
Tree-Shakeable Imports

For optimal bundle sizes, use the tree-shakeable import syntax:

import { DapDSScrollProgress } from 'dap-design-system/components'
Attributes
PropertyTypeDefaultDescription
targetstring, undefinedCSS selector for scroll container. If not set, tracks global page scroll.
When set, the component uses sticky positioning within the container.
variant"neutral", "brand" , "negative" , "positive" , "inverted"'brand'The color variant of the progress indicator.
size"xs", "sm" , "md"'sm'The size of the progress bar.
Slots

No slots available.

Events

No custom events available.

CSS Parts
Part NameDescription
baseThe main scroll progress container.
trackThe progress track (background).
fillThe progress fill bar.
How to Use CSS Parts

You can style CSS parts using the ::part() pseudo-element selector:

/* Target a specific part */
.my-custom-dap-ds-scroll-progress::part(base) {
  /* Your custom styles */
}

/* Target multiple parts */
.my-custom-dap-ds-scroll-progress::part(base),
.my-custom-dap-ds-scroll-progress::part(track) {
  /* Shared styles */
}

Example usage:

<dap-ds-scroll-progress class="my-custom-dap-ds-scroll-progress">
  Scroll Progress
</dap-ds-scroll-progress>
.my-custom-dap-ds-scroll-progress::part(base) {
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

CSS parts allow you to style internal elements of the component while maintaining encapsulation. Learn more in our styling guide.

CSS Custom Properties
Property NameDescription
--dds-scroll-progress-z-indexZ-index for fixed positioning (default: 1000)
--dds-scroll-progress-transitionTransition for progress updates (default: width 0.1s ease-out)
--dds-scroll-progress-track-colorBackground color of the progress track (default: var(--dds-neutral-200))
--dds-scroll-progress-fill-color-neutralFill color for neutral variant
--dds-scroll-progress-fill-color-brandFill color for brand variant
--dds-scroll-progress-fill-color-negativeFill color for negative variant
--dds-scroll-progress-fill-color-positiveFill color for positive variant
--dds-scroll-progress-fill-color-invertedFill color for inverted variant
How to Use CSS Custom Properties

CSS custom properties (CSS variables) can be set directly on the component or in your stylesheet:

Method 1: Inline styles (Quick customization)

<dap-ds-scroll-progress
  style="--dds-scroll-progress-z-index: value; --dds-scroll-progress-transition: value;">
  Scroll Progress
</dap-ds-scroll-progress>

Method 2: CSS classes (Reusable styles)

.my-custom-dap-ds-scroll-progress {
  --dds-scroll-progress-z-index: value;
  --dds-scroll-progress-transition: value;
  --dds-scroll-progress-track-color: value;
}
<dap-ds-scroll-progress class="my-custom-dap-ds-scroll-progress">
  Scroll Progress
</dap-ds-scroll-progress>

Method 3: Global theme customization

/* Apply to all instances */
dap-ds-scroll-progress {
  --dds-scroll-progress-z-index: value;
  --dds-scroll-progress-transition: value;
}

CSS custom properties inherit through the Shadow DOM, making them perfect for theming. Changes apply immediately without rebuilding.