logo
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>SoftLAN Launcher — Live Logo</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">
|
||||
<style>
|
||||
:root { --accent: #3b82f6; }
|
||||
html, body { margin: 0; padding: 0; height: 100%; }
|
||||
body {
|
||||
background: radial-gradient(ellipse 90% 70% at 50% 0%, #141c26 0%, #0a0e13 60%, #070a0e 100%);
|
||||
color: #e6edf3;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
min-height: 100%;
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
gap: 34px; padding: 30px 24px 60px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* in-context launcher top bar */
|
||||
.topbar {
|
||||
width: min(960px, 100%);
|
||||
height: 58px; border-radius: 12px;
|
||||
background: linear-gradient(180deg, #161d27 0%, #10151c 100%);
|
||||
border: 1px solid rgba(255,255,255,0.07);
|
||||
box-shadow: 0 12px 40px -16px rgba(0,0,0,0.8), inset 0 1px 0 rgba(255,255,255,0.05);
|
||||
display: flex; align-items: center; gap: 22px; padding: 0 18px;
|
||||
}
|
||||
.brand { display: flex; align-items: center; gap: 11px; }
|
||||
.brand .wm { font-size: 19px; font-weight: 700; letter-spacing: -0.01em; line-height: 1; }
|
||||
.brand .wm b { color: var(--accent); font-weight: 700; }
|
||||
.nav { display: flex; align-items: center; gap: 4px; margin-left: 8px; }
|
||||
.nav a {
|
||||
font-size: 12.5px; font-weight: 600; letter-spacing: 0.02em; color: #8b97a6;
|
||||
padding: 7px 12px; border-radius: 7px; text-decoration: none;
|
||||
}
|
||||
.nav a.on { color: #fff; background: rgba(255,255,255,0.07); }
|
||||
.topbar .spacer { flex: 1; }
|
||||
.pill {
|
||||
font-size: 11.5px; font-weight: 700; letter-spacing: 0.04em; color: #9fe6c4;
|
||||
background: rgba(33,227,168,0.12); border: 1px solid rgba(33,227,168,0.25);
|
||||
padding: 6px 11px; border-radius: 999px;
|
||||
}
|
||||
|
||||
/* hero stage */
|
||||
.stage {
|
||||
width: min(960px, 100%);
|
||||
border-radius: 20px;
|
||||
background:
|
||||
radial-gradient(ellipse 60% 55% at 50% 42%, color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 70%),
|
||||
linear-gradient(180deg, #0e141b 0%, #0a0e13 100%);
|
||||
border: 1px solid rgba(255,255,255,0.07);
|
||||
padding: 56px 32px 48px; display: flex; flex-direction: column; align-items: center; gap: 30px;
|
||||
}
|
||||
.hero-mark {
|
||||
width: 220px; height: 220px; display: grid; place-items: center;
|
||||
filter: drop-shadow(0 10px 30px color-mix(in srgb, var(--accent) 45%, transparent));
|
||||
}
|
||||
.hint { font-size: 13px; color: #6b7785; letter-spacing: 0.02em; }
|
||||
.hint b { color: #9aa6b4; font-weight: 600; }
|
||||
|
||||
.controls { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
|
||||
.controls button {
|
||||
font: inherit; font-size: 13px; font-weight: 600; letter-spacing: 0.01em;
|
||||
color: #cdd6e0; background: rgba(255,255,255,0.05);
|
||||
border: 1px solid rgba(255,255,255,0.1); border-radius: 9px;
|
||||
padding: 10px 16px; cursor: pointer; transition: background .15s, border-color .15s, transform .05s;
|
||||
}
|
||||
.controls button:hover { background: rgba(255,255,255,0.09); border-color: rgba(255,255,255,0.2); }
|
||||
.controls button:active { transform: translateY(1px); }
|
||||
.controls button.primary {
|
||||
color: #fff; background: color-mix(in srgb, var(--accent) 88%, black);
|
||||
border-color: color-mix(in srgb, var(--accent) 70%, white);
|
||||
}
|
||||
.controls button.primary:hover { background: var(--accent); }
|
||||
|
||||
.caption { max-width: 640px; text-align: center; font-size: 14px; line-height: 1.6; color: #8b97a6; text-wrap: pretty; }
|
||||
.caption .k { color: #cdd6e0; font-weight: 600; }
|
||||
h1 { margin: 0; font-family: 'Bebas Neue', sans-serif; font-weight: 400; font-size: 30px; letter-spacing: 0.03em; color: #e6edf3; }
|
||||
.swatches { display: flex; gap: 8px; align-items: center; margin-top: 2px; }
|
||||
.swatches span { font-size: 12px; color: #6b7785; margin-right: 4px; }
|
||||
.swatches button { width: 22px; height: 22px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.18); cursor: pointer; padding: 0; }
|
||||
</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="pixel-live.jsx"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script type="text/babel">
|
||||
const { useRef, useState } = React;
|
||||
|
||||
function App() {
|
||||
const [accent, setAccent] = useState('#3b82f6');
|
||||
const heroRef = useRef(null);
|
||||
const barRef = useRef(null);
|
||||
const playBoth = (trick) => { heroRef.current && heroRef.current.play(trick); barRef.current && barRef.current.play(trick); };
|
||||
const setAcc = (c) => { setAccent(c); document.documentElement.style.setProperty('--accent', c); };
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* in-context: the launcher top bar */}
|
||||
<div className="topbar">
|
||||
<div className="brand">
|
||||
<LiveLogo ref={barRef} accent={accent} size={30} idleAuto={true} />
|
||||
<span className="wm">Soft<b>LAN</b></span>
|
||||
</div>
|
||||
<nav className="nav">
|
||||
<a className="on">Library</a>
|
||||
<a>Store</a>
|
||||
<a>Downloads</a>
|
||||
<a>Servers</a>
|
||||
</nav>
|
||||
<span className="spacer" style={{ flex: 1 }}></span>
|
||||
<span className="pill">3 PEERS ONLINE</span>
|
||||
</div>
|
||||
|
||||
{/* hero stage */}
|
||||
<div className="stage">
|
||||
<h1>The mark, alive</h1>
|
||||
<div className="hero-mark">
|
||||
<LiveLogo ref={heroRef} accent={accent} size={190} idleAuto={true} />
|
||||
</div>
|
||||
<div className="hint"><b>Hover the mark</b> — or just wait. It comes alive on its own every few seconds.</div>
|
||||
|
||||
<div className="controls">
|
||||
<button className="primary" onClick={() => playBoth('snake')}>▶ Snake</button>
|
||||
<button onClick={() => playBoth('rgb')}>RGB datamosh</button>
|
||||
<button onClick={() => playBoth('glitch')}>Signal glitch</button>
|
||||
</div>
|
||||
|
||||
<div className="swatches">
|
||||
<span>Accent</span>
|
||||
{['#3b82f6','#21e3a8','#f59e0b','#ec4899','#a855f7'].map(c => (
|
||||
<button key={c} style={{ background: c }} onClick={() => setAcc(c)} title={c}></button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="caption">
|
||||
At rest it's the baseline pixel <span className="k">S</span>. The <span className="k">Snake</span> trick dissolves
|
||||
the letter into a single segment that slithers across the 5×5 board — a nod to the LAN-party era — then re-lays
|
||||
itself back into the S. <span className="k">RGB datamosh</span> and <span className="k">Signal glitch</span> are
|
||||
shorter flickers for a quicker hit. Both the big mark and the one in the top bar are the same live component.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user