High-fidelity HTML/CSS/JS design references for the tactical-CRPG direction — title, character creation, character sheet, inventory, world map, quest log, shop, pause/settings, dialogue, combat HUD, main exploration window. README is the handoff spec (visual system, per-screen state models, Godot translation notes). Not production code — the source of truth for the game's look + systems. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
205 lines
12 KiB
HTML
205 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<script src="./support.js"></script>
|
||
</head>
|
||
<body>
|
||
<x-dc>
|
||
<helmet data-dc-atomics>
|
||
<link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&display=swap" rel="stylesheet">
|
||
<style>
|
||
body{margin:0;background:#0f0d0b;font-family:system-ui,sans-serif}
|
||
a{color:#8f3a34}a:hover{color:#6f2b26}
|
||
input[type=range]{-webkit-appearance:none;appearance:none;width:230px;height:6px;border-radius:4px;background:#cbb684;outline:none;cursor:pointer}
|
||
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:20px;height:20px;border-radius:50%;background:#8f6a2a;border:2px solid #5a4326;cursor:pointer}
|
||
input[type=range]::-moz-range-thumb{width:18px;height:18px;border-radius:50%;background:#8f6a2a;border:2px solid #5a4326;cursor:pointer}
|
||
select{font-family:ui-monospace,monospace;font-size:13px;color:#3a2f1c;background:#f6efdd;border:1px solid #a99464;border-radius:8px;padding:9px 12px;outline:none;cursor:pointer;min-width:200px}
|
||
</style>
|
||
</helmet>
|
||
<div style="width:1920px;height:1080px;position:relative;display:flex;align-items:center;justify-content:center;background:repeating-linear-gradient(135deg,#26221c 0 16px,#221e18 16px 32px)">
|
||
<!-- dim -->
|
||
<div style="position:absolute;inset:0;background:rgba(8,6,4,.72);backdrop-filter:blur(2px)"></div>
|
||
|
||
<!-- panel -->
|
||
<div style="position:relative;width:1560px;height:920px;display:flex;border:8px solid #1a1512;border-radius:16px;box-shadow:0 30px 80px rgba(0,0,0,.6);overflow:hidden">
|
||
<div style="position:absolute;inset:8px;border:1.5px solid #a9843f;border-radius:9px;pointer-events:none;opacity:.5;z-index:2"></div>
|
||
|
||
<!-- ===== LEFT RAIL ===== -->
|
||
<div style="width:400px;flex:none;background:repeating-linear-gradient(135deg,#211d17 0 16px,#1d1914 16px 32px);box-sizing:border-box;padding:44px 38px;display:flex;flex-direction:column;position:relative">
|
||
<div style="position:absolute;inset:0;background:radial-gradient(120% 50% at 50% 0%,rgba(0,0,0,0) 50%,rgba(0,0,0,.5) 100%);pointer-events:none"></div>
|
||
<div style="position:relative">
|
||
<div style="font:13px ui-monospace,monospace;letter-spacing:.24em;color:#8a7f6c">THE WORLD WAITS</div>
|
||
<div style="font:600 54px 'Architects Daughter',cursive;color:#e8d9b6;line-height:1;margin-top:6px">Paused</div>
|
||
<div style="font:13px ui-monospace,monospace;letter-spacing:.06em;color:#a9843f;margin-top:10px">CHAPTER II · THE LOWER WARD</div>
|
||
</div>
|
||
<div style="position:relative;display:flex;flex-direction:column;gap:12px;margin-top:44px">
|
||
<div style="padding:16px 20px;border:1.5px solid #8f3a34;border-radius:10px;background:linear-gradient(180deg,#a5423a,#7a2f29);color:#f4e6cf;font:600 18px system-ui;letter-spacing:.04em;cursor:pointer" style-hover="background:linear-gradient(180deg,#b8524a,#8f3a34)">▸ Resume</div>
|
||
<div style="padding:15px 20px;border:1px solid #4a4238;border-radius:10px;background:rgba(30,26,20,.7);color:#d8ccb2;font:600 16px system-ui;cursor:pointer" style-hover="border-color:#a9843f">Save Game</div>
|
||
<div style="padding:15px 20px;border:1px solid #4a4238;border-radius:10px;background:rgba(30,26,20,.7);color:#d8ccb2;font:600 16px system-ui;cursor:pointer" style-hover="border-color:#a9843f">Load Game</div>
|
||
<div style="padding:15px 20px;border:1px solid #4a4238;border-radius:10px;background:rgba(30,26,20,.7);color:#caa39c;font:600 16px system-ui;cursor:pointer" style-hover="border-color:#8f3a34">Quit to Title</div>
|
||
</div>
|
||
<div style="position:relative;margin-top:auto;font:12px ui-monospace,monospace;line-height:1.9;color:#7a7060">
|
||
<div>PLAYTIME · <span style="color:#c9bda3">14:22:07</span></div>
|
||
<div>AUTOSAVED · <span style="color:#c9bda3">6 min ago</span></div>
|
||
<div style="color:#8f6a5a;margin-top:6px;font-family:'Architects Daughter',cursive;font-size:13px">Permadeath is on. Be careful out there.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== RIGHT: SETTINGS ===== -->
|
||
<div style="flex:1;min-width:0;background:linear-gradient(180deg,#ece2ca,#e4d8bd);box-sizing:border-box;padding:38px 46px;display:flex;flex-direction:column;gap:22px">
|
||
<div style="display:flex;align-items:flex-end;justify-content:space-between;border-bottom:2px solid #bfa878;padding-bottom:16px">
|
||
<div style="font:600 30px 'Architects Daughter',cursive;letter-spacing:.04em;color:#5a4326">Settings</div>
|
||
<div style="font:11px ui-monospace,monospace;color:#a99464;border:1px solid #cbb684;border-radius:6px;padding:6px 10px;cursor:pointer" style-hover="background:#e6d9b8">RESTORE DEFAULTS</div>
|
||
</div>
|
||
<!-- tabs -->
|
||
<div style="display:flex;gap:8px">
|
||
<sc-for list="{{ tabs }}" as="t" hint-placeholder-count="4">
|
||
<div onClick="{{ t.onSelect }}" style="font:12px ui-monospace,monospace;letter-spacing:.1em;padding:9px 18px;border-radius:8px;border:1px solid {{ t.border }};background:{{ t.bg }};color:{{ t.fg }};cursor:pointer">{{ t.label }}</div>
|
||
</sc-for>
|
||
</div>
|
||
<!-- controls -->
|
||
<div style="flex:1;overflow-y:auto;display:flex;flex-direction:column">
|
||
<sc-for list="{{ controls }}" as="c" hint-placeholder-count="6">
|
||
<div style="display:flex;align-items:center;gap:20px;padding:16px 4px;border-bottom:1px solid #d9cba6">
|
||
<div style="flex:1;min-width:0">
|
||
<div style="font:600 17px system-ui;color:#3a2f1c">{{ c.label }}</div>
|
||
<sc-if value="{{ c.hasDesc }}" hint-placeholder-val="{{ false }}"><div style="font:13px/1.4 Georgia,serif;font-style:italic;color:#7a684a;margin-top:2px">{{ c.desc }}</div></sc-if>
|
||
</div>
|
||
<div style="flex:none;display:flex;align-items:center;gap:14px">
|
||
<sc-if value="{{ c.isSlider }}" hint-placeholder-val="{{ false }}">
|
||
<span style="font:600 14px ui-monospace,monospace;color:#8f6a2a;width:52px;text-align:right">{{ c.valueLabel }}</span>
|
||
<input type="range" min="{{ c.min }}" max="{{ c.max }}" step="1" value="{{ c.def }}" onInput="{{ c.onChange }}" />
|
||
</sc-if>
|
||
<sc-if value="{{ c.isToggle }}" hint-placeholder-val="{{ false }}">
|
||
<div onClick="{{ c.onToggle }}" style="width:50px;height:28px;border-radius:15px;background:{{ c.trackBg }};border:1px solid #8a7748;position:relative;cursor:pointer;transition:background .15s"><div style="position:absolute;top:2px;left:2px;width:22px;height:22px;border-radius:50%;background:#f6efdd;box-shadow:0 1px 2px rgba(0,0,0,.3);transform:{{ c.knobShift }};transition:transform .15s"></div></div>
|
||
</sc-if>
|
||
<sc-if value="{{ c.isSelect }}" hint-placeholder-val="{{ false }}">
|
||
<select onChange="{{ c.onChange }}" value="{{ c.current }}">
|
||
<sc-for list="{{ c.options }}" as="o" hint-placeholder-count="3">
|
||
<option value="{{ o.value }}">{{ o.label }}</option>
|
||
</sc-for>
|
||
</select>
|
||
</sc-if>
|
||
<sc-if value="{{ c.isKeybind }}" hint-placeholder-val="{{ false }}">
|
||
<div style="display:flex;gap:6px">
|
||
<sc-for list="{{ c.keys }}" as="k" hint-placeholder-count="1">
|
||
<span style="font:600 13px ui-monospace,monospace;color:#4a3f2c;background:#f6efdd;border:1px solid #a99464;border-bottom-width:3px;border-radius:6px;padding:6px 11px;min-width:14px;text-align:center">{{ k }}</span>
|
||
</sc-for>
|
||
</div>
|
||
</sc-if>
|
||
</div>
|
||
</div>
|
||
</sc-for>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</x-dc>
|
||
<script type="text/x-dc" data-dc-script data-props="{"$preview":{"width":1920,"height":1080}}">
|
||
class Component extends DCLogic {
|
||
state = {
|
||
tab: 'gameplay',
|
||
s: {
|
||
master: 80, music: 60, sfx: 75, ambience: 50, subtitles: true,
|
||
windowMode: 'fullscreen', resolution: '1920x1080', brightness: 55, motionBlur: false, vsync: true, damageFlash: true,
|
||
difficulty: 'bastard', permadeath: true, tutorial: true, gore: true, textSpeed: 70, autosave: 'camp',
|
||
mouseSens: 50, invertY: false,
|
||
},
|
||
};
|
||
|
||
set(key, val) { this.setState(st => ({ s: { ...st.s, [key]: val } })); }
|
||
|
||
slider(key, label, desc) {
|
||
return {
|
||
isSlider: true, label, desc: desc || '', hasDesc: !!desc,
|
||
min: 0, max: 100, def: this.state.s[key],
|
||
valueLabel: this.state.s[key] + '%',
|
||
onChange: (e) => this.set(key, +e.target.value),
|
||
};
|
||
}
|
||
toggle(key, label, desc) {
|
||
const on = this.state.s[key];
|
||
return {
|
||
isToggle: true, label, desc: desc || '', hasDesc: !!desc,
|
||
trackBg: on ? '#8f6a2a' : '#c9b78a',
|
||
knobShift: on ? 'translateX(22px)' : 'translateX(0)',
|
||
onToggle: () => this.set(key, !on),
|
||
};
|
||
}
|
||
select(key, label, options, desc) {
|
||
return {
|
||
isSelect: true, label, desc: desc || '', hasDesc: !!desc,
|
||
current: this.state.s[key],
|
||
options: options.map(o => ({ value: o[0], label: o[1] })),
|
||
onChange: (e) => this.set(key, e.target.value),
|
||
};
|
||
}
|
||
keybind(label, keys) { return { isKeybind: true, label, hasDesc: false, keys }; }
|
||
|
||
renderVals() {
|
||
const tab = this.state.tab;
|
||
const tabs = [
|
||
{ id: 'gameplay', label: 'GAMEPLAY' },
|
||
{ id: 'video', label: 'VIDEO' },
|
||
{ id: 'audio', label: 'AUDIO' },
|
||
{ id: 'controls', label: 'CONTROLS' },
|
||
].map(t => ({
|
||
label: t.label,
|
||
onSelect: () => this.setState({ tab: t.id }),
|
||
bg: t.id === tab ? '#8f3a34' : '#efe6cf',
|
||
fg: t.id === tab ? '#f1e8d2' : '#6a5a3a',
|
||
border: t.id === tab ? '#8f3a34' : '#cbb684',
|
||
}));
|
||
|
||
let controls = [];
|
||
if (tab === 'gameplay') {
|
||
controls = [
|
||
this.select('difficulty', 'Difficulty', [['cutthroat', 'Cutthroat — story'], ['bastard', 'Bastard — normal'], ['nightmare', 'Nightmare — hard'], ['ironblood', 'Ironblood — brutal']]),
|
||
this.toggle('permadeath', 'Permadeath', 'When you die, the save dies with you.'),
|
||
this.toggle('gore', 'Gore & Wounds', 'Blood, infection, and the state bodies get into.'),
|
||
this.toggle('tutorial', 'Tutorial Hints', 'The DM explains the rules as you go.'),
|
||
this.slider('textSpeed', 'Narration Speed', 'How fast the DM writes the world.'),
|
||
this.select('autosave', 'Autosave', [['scene', 'Every scene'], ['camp', 'At camp only'], ['manual', 'Manual only']]),
|
||
];
|
||
} else if (tab === 'video') {
|
||
controls = [
|
||
this.select('windowMode', 'Window Mode', [['fullscreen', 'Fullscreen'], ['borderless', 'Borderless'], ['windowed', 'Windowed']]),
|
||
this.select('resolution', 'Resolution', [['2560x1440', '2560 × 1440'], ['1920x1080', '1920 × 1080'], ['1600x900', '1600 × 900'], ['1280x720', '1280 × 720']]),
|
||
this.slider('brightness', 'Brightness', 'The Margreave is meant to be dark. A little.'),
|
||
this.toggle('vsync', 'VSync', ''),
|
||
this.toggle('motionBlur', 'Motion Blur', ''),
|
||
this.toggle('damageFlash', 'Damage Flash', 'The screen reddens when you take a hit.'),
|
||
];
|
||
} else if (tab === 'audio') {
|
||
controls = [
|
||
this.slider('master', 'Master Volume', ''),
|
||
this.slider('music', 'Music', ''),
|
||
this.slider('sfx', 'Effects', ''),
|
||
this.slider('ambience', 'Ambience', 'Rain, rats, and distant screaming.'),
|
||
this.toggle('subtitles', 'Subtitles', 'Show the DM\u2019s narration as text.'),
|
||
];
|
||
} else {
|
||
controls = [
|
||
this.keybind('Move', ['W', 'A', 'S', 'D']),
|
||
this.keybind('Interact', ['E']),
|
||
this.keybind('Attack', ['LMB']),
|
||
this.keybind('End Turn', ['↵']),
|
||
this.keybind('Inventory', ['I']),
|
||
this.keybind('Character', ['C']),
|
||
this.keybind('Quest Log', ['J']),
|
||
this.keybind('World Map', ['M']),
|
||
this.slider('mouseSens', 'Mouse Sensitivity', ''),
|
||
this.toggle('invertY', 'Invert Y-Axis', ''),
|
||
];
|
||
}
|
||
|
||
return { tabs, controls };
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|