Files
lemonade/src/SkinProvider.tsx
T
jcoffey-dev 7fcb863771 Remaster: cel-shaded skin, a crowd that tracks trade, and a funk band
Two skins over one game. 1979 is the machine as it was; the remaster is
the same rules in cel-shaded vector art with a modern palette. The
simulation is untouched - only the paint differs - and the toggle sits
under the set.

The street is now driven by the game. Before prices are in, the number
of people walking on comes from the forecast; afterwards it comes from
the glasses that actually sold, so the report shows the crowd you
earned. A heat wave has them fanning themselves; a downpour leaves one
figure hurrying past under an umbrella.

Selling no longer snaps straight to the books. The stand trades for a
few seconds first, tally climbing and till filling, which is where that
crowd animation pays off. Skippable.

The synth grew a kit (pitched-sine kick, snare with body, hats), a
sweeping resonant lowpass for the bass, chords and a shuffle - enough
for a funk band. The chiptunes stay for the 1979 skin.

Boot is now a cassette load, which doubles as the gesture browsers
require before audio will play.

Fixes found on the way: the picture scaled about its centre while flex
auto-margins collapsed to zero on overflow, which pushed the report's
buttons off the bottom; and the validation line moved the button when
it appeared, so it is now reserved and faded. Sibling tabs in one
browser also keep their score tables in step.
2026-09-08 15:59:52 -07:00

33 lines
1.1 KiB
TypeScript

import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'
import { SKIN_KEY, SkinContext, readSkin, writeSkin, type Skin, type SkinApi } from './skin'
export function SkinProvider({ children }: { children: ReactNode }) {
const [skin, setSkinState] = useState<Skin>(readSkin)
const setSkin = useCallback((s: Skin) => {
setSkinState(s)
writeSkin(s)
}, [])
// The attribute lives on <html> so the page background can follow the skin.
useEffect(() => {
document.documentElement.dataset.skin = skin
}, [skin])
// Keep sibling tabs in the same browser in step.
useEffect(() => {
const onStorage = (e: StorageEvent) => {
if (e.key === SKIN_KEY || e.key === null) setSkinState(readSkin())
}
window.addEventListener('storage', onStorage)
return () => window.removeEventListener('storage', onStorage)
}, [])
const value = useMemo<SkinApi>(
() => ({ skin, setSkin, toggle: () => setSkin(skin === 'crt' ? 'modern' : 'crt') }),
[skin, setSkin],
)
return <SkinContext value={value}>{children}</SkinContext>
}