frontend: standardise metric colours + crisp activity icons platform-wide
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 8s

Colours (Health, Dashboard, Activities, ActivityDetail, Profile):
- Heart rate (resting/avg/24h/intraday + activity HR metric) → red
- Sleep stat text → light-sleep violet; unify dashboard sleep-stage palette
  to the Health-page hypnogram colours
- VO2 max graph + stat coloured by its gauge rating category (shared utils/vo2.js)
- Stress → orange; Steps stay yellow; Weight → blue
- Running → green, Cycling → orange (sportColor)

Icons: new stroke-based SVG SportIcon component replaces emoji everywhere
(lists, filters, activity detail, body-battery overlays, YTD stats) — crisp
at any resolution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 22:31:23 +01:00
co-authored by Claude Opus 4.8
parent a0912fd09a
commit 0e1d35364f
10 changed files with 234 additions and 115 deletions
+16 -8
View File
@@ -5,9 +5,10 @@ import { format } from 'date-fns'
import api from '../utils/api'
import {
formatDuration, formatDistance, formatPace, formatHeartRate, formatElevation,
formatDate, sportIcon, sportColor, convertKm, distanceUnitLabel,
formatDate, sportColor, convertKm, distanceUnitLabel,
} from '../utils/format'
import { useUnit } from '../hooks/useUnits'
import SportIcon from '../components/ui/SportIcon'
const SPORTS = ['all', 'running', 'cycling', 'hiking', 'walking']
@@ -59,10 +60,16 @@ export default function ActivitiesPage() {
{ytdStats && (
<div className="flex flex-wrap gap-x-4 gap-y-1 mb-4 text-sm">
{ytdStats.running_km > 0 && (
<span className="text-blue-400">🏃 {convertKm(ytdStats.running_km, unit).toFixed(0)} {distLabel} this year</span>
<span className="inline-flex items-center gap-1.5" style={{ color: sportColor('running') }}>
<SportIcon sport="running" size={15} color="currentColor" />
{convertKm(ytdStats.running_km, unit).toFixed(0)} {distLabel} this year
</span>
)}
{ytdStats.cycling_km > 0 && (
<span className="text-orange-400">🚴 {convertKm(ytdStats.cycling_km, unit).toFixed(0)} {distLabel} this year</span>
<span className="inline-flex items-center gap-1.5" style={{ color: sportColor('cycling') }}>
<SportIcon sport="cycling" size={15} color="currentColor" />
{convertKm(ytdStats.cycling_km, unit).toFixed(0)} {distLabel} this year
</span>
)}
</div>
)}
@@ -83,13 +90,14 @@ export default function ActivitiesPage() {
<button
key={s}
onClick={() => { setSport(s); setPage(1) }}
className={`capitalize text-sm px-3 py-1.5 rounded-full border transition-colors ${
className={`capitalize text-sm px-3 py-1.5 rounded-full border transition-colors inline-flex items-center gap-1.5 ${
sport === s
? 'bg-blue-600 border-blue-600 text-white'
: 'border-gray-700 text-gray-400 hover:text-white hover:border-gray-500'
}`}
>
{s === 'all' ? 'All' : `${sportIcon(s)} ${s}`}
{s !== 'all' && <SportIcon sport={s} size={15} color="currentColor" />}
{s === 'all' ? 'All' : s}
</button>
))}
</div>
@@ -107,10 +115,10 @@ export default function ActivitiesPage() {
>
{/* Sport indicator */}
<div
className="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 text-lg"
className="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: sportColor(activity.sport_type) + '22' }}
>
{sportIcon(activity.sport_type)}
<SportIcon sport={activity.sport_type} size={20} color={sportColor(activity.sport_type)} />
</div>
{/* Name + date */}
@@ -160,7 +168,7 @@ export default function ActivitiesPage() {
{activities?.length === 0 && (
<div className="text-center py-16 text-gray-600">
<p className="text-4xl mb-3">🏃</p>
<SportIcon sport="running" size={44} color="currentColor" className="mx-auto mb-3" />
<p className="text-lg">No activities yet</p>
<p className="text-sm mt-1">
<Link to="/upload" className="text-blue-400 hover:underline">Import your Garmin or Strava data</Link> to get started