WingoUI
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime Picker
Forms
Address InputCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text Editor
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselImage CropperImage LightboxLocation MapQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Gravity Letters
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowPost Listing WizardPricing PageRegister PageSettings PageUsers Admin Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryThemeCredits

Navigation

Scroll Progress

NavigationScroll Progress
Proximity SidebarSidebar
Live props
Behavior
offset120px
Appearance
size
position
inset24px
showLabel
accentColor
trackColor
surfaceColor
textColor
radius26px
Content
triggerLabel
currentLabel
listLabel
Code
import ScrollProgress from "@/components/ui/scroll-progress"

<ScrollProgress />
FreeRare UI

v1.0.0·Added in 1.0.0

A scroll progress pill that tracks reading position and expands into a squircle menu of sections you can jump to.

Scroll to fill the ring and watch the active section label crossfade in. Click the pill to morph it into a squircle menu, then tap any section to smooth-scroll there. From the keyboard, the menu opens with focus on the current section, the arrow keys, Home and End move between sections, and Escape closes it and hands focus back to the pill. Click outside to close it too.

Add to a project

npx wingo-ui@latest add scroll-progress

Dependencies

motionmotionui-config

Files

  • components/ui/scroll-progress.tsx
PreviousProximity SidebarNext Sidebar
Scroll Progress
Scroll Progress
Behavior
Styling
Usage

Introduction

Scroll Progress

A small pill that reports how far the reader has moved through a page or a scroll container. A ring fills as the content advances, and the label names the section you are in.

Why it exists

A sense of position on long pages

Long articles hide their own length. The ring gives readers a continuous cue for how much remains, without adding another block of chrome to the layout.

The signal

One value, mapped to a ring

The component tracks a single scalar between zero and one and maps it to the stroke of the ring. The label, the menu and the placement are presentation on top of that value.

Placement

Pinned to the bottom center

The pill sits at the bottom of the viewport, within reach of the thumb, while the content scrolls beneath it. Pass className to move it, or point it at a specific scroll container.

Interaction

Behavior

The ring follows scroll position directly and the label swaps as each section crosses the offset line. It should feel like a readout of the page rather than an animation that plays on its own.

Tracking

Progress is derived, not stored

Scroll offset divided by the total scrollable distance gives the progress value, so it stays correct through resizes and content changes.

Smoothing

A spring keeps the ring from stuttering

Raw scroll values can arrive in jumps. Passing the value through a spring lets the ring glide toward its target instead of snapping between frames.

Jumping

The pill opens into a menu

Tap the pill and it morphs into a squircle list of every section. Pick one to smooth-scroll there, or press Escape or tap outside to close it again.

Visual System

Styling

The default look is deliberately quiet: a frosted pill with a thin border, tied to the background and foreground tokens so it reads clearly in both themes.

Surface

Frosted, not solid

A translucent background with a backdrop blur keeps the text underneath faintly visible, so the pill feels like part of the page instead of a sticker on top of it.

Contrast

Lean on the theme tokens

The ring, the label and the menu all use the foreground color, so everything inverts correctly when the theme flips between light and dark.

Motion

Sizes spring, labels crossfade

The surface springs between the pill and the menu sizes, while labels crossfade through a light blur so a change of section never feels abrupt.

Implementation

Usage

Drop the component into a scrollable layout and pass the sections you want to name. Everything past that is optional refinement.

Mounting

One instance per scroll surface

Render a single indicator for the page. If you have an independent inner scroll area, give it its own instance and pass that element as containerRef.

Offset

Tune when a section becomes active

The offset sets how far below the top edge a heading has to travel before its label takes over. Raise it for pages with a tall sticky header.

Accessibility

Respect reduced motion

When reduced motion is requested, the surface stops springing, the blur fades are dropped, and jumping to a section scrolls instantly.