feat(levels): redesign slider with abacus-themed beads

Replaced standard slider with custom abacus-themed design:
- Added bead-like circular ticks for all 40 levels
- Color-coded beads (green for Kyu, violet for Dan)
- Active bead glows and scales up (16px) with shadow effect
- Inactive beads are semi-transparent (12px)
- Increased hit target with vertical padding (py: '6')
- Added horizontal "reckoning bar" as the track
- Smooth transitions on bead state changes

This creates a more forgiving hover/drag experience and prevents
confusion from minor cursor deviations while interacting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-10-20 09:13:16 -05:00
parent 3b6284ae18
commit f3dce84532

View File

@@ -300,19 +300,77 @@ export default function LevelsPage() {
)}
</div>
{/* Range Slider with hover support */}
{/* Range Slider with hover support - Abacus themed */}
<div className={css({ mb: '6', px: { base: '2', md: '8' } })}>
<div className={css({ mb: '3', textAlign: 'center' })}>
<p className={css({ fontSize: 'sm', color: 'gray.400' })}>
Hover, drag, or touch the slider to explore all levels
Hover, drag, or touch the beads to explore all levels
</p>
</div>
<div
onMouseMove={handleSliderHover}
onTouchMove={handleSliderHover}
className={css({ position: 'relative' })}
className={css({
position: 'relative',
py: '6',
cursor: 'pointer',
})}
>
{/* Reckoning Bar (Track) */}
<div
className={css({
position: 'absolute',
top: '50%',
left: 0,
right: 0,
h: '3px',
bg: 'rgba(255, 255, 255, 0.2)',
transform: 'translateY(-50%)',
rounded: 'full',
})}
/>
{/* Beads (Tick Marks) */}
{allLevels.map((level, index) => {
const isActive = index === currentIndex
const isDan = 'color' in level && level.color === 'violet'
const beadColor = isActive
? isDan
? 'violet.400'
: 'green.400'
: isDan
? 'rgba(139, 92, 246, 0.4)'
: 'rgba(34, 197, 94, 0.4)'
return (
<div
key={index}
className={css({
position: 'absolute',
top: '50%',
left: `${(index / (allLevels.length - 1)) * 100}%`,
transform: 'translate(-50%, -50%)',
w: isActive ? '16px' : '12px',
h: isActive ? '16px' : '12px',
bg: beadColor,
rounded: 'full',
border: '2px solid',
borderColor: isActive
? isDan
? 'violet.200'
: 'green.200'
: 'rgba(255, 255, 255, 0.3)',
transition: 'all 0.2s',
boxShadow: isActive
? `0 0 12px ${isDan ? 'rgba(139, 92, 246, 0.6)' : 'rgba(34, 197, 94, 0.6)'}`
: 'none',
})}
/>
)
})}
{/* Invisible input for accessibility and native slider behavior */}
<input
ref={sliderRef}
type="range"
@@ -321,11 +379,12 @@ export default function LevelsPage() {
value={currentIndex}
onChange={(e) => setCurrentIndex(Number(e.target.value))}
className={css({
position: 'absolute',
top: 0,
left: 0,
w: '100%',
h: '2',
bg: 'gray.700',
rounded: 'full',
outline: 'none',
h: '100%',
opacity: 0,
cursor: 'pointer',
})}
/>
@@ -336,7 +395,7 @@ export default function LevelsPage() {
className={css({
display: 'flex',
justifyContent: 'space-between',
mt: '3',
mt: '1',
fontSize: 'xs',
color: 'gray.500',
})}