Skip to content

UnderlineNav: loading counter pulses regardless of prefers-reduced-motion #8651

Open

Description

@adityalenova

Component

UnderlineNav / TabNav (loading counter)

Description

While a count is being fetched, UnderlineNav and TabNav show a grey pill that pulses between opacity 1 and 0.2 forever. The animation is unconditional.

packages/react/src/internal/components/UnderlineTabbedInterface.module.css:170

.LoadingCounter {
  ...
  animation: loadingCounterKeyFrames 1.2s ease-in-out infinite alternate;
}

@keyframes loadingCounterKeyFrames {
  from {
    opacity: 1;
  }
  to {
    opacity: 0.2;
  }
}

SkeletonBox — which is the same "something is coming" affordance — already handles this correctly:

@keyframes shimmer { ... }

.SkeletonBox {
  animation: shimmer;

  @media (prefers-reduced-motion: no-preference) {
    mask-image: ...;
    mask-size: 200%;
    animation: shimmer;
    animation-duration: var(--base-duration-1000);
    animation-iteration-count: infinite;
  }
}

so the shimmer only runs when the user has not asked for reduced motion, and the placeholder stays plainly visible without it. ProgressBar and SegmentedControl follow the same opt-in pattern.

A repeating animation is the case prefers-reduced-motion exists for, and WCAG 2.2 SC 2.2.2 (Pause, Stop, Hide) applies to moving, blinking or scrolling content that starts on load and lasts more than five seconds — which an indefinite pulse does.

Fix

Wrap the animation declaration in @media (prefers-reduced-motion: no-preference) and let the rule's static opacity stay at its default (1). Reduced-motion users then get a steady grey pill instead of a blinking one — same affordance, no motion, and the loading state is still clearly communicated.

I'll PR this with an e2e test that reads the computed animation-name under reducedMotion: 'reduce' and no-preference against an UnderlineNav story in its loading state.

Activity

  1. RafaelHDSV commented on Oct 31, 2026

    @RafaelHDSV

    Opened #8656 — the loading-counter pulse is now behind prefers-reduced-motion: no-preference, matching SkeletonBox / ProgressBar. Reduced-motion visitors keep a steady grey pill.

  2. self-assigned this
    on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions