webdevpuneet.com

Mobile Fitness Screen — Free HTML CSS JS UI Snippet

Mobile Fitness Screen · Mobile · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="mft-phone">
  <div class="mft-screen">
    <div class="mft-status"><span>9:41</span><span class="mft-batt"><i></i></span></div>
    <header class="mft-head">
      <div><small>Wednesday, Mar 11</small><h1>Your activity</h1></div>
      <div class="mft-ava">AM</div>
    </header>

    <div class="mft-rings">
      <svg viewBox="0 0 120 120" class="mft-svg">
        <circle class="mft-bg" cx="60" cy="60" r="52"/>
        <circle class="mft-bg" cx="60" cy="60" r="40"/>
        <circle class="mft-bg" cx="60" cy="60" r="28"/>
        <circle class="mft-ring r1" id="mftR1" cx="60" cy="60" r="52"/>
        <circle class="mft-ring r2" id="mftR2" cx="60" cy="60" r="40"/>
        <circle class="mft-ring r3" id="mftR3" cx="60" cy="60" r="28"/>
      </svg>
      <div class="mft-legend">
        <div class="mft-lg"><span class="mft-ld l1"></span>Move <b>420</b><small>/ 500 cal</small></div>
        <div class="mft-lg"><span class="mft-ld l2"></span>Exercise <b>38</b><small>/ 45 min</small></div>
        <div class="mft-lg"><span class="mft-ld l3"></span>Stand <b>9</b><small>/ 12 hr</small></div>
      </div>
    </div>

    <div class="mft-stats">
      <div class="mft-stat"><span class="mft-sic s1">👣</span><b>8,240</b><small>Steps</small></div>
      <div class="mft-stat"><span class="mft-sic s2">🔥</span><b>612</b><small>Calories</small></div>
      <div class="mft-stat"><span class="mft-sic s3">📍</span><b>5.2</b><small>km</small></div>
    </div>

    <div class="mft-wk">
      <div class="mft-wkhd"><b>Workouts</b><button class="mft-see">History</button></div>
      <div class="mft-wo"><span class="mft-wic w1">🏃</span><div class="mft-wm"><b>Morning Run</b><small>32 min · 5.2 km · 412 cal</small></div><em>&#8250;</em></div>
      <div class="mft-wo"><span class="mft-wic w2">🚴</span><div class="mft-wm"><b>Cycling</b><small>45 min · 14 km · 380 cal</small></div><em>&#8250;</em></div>
      <div class="mft-wo"><span class="mft-wic w3">🧘</span><div class="mft-wm"><b>Yoga Flow</b><small>20 min · 95 cal</small></div><em>&#8250;</em></div>
    </div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Three SVG rings
Concentric arcs for move, exercise, and stand.
Dash-offset progress
Circumference-based fill, the wearable technique.
Fill-on-load animation
Double rAF sweeps rings up from empty.
Per-ring math
Each radius gets its own circumference.
Rotated start point
Arcs begin at twelve o'clock via -90deg rotate.
Stat grid
Steps, calories, and distance tiles.
Workout list
Tinted icons with duration and calorie details.
No dependency
Pure HTML, CSS, and vanilla JavaScript.

About this UI Snippet

Mobile Fitness Screen — Activity Rings UI

Screenshot of the Mobile Fitness Screen snippet rendered live

A fitness home screen leads with the activity rings — concentric progress arcs for move, exercise, and stand — then backs them with daily stats and a workout list. This snippet builds a complete one inside a CSS phone frame on a dark theme: three SVG rings that animate from empty to their day's progress on load, a three-up stat grid, and a tappable workout history — in HTML, CSS, and vanilla JavaScript with no charting dependency.

The three concentric rings

Each ring is an SVG <circle> with a grey track behind it and a colored arc on top. Progress is drawn with the classic stroke-dasharray / stroke-dashoffset technique: the dash length is set to the circle's full circumference (2πr), and the offset is reduced toward zero to reveal more of the arc. Each ring is rotated -90 degrees around its center so the arc starts at twelve o'clock, exactly like the rings you know from wearables.

Animating from empty on load

The rings start fully offset (empty), then a double requestAnimationFrame sets the target offset on the next frame so the browser registers a transition from empty to filled rather than jumping straight to the final state. The stroke-dashoffset transition uses a smooth easing curve over just over a second, so all three rings sweep up together when the screen appears — the satisfying "closing your rings" moment.

Per-ring circumference math

Because the three rings have different radii, each needs its own circumference for the dash math. The code stores each radius, computes 2πr per ring, and drives the fill from a percentage, so a ring at 84% lands at the right visual arc regardless of its size. This is the correct way to build nested progress rings — sharing one circumference would misalign the inner arcs.

Stats and workouts on a dark canvas

Below the rings, three stat tiles show steps, calories, and distance, and a workout list pairs a tinted activity icon with duration and calorie details. The dark theme uses near-black cards on a deep navy background so the vivid ring colors and stat numbers pop, which is the standard look for fitness apps.

Accessibility and performance

The rings are decorative SVG, so their meaning must live in text for non-visual users: the legend beside them already spells out each metric with its current and goal value, which is what a screen reader should read rather than the arcs themselves. When you adapt this, mark the SVG aria-hidden and expose the same numbers through the legend, or give each ring an aria-label describing its progress. The workout rows are real buttons, so the history is keyboard-operable. Performance is a highlight of the dash-offset technique: each ring animates a single stroke-dashoffset value that the browser can composite smoothly, with no per-frame JavaScript and no canvas redraws, so all three rings sweep up together without touching the main thread after the initial set. Because the fill is driven by CSS transition rather than a timer, it respects the user's reduced-motion preference if you gate it behind a media query. Feeding real percentages is a single style write per ring, so live updates stay cheap.

Reusing it

Feed each ring a real percentage from your health data, update the stats and workouts from your API, and wire the workout rows to detail screens. Lift the rings out of the phone frame for a responsive web dashboard, or keep them framed beside a set of activity rings to present a full tracker.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA dark fitness screen renders and the three activity rings animate up from empty.
  2. 2
    Watch the rings fillMove, exercise, and stand arcs sweep to their day's progress together on load.
  3. 3
    Read the legendEach ring's current and goal values sit beside a matching color dot.
  4. 4
    Scan the stat gridSteps, calories, and distance tiles summarize the day.
  5. 5
    Tap a workoutRows in the history list flash to acknowledge the tap.
  6. 6
    Bind your health dataSet each ring's percentage and update stats from your API.

Real-world uses

Common Use Cases

Fitness app home screens
Lead with three concentric SVG rings for move, exercise and stand, next to a set of activity rings for a wider dashboard.
Health stat tiles
Back the rings with a daily stat grid and pair with a user stats card for a more detailed profile view.
Goal tracking visuals
Reuse the ring technique like a progress circle with steps, where each radius gets its own circumference calculation.
Streak and habit features
Add a streak tracker beneath the rings, using a double requestAnimationFrame so the rings sweep up from empty on load.
Wearable-style prototypes
Show the dark activity screen in a phone mockup to present a wearable companion app concept to clients or investors.
Related: Mobile Biometric Unlock Screen
See the Mobile Biometric Unlock Screen for a related mobile pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Each ring is an SVG circle with a grey track behind it. The colored arc uses stroke-dasharray set to the circle's circumference (2 times pi times the radius) and stroke-dashoffset to hide part of it. Reducing the offset toward zero reveals more of the arc, and a -90 degree rotation makes it start at the top.

The rings start fully offset, then a double requestAnimationFrame sets the target offset on a later frame. That two-frame delay lets the browser register a transition between the empty and filled states rather than painting the final value immediately, so the CSS transition on stroke-dashoffset animates the sweep on load.

The three rings have different radii, so their circumferences differ. The dash math must use each ring's own 2-pi-r value; sharing one circumference would make the inner rings land at the wrong arc length for the same percentage. The code stores each radius and computes the length per ring.

Each ring is driven by a pct value between 0 and 1. The final offset is circumference times (1 minus pct), so 0.84 fills 84 percent of the arc. Replace the hard-coded percentages with values derived from your health data and the rings will fill to match.

Hold each ring's percentage in state and bind stroke-dashoffset to a computed offset. Trigger the fill-on-mount in a useEffect (React), onMounted (Vue), or ngAfterViewInit (Angular), setting the target after the first paint. Render stats and workouts from your data. Tailwind expresses the dark cards and layout with utilities while the SVG stays as-is.