docs(design): add SoftLAN launcher redesign handoff and references
Add the `design/` directory containing the design handoff document and
HTML/React reference prototypes for the planned Steam-inspired redesign
of the launcher UI.
Contents:
- `design/README.md` — handoff spec. Defines screens (main library,
game detail overlay, in-app Settings dialog), the game card anatomy,
interaction behavior, transitions, state shape, design tokens
(colors, typography, spacing, shadows) and out-of-scope items.
Selects layout variant A (single-row top bar) as the primary
direction. High-fidelity: colors / typography / spacing / animations
are decided, pixel-fidelity to the mock is the goal.
- `design/design_reference/` — Babel-in-browser React prototypes built
to communicate intended look and behavior. Includes:
* `SoftLAN Launcher.html` — entry that wires React + Babel and
mounts the design canvas with all variants side-by-side.
* `styles.css` — full visual spec as CSS custom properties + named
component classes (`.topbar`, `.seg`, `.card`, `.modal`, etc.).
* `data.jsx` — mock game catalog plus filter/sort helpers and a
mock STORAGE record used by the storage meter.
* `components.jsx` — reusable building blocks (Icon set, GameCover
placeholder generator, StateChip, ActionButton, GameCard,
SegmentedFilters, UnderlineFilters, SearchField, SortMenu,
StorageMeter, DirectoryButton, KebabMenu, GameDetailModal,
SettingsDialog).
* `launcher.jsx` — composes top bar + grid + modals into a complete
launcher screen, in both `single`-row and `two`-row chrome
variants.
These files are reference material, not production code. They are not
imported by the Vite/Tauri build and ship outside the frontend crate
(`crates/lanspread-tauri-deno-ts/`). They are committed so the design
intent is reviewable in-repo and surviving across implementations.
The accompanying production implementation against this spec is in
follow-up commits.
Trailer
-------
Refs: design/README.md (canonical handoff)
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>SoftLAN Launcher — Redesign</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; background: #f0eee9; height: 100%; }
|
||||
/* Tighten canvas chrome a hair */
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; }
|
||||
#root { width: 100%; height: 100%; }
|
||||
</style>
|
||||
|
||||
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
||||
|
||||
<script type="text/babel" src="design-canvas.jsx"></script>
|
||||
<script type="text/babel" src="tweaks-panel.jsx"></script>
|
||||
<script type="text/babel" src="data.jsx"></script>
|
||||
<script type="text/babel" src="components.jsx"></script>
|
||||
<script type="text/babel" src="launcher.jsx"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script type="text/babel">
|
||||
const { useState } = React;
|
||||
|
||||
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
|
||||
"accent": "#3b82f6",
|
||||
"density": "normal",
|
||||
"aspect": "square",
|
||||
"bg": "gradient"
|
||||
}/*EDITMODE-END*/;
|
||||
|
||||
const ACCENTS = ['#3b82f6', '#22d3ee', '#a855f7', '#22c55e', '#f59e0b', '#ef4444'];
|
||||
|
||||
function App() {
|
||||
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
|
||||
const heroGame = GAMES.find(g => g.id === 'ra3'); // installed → modal shows Play + Uninstall
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DesignCanvas>
|
||||
<DCSection id="chrome" title="Chrome variations"
|
||||
subtitle="Two ways to organize the top bar — pick whichever density of controls you prefer. Click any card to open the detail overlay, or the kebab menu to open Settings.">
|
||||
<DCArtboard id="single-row" label="A · Single-row + segmented filters" width={1340} height={840}>
|
||||
<Launcher variant="single" tweaks={t} setTweak={setTweak}
|
||||
initialFilter="all" initialSort="recent"/>
|
||||
</DCArtboard>
|
||||
|
||||
<DCArtboard id="two-row" label="B · Two-row + underlined tabs" width={1340} height={840}>
|
||||
<Launcher variant="two" tweaks={t} setTweak={setTweak}
|
||||
initialFilter="installed" initialSort="az"/>
|
||||
</DCArtboard>
|
||||
</DCSection>
|
||||
|
||||
<DCSection id="detail" title="Game detail overlay"
|
||||
subtitle="Opens when you click a card. Full description, metadata, primary action + secondary actions (incl. uninstall).">
|
||||
<DCArtboard id="detail-modal" label="C · Detail overlay (installed game)" width={1340} height={840}>
|
||||
<Launcher variant="single" tweaks={t} setTweak={setTweak}
|
||||
initialFilter="installed" initialSort="az"
|
||||
initialOpenGame={heroGame}/>
|
||||
</DCArtboard>
|
||||
<DCArtboard id="detail-modal-local" label="D · Detail overlay (downloaded, not installed)" width={1340} height={840}>
|
||||
<Launcher variant="single" tweaks={t} setTweak={setTweak}
|
||||
initialFilter="local" initialSort="az"
|
||||
initialOpenGame={GAMES.find(g => g.id === 'cod4mw')}/>
|
||||
</DCArtboard>
|
||||
</DCSection>
|
||||
|
||||
<DCSection id="settings" title="Settings dialog"
|
||||
subtitle="Same controls as the dev Tweaks panel, surfaced as an in-app preferences dialog. Open via top-bar menu → Settings.">
|
||||
<DCArtboard id="settings-open" label="E · Settings dialog (open)" width={1340} height={840}>
|
||||
<Launcher variant="single" tweaks={t} setTweak={setTweak}
|
||||
initialFilter="all" initialSort="recent"
|
||||
initialSettingsOpen={true}/>
|
||||
</DCArtboard>
|
||||
</DCSection>
|
||||
</DesignCanvas>
|
||||
|
||||
<TweaksPanel>
|
||||
<TweakSection label="Theme"/>
|
||||
<TweakColor label="Accent" value={t.accent} options={ACCENTS}
|
||||
onChange={(v) => setTweak('accent', v)}/>
|
||||
<TweakRadio label="Background" value={t.bg}
|
||||
options={['flat', 'gradient', 'animated']}
|
||||
onChange={(v) => setTweak('bg', v)}/>
|
||||
|
||||
<TweakSection label="Grid"/>
|
||||
<TweakRadio label="Density" value={t.density}
|
||||
options={['compact', 'normal', 'large']}
|
||||
onChange={(v) => setTweak('density', v)}/>
|
||||
<TweakRadio label="Cover aspect" value={t.aspect}
|
||||
options={['box', 'square', 'banner']}
|
||||
onChange={(v) => setTweak('aspect', v)}/>
|
||||
</TweaksPanel>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user