feat: migrate SCSS → Tailwind CSS + remove sass dependency

12 SCSS files (1167 lines) replaced by centralized index.css with
Tailwind v4 @theme tokens, @layer components, and utility classes.
Game panel design system (gp-*) preserved as CSS components.
Inline styles in Settings/Login/MilestoneBar converted to Tailwind utilities.
sass removed from dependencies. Build clean, 53 tests pass.
This commit is contained in:
2026-03-28 11:19:45 +01:00
parent 8cc9fdaa62
commit b58d39e707
34 changed files with 1055 additions and 1259 deletions

View File

@@ -14,7 +14,6 @@
"react-helmet": "^6.1.0",
"react-lottie-player": "^1.5.5",
"react-router-dom": "^6.19.0",
"sass": "^1.69.5",
"tailwindcss": "^4.2.2",
"zustand": "^5.0.12"
},
@@ -1033,9 +1032,11 @@
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
"integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"detect-libc": "^2.0.3",
"is-glob": "^4.0.3",
@@ -1072,11 +1073,13 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1092,11 +1095,13 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1112,11 +1117,13 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1132,11 +1139,13 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1152,11 +1161,13 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1172,11 +1183,13 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1192,11 +1205,13 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1212,11 +1227,13 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1232,11 +1249,13 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1252,11 +1271,13 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1272,11 +1293,13 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1292,11 +1315,13 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1312,11 +1337,13 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -2579,6 +2606,8 @@
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"readdirp": "^4.0.1"
},
@@ -3600,7 +3629,9 @@
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz",
"integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==",
"license": "MIT"
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/import-fresh": {
"version": "3.3.0",
@@ -3757,7 +3788,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3793,7 +3824,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"devOptional": true,
"dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -4442,8 +4473,10 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT",
"optional": true
"optional": true,
"peer": true
},
"node_modules/node-releases": {
"version": "2.0.13",
@@ -4685,7 +4718,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -4885,6 +4918,8 @@
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 14.18.0"
},
@@ -5114,6 +5149,8 @@
"resolved": "https://registry.npmjs.org/sass/-/sass-1.98.0.tgz",
"integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.1.5",

View File

@@ -17,7 +17,6 @@
"react-helmet": "^6.1.0",
"react-lottie-player": "^1.5.5",
"react-router-dom": "^6.19.0",
"sass": "^1.69.5",
"tailwindcss": "^4.2.2",
"zustand": "^5.0.12"
},

View File

