// Shared tokens, placeholders, and bits used across all three variations.
const SN_PALETTE = {
dirt: '#7B5A3A',
dirtDark: '#5A4229',
dirtLight: '#A07A4F',
grass: '#6B8E3D',
grassDark: '#4E6B28',
grassLight: '#8FB05A',
stone: '#8A8A8A',
stoneDark: '#5F5F5F',
stoneLight: '#B5B5B5',
parchment: '#F3ECDC',
parchmentDark: '#E4D9BD',
ink: '#1F1A14',
linkBlue: '#0000EE',
linkVisited: '#551A8B',
};
// Tiled block background as an SVG data URI — 32x32 tile.
// Slight per-cell variation to avoid grid pattern readability.
function tiledBlockBg(top, mid, dark, size = 32) {
const svg = ``;
return `url("data:image/svg+xml;utf8,${encodeURIComponent(svg)}")`;
}
// A "grass top" strip bg — darker dirt below a lighter grass cap.
function grassTopBg() {
const svg = ``;
return `url("data:image/svg+xml;utf8,${encodeURIComponent(svg)}")`;
}
// Diagonal-stripe placeholder SVG w/ a mono label centered.
function StripedPlaceholder({ w = 320, h = 180, label = 'screenshot', tone = 'dirt' }) {
const stroke = tone === 'stone' ? '#6a6a6a' : tone === 'grass' ? '#4E6B28' : '#5A4229';
const fill = tone === 'stone' ? '#c5c5c2' : tone === 'grass' ? '#a8c073' : '#cdb48a';
const fg = tone === 'stone' ? '#2a2a2a' : '#2a1f12';
const svg = (
);
return svg;
}
// Tiny pixel logo: spells "SN" in blocks. Uses a grid of colored pixels.
function PixelLogoSN({ scale = 6, top = SN_PALETTE.grass, side = SN_PALETTE.grassDark, base = SN_PALETTE.dirt, shadow = SN_PALETTE.dirtDark }) {
// 2-color block lettering on a 15x7 grid. S then N.
const S = [
'.###.',
'#....',
'#....',
'.##..',
'...#.',
'....#',
'###..',
];
const N = [
'#...#',
'##..#',
'#.#.#',
'#..##',
'#...#',
'#...#',
'#...#',
];
const rows = 7, cols = 11; // 5 + 1 gap + 5
const grid = [];
for (let r = 0; r < rows; r++) {
let line = '';
for (let c = 0; c < 5; c++) line += S[r][c];
line += '.';
for (let c = 0; c < 5; c++) line += N[r][c];
grid.push(line);
}
return (
{grid.map((row, r) => (
{row.split('').map((ch, c) => {
const on = ch === '#';
const bg = on ? (r === 0 ? top : r === rows - 1 ? shadow : side) : 'transparent';
return
;
})}
))}
);
}
// A rendered 3D-ish isometric "grass block" drawn with CSS/SVG pixels.
function PixelGrassBlock({ size = 64 }) {
const p = size / 16;
const svg = ``;
return
;
}
// A simple day/night toggle — pixel sun/moon with 2 states.
function DayNightToggle({ night, onToggle, scale = 3 }) {
// 12x12 pixel sun/moon
const sun = [
'....####....',
'...#....#...',
'..#......#..',
'.#........#.',
'#..........#',
'#..........#',
'#..........#',
'#..........#',
'.#........#.',
'..#......#..',
'...#....#...',
'....####....',
];
const moon = [
'....####....',
'...#....#...',
'..#..##...#.',
'.#..####..#.',
'#..####....#',
'#..####....#',
'#..####....#',
'#..####....#',
'.#..####..#.',
'..#..##...#.',
'...#....#...',
'....####....',
];
const art = night ? moon : sun;
const fill = night ? '#e6e2c7' : '#e8c24a';
const stroke = night ? '#7a7560' : '#8a6a1e';
return (
);
}
// Pixel cursor trail — spawns small colored squares that fade.
// Activated inside a container via onMouseMove.
function useCursorTrail(containerRef, { enabled = true, color = '#6B8E3D' } = {}) {
React.useEffect(() => {
if (!enabled) return;
const el = containerRef.current;
if (!el) return;
let last = 0;
const onMove = (e) => {
const now = performance.now();
if (now - last < 28) return;
last = now;
const r = el.getBoundingClientRect();
const x = e.clientX - r.left;
const y = e.clientY - r.top;
const dot = document.createElement('div');
dot.style.position = 'absolute';
dot.style.left = (x - 3) + 'px';
dot.style.top = (y - 3) + 'px';
dot.style.width = '6px';
dot.style.height = '6px';
dot.style.background = color;
dot.style.pointerEvents = 'none';
dot.style.opacity = '1';
dot.style.transition = 'opacity 600ms linear, transform 600ms linear';
dot.style.zIndex = '999';
el.appendChild(dot);
requestAnimationFrame(() => {
dot.style.opacity = '0';
dot.style.transform = `translate(${(Math.random()-0.5)*6}px, ${6 + Math.random()*8}px)`;
});
setTimeout(() => { dot.remove(); }, 650);
};
el.addEventListener('mousemove', onMove);
return () => el.removeEventListener('mousemove', onMove);
}, [containerRef, enabled, color]);
}
Object.assign(window, {
SN_PALETTE, tiledBlockBg, grassTopBg,
StripedPlaceholder, PixelLogoSN, PixelGrassBlock,
DayNightToggle, useCursorTrail,
});