generated from nhcarrigan/template
feat: initial elysium idle game prototype
Sets up the full monorepo with pnpm workspaces. Includes shared types package, Hono API with Discord OAuth/JWT auth, Prisma v6 + MongoDB Atlas, and React + Vite frontend with game loop, five tabs, and Discord-linked save/load.
This commit is contained in:
@@ -0,0 +1,631 @@
|
||||
/* ===================== RESET & BASE ===================== */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:root {
|
||||
--colour-bg: #0d0d1a;
|
||||
--colour-surface: #1a1a2e;
|
||||
--colour-surface-2: #16213e;
|
||||
--colour-border: #2a2a4a;
|
||||
--colour-accent: #7c3aed;
|
||||
--colour-accent-light: #a855f7;
|
||||
--colour-gold: #f59e0b;
|
||||
--colour-essence: #8b5cf6;
|
||||
--colour-crystal: #06b6d4;
|
||||
--colour-rune: #ec4899;
|
||||
--colour-text: #e2e8f0;
|
||||
--colour-text-muted: #94a3b8;
|
||||
--colour-success: #10b981;
|
||||
--colour-error: #ef4444;
|
||||
--colour-warning: #f59e0b;
|
||||
--radius: 8px;
|
||||
--radius-lg: 12px;
|
||||
--font: "Segoe UI", system-ui, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--colour-bg);
|
||||
color: var(--colour-text);
|
||||
font-family: var(--font);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ===================== RESOURCE BAR ===================== */
|
||||
.resource-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--colour-surface);
|
||||
border-bottom: 1px solid var(--colour-border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.resource {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.resource-value {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.resource-label {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.prestige-badge {
|
||||
margin-left: auto;
|
||||
background: linear-gradient(135deg, var(--colour-accent), var(--colour-accent-light));
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ===================== GAME LAYOUT ===================== */
|
||||
.game-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.game-main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.game-sidebar {
|
||||
width: 220px;
|
||||
padding: 1rem;
|
||||
background: var(--colour-surface-2);
|
||||
border-right: 1px solid var(--colour-border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.game-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ===================== TABS ===================== */
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--colour-surface);
|
||||
border-bottom: 1px solid var(--colour-border);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--radius);
|
||||
color: var(--colour-text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
padding: 0.4rem 0.9rem;
|
||||
transition: all 0.15s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-button:hover {
|
||||
background: var(--colour-surface-2);
|
||||
color: var(--colour-text);
|
||||
}
|
||||
|
||||
.tab-button.active {
|
||||
background: var(--colour-accent);
|
||||
border-color: var(--colour-accent-light);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* ===================== CLICK AREA ===================== */
|
||||
.click-area {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.click-area h2 {
|
||||
font-size: 0.95rem;
|
||||
color: var(--colour-text-muted);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.click-button {
|
||||
background: linear-gradient(135deg, var(--colour-accent), var(--colour-accent-light));
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
font-size: 3rem;
|
||||
height: 120px;
|
||||
transition: transform 0.1s, box-shadow 0.1s;
|
||||
width: 120px;
|
||||
box-shadow: 0 0 20px rgba(124, 58, 237, 0.4);
|
||||
}
|
||||
|
||||
.click-button:active {
|
||||
transform: scale(0.93);
|
||||
box-shadow: 0 0 10px rgba(124, 58, 237, 0.2);
|
||||
}
|
||||
|
||||
.click-power {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* ===================== PANEL ===================== */
|
||||
.panel {
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--colour-accent-light);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
color: var(--colour-text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* ===================== ADVENTURERS ===================== */
|
||||
.adventurer-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.adventurer-card {
|
||||
align-items: center;
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.adventurer-card.locked {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.adventurer-icon {
|
||||
font-size: 1.75rem;
|
||||
min-width: 2.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.adventurer-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.adventurer-info h3 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.adventurer-info p {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.adventurer-count {
|
||||
color: var(--colour-accent-light);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
min-width: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ===================== BUTTONS ===================== */
|
||||
.buy-button,
|
||||
.start-quest-button,
|
||||
.attack-button,
|
||||
.prestige-button {
|
||||
background: var(--colour-accent);
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
padding: 0.4rem 0.9rem;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.buy-button:hover:not(:disabled),
|
||||
.start-quest-button:hover,
|
||||
.attack-button:hover,
|
||||
.prestige-button:hover:not(:disabled) {
|
||||
background: var(--colour-accent-light);
|
||||
}
|
||||
|
||||
.buy-button:disabled,
|
||||
.prestige-button:disabled {
|
||||
background: var(--colour-border);
|
||||
color: var(--colour-text-muted);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ===================== UPGRADES ===================== */
|
||||
.upgrade-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.upgrade-card {
|
||||
align-items: center;
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.upgrade-card.purchased {
|
||||
border-color: var(--colour-success);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.upgrade-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.upgrade-info h3 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.upgrade-info p {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.upgrade-multiplier {
|
||||
color: var(--colour-gold) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.upgrade-cost {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* ===================== QUESTS ===================== */
|
||||
.quest-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.quest-card {
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.quest-card.quest-completed {
|
||||
border-color: var(--colour-success);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.quest-card.quest-active {
|
||||
border-color: var(--colour-warning);
|
||||
}
|
||||
|
||||
.quest-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quest-info h3 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.quest-info p {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.quest-rewards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.reward-tag {
|
||||
background: var(--colour-surface-2);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: 999px;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.15rem 0.5rem;
|
||||
}
|
||||
|
||||
.quest-badge {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
padding: 0.3rem 0.6rem;
|
||||
border-radius: var(--radius);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.quest-badge.locked { color: var(--colour-text-muted); }
|
||||
.quest-badge.active { color: var(--colour-warning); }
|
||||
.quest-badge.completed { color: var(--colour-success); }
|
||||
|
||||
/* ===================== BOSSES ===================== */
|
||||
.boss-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.boss-card {
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius-lg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.boss-card.boss-defeated {
|
||||
opacity: 0.6;
|
||||
border-color: var(--colour-text-muted);
|
||||
}
|
||||
|
||||
.boss-card.boss-in_progress {
|
||||
border-color: var(--colour-error);
|
||||
box-shadow: 0 0 12px rgba(239, 68, 68, 0.2);
|
||||
}
|
||||
|
||||
.boss-info h3 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.boss-info p {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.prestige-lock {
|
||||
color: var(--colour-warning) !important;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.hp-bar {
|
||||
background: var(--colour-border);
|
||||
border-radius: 999px;
|
||||
height: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hp-fill {
|
||||
background: linear-gradient(90deg, #ef4444, #f97316);
|
||||
height: 100%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.hp-text {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.boss-rewards {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.boss-badge.defeated {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.attack-button {
|
||||
align-self: flex-start;
|
||||
background: linear-gradient(135deg, #ef4444, #b91c1c);
|
||||
font-size: 0.95rem;
|
||||
padding: 0.5rem 1.2rem;
|
||||
}
|
||||
|
||||
.attack-button:hover {
|
||||
background: linear-gradient(135deg, #f87171, #dc2626);
|
||||
}
|
||||
|
||||
/* ===================== PRESTIGE ===================== */
|
||||
.prestige-panel p {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.prestige-status {
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.prestige-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.prestige-form input {
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
color: var(--colour-text);
|
||||
font-size: 0.95rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.prestige-button {
|
||||
background: linear-gradient(135deg, #7c3aed, #a855f7);
|
||||
font-size: 1rem;
|
||||
padding: 0.6rem 1.5rem;
|
||||
}
|
||||
|
||||
.prestige-locked {
|
||||
color: var(--colour-text-muted) !important;
|
||||
}
|
||||
|
||||
/* ===================== LOGIN PAGE ===================== */
|
||||
.login-page {
|
||||
align-items: center;
|
||||
background: radial-gradient(ellipse at center, #1a1a3e 0%, #0d0d1a 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius-lg);
|
||||
max-width: 480px;
|
||||
padding: 2.5rem;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-card h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.login-card p {
|
||||
color: var(--colour-text-muted);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.discord-login-button {
|
||||
background: #5865f2;
|
||||
border-radius: var(--radius);
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
padding: 0.75rem 2rem;
|
||||
text-decoration: none;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.discord-login-button:hover {
|
||||
background: #4752c4;
|
||||
}
|
||||
|
||||
.login-note {
|
||||
font-size: 0.8rem !important;
|
||||
margin-top: 1rem !important;
|
||||
}
|
||||
|
||||
/* ===================== MODAL ===================== */
|
||||
.modal-overlay {
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
inset: 0;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius-lg);
|
||||
max-width: 420px;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.modal p {
|
||||
color: var(--colour-text-muted);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.modal-note {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.modal-close-button {
|
||||
background: var(--colour-accent);
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.6rem 2rem;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.modal-close-button:hover {
|
||||
background: var(--colour-accent-light);
|
||||
}
|
||||
|
||||
/* ===================== UTILITY ===================== */
|
||||
.error {
|
||||
color: var(--colour-error);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: var(--colour-success);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.loading-screen,
|
||||
.error-screen {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
}
|
||||
Reference in New Issue
Block a user