add tutorial overlay component with tips for new players

Shows 4 gameplay tips (return foot to center, stay in one spot,
face camera, stay fully visible). Stores dismissal in localStorage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
main
name 2026-04-12 08:59:30 -07:00
parent e4076c790b
commit a5da08ce3a
2 changed files with 149 additions and 0 deletions

View File

@ -0,0 +1,105 @@
.overlay {
position: fixed;
inset: 0;
z-index: 600;
background: rgba(0, 0, 0, 0.92);
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
}
.card {
max-width: 400px;
width: 100%;
padding: 32px 28px;
border-radius: 16px;
background: rgba(217, 70, 239, 0.08);
border: 2px solid rgba(217, 70, 239, 0.3);
}
.title {
font-size: 36px;
color: #d946ef;
text-align: center;
margin: 0 0 24px;
text-shadow: 0 0 30px rgba(217, 70, 239, 0.5);
}
.tips {
list-style: none;
counter-reset: tip;
padding: 0;
margin: 0 0 28px;
display: flex;
flex-direction: column;
gap: 16px;
}
.tips li {
counter-increment: tip;
position: relative;
padding-left: 32px;
color: rgba(255, 255, 255, 0.9);
font-size: 16px;
line-height: 1.5;
}
.tips li::before {
content: counter(tip);
position: absolute;
left: 0;
top: 0;
width: 22px;
height: 22px;
border-radius: 50%;
background: rgba(217, 70, 239, 0.25);
color: #d946ef;
font-size: 13px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
}
.dismissBtn {
display: block;
width: 100%;
padding: 14px 0;
border: 2px solid #d946ef;
border-radius: 8px;
background: rgba(217, 70, 239, 0.2);
color: #fff;
font-size: 20px;
cursor: pointer;
transition: background 0.15s, transform 0.1s;
}
.dismissBtn:hover {
background: rgba(217, 70, 239, 0.4);
}
.dismissBtn:active {
transform: scale(0.97);
}
@media (max-width: 480px) {
.card {
padding: 24px 20px;
}
.title {
font-size: 28px;
margin-bottom: 20px;
}
.tips li {
font-size: 15px;
}
.tips {
gap: 14px;
margin-bottom: 24px;
}
}

View File

@ -0,0 +1,44 @@
import { useState } from 'react'
import { playSound, SFX_BUTTON_DEFAULT } from '../lib/sounds'
import styles from './Tutorial.module.css'
const TUTORIAL_SEEN_KEY = 'tutorialSeen'
export function useTutorialSeen() {
const [seen, setSeen] = useState(
() => localStorage.getItem(TUTORIAL_SEEN_KEY) === 'true',
)
const markSeen = () => {
localStorage.setItem(TUTORIAL_SEEN_KEY, 'true')
setSeen(true)
}
return { seen, markSeen }
}
export function Tutorial({ onDismiss }: { onDismiss: () => void }) {
return (
<div className={styles.overlay}>
<div className={styles.card}>
<h2 className={`${styles.title} font-title`}>TIPS</h2>
<ol className={styles.tips}>
<li>Bring your foot fully back to center after each step</li>
<li>Try to stay in one spot</li>
<li>Face the camera with your body</li>
<li>
Stand so you're fully visible your body should be above the red
line, and keep your head in frame too
</li>
</ol>
<button
className={`${styles.dismissBtn} font-title`}
onClick={() => {
playSound(SFX_BUTTON_DEFAULT)
onDismiss()
}}
>
GOT IT
</button>
</div>
</div>
)
}