Skip to content
SuperCoach

Components · Data display

Scroll area

Beta

A scrollable region with styled scrollbars.

Exercise library

When to use

  • For a region with a fixed height inside a page, whose content is longer: the exercise library list next to the program builder, a message thread, a list of meals in a sheet.
  • For a horizontal strip that would otherwise break the layout: the weeks of a program, progress photos.
  • When the scrollbar should look the same in every browser and both themes.

When not to use

  • For the page itself. Let the page scroll with the browser's own scrollbar.
  • For short content. Fixed heights with scrolling inside hide content; let the content set the height when it can.
  • For tables: Table has its own scrolling container.
  • Inside a Dialog or sheet body, which already scrolls. Nested scrolling regions are hard to use with a trackpad and a keyboard.

Anatomy

  1. Mon: Upper body A
  2. Tue: Lower body A
  3. Wed: Rest
  4. Thu: Upper body B
  5. Fri: Lower body B
  6. Sat: Conditioning
  7. Sun: Rest
  1. 1. Viewport: the visible region; content scrolls inside it
  2. 2. Scrollbar and thumb, at the end edge; horizontal ones sit at the bottom

When both directions scroll, a corner fills the space where the two scrollbars meet.

Variants

  1. Sara: Morning coach! Finished upper body A yesterday.
  2. You: Great work, Sara. How did the bench press feel?
  3. Sara: The last set at 40 kg was hard, RPE 9.
  4. You: Then stay at 40 kg next week and aim for clean reps.
  5. Sara: Okay. Can I swap Thursday's session to Friday?
  6. You: Yes, I've moved it. Rest on Thursday instead.
  7. Sara: Thanks! Check-in photos are coming tonight.
  8. You: Perfect. Remember to weigh in before breakfast.
  1. Week 1Foundation
  2. Week 2Volume
  3. Week 3Volume
  4. Week 4Deload
  5. Week 5Strength
  6. Week 6Strength
  7. Week 7Peak
  8. Week 8Test
VariantUse for
Vertical (default)Lists and threads with a fixed height.
Horizontal (add <ScrollBar orientation="horizontal" />)A row of cards or columns, such as program weeks. Give the content w-max so it doesn't wrap.

When the scrollbar shows

Radix's type prop decides when the scrollbar is visible. shadcn's default is "hover", which hides it until the pointer is over the area, so nothing tells the coach that there is more to see.

typeScrollbarUse
"auto"Visible whenever the content overflowsThe default for this system. Used in every example on this page.
"always"Always visible, even when nothing overflowsLists whose length changes, so the layout doesn't shift.
"scroll"Only while scrollingAvoid.
"hover" (shadcn default)Only while the pointer is over the areaAvoid: invisible on touch screens and to keyboard users.

States

StateWhat changesToken
Content overflowsThumb shows, sized to the visible sharebg-border, rounded-full
ScrollingThumb moves; the track stays transparent—
Focus-visible (viewport)Ringring-ring/50

The thumb is bg-border, which is a very light line on the background. A proposal for a stronger thumb is listed under Accessibility.

Usage guidelines

  • Size the area so a partial item shows at the edge. A cut-off row is the clearest sign that the list scrolls.
  • Never hide the scrollbar completely. Use type="auto".
  • Keep a heading or label outside the area, so it stays visible while the content scrolls.
  • For long message threads, start scrolled to the newest message.
Do.type="auto": the scrollbar and a cut-off row show there is more.
Don't.The default hover scrollbar and a height that ends between rows. It looks like the list is complete.

Content guidelines

  • Name what scrolls with a visible heading: "Exercise library", "Messages with Sara Al-Harbi".
  • In a thread, mark who wrote each message for screen readers ("You:", "Sara:") even when the layout shows it.

Accessibility

KeyDoes
TabMoves to the next focusable item inside the area, scrolling it into view
Arrow up / downScroll the area, once focus is inside it
Page up / Page down, SpaceScroll by one screen
Home / EndScroll to the top / bottom
  • When its content overflows, the viewport becomes focusable, so keyboard users can always scroll it, even when nothing inside can take focus (a message thread, the program weeks). It shows the focus ring.
    • Give the area an aria-label: it is then announced as a named region ("Messages with Sara Al-Harbi, region").
    • Upstream shadcn's viewport is never focusable; this version adds it (axe scrollable-region-focusable).
  • The scrollbar is visual only; screen readers ignore it, and scroll as they read.
  • Never trap focus in an area: Tab always continues past the end.
  • The thumb is bg-border, 1.2:1 on the background in both themes, below the 3:1 that WCAG 1.4.11 asks of a control you can see and drag. muted-foreground would be 6.0:1. This is a token choice, so it is raised as a question rather than changed.

RTL

The vertical scrollbar moves to the left edge, the end in RTL, because the site provides Radix's DirectionProvider. Horizontal areas start at the right and scroll toward the left; the thumb starts at the right. Content inside follows the reading order. The scrollbar's 1px spacer border is set with border-l; it's transparent, so it doesn't show in either direction.

Tokens used

Read from components/ui/scroll-area.tsx at build time, so this list changes when the component does. Opacity modifiers such as /90 are shadcn hover and focus tints on top of the token.

Semantic color tokens used by this component
TokenUtilitiesSuperCoachCoach
--borderbg-borderneutral-200neutral-200
--ringring-ring/50brand-600orange-500
  • Radiusrounded-full

Props

<ScrollArea>

Also takes every prop of Radix ScrollArea.Root

No props of its own.

<ScrollBar>

Also takes every prop of Radix ScrollArea.ScrollAreaScrollbar

No props of its own.

type, scrollHideDelay and dir come from Radix ScrollArea.

Replaces in coach-admin

From the read-only audit of coach-admin: 46 style values, 84 uses.

coach-admin styles scrollbars one container at a time with ::-webkit-scrollbar rules (exercise library, drawers, the matrix slider, schedule lists), each with its own width and colors, and Firefox's scrollbar-color in a few places. ScrollArea replaces them with one style.

Classes and selectors

  • &::-webkit-scrollbar-thumb29
  • &::-webkit-scrollbar17
  • &::-webkit-scrollbar-track15
  • .videos-scroll-container5
  • &::-webkit-scrollbar-thumb:hover3
  • .exercise-library__content::-webkit-scrollbar-thumb, .exerci2
  • .scroll-list-container2
  • .drawer__content::-webkit-scrollbar-thumb1
  • .matrix-slider__content::-webkit-scrollbar-thumb1
  • .exercise-library__content::-webkit-scrollbar-thumb:hover, .1
  • .matrix-slider__content::-webkit-scrollbar-thumb:hover1
  • .drawer__content::-webkit-scrollbar-thumb:hover1

SCSS variables in these styles

  • $sidebar-bg→ nearest neutral-800 (no semantic token)
  • $aside-menu-bgnot a color: spacing, size or layout
  • $scroll-thumb-color→ nearest neutral-300 (no semantic token)
  • $disable-color→ nearest neutral-400 (no semantic token)
  • $ai-primary-color→ nearest violet-ai-500 (no semantic token)

Table, Sheet, Dialog, Carousel.