@@ -6,10 +6,6 @@ import Footer from "./components/footer";
import { GameTick } from "./components/GameTick";
import { GameSync } from "./components/GameSync";
import "./scss/root.scss";
import "./scss/zones.scss";
import "./scss/components/footer.scss";
import navData from "./data/NavBarData.json";
function App() {

View File

@@ -29,7 +29,7 @@ function NodeRow({
return (
<div className={rowClass}>
<div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
<div className="flex flex-col min-w-0">
<span className="gp-value">{node.name}</span>
<span className="gp-label">{EFFECT_LABELS[node.effect](node.value)}</span>
</div>
@@ -39,8 +39,7 @@ function NodeRow({
<button
disabled={!canBuy}
onClick={onBuy}
className={`gp-btn ${canBuy ? "gp-btn--buy" : "gp-btn--disabled"}`}
style={canBuy ? { background: "#d97706" } : {}}
className={`gp-btn ${canBuy ? "gp-btn--buy bg-amber-600!" : "gp-btn--disabled"}`}
>
{node.cost} ADN
</button>
@@ -58,7 +57,7 @@ export function EvolutionTree() {
return (
<div className="gp">
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<div className="flex justify-between items-center">
<span className="gp-title">Évolution</span>
<span className="gp-value gp-accent-amber">{state.ancestralDna} ADN</span>
</div>

View File

@@ -12,7 +12,7 @@ export function GeneratorShop() {
return (
<div className="gp">
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<div className="flex justify-between items-center">
<span className="gp-title" title="Achète des générateurs pour produire des têtards automatiquement">Générateurs</span>
<span className="gp-value gp-accent-green" title="Production totale par seconde">{formatNumber(productionPerSecond)}/s</span>
</div>
@@ -26,8 +26,8 @@ export function GeneratorShop() {
key={gen.id}
className={`gp-row ${canAfford ? "gp-row--active" : "gp-row--locked"}`}
>
<div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: "0.3rem" }}>
<div className="flex flex-col min-w-0">
<div className="flex items-center gap-1">
<span className="gp-value">{gen.name}</span>
{gen.owned > 0 && (
<span className="gp-label gp-accent-green">x{gen.owned}</span>

View File

@@ -13,8 +13,8 @@ export function MilestoneBar() {
const remaining = Math.max(PRESTIGE_THRESHOLD - resources, 0);
return (
<div className="gp" style={{ gap: "0.25rem" }}>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div className="gp gap-1">
<div className="flex justify-between">
<span className="gp-label">Prochaine Génération</span>
<span className="gp-label">
{formatNumber(resources)} / {formatNumber(PRESTIGE_THRESHOLD)}
@@ -22,14 +22,11 @@ export function MilestoneBar() {
</div>
<div className="gp-progress">
<div
className="gp-progress-fill"
style={{
width: `${progressPercent}%`,
background: "linear-gradient(90deg, #7c3aed, #a78bfa)",
}}
className="gp-progress-fill bg-gradient-to-r from-violet-600 to-violet-400"
style={{ width: `${progressPercent}%` }}
/>
</div>
<span className="gp-label" style={{ textAlign: "right" }}>
<span className="gp-label text-right">
{remaining > 0
? `${formatNumber(remaining)} restants`
: "Nouvelle Génération disponible !"}

View File

@@ -26,7 +26,7 @@ export function PrestigePanel() {
<div className="gp">
<span className="gp-title" title="Recommence à zéro en échange d'un bonus permanent — tes têtards et générateurs sont réinitialisés mais tu gagnes de l'ADN et un multiplicateur">Prestige</span>
{canPrestige ? (
<div style={{ display: "flex", flexDirection: "column", gap: "0.4rem" }}>
<div className="flex flex-col gap-1.5">
<span className="gp-value gp-accent-purple">
+{dnaPreview} ADN · +0.1x mult
</span>

View File

@@ -1,8 +1,5 @@
import { NavLink as Link } from "react-router-dom";
import PropTypes from "prop-types";
import "../scss/components/navbar.scss";
import "../scss/root.scss";
import PrimaryButton from "./buttons/PrimaryButton";
export default function Burger({ navData }) {

View File

@@ -1,5 +1,4 @@
import PropTypes from "prop-types";
import "../../scss/components/buttons.scss";
import { Link } from "react-router-dom";
export default function PrimaryButton({ btnText, btnLink }) {

View File

@@ -1,4 +1,3 @@
import "../../scss/components/buttons.scss";
import PropTypes from "prop-types";
import { Link } from "react-router";

View File

@@ -1,4 +1,3 @@
import "../scss/components/footer.scss";
import { NavLink as Link } from "react-router-dom";
export default function Footer() {

View File

@@ -1,9 +1,6 @@
import { NavLink as Link } from "react-router-dom";
import PropTypes from "prop-types";
import "../scss/components/navbar.scss";
import "../scss/root.scss";
import PrimaryButton from "./buttons/PrimaryButton";
import Burger from "./burger";
import { useAuth } from "../context/AuthContext";

View File

@@ -1,12 +1,979 @@
@import "tailwindcss";
:root {
/* ── Tailwind v4 theme — tokens du jeu ── */
@theme {
/* Base colors */
--color-blue-light: #dcecf3;
--color-purple-light: #e4e3f3;
--color-red-light: #c33636;
--color-light: #eaeaea;
--color-grey: #202020;
--color-grey-hover: #606060;
/* Game panel tokens */
--color-gp-bg: rgba(17, 17, 17, 0.75);
--color-gp-bg-hover: rgba(17, 17, 17, 0.85);
--color-gp-border: rgba(255, 255, 255, 0.08);
--color-gp-text: rgba(255, 255, 255, 0.9);
--color-gp-text-muted: rgba(255, 255, 255, 0.5);
--color-gp-accent-green: #34d399;
--color-gp-accent-purple: #a78bfa;
--color-gp-accent-amber: #fbbf24;
--color-gp-accent-green-bg: rgba(16, 185, 129, 0.12);
--color-gp-accent-purple-bg: rgba(139, 92, 246, 0.12);
--color-gp-accent-amber-bg: rgba(251, 191, 36, 0.12);
--color-gp-btn: #059669;
--color-gp-btn-hover: #10b981;
--color-gp-btn-disabled: rgba(255, 255, 255, 0.08);
--color-gp-btn-text-disabled: rgba(255, 255, 255, 0.3);
/* Spacing / sizing tokens */
--radius-gp: 0.75rem;
--spacing-gp: 0.75rem;
--spacing-gp-gap: 0.5rem;
/* Font sizes */
--font-size-gp-title: 0.8rem;
--font-size-gp-text: 0.75rem;
--font-size-gp-sm: 0.65rem;
/* Animation */
--animate-gp-pulse: gp-pulse 2s ease-in-out infinite;
}
@keyframes gp-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(124, 58, 237, 0.4); }
50% { box-shadow: 0 0 0 6px rgba(124, 58, 237, 0); }
}
/* ── Global reset & base ── */
@layer base {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
:root {
--font: "Hanken Grotesk", sans-serif;
--bg-color: var(--color-blue-light);
}
a {
text-decoration: none;
}
main {
min-height: 92vh;
margin-top: 80px;
padding: 0 0 2rem;
background-color: var(--bg-color);
}
::-webkit-scrollbar {
width: 1px;
display: none;
}
}
/* ── Zone system (biomes) ── */
@layer components {
.zone {
width: 100%;
min-height: 92vh;
position: relative;
display: flex;
align-items: flex-end;
justify-content: center;
background-size: cover;
background-repeat: no-repeat;
transition: background-image 0.5s ease;
}
[data-zone="swamp"] {
background-image: url("/webp/bg-cover.webp");
background-position: center 70%;
}
[data-zone="landing"] {
background: var(--bg-color);
align-items: center;
}
[data-zone="page"] {
background: var(--bg-color);
align-items: flex-start;
min-height: auto;
}
/* ── Game panels design system ── */
.gp {
display: flex;
flex-direction: column;
gap: var(--spacing-gp-gap);
padding: var(--spacing-gp);
background: var(--color-gp-bg);
backdrop-filter: blur(8px);
border: 1px solid var(--color-gp-border);
border-radius: var(--radius-gp);
}
.gp-title {
font-family: var(--font);
font-size: var(--font-size-gp-title);
font-weight: 700;
color: var(--color-gp-text);
letter-spacing: 0.02em;
text-transform: uppercase;
}
.gp-label {
font-family: var(--font);
font-size: var(--font-size-gp-sm);
font-weight: 500;
color: var(--color-gp-text-muted);
}
.gp-value {
font-family: var(--font);
font-size: var(--font-size-gp-text);
font-weight: 600;
color: var(--color-gp-text);
}
.gp-accent-green { color: var(--color-gp-accent-green); }
.gp-accent-purple { color: var(--color-gp-accent-purple); }
.gp-accent-amber { color: var(--color-gp-accent-amber); }
/* Row item (générateur, noeud évolution) */
.gp-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.4rem;
padding: 0.4rem 0.5rem;
border-radius: calc(var(--radius-gp) - 0.15rem);
border: 1px solid transparent;
transition: background 0.15s ease, border-color 0.15s ease;
}
.gp-row--active {
border-color: rgba(16, 185, 129, 0.3);
background: var(--color-gp-accent-green-bg);
}
.gp-row--active:hover {
background: rgba(16, 185, 129, 0.18);
}
.gp-row--locked {
border-color: var(--color-gp-border);
background: rgba(255, 255, 255, 0.02);
opacity: 0.5;
}
.gp-row--evolution {
border-color: rgba(251, 191, 36, 0.3);
background: var(--color-gp-accent-amber-bg);
}
.gp-row--unlocked {
border-color: rgba(16, 185, 129, 0.3);
background: var(--color-gp-accent-green-bg);
}
/* Bouton achat */
.gp-btn {
font-family: var(--font);
font-size: var(--font-size-gp-sm);
font-weight: 600;
padding: 0.3rem 0.6rem;
border-radius: 0.4rem;
border: none;
cursor: pointer;
transition: background 0.15s ease;
white-space: nowrap;
}
.gp-btn--buy {
background: var(--color-gp-btn);
color: white;
}
.gp-btn--buy:hover {
background: var(--color-gp-btn-hover);
}
.gp-btn--disabled {
background: var(--color-gp-btn-disabled);
color: var(--color-gp-btn-text-disabled);
cursor: not-allowed;
}
.gp-btn--prestige {
background: #7c3aed;
color: white;
padding: 0.4rem 0.8rem;
font-size: var(--font-size-gp-text);
animation: var(--animate-gp-pulse);
}
.gp-btn--prestige:hover {
background: #8b5cf6;
}
/* Header cockpit (stats résumé) */
.gp-cockpit-header {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0.2rem;
padding: 0.5rem;
}
.gp-stat {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.05rem;
}
/* Progress bar */
.gp-progress {
height: 0.35rem;
background: rgba(255, 255, 255, 0.08);
border-radius: 1rem;
overflow: hidden;
}
.gp-progress-fill {
height: 100%;
border-radius: 1rem;
transition: width 0.5s ease;
}
/* Section separator */
.gp-sep {
height: 1px;
background: var(--color-gp-border);
margin: 0.15rem 0;
}
/* Zone titles in sidebar */
.gp-zone-label {
font-family: var(--font);
font-size: var(--font-size-gp-sm);
font-weight: 600;
color: var(--color-gp-text-muted);
text-transform: uppercase;
letter-spacing: 0.06em;
padding-left: 0.2rem;
}
/* ── Home / Game view ── */
.click-zone {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
gap: 1rem;
padding-bottom: 2vh;
cursor: pointer;
flex: 1;
}
@media (min-width: 768px) {
.click-zone {
padding-right: 22rem;
}
}
.tadpole-sprite {
width: 280px;
height: 280px;
background: url("/svg/tadpole.svg") no-repeat center / contain;
transition: transform 0.1s ease;
}
@media (min-width: 768px) {
.tadpole-sprite {
width: 320px;
height: 320px;
}
}
.click-zone:active .tadpole-sprite {
transform: scale(0.95) rotate(2deg);
}
.click-zone-counter {
font-family: var(--font);
font-size: 2rem;
font-weight: 800;
color: white;
text-shadow: 0 0 12px rgba(52, 211, 153, 0.5), 0 2px 6px rgba(0, 0, 0, 0.7);
pointer-events: none;
user-select: none;
letter-spacing: 0.02em;
}
@media (min-width: 768px) {
.click-zone-counter {
font-size: 2.5rem;
}
}
.achieve-badge {
display: block;
text-align: center;
padding: 0.4rem;
border-radius: var(--radius-gp);
background: var(--color-gp-accent-green-bg);
border: 1px solid rgba(16, 185, 129, 0.2);
font-family: var(--font);
font-size: var(--font-size-gp-sm);
font-weight: 600;
color: var(--color-gp-accent-green);
text-decoration: none;
transition: all 0.15s ease;
}
.achieve-badge:hover {
background: rgba(16, 185, 129, 0.2);
}
.click-particle {
position: fixed;
pointer-events: none;
font-family: var(--font);
font-size: 1.6rem;
font-weight: 800;
color: #34d399;
text-shadow: 0 0 8px rgba(52, 211, 153, 0.6), 0 2px 4px rgba(0, 0, 0, 0.7);
z-index: 100;
animation: float-up 1.2s ease-out forwards;
}
.game-sidebar {
position: fixed;
right: 0.75rem;
top: 5.5rem;
bottom: 0.75rem;
width: 20rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
overflow-y: auto;
z-index: 10;
padding-right: 0.25rem;
}
@media (max-width: 767px) {
.game-sidebar {
right: 0;
left: 0;
top: auto;
bottom: 0;
width: 100%;
max-height: 45vh;
padding: 0.75rem;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(8px);
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
}
}
}
@keyframes float-up {
0% {
opacity: 1;
transform: translateY(0) scale(1.2);
}
60% {
opacity: 0.9;
}
100% {
opacity: 0;
transform: translateY(-80px) scale(1.5);
}
}
/* ── Navbar ── */
@layer components {
.header-main {
display: flex;
justify-content: space-between;
position: absolute;
width: 100%;
height: 80px;
padding: 0 2rem;
top: 0;
background-color: var(--bg-color);
background-blend-mode: darken;
background-size: cover;
z-index: 99;
box-sizing: border-box;
}
@media (max-width: 999px) {
.header-main {
padding: 0 0.4rem;
}
}
.logo {
width: 5rem;
content: url(/svg/tadpole.svg);
transition: 0.2s;
}
.logo:hover {
transform: scale(0.9);
}
.navbar {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 1rem;
box-sizing: border-box;
cursor: pointer;
}
.nav-list {
display: flex;
justify-content: space-between;
gap: 1.6rem;
align-items: center;
list-style-type: none;
}
@media (max-width: 999px) {
.nav-list {
display: none;
}
}
.nav-list li {
list-style: none;
font-family: var(--font);
font-weight: 300;
font-size: 1rem;
color: white;
float: left;
width: fit-content;
}
.mainLink {
text-decoration: none;
color: var(--color-grey);
font-weight: 500;
padding: 30px 0;
}
.mainLink:hover {
color: var(--color-red-light);
}
.dropLink {
text-decoration: none;
color: white;
font-weight: 400;
}
.dropLink:hover {
color: var(--color-red-light);
}
.dropdown-content {
display: none;
position: absolute;
background: var(--color-grey);
transform: translateY(30px);
min-width: 160px;
box-shadow: 0 8px 16px rgba(10, 10, 10, 0.2);
z-index: 1;
}
.dropdown-content a {
color: white;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
}
.dropdown-content a:hover {
background-color: var(--color-grey-hover);
}
.dropdown:hover .dropdown-content {
display: block;
}
.auth-nav {
display: flex;
align-items: center;
gap: 0.6rem;
font-family: var(--font);
}
.auth-nickname {
font-size: 0.9rem;
font-weight: 500;
color: var(--color-grey);
}
.auth-btn {
padding: 0.3rem 0.8rem;
border: 1px solid var(--color-grey);
border-radius: 0.4rem;
background: none;
font-family: var(--font);
font-size: 0.8rem;
color: var(--color-grey);
cursor: pointer;
transition: all 0.15s ease;
}
.auth-btn:hover {
background: var(--color-grey);
color: white;
}
/* ── Burger menu (mobile) ── */
@media (min-width: 1000px) {
.menuToggle {
display: none;
}
}
}
@media (max-width: 999px) {
.menuToggle {
float: left;
position: relative;
box-sizing: border-box;
top: 2px;
left: -10px;
z-index: 99;
user-select: none;
}
.menuToggle a {
text-decoration: none;
color: var(--color-grey);
transition: color 0.3s ease;
}
.menuToggle a:hover {
color: var(--color-red-light);
}
.menuToggle input {
display: block;
width: 40px;
height: 32px;
position: absolute;
top: -7px;
left: -5px;
cursor: pointer;
opacity: 0;
z-index: 2;
}
.menuToggle span {
display: block;
width: 33px;
height: 4px;
margin-bottom: 5px;
position: relative;
background: var(--color-grey);
border-radius: 3px;
z-index: 1;
transform-origin: 4px 0;
transition: transform 0.2s cubic-bezier(0.77, 0.2, 0.05, 1),
background 0.2s cubic-bezier(0.77, 0.2, 0.05, 1), opacity 0.55s ease;
}
.menuToggle span:first-child {
transform-origin: 0% 0%;
}
.menuToggle span:nth-last-child(2) {
transform-origin: 0% 100%;
}
.menuToggle input:checked ~ span {
opacity: 1;
transform: rotate(45deg) translate(-2px, -1px);
background: white;
}
.menuToggle input:checked ~ span:nth-last-child(3) {
opacity: 0;
transform: rotate(0deg) scale(0.2, 0.2);
}
.menuToggle input:checked ~ span:nth-last-child(2) {
transform: rotate(-45deg) translate(0, -1px);
}
.menu {
position: absolute;
display: flex;
flex-direction: column;
width: 280px;
height: 110vh;
margin: -100px 0 0 -231px;
padding: 1.2rem;
padding-top: 100px;
background: var(--color-grey);
list-style-type: none;
transform-origin: 0% 0%;
overflow: hidden;
visibility: hidden;
opacity: 0;
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.menu li {
padding: 10px 0;
font-size: 1.2rem;
font-family: var(--font);
font-weight: 500;
color: white;
}
.menuToggle input:checked ~ ul {
visibility: visible;
opacity: 1;
}
.sousmenu {
display: flex;
flex-direction: column;
margin-left: 1.2rem;
color: white;
font-size: 1.2rem;
font-family: var(--font);
font-weight: 500;
padding-bottom: 1rem;
}
}
/* ── Buttons ── */
@layer components {
.primary-button {
display: flex;
padding: 0.6rem 1rem;
height: fit-content;
background-color: var(--color-red-light);
border-radius: 0.6rem;
justify-content: center;
text-decoration: none;
font-family: var(--font);
color: white !important;
text-align: center;
font-size: 1rem;
font-weight: 400;
transition: transform 0.1s ease-in-out;
border: none;
}
.primary-button:hover {
transform: scale(0.95);
}
.secondary-button {
display: flex;
padding: 1rem;
background-color: white;
border-radius: 0.6rem;
justify-content: center;
width: fit-content;
height: fit-content;
text-decoration: none;
font-family: var(--font);
color: var(--color-grey) !important;
text-align: center;
font-size: 1rem;
transition: transform 0.1s ease-in-out;
border: none;
}
.secondary-button:hover {
transform: scale(0.95);
background-color: var(--color-grey-hover);
}
}
/* ── Footer ── */
@layer components {
.footer {
display: flex;
flex-direction: column;
position: relative;
align-items: center;
bottom: 0;
left: 0;
width: 100%;
background-color: var(--bg-color);
border-top: solid 1px var(--color-grey);
padding: 2rem 0;
gap: 2rem;
}
.footer-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
width: 90%;
gap: 2rem;
}
.footer-logo {
background-image: url(/svg/tadpole.svg);
background-size: contain;
background-position: center;
background-repeat: no-repeat;
width: 250px;
height: 100px;
transition: all 0.15s ease-in-out;
}
.footer-logo:hover {
transform: scale(0.9);
}
.footer .section {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 1.4rem;
}
.section-title {
font-family: var(--font);
font-size: 1.2rem;
color: var(--color-grey);
text-decoration-line: underline;
text-underline-offset: 0.5rem;
}
.section-text {
max-width: 26ch;
font-family: var(--font);
font-size: 1rem;
color: var(--color-grey);
}
.section-list {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 1rem;
list-style: none;
}
.section-list .section-item,
.section-list a {
width: fit-content;
font-family: var(--font);
font-size: 1rem;
color: var(--color-grey);
transition: all 0.15s ease-in-out;
}
.section-list .section-item:hover,
.section-list a:hover {
transform: scale(0.9);
}
.spacing {
min-width: 150px;
width: 10%;
}
.footer-github {
font-family: var(--font);
font-size: 0.9rem;
font-weight: 500;
color: var(--color-grey);
text-decoration: none;
transition: all 0.15s ease-in-out;
}
.footer-github:hover {
transform: scale(0.95);
}
.copyright {
font-family: var(--font);
font-size: 0.8rem;
font-weight: 300;
color: var(--color-grey);
text-align: center;
}
/* ── Pages layout (error, legal, settings, login) ── */
.container {
display: flex;
flex-direction: column;
max-width: 132ch;
width: 80%;
gap: 3rem;
margin: 150px auto 50px;
}
.container h1 {
font-family: var(--font);
color: var(--color-grey);
font-size: 1.8rem;
text-align: center;
width: fit-content;
}
.container h2 {
font-family: var(--font);
font-size: 2rem;
font-weight: 600;
color: var(--color-grey);
}
.container .subtitle {
font-family: var(--font);
color: var(--color-grey);
font-size: 1.2rem;
font-weight: 600;
text-align: left;
margin-bottom: 0.8rem;
}
.container .content {
display: flex;
flex-direction: column;
gap: 0.6rem;
}
.container .paragraphe {
font-family: var(--font);
color: var(--color-grey);
font-size: 1rem;
font-weight: 400;
margin-bottom: 0.5rem;
list-style: inside;
}
.container .info {
font-family: var(--font);
color: var(--color-grey);
font-size: 1rem;
font-weight: 400;
}
section {
display: flex;
flex-direction: column;
height: 90vh;
justify-content: center;
width: 100%;
}
.containererror {
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
}
.containererror h1 {
font-family: var(--font);
color: var(--color-grey);
font-size: 2rem;
text-align: center;
width: fit-content;
}
.message {
font-family: var(--font);
color: var(--color-grey);
font-size: 1rem;
font-weight: 300;
text-align: center;
}
.btn-return {
display: flex;
justify-content: center;
width: fit-content;
margin: auto;
padding: 0.5rem 1rem;
background-color: var(--color-grey);
border: none;
border-radius: 0.6rem;
font-family: var(--font);
color: white;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
}
.btn-return:hover {
transform: scale(0.9);
}
/* ── Achievements ── */
.fullachieve {
display: flex;
flex-direction: column;
padding-top: 6rem;
padding-bottom: 3rem;
background-color: var(--color-blue-light);
width: 100%;
max-width: 1280px;
margin: 0 auto;
min-height: 80vh;
}
.fullachieve h1 {
text-align: center;
font-family: var(--font);
font-size: 2.5rem;
color: var(--color-grey);
margin-bottom: 0.5rem;
}
.achieve-counter {
text-align: center;
font-family: var(--font);
font-size: 1.1rem;
color: var(--color-grey);
opacity: 0.7;
margin-bottom: 2rem;
}
.achievementscontainer {
margin: auto;
display: flex;
align-items: center;
width: 100%;
padding: 0 2rem;
}
.achievementscardcontainer {
display: flex;
justify-content: center;
flex-wrap: wrap;
min-height: 200px;
gap: 1rem;
width: 100%;
}
.achieve-card {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem 1.2rem;
border-radius: 0.75rem;
width: 100%;
max-width: 380px;
transition: transform 0.15s ease;
}
.achieve-card:hover {
transform: translateY(-2px);
}
.achieve-unlocked {
background: rgba(16, 185, 129, 0.12);
border: 1px solid rgba(16, 185, 129, 0.3);
}
.achieve-locked {
background: rgba(107, 114, 128, 0.08);
border: 1px solid rgba(107, 114, 128, 0.15);
opacity: 0.5;
}
.achieve-icon {
font-size: 2rem;
flex-shrink: 0;
width: 3rem;
text-align: center;
}
.achieve-info {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.achieve-name {
font-family: var(--font);
font-size: 1rem;
font-weight: 600;
color: var(--color-grey);
}
.achieve-desc {
font-family: var(--font);
font-size: 0.85rem;
color: var(--color-grey);
opacity: 0.7;
}
/* ── Legal / Cookie pages ── */
.mentionslegales {
width: 100%;
margin: 0 auto;
max-width: 1280px;
font-family: var(--font);
display: flex;
flex-direction: column;
gap: 3rem;
padding: 15rem 1rem 4rem;
}
.mentionslegales h2 {
font-family: var(--font);
font-size: 2rem;
font-weight: 600;
color: var(--color-grey);
}
}

View File

@@ -1,5 +1,6 @@
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import "./index.css";
import App from "./App";
import Landing from "./pages/Landing";
import Home from "./pages/Home";

View File

@@ -1,5 +1,4 @@
import { Link } from "react-router-dom";
import "../scss/pages.scss";
import Lottie from "react-lottie-player";
import animation404 from "../data/404-animation.json";

View File

@@ -1,6 +1,5 @@
import { useGameStore } from "../store/useGameStore";
import { ACHIEVEMENTS } from "../data/achievements";
import "../scss/achievements.scss";
function Achievements() {
const state = useGameStore((s) => s.state);

View File

@@ -3,7 +3,6 @@ import { useNavigate, Link } from "react-router-dom";
import { exchangeCode, loadVerifier, clearVerifier } from "../lib/oauth";
import { apiFetch } from "../lib/api";
import { useAuth } from "../context/AuthContext";
import "../scss/pages.scss";
export default function AuthCallback() {
const navigate = useNavigate();

View File

@@ -1,4 +1,3 @@
import "../scss/Cookie.scss";
function Cookie() {
return (
<div className="container">

View File

@@ -11,8 +11,6 @@ import { EvolutionTree } from "../components/EvolutionTree";
import { MilestoneBar } from "../components/MilestoneBar";
import { CockpitHeader } from "../components/CockpitHeader";
import { ACHIEVEMENTS } from "../data/achievements";
import "../scss/home.scss";
import "../scss/components/game-panels.scss";
export default function Home() {
const [toggleRain] = useOutletContext();
@@ -113,7 +111,7 @@ export default function Home() {
if (!ready) {
return (
<section className="game-container">
<p style={{ textAlign: "center", color: "#6b7a99", marginTop: "20vh" }}>
<p className="text-center text-slate-400 mt-[20vh]">
Chargement de ta progression...
</p>
</section>

View File

@@ -1,4 +1,3 @@
import "../scss/Legal.scss";
function Legal() {
return (
<div className="mentionslegales">

View File

@@ -2,7 +2,6 @@ import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useAuth } from "../context/AuthContext";
import { buildAuthUrl, saveVerifier } from "../lib/oauth";
import "../scss/pages.scss";
const PROVIDERS = [
{ id: "discord", label: "Discord", emoji: "🎮" },
@@ -31,7 +30,7 @@ export default function Login() {
<div className="containererror">
<h1>Connexion</h1>
<p className="message">Connecte-toi pour sauvegarder ta progression.</p>
<div style={{ display: "flex", flexDirection: "column", gap: 8, marginTop: 16 }}>
<div className="flex flex-col gap-2 mt-4">
{PROVIDERS.map((p) => (
<button
key={p.id}

View File

@@ -1,6 +1,5 @@
import { useEffect, useState } from "react";
import { useAuth } from "../context/AuthContext";
import "../scss/pages.scss";
const OAUTH_URL = import.meta.env.VITE_OAUTH_URL || "";
const PROVIDERS = ["discord", "github", "google", "twitch"];
@@ -87,7 +86,6 @@ export default function Settings() {
returnUrl: `${window.location.origin}/settings`,
}),
});
// Redirect to OAuth provider
window.location.href = data.data.authUrl;
} catch (e) {
setError(e.message);
@@ -134,30 +132,28 @@ export default function Settings() {
return (
<section>
<div className="containererror" style={{ maxWidth: 500 }}>
<div className="containererror max-w-[500px]">
<h1>Paramètres</h1>
{error && (
<p style={{ color: "#ef4444", fontSize: 13, marginBottom: 16 }}>
{error}
</p>
<p className="text-red-500 text-[13px] mb-4">{error}</p>
)}
{/* Profile info */}
{profile && (
<div style={{ marginBottom: 24, textAlign: "left" }}>
<p style={{ fontSize: 14, color: "#9ca3af", margin: "4px 0" }}>
<div className="mb-6 text-left">
<p className="text-sm text-gray-400 my-1">
<strong>Pseudo :</strong> {profile.nickname}
</p>
<p style={{ fontSize: 14, color: "#9ca3af", margin: "4px 0" }}>
<p className="text-sm text-gray-400 my-1">
<strong>Email :</strong> {profile.email || "—"}
</p>
</div>
)}
{/* Linked providers */}
<h2 style={{ fontSize: 18, marginBottom: 12 }}>Comptes liés</h2>
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<h2 className="text-lg mb-3">Comptes liés</h2>
<div className="flex flex-col gap-2">
{PROVIDERS.map((provider) => {
const linked = linkedNames.has(provider);
const isLoading = actionLoading === provider;
@@ -165,39 +161,32 @@ export default function Settings() {
return (
<div
key={provider}
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "8px 12px",
background: linked ? "#1a2a1a" : "#1a1a2a",
borderRadius: 8,
border: `1px solid ${linked ? "#2a4a2a" : "#2a2a4a"}`,
}}
className={`flex items-center justify-between px-3 py-2 rounded-lg border ${
linked
? "bg-[#1a2a1a] border-[#2a4a2a]"
: "bg-[#1a1a2a] border-[#2a2a4a]"
}`}
>
<span style={{ fontSize: 14 }}>
<span className="text-sm">
{EMOJIS[provider]} {provider.charAt(0).toUpperCase() + provider.slice(1)}
{linked && (
<span style={{ color: "#4ade80", fontSize: 12, marginLeft: 8 }}>
lié
</span>
<span className="text-green-400 text-xs ml-2"> lié</span>
)}
</span>
{linked ? (
<button
className="btn-return"
style={{ fontSize: 12, padding: "4px 10px", opacity: canUnlink ? 1 : 0.4 }}
className="btn-return text-xs! py-1! px-2.5!"
disabled={!canUnlink || isLoading}
onClick={() => handleUnlink(provider)}
type="button"
style={{ opacity: canUnlink ? 1 : 0.4 }}
>
{isLoading ? "..." : "Délier"}
</button>
) : (
<button
className="btn-return"
style={{ fontSize: 12, padding: "4px 10px" }}
className="btn-return text-xs! py-1! px-2.5!"
disabled={isLoading}
onClick={() => handleLink(provider)}
type="button"
@@ -212,8 +201,7 @@ export default function Settings() {
{/* Logout */}
<button
className="btn-return"
style={{ marginTop: 24, width: "100%" }}
className="btn-return mt-6 w-full!"
onClick={logout}
type="button"
>

View File

@@ -1,3 +0,0 @@
a {
text-decoration: none;
}

View File

@@ -1,19 +0,0 @@
.container {
width: 100%;
margin: 0 auto;
max-width: 1280px;
font-family: var(--font);
display: flex;
flex-direction: column;
gap: 3rem;
padding: 15rem 1rem 4rem;
}
h2 {
font-family: var(--font);
font-size: 2rem;
font-weight: 600;
color: var(--color-grey);
}

View File

@@ -1,19 +0,0 @@
.mentionslegales {
width: 100%;
margin: 0 auto;
max-width: 1280px;
font-family: var(--font);
display: flex;
flex-direction: column;
gap: 3rem;
padding: 15rem 1rem 4rem;
}
h2 {
font-family: var(--font);
font-size: 2rem;
font-weight: 600;
color: var(--color-grey);
}

View File

@@ -1,98 +0,0 @@
.fullachieve {
display: flex;
flex-direction: column;
padding-top: 6rem;
padding-bottom: 3rem;
background-color: var(--color-blue-light);
width: 100%;
max-width: 1280px;
margin: 0 auto;
min-height: 80vh;
h1 {
text-align: center;
font-family: var(--font);
font-size: 2.5rem;
color: var(--color-grey);
margin-bottom: 0.5rem;
}
}
.achieve-counter {
text-align: center;
font-family: var(--font);
font-size: 1.1rem;
color: var(--color-grey);
opacity: 0.7;
margin-bottom: 2rem;
}
.achievementscontainer {
margin: auto;
display: flex;
align-items: center;
width: 100%;
padding: 0 2rem;
}
.achievementscardcontainer {
display: flex;
justify-content: center;
flex-wrap: wrap;
min-height: 200px;
gap: 1rem;
width: 100%;
}
.achieve-card {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem 1.2rem;
border-radius: 0.75rem;
width: 100%;
max-width: 380px;
transition: transform 0.15s ease;
&:hover {
transform: translateY(-2px);
}
}
.achieve-unlocked {
background: rgba(16, 185, 129, 0.12);
border: 1px solid rgba(16, 185, 129, 0.3);
}
.achieve-locked {
background: rgba(107, 114, 128, 0.08);
border: 1px solid rgba(107, 114, 128, 0.15);
opacity: 0.5;
}
.achieve-icon {
font-size: 2rem;
flex-shrink: 0;
width: 3rem;
text-align: center;
}
.achieve-info {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.achieve-name {
font-family: var(--font);
font-size: 1rem;
font-weight: 600;
color: var(--color-grey);
}
.achieve-desc {
font-family: var(--font);
font-size: 0.85rem;
color: var(--color-grey);
opacity: 0.7;
}

View File

@@ -1,46 +0,0 @@
.primary-button {
display: flex;
padding: 0.6rem 1rem;
height: fit-content;
background-color: var(--color-red-light);
border-radius: 0.6rem;
justify-content: center;
text-decoration: none;
font-family: var(--font);
color: var(--color-white) !important;
text-align: center;
font-size: 1rem;
font-weight: 400;
transition: transform 0.1s ease-in-out;
border: none;
&:hover {
transform: scale(0.95);
background-color: var(--color-red-light);
}
}
.secondary-button {
display: flex;
padding: 1rem 1rem;
background-color: var(--color-white);
border-radius: 0.6rem;
justify-content: center;
width: fit-content;
height: fit-content;
text-decoration: none;
font-family: var(--font);
color: var(--color-grey)!important;
text-align: center;
font-size: 1rem;
transition: transform 0.1s ease-in-out;
border: none;
&:hover {
transform: scale(0.95);
background-color: var(--color-grey-hover);
}
}

View File

@@ -1,105 +0,0 @@
.footer {
display: flex;
flex-direction: column;
position: relative;
align-items: center;
bottom: 0;
left: 0;
width: 100%;
background-color: var(--bg-color);
border-top: solid 1px var(--color-grey);
padding: 2rem 0;
gap: 2rem;
.footer-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
width: 90%;
gap: 2rem;
}
.footer-logo {
background-image: url(/svg/tadpole.svg);
background-size: contain;
background-position: center;
background-repeat: no-repeat;
width: 250px;
height: 100px;
transition: all 0.15s ease-in-out;
&:hover {
transform: scale(0.9);
}
}
.section {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 1.4rem;
.section-title {
font-family: var(--font);
font-size: 1.2rem;
color: var(--color-grey);
text-decoration-line: underline;
text-underline-offset: 0.5rem;
}
.section-text {
max-width: 26ch;
font-family: var(--font);
font-size: 1rem;
color: var(--color-grey);
}
.section-list {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 1rem;
list-style: none;
.section-item,
a {
width: fit-content;
font-family: var(--font);
font-size: 1rem;
color: var(--color-grey);
transition: all 0.15s ease-in-out;
&:hover {
transform: scale(0.9);
}
}
}
}
.spacing {
min-width: 150px;
width: 10%;
}
.footer-github {
font-family: var(--font);
font-size: 0.9rem;
font-weight: 500;
color: var(--color-grey);
text-decoration: none;
transition: all 0.15s ease-in-out;
&:hover {
transform: scale(0.95);
}
}
.copyright {
font-family: var(--font);
font-size: 0.8rem;
font-weight: 300;
color: var(--color-grey);
text-align: center;
}
}

View File

@@ -1,170 +0,0 @@
// game-panels.scss — Système de style partagé pour tous les panels du cockpit
// Modifier les tokens dans root.scss, pas ici.
// --- Panel de base ---
.gp {
display: flex;
flex-direction: column;
gap: var(--gp-gap);
padding: var(--gp-padding);
background: var(--gp-bg);
backdrop-filter: blur(8px);
border: 1px solid var(--gp-border);
border-radius: var(--gp-radius);
}
.gp-title {
font-family: var(--font);
font-size: var(--gp-title);
font-weight: 700;
color: var(--gp-text-color);
letter-spacing: 0.02em;
text-transform: uppercase;
}
.gp-label {
font-family: var(--font);
font-size: var(--gp-text-sm);
font-weight: 500;
color: var(--gp-text-muted);
}
.gp-value {
font-family: var(--font);
font-size: var(--gp-text);
font-weight: 600;
color: var(--gp-text-color);
}
.gp-accent-green { color: var(--gp-accent-green); }
.gp-accent-purple { color: var(--gp-accent-purple); }
.gp-accent-amber { color: var(--gp-accent-amber); }
// --- Row item (générateur, noeud évolution) ---
.gp-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.4rem;
padding: 0.4rem 0.5rem;
border-radius: calc(var(--gp-radius) - 0.15rem);
border: 1px solid transparent;
transition: background 0.15s ease, border-color 0.15s ease;
}
.gp-row--active {
border-color: rgba(16, 185, 129, 0.3);
background: var(--gp-accent-green-bg);
&:hover {
background: rgba(16, 185, 129, 0.18);
}
}
.gp-row--locked {
border-color: var(--gp-border);
background: rgba(255, 255, 255, 0.02);
opacity: 0.5;
}
.gp-row--evolution {
border-color: rgba(251, 191, 36, 0.3);
background: var(--gp-accent-amber-bg);
}
.gp-row--unlocked {
border-color: rgba(16, 185, 129, 0.3);
background: var(--gp-accent-green-bg);
}
// --- Bouton achat ---
.gp-btn {
font-family: var(--font);
font-size: var(--gp-text-sm);
font-weight: 600;
padding: 0.3rem 0.6rem;
border-radius: 0.4rem;
border: none;
cursor: pointer;
transition: background 0.15s ease;
white-space: nowrap;
}
.gp-btn--buy {
background: var(--gp-btn-bg);
color: white;
&:hover {
background: var(--gp-btn-bg-hover);
}
}
.gp-btn--disabled {
background: var(--gp-btn-disabled);
color: var(--gp-btn-text-disabled);
cursor: not-allowed;
}
.gp-btn--prestige {
background: #7c3aed;
color: white;
padding: 0.4rem 0.8rem;
font-size: var(--gp-text);
animation: gp-pulse 2s ease-in-out infinite;
&:hover {
background: #8b5cf6;
}
}
@keyframes gp-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(124, 58, 237, 0.4); }
50% { box-shadow: 0 0 0 6px rgba(124, 58, 237, 0); }
}
// --- Header cockpit (stats résumé) ---
.gp-cockpit-header {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0.2rem;
padding: 0.5rem;
}
.gp-stat {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.05rem;
}
// --- Progress bar ---
.gp-progress {
height: 0.35rem;
background: rgba(255, 255, 255, 0.08);
border-radius: 1rem;
overflow: hidden;
}
.gp-progress-fill {
height: 100%;
border-radius: 1rem;
transition: width 0.5s ease;
}
// --- Section separator ---
.gp-sep {
height: 1px;
background: var(--gp-border);
margin: 0.15rem 0;
}
// --- Zone titles in sidebar ---
.gp-zone-label {
font-family: var(--font);
font-size: var(--gp-text-sm);
font-weight: 600;
color: var(--gp-text-muted);
text-transform: uppercase;
letter-spacing: 0.06em;
padding-left: 0.2rem;
}

View File

@@ -1,306 +0,0 @@
.header-main {
display: flex;
justify-content: space-between;
position: absolute;
width: 100%;
height: 80px;
padding: 0rem 2rem;
top: 0;
background-color: var(--bg-color);
background-blend-mode: darken;
background-size: cover;
z-index: 99;
box-sizing: border-box;
@media (max-width: 999px) {
padding: 0rem 0.4rem;
box-sizing: border-box;
}
}
.logo {
width: 5rem;
content: url(/svg/tadpole.svg);
transition: 0.2s;
&:hover {
width: 5rem;
transition: 0.2s;
transform: scale(0.9);
}
}
.navbar {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 1rem;
box-sizing: border-box;
cursor: pointer;
.nav-list {
display: flex;
justify-content: space-between;
gap: 1.6rem;
align-items: center;
@media screen and (max-width: 999px) {
display: none;
}
li {
list-style: none;
font-family: var(--font);
font-weight: 300;
font-size: 1rem;
color: var(--color-white);
height: 100%;
.mainLink {
text-decoration: none;
color: var(--color-grey);
font-weight: 500;
padding: 30px 0;
&:hover {
color: var(--color-red-light);
font-weight: 500;
}
}
.dropLink {
text-decoration: none;
color: var(--color-white);
font-weight: 400;
&:hover {
color: var(--color-red-light);
font-weight: 400;
}
}
}
}
ul {
list-style-type: none;
@media screen and (max-width: 999px) {
color: var(--color-white) !important;
}
li {
float: left;
width: fit-content;
.dropdown-content {
display: none;
position: absolute;
background: var(--color-black);
transform: translateY(30px);
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(10, 10, 10, 0.2);
z-index: 1;
}
li.dropdown {
display: inline-block;
}
a {
color: var(--color-white);
width: fit-content;
}
a:hover,
.dropdown:hover {
color: var(--color-red-light);
font-weight: 400;
}
}
}
.dropdown-content a {
color: var(--color-black);
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
&:hover {
background-color: var(--color-black);
color: var(--color-gold);
}
}
.dropdown:hover .dropdown-content {
display: block;
}
}
@media screen and (min-width: 1000px) {
.menuToggle {
display: none;
}
}
@media screen and (max-width: 999px) {
.menuToggle {
float: left;
position: relative;
box-sizing: border-box;
top: 2px;
left: -10px;
z-index: 99;
-webkit-user-select: none;
user-select: none;
}
.menuToggle a {
text-decoration: none;
color: var(--color-grey);
transition: color 0.3s ease;
}
.menuToggle a:hover {
color: var(--color-red-light);
}
.menuToggle input {
display: block;
width: 40px;
height: 32px;
position: absolute;
top: -7px;
left: -5px;
cursor: pointer;
opacity: 0;
z-index: 2;
-webkit-touch-callout: none;
}
.menuToggle span {
display: block;
width: 33px;
height: 4px;
margin-bottom: 5px;
position: relative;
background: var(--color-grey);
border-radius: 3px;
z-index: 1;
transform-origin: 4px 0px;
transition: transform 0.2s cubic-bezier(0.77, 0.2, 0.05, 1),
background 0.2s cubic-bezier(0.77, 0.2, 0.05, 1), opacity 0.55s ease;
}
.menuToggle span:first-child {
transform-origin: 0% 0%;
}
.menuToggle span:nth-last-child(2) {
transform-origin: 0% 100%;
}
.menuToggle input:checked ~ span {
opacity: 1;
transform: rotate(45deg) translate(-2px, -1px);
background: var(--color-white);
}
.menuToggle input:checked ~ span:nth-last-child(3) {
opacity: 0;
transform: rotate(0deg) scale(0.2, 0.2);
}
.menuToggle input:checked ~ span:nth-last-child(2) {
transform: rotate(-45deg) translate(0, -1px);
}
.menu {
position: absolute;
display: flex;
flex-direction: column;
width: 280px;
height: 110vh;
margin: -100px 0 0 -231px;
padding: 1.2rem;
padding-top: 100px;
background: var(--color-grey);
list-style-type: none;
transform-origin: 0% 0%;
overflow: hidden !important;
visibility: hidden;
opacity: 0%;
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.menu li {
padding: 10px 0;
font-size: 1.2rem;
font-family: var(--font);
font-weight: 500;
color: var(--color-white);
}
.menuToggle input:checked ~ ul {
visibility: visible;
opacity: 100;
}
.sousmenu {
display: flex;
flex-direction: column;
margin-left: 1.2rem;
color: var(--color-white);
font-size: 1.2rem;
font-family: var(--font);
font-weight: 500;
padding-bottom: 1rem;
}
.empty {
line-height: 20rem;
}
}
.auth-nav {
display: flex;
align-items: center;
gap: 0.6rem;
font-family: var(--font);
.auth-nickname {
font-size: 0.9rem;
font-weight: 500;
color: var(--color-grey);
}
.auth-btn {
padding: 0.3rem 0.8rem;
border: 1px solid var(--color-grey);
border-radius: 0.4rem;
background: none;
font-family: var(--font);
font-size: 0.8rem;
color: var(--color-grey);
cursor: pointer;
transition: all 0.15s ease;
&:hover {
background: var(--color-grey);
color: var(--color-white);
}
}
}

View File

@@ -1,133 +0,0 @@
// home.scss — Game view styles (layout géré par zones.scss)
// --- Clicker zone ---
.click-zone {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
gap: 1rem;
padding-bottom: 2vh;
cursor: pointer;
flex: 1;
// Desktop: center
@media (min-width: 768px) {
padding-right: 22rem; // offset for sidebar
}
}
.tadpole-sprite {
width: 280px;
height: 280px;
background: url("/svg/tadpole.svg") no-repeat center / contain;
transition: transform 0.1s ease;
@media (min-width: 768px) {
width: 320px;
height: 320px;
}
.click-zone:active & {
transform: scale(0.95) rotate(2deg);
}
}
// --- Compteur ressources sous le têtard ---
.click-zone-counter {
font-family: var(--font);
font-size: 2rem;
font-weight: 800;
color: white;
text-shadow: 0 0 12px rgba(52, 211, 153, 0.5), 0 2px 6px rgba(0, 0, 0, 0.7);
pointer-events: none;
user-select: none;
letter-spacing: 0.02em;
@media (min-width: 768px) {
font-size: 2.5rem;
}
}
// --- Badge achievements sidebar ---
.achieve-badge {
display: block;
text-align: center;
padding: 0.4rem;
border-radius: var(--gp-radius);
background: var(--gp-accent-green-bg);
border: 1px solid rgba(16, 185, 129, 0.2);
font-family: var(--font);
font-size: var(--gp-text-sm);
font-weight: 600;
color: var(--gp-accent-green);
text-decoration: none;
transition: all 0.15s ease;
&:hover {
background: rgba(16, 185, 129, 0.2);
}
}
// --- Click feedback particle ---
.click-particle {
position: fixed;
pointer-events: none;
font-family: var(--font);
font-size: 1.6rem;
font-weight: 800;
color: #34d399;
text-shadow: 0 0 8px rgba(52, 211, 153, 0.6), 0 2px 4px rgba(0, 0, 0, 0.7);
z-index: 100;
animation: float-up 1.2s ease-out forwards;
}
@keyframes float-up {
0% {
opacity: 1;
transform: translateY(0) scale(1.2);
}
60% {
opacity: 0.9;
}
100% {
opacity: 0;
transform: translateY(-80px) scale(1.5);
}
}
// --- Game sidebar ---
.game-sidebar {
position: fixed;
right: 0.75rem;
top: 5.5rem;
bottom: 0.75rem;
width: 20rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
overflow-y: auto;
z-index: 10;
padding-right: 0.25rem;
// Mobile: bottom drawer
@media (max-width: 767px) {
position: fixed;
right: 0;
left: 0;
top: auto;
bottom: 0;
width: 100%;
max-height: 45vh;
padding: 0.75rem;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(8px);
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
}
}

View File

@@ -1,171 +0,0 @@
.container {
display: flex;
flex-direction: column;
max-width: 132ch;
width: 80%;
gap: 3rem;
margin: 150px auto 50px;
h1 {
font-family: var(--font);
color: var(--color-black);
font-size: 1.8rem;
text-align: center;
width: fit-content;
}
.separator {
border: solid 1px var(--color-gold-hover);
}
.massageinfo {
display: flex;
flex-direction: column;
gap: 0.4rem;
.info {
font-family: var(--font);
color: var(--color-grey);
font-size: 1rem;
font-weight: 400;
}
}
.subtitle {
font-family: var(--font);
color: var(--color-black);
font-size: 1.2rem;
font-weight: 600;
text-align: left;
margin-bottom: 0.8rem;
}
.content {
display: flex;
flex-direction: column;
gap: 0.6rem;
.subtitle {
font-family: var(--font);
color: var(--color-black);
font-size: 1.2rem;
font-weight: 600;
text-align: left;
margin-bottom: 0.5rem;
}
.paragraphe {
font-family: var(--font);
color: var(--color-grey);
font-size: 1rem;
font-weight: 400;
margin-bottom: 0.5rem;
list-style: inside;
a {
font-family: var(--font);
color: var(--color-gold-link);
font-size: 1rem;
font-weight: 600;
margin-bottom: 0.5rem;
list-style: inside;
text-decoration: none;
&:hover {
color: var(--color-gold-hover);
}
}
}
.picture-container {
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 1rem;
@media (max-width: 449px) {
flex-direction: column;
}
.picture {
background-position: center;
background-size: cover;
width: 100%;
height: 300px;
}
}
}
//Massages pages
.listing-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 3rem;
.listcontent {
display: flex;
flex-direction: column;
.listdetail {
font-family: var(--font);
color: var(--color-grey);
font-size: 0.95rem;
font-weight: 400;
margin-bottom: 0.5rem;
list-style-type: none;
padding-left: 1rem;
}
}
}
}
//error pages
section {
display: flex;
flex-direction: column;
height: 90vh;
justify-content: center;
width: 100%;
.containererror {
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
h1 {
font-family: var(--font);
color: var(--color-black);
font-size: 2rem;
text-align: center;
width: fit-content;
}
.message {
font-family: var(--font);
color: var(--color-grey);
font-size: 1rem;
font-weight: 300;
text-align: center;
}
.btn-return {
display: flex;
justify-content: center;
width: fit-content;
margin: auto;
padding: 0.5rem 1rem;
background-color: var(--color-grey);
border: none;
border-radius: 0.6rem;
font-family: var(--font);
color: var(--color-white);
font-size: 1rem;
font-weight: 600;
cursor: pointer;
&:hover {
background-color: var(--color-grey);
transform: scale(0.9);
}
}
}
}

View File

@@ -1,60 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
:root {
--color-blue-light: #dcecf3;
--color-purple-light: #e4e3f3;
--color-red-light: #c33636;
--color-white: #ffffff;
--color-light: #eaeaea;
--color-grey: #202020;
--color-grey-hover: #606060;
--bg-color: var(--color-blue-light);
--font: "Hanken Grotesk", sans-serif;
// --- Game panel tokens ---
--gp-bg: rgba(17, 17, 17, 0.75);
--gp-bg-hover: rgba(17, 17, 17, 0.85);
--gp-border: rgba(255, 255, 255, 0.08);
--gp-radius: 0.75rem;
--gp-padding: 0.75rem;
--gp-gap: 0.5rem;
// Text
--gp-title: 0.8rem;
--gp-text: 0.75rem;
--gp-text-sm: 0.65rem;
--gp-text-color: rgba(255, 255, 255, 0.9);
--gp-text-muted: rgba(255, 255, 255, 0.5);
// Accent colors
--gp-accent-green: #34d399;
--gp-accent-purple: #a78bfa;
--gp-accent-amber: #fbbf24;
--gp-accent-green-bg: rgba(16, 185, 129, 0.12);
--gp-accent-purple-bg: rgba(139, 92, 246, 0.12);
--gp-accent-amber-bg: rgba(251, 191, 36, 0.12);
// Buttons
--gp-btn-bg: #059669;
--gp-btn-bg-hover: #10b981;
--gp-btn-disabled: rgba(255, 255, 255, 0.08);
--gp-btn-text-disabled: rgba(255, 255, 255, 0.3);
}
a {
text-decoration: none;
}
main {
min-height: 92vh;
margin-top: 80px;
padding: 0 0 2rem;
background-color: var(--bg-color);
}

View File

@@ -1,41 +0,0 @@
// zones.scss — Système de zones visuelles
// Chaque page déclare sa zone via data-zone sur le wrapper.
// Le background, les tons, l'ambiance changent — navbar/footer restent fixes.
//
// Usage : <div className="zone" data-zone="swamp"> ... </div>
// Ajouter un nouveau biome : juste un nouveau [data-zone="xxx"] ici.
.zone {
width: 100%;
min-height: 92vh;
position: relative;
display: flex;
align-items: flex-end;
justify-content: center;
background-size: cover;
background-repeat: no-repeat;
transition: background-image 0.5s ease;
}
// --- Biomes ---
[data-zone="swamp"] {
background-image: url("/webp/bg-cover.webp");
background-position: center 70%;
}
[data-zone="landing"] {
background: var(--bg-color);
align-items: center;
}
[data-zone="page"] {
background: var(--bg-color);
align-items: flex-start;
min-height: auto;
}
// Futur :
// [data-zone="cave"] { background-image: url("/webp/bg-cave.webp"); }
// [data-zone="volcano"] { background-image: url("/webp/bg-volcano.webp"); }
// [data-zone="ocean"] { background-image: url("/webp/bg-ocean.webp"); }