generated from nhcarrigan/template
feat: mode bar, goddess tab row, themed resource dropdown (chunk 5)
Add Mortal/Goddess/Vampire mode selector bar, dynamic second tab row that swaps per mode, goddess currencies in the resource bar dropdown (locked pre-apotheosis), full CSS goddess theme with 300ms fade transition, and localStorage persistence of the active mode.
This commit is contained in:
@@ -4,9 +4,10 @@
|
|||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
/* eslint-disable max-lines -- Complex layout with many conditional renders */
|
||||||
/* eslint-disable max-lines-per-function -- Complex layout with many conditional renders */
|
/* eslint-disable max-lines-per-function -- Complex layout with many conditional renders */
|
||||||
/* eslint-disable complexity -- Many tab render paths */
|
/* eslint-disable complexity -- Many tab render paths */
|
||||||
import { type JSX, useState } from "react";
|
import { type JSX, useEffect, useState } from "react";
|
||||||
import { useGame } from "../../context/gameContext.js";
|
import { useGame } from "../../context/gameContext.js";
|
||||||
import { ResourceBar } from "../ui/resourceBar.js";
|
import { ResourceBar } from "../ui/resourceBar.js";
|
||||||
import { AboutPanel } from "./aboutPanel.js";
|
import { AboutPanel } from "./aboutPanel.js";
|
||||||
@@ -41,6 +42,8 @@ import { StoryToast } from "./storyToast.js";
|
|||||||
import { TranscendencePanel } from "./transcendencePanel.js";
|
import { TranscendencePanel } from "./transcendencePanel.js";
|
||||||
import { UpgradePanel } from "./upgradePanel.js";
|
import { UpgradePanel } from "./upgradePanel.js";
|
||||||
|
|
||||||
|
type Mode = "mortal" | "goddess" | "vampire";
|
||||||
|
|
||||||
type Tab =
|
type Tab =
|
||||||
| "adventurers"
|
| "adventurers"
|
||||||
| "upgrades"
|
| "upgrades"
|
||||||
@@ -62,6 +65,19 @@ type Tab =
|
|||||||
| "story"
|
| "story"
|
||||||
| "debug";
|
| "debug";
|
||||||
|
|
||||||
|
type GoddessTab =
|
||||||
|
| "goddess-zones"
|
||||||
|
| "goddess-bosses"
|
||||||
|
| "goddess-quests"
|
||||||
|
| "disciples"
|
||||||
|
| "goddess-equipment"
|
||||||
|
| "goddess-upgrades"
|
||||||
|
| "consecration"
|
||||||
|
| "enlightenment"
|
||||||
|
| "goddess-crafting"
|
||||||
|
| "goddess-exploration"
|
||||||
|
| "goddess-achievements";
|
||||||
|
|
||||||
const baseTabs: Array<{ id: Tab; label: string }> = [
|
const baseTabs: Array<{ id: Tab; label: string }> = [
|
||||||
{ id: "adventurers", label: "⚔️ Adventurers" },
|
{ id: "adventurers", label: "⚔️ Adventurers" },
|
||||||
{ id: "upgrades", label: "🔧 Upgrades" },
|
{ id: "upgrades", label: "🔧 Upgrades" },
|
||||||
@@ -84,6 +100,40 @@ const baseTabs: Array<{ id: Tab; label: string }> = [
|
|||||||
{ id: "debug", label: "🔧 Debug" },
|
{ id: "debug", label: "🔧 Debug" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const goddessTabs: Array<{ id: GoddessTab; label: string }> = [
|
||||||
|
{ id: "goddess-zones", label: "🌟 Zones" },
|
||||||
|
{ id: "goddess-bosses", label: "👁️ Bosses" },
|
||||||
|
{ id: "goddess-quests", label: "📿 Quests" },
|
||||||
|
{ id: "disciples", label: "🙏 Disciples" },
|
||||||
|
{ id: "goddess-equipment", label: "🔮 Equipment" },
|
||||||
|
{ id: "goddess-upgrades", label: "✨ Upgrades" },
|
||||||
|
{ id: "consecration", label: "🕯️ Consecration" },
|
||||||
|
{ id: "enlightenment", label: "💫 Enlightenment" },
|
||||||
|
{ id: "goddess-crafting", label: "⚗️ Crafting" },
|
||||||
|
{ id: "goddess-exploration", label: "🌌 Exploration" },
|
||||||
|
{ id: "goddess-achievements", label: "🏆 Achievements" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const modes: Array<Mode> = [ "mortal", "goddess", "vampire" ];
|
||||||
|
|
||||||
|
const modeLabels: Record<Mode, string> = {
|
||||||
|
goddess: "✨ Goddess",
|
||||||
|
mortal: "⚔️ Mortal",
|
||||||
|
vampire: "🧛 Vampire",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the saved active mode from localStorage, defaulting to "mortal".
|
||||||
|
* @returns The saved mode or "mortal".
|
||||||
|
*/
|
||||||
|
const readSavedMode = (): Mode => {
|
||||||
|
const saved = localStorage.getItem("elysium-active-mode");
|
||||||
|
if (saved === "goddess" || saved === "vampire") {
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
return "mortal";
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the main game layout with tabs and panels.
|
* Renders the main game layout with tabs and panels.
|
||||||
* @returns The JSX element.
|
* @returns The JSX element.
|
||||||
@@ -104,11 +154,18 @@ const GameLayout = (): JSX.Element => {
|
|||||||
dismissLoginBonus,
|
dismissLoginBonus,
|
||||||
schemaOutdated,
|
schemaOutdated,
|
||||||
} = useGame();
|
} = useGame();
|
||||||
|
const [ activeMode, setActiveMode ] = useState<Mode>(readSavedMode);
|
||||||
const [ activeTab, setActiveTab ] = useState<Tab>("adventurers");
|
const [ activeTab, setActiveTab ] = useState<Tab>("adventurers");
|
||||||
|
const [ activeGoddessTab, setActiveGoddessTab ]
|
||||||
|
= useState<GoddessTab>("goddess-zones");
|
||||||
const [ editingProfile, setEditingProfile ] = useState(false);
|
const [ editingProfile, setEditingProfile ] = useState(false);
|
||||||
const [ dismissedOutdatedWarning, setDismissedOutdatedWarning ]
|
const [ dismissedOutdatedWarning, setDismissedOutdatedWarning ]
|
||||||
= useState(false);
|
= useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.classList.toggle("goddess-mode", activeMode === "goddess");
|
||||||
|
}, [ activeMode ]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="loading-screen">
|
<div className="loading-screen">
|
||||||
@@ -151,6 +208,11 @@ const GameLayout = (): JSX.Element => {
|
|||||||
setDismissedOutdatedWarning(true);
|
setDismissedOutdatedWarning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSetMode(mode: Mode): void {
|
||||||
|
localStorage.setItem("elysium-active-mode", mode);
|
||||||
|
setActiveMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="game-layout">
|
<div className="game-layout">
|
||||||
<ResourceBar
|
<ResourceBar
|
||||||
@@ -197,55 +259,138 @@ const GameLayout = (): JSX.Element => {
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main className="game-content">
|
<main className="game-content">
|
||||||
<nav className="tab-bar">
|
<nav className="mode-bar">
|
||||||
{baseTabs.map((tab) => {
|
{modes.map((mode) => {
|
||||||
const { id: tabId, label } = tab;
|
const apotheosisCount = state.apotheosis?.count ?? 0;
|
||||||
function handleTabClick(): void {
|
const goddessLocked = mode === "goddess" && apotheosisCount === 0;
|
||||||
setActiveTab(tabId);
|
const isLocked = goddessLocked || mode === "vampire";
|
||||||
|
function handleModeClick(): void {
|
||||||
|
if (!isLocked) {
|
||||||
|
handleSetMode(mode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`tab-button ${
|
className={`mode-button${activeMode === mode
|
||||||
activeTab === tabId
|
? " active"
|
||||||
? "active"
|
: ""}${isLocked
|
||||||
: ""
|
? " locked"
|
||||||
}`}
|
: ""}`}
|
||||||
key={tabId}
|
disabled={isLocked}
|
||||||
onClick={handleTabClick}
|
key={mode}
|
||||||
|
onClick={handleModeClick}
|
||||||
|
title={isLocked
|
||||||
|
? "Not yet unlocked"
|
||||||
|
: modeLabels[mode]}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
{label}
|
{modeLabels[mode]}
|
||||||
{tabId === "codex" && codexBadgeCount > 0
|
{isLocked
|
||||||
&& <span className="tab-badge">{codexBadgeCount}</span>
|
? <span className="mode-lock">{"🔒"}</span>
|
||||||
}
|
: null}
|
||||||
{tabId === "story" && storyBadgeCount > 0
|
|
||||||
&& <span className="tab-badge">{storyBadgeCount}</span>
|
|
||||||
}
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
{activeMode === "mortal"
|
||||||
|
? <nav className="tab-bar">
|
||||||
|
{baseTabs.map((tab) => {
|
||||||
|
const { id: tabId, label } = tab;
|
||||||
|
function handleTabClick(): void {
|
||||||
|
setActiveTab(tabId);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`tab-button${activeTab === tabId
|
||||||
|
? " active"
|
||||||
|
: ""}`}
|
||||||
|
key={tabId}
|
||||||
|
onClick={handleTabClick}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
{tabId === "codex" && codexBadgeCount > 0
|
||||||
|
&& <span className="tab-badge">{codexBadgeCount}</span>
|
||||||
|
}
|
||||||
|
{tabId === "story" && storyBadgeCount > 0
|
||||||
|
&& <span className="tab-badge">{storyBadgeCount}</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
: <nav className="tab-bar goddess-tab-bar">
|
||||||
|
{goddessTabs.map((tab) => {
|
||||||
|
const { id: tabId, label } = tab;
|
||||||
|
function handleGoddessTabClick(): void {
|
||||||
|
setActiveGoddessTab(tabId);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`tab-button${activeGoddessTab === tabId
|
||||||
|
? " active"
|
||||||
|
: ""}`}
|
||||||
|
key={tabId}
|
||||||
|
onClick={handleGoddessTabClick}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
|
|
||||||
<div className="tab-content">
|
<div className="tab-content">
|
||||||
{activeTab === "adventurers" && <AdventurerPanel />}
|
{activeMode === "mortal" && activeTab === "adventurers"
|
||||||
{activeTab === "upgrades" && <UpgradePanel />}
|
&& <AdventurerPanel />}
|
||||||
{activeTab === "quests" && <QuestPanel />}
|
{activeMode === "mortal" && activeTab === "upgrades"
|
||||||
{activeTab === "bosses" && <BossPanel />}
|
&& <UpgradePanel />}
|
||||||
{activeTab === "equipment" && <EquipmentPanel />}
|
{activeMode === "mortal" && activeTab === "quests"
|
||||||
{activeTab === "achievements" && <AchievementPanel />}
|
&& <QuestPanel />}
|
||||||
{activeTab === "prestige" && <PrestigePanel />}
|
{activeMode === "mortal" && activeTab === "bosses"
|
||||||
{activeTab === "transcendence" && <TranscendencePanel />}
|
&& <BossPanel />}
|
||||||
{activeTab === "apotheosis" && <ApotheosisPanel />}
|
{activeMode === "mortal" && activeTab === "equipment"
|
||||||
{activeTab === "exploration" && <ExplorationPanel />}
|
&& <EquipmentPanel />}
|
||||||
{activeTab === "crafting" && <CraftingPanel />}
|
{activeMode === "mortal" && activeTab === "achievements"
|
||||||
{activeTab === "statistics" && <StatisticsPanel />}
|
&& <AchievementPanel />}
|
||||||
{activeTab === "daily" && <DailyChallengePanel />}
|
{activeMode === "mortal" && activeTab === "prestige"
|
||||||
{activeTab === "companions" && <CompanionPanel />}
|
&& <PrestigePanel />}
|
||||||
{activeTab === "character" && <CharacterSheetPanel />}
|
{activeMode === "mortal" && activeTab === "transcendence"
|
||||||
{activeTab === "story" && <StoryPanel />}
|
&& <TranscendencePanel />}
|
||||||
{activeTab === "codex" && <CodexPanel />}
|
{activeMode === "mortal" && activeTab === "apotheosis"
|
||||||
{activeTab === "about" && <AboutPanel />}
|
&& <ApotheosisPanel />}
|
||||||
{activeTab === "debug" && <DebugPanel />}
|
{activeMode === "mortal" && activeTab === "exploration"
|
||||||
|
&& <ExplorationPanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "crafting"
|
||||||
|
&& <CraftingPanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "statistics"
|
||||||
|
&& <StatisticsPanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "daily"
|
||||||
|
&& <DailyChallengePanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "companions"
|
||||||
|
&& <CompanionPanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "character"
|
||||||
|
&& <CharacterSheetPanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "story"
|
||||||
|
&& <StoryPanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "codex"
|
||||||
|
&& <CodexPanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "about"
|
||||||
|
&& <AboutPanel />}
|
||||||
|
{activeMode === "mortal" && activeTab === "debug"
|
||||||
|
&& <DebugPanel />}
|
||||||
|
{activeMode === "goddess"
|
||||||
|
&& <div className="goddess-placeholder">
|
||||||
|
<p>{"✨ Goddess panels coming soon..."}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{activeMode === "vampire"
|
||||||
|
&& <div className="goddess-placeholder">
|
||||||
|
<p>{"🧛 Vampire panels coming soon..."}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ const ResourceBar = ({
|
|||||||
const [ isProfileOpen, setIsProfileOpen ] = useState(false);
|
const [ isProfileOpen, setIsProfileOpen ] = useState(false);
|
||||||
const [ isResourcesOpen, setIsResourcesOpen ] = useState(false);
|
const [ isResourcesOpen, setIsResourcesOpen ] = useState(false);
|
||||||
|
|
||||||
const { gold, essence, crystals } = resources;
|
const { gold, essence, crystals, prayers, divinity, stardust } = resources;
|
||||||
|
const hasApotheosis = apotheosisCount > 0;
|
||||||
let partyCombatPower = 0;
|
let partyCombatPower = 0;
|
||||||
let goldPerSecond = 0;
|
let goldPerSecond = 0;
|
||||||
let essencePerSecond = 0;
|
let essencePerSecond = 0;
|
||||||
@@ -251,6 +252,40 @@ const ResourceBar = ({
|
|||||||
</span>
|
</span>
|
||||||
<span className="resource-label">{"Combat Power"}</span>
|
<span className="resource-label">{"Combat Power"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<hr className="resources-divider" />
|
||||||
|
<div className={`resource${hasApotheosis
|
||||||
|
? ""
|
||||||
|
: " resource-locked"}`}>
|
||||||
|
<span className="resource-icon">{"🙏"}</span>
|
||||||
|
<span className="resource-value">
|
||||||
|
{hasApotheosis
|
||||||
|
? formatNumber(prayers ?? 0)
|
||||||
|
: "🔒"}
|
||||||
|
</span>
|
||||||
|
<span className="resource-label">{"Prayers"}</span>
|
||||||
|
</div>
|
||||||
|
<div className={`resource${hasApotheosis
|
||||||
|
? ""
|
||||||
|
: " resource-locked"}`}>
|
||||||
|
<span className="resource-icon">{"✨"}</span>
|
||||||
|
<span className="resource-value">
|
||||||
|
{hasApotheosis
|
||||||
|
? formatNumber(divinity ?? 0)
|
||||||
|
: "🔒"}
|
||||||
|
</span>
|
||||||
|
<span className="resource-label">{"Divinity"}</span>
|
||||||
|
</div>
|
||||||
|
<div className={`resource${hasApotheosis
|
||||||
|
? ""
|
||||||
|
: " resource-locked"}`}>
|
||||||
|
<span className="resource-icon">{"⭐"}</span>
|
||||||
|
<span className="resource-value">
|
||||||
|
{hasApotheosis
|
||||||
|
? formatNumber(stardust ?? 0)
|
||||||
|
: "🔒"}
|
||||||
|
</span>
|
||||||
|
<span className="resource-label">{"Stardust"}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4678,3 +4678,112 @@ body::before {
|
|||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===================== MODE BAR ===================== */
|
||||||
|
.mode-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.4rem 1rem;
|
||||||
|
background: var(--colour-surface-2);
|
||||||
|
border-bottom: 1px solid var(--colour-border);
|
||||||
|
overflow-x: auto;
|
||||||
|
transition: background 0.3s, border-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-button {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--colour-border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
color: var(--colour-text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.3rem 1rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-button:hover:not(:disabled) {
|
||||||
|
background: var(--colour-surface);
|
||||||
|
color: var(--colour-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-button.active {
|
||||||
|
background: var(--colour-accent);
|
||||||
|
border-color: var(--colour-accent-light);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-button.locked {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-lock {
|
||||||
|
font-size: 0.75em;
|
||||||
|
margin-left: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== GODDESS THEME ===================== */
|
||||||
|
body.goddess-mode {
|
||||||
|
--colour-bg: #03080f;
|
||||||
|
--colour-surface: #081422;
|
||||||
|
--colour-surface-2: #0b1c30;
|
||||||
|
--colour-border: #173554;
|
||||||
|
--colour-accent: #1d6fa6;
|
||||||
|
--colour-accent-light: #3b9fd6;
|
||||||
|
--colour-gold: #f5c842;
|
||||||
|
--colour-text: #e4f2fc;
|
||||||
|
--colour-text-muted: #7aadcc;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
.resource-bar,
|
||||||
|
.mode-bar,
|
||||||
|
.tab-bar,
|
||||||
|
.game-content,
|
||||||
|
.game-sidebar,
|
||||||
|
.panel,
|
||||||
|
.panel-header {
|
||||||
|
transition:
|
||||||
|
background-color 0.3s,
|
||||||
|
background 0.3s,
|
||||||
|
border-color 0.3s,
|
||||||
|
color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== GODDESS TAB BAR ===================== */
|
||||||
|
.goddess-tab-bar .tab-button.active {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--colour-accent),
|
||||||
|
var(--colour-accent-light)
|
||||||
|
);
|
||||||
|
border-color: var(--colour-accent-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== RESOURCE LOCKED STATE ===================== */
|
||||||
|
.resource-locked {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-locked .resource-value {
|
||||||
|
color: var(--colour-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resources-divider {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--colour-border);
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== GODDESS PLACEHOLDER ===================== */
|
||||||
|
.goddess-placeholder {
|
||||||
|
align-items: center;
|
||||||
|
color: var(--colour-text-muted);
|
||||||
|
display: flex;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|||||||
+12
-10
@@ -50,16 +50,18 @@ Branch: `feat/goddess`
|
|||||||
- [x] Tests for all 6 routes (100% coverage)
|
- [x] Tests for all 6 routes (100% coverage)
|
||||||
- Lint ✅ · Build ✅ · Tests ✅ (100% coverage)
|
- Lint ✅ · Build ✅ · Tests ✅ (100% coverage)
|
||||||
|
|
||||||
## Chunk 5 — UI: Resource Bar + Mode/Tab Nav
|
## Chunk 5 — UI: Resource Bar + Mode/Tab Nav ✅ COMPLETE
|
||||||
- [ ] Add goddess currencies to resource bar dropdown (greyed pre-apotheosis)
|
- [x] Add goddess currencies (Prayers, Divinity, Stardust) to resource bar dropdown — locked icon pre-apotheosis, live values post-apotheosis
|
||||||
- [ ] Add **Mode bar** (Row 1) — `Mortal | Goddess | Vampire` — always visible, locked modes show padlock pre-unlock
|
- [x] Add **Mode bar** (Row 1) — `⚔️ Mortal | ✨ Goddess | 🧛 Vampire` — always visible, locked modes show 🔒 and are disabled
|
||||||
- [ ] Add **Tab bar** (Row 2) — swaps entirely based on selected mode:
|
- [x] Add **Tab bar** (Row 2) — swaps entirely based on selected mode
|
||||||
- Mortal: Zones · Quests · Adventurers · Equipment · Upgrades · Prestige · Transcendence · Crafting · Exploration · Achievements · Codex · Story · Daily
|
- Mortal: all existing tabs (unchanged)
|
||||||
- Goddess: Zones · Disciples · Quests · Equipment · Upgrades · Consecration · Enlightenment · Crafting · Exploration · Achievements
|
- Goddess: Zones · Bosses · Quests · Disciples · Equipment · Upgrades · Consecration · Enlightenment · Crafting · Exploration · Achievements
|
||||||
- Vampire: *(future — TBD)*
|
- Vampire: placeholder (future)
|
||||||
- [ ] Persist selected mode in game state (survives page reload)
|
- [x] Mode persists across page reloads via localStorage
|
||||||
- [ ] `.goddess-mode` CSS class toggle on root when Goddess mode selected
|
- [x] `body.goddess-mode` CSS class toggled via `useEffect` when Goddess mode active
|
||||||
- [ ] 300ms CSS fade transition between base and goddess themes
|
- [x] 300ms CSS fade transition on major layout elements
|
||||||
|
- [x] Goddess theme: dark navy bg, divine blue accent, gold/white accents
|
||||||
|
- Lint ✅ · Build ✅ · Tests ✅ (100% coverage)
|
||||||
|
|
||||||
## Chunk 6 — UI: Goddess Panels
|
## Chunk 6 — UI: Goddess Panels
|
||||||
- [ ] `GoddessZonesPanel` — zones with lock states
|
- [ ] `GoddessZonesPanel` — zones with lock states
|
||||||
|
|||||||
Reference in New Issue
Block a user