fix(ux): apply UX quick wins (#54)

Get started links to registration and says an admin approves accounts.
Back navigation restores the library scroll; search no longer fills history.
Steam and PSN unlink ask first. Any close marks news as seen.
Confetti plays only when the level rises on the page.
Count-ups respect reduced motion. Html lang follows the UI language.
Add a 404 page, translated role and dialog close label.
This commit is contained in:
lobo committed 2026-10-21 08:21:39 +02:00
1 parent 60dcfe8992
commit a60010e055
28 files changed
+427 -58

No files matched your search

+7 -5
View File
@@ -2,14 +2,15 @@ import {useEffect, useState} from 'react';
import {useTranslation} from 'react-i18next';
import {Card} from '@/components/ui/card';
import useReducedMotion from '../hooks/useReducedMotion.js';
import {useDrillTarget} from './stats/StatDrill.jsx';
function useCountUp(target, durationMs = 1600) {
function useCountUp(target, instant, durationMs = 1600) {
const [value, setValue] = useState(0);
useEffect(() => {
const safeTarget = typeof target === 'number' && Number.isFinite(target) ? target : 0;
if (safeTarget === 0) {
setValue(0);
if (instant || safeTarget === 0) {
setValue(safeTarget);
return;
}
let raf;
@@ -22,7 +23,7 @@ function useCountUp(target, durationMs = 1600) {
};
raf = requestAnimationFrame(step);
return () => cancelAnimationFrame(raf);
}, [target, durationMs]);
}, [target, instant, durationMs]);
return value;
}
@@ -32,7 +33,8 @@ function useCountUp(target, durationMs = 1600) {
*/
export default function StatCard({label, value, icon: Icon, suffix, drill, onClick}) {
const {i18n} = useTranslation();
const animated = useCountUp(value);
const reduce = useReducedMotion();
const animated = useCountUp(value, reduce);
const target = useDrillTarget({title: label, count: value, ...drill}) ?? (onClick && {onClick});
const card = (
<Card className="gap-3 p-5">