Repository navigation
Expand file tree
/
Copy pathapp.css
More file actions
242 lines (215 loc) · 15.2 KB
/
Copy pathapp.css
File metadata and controls
242 lines (215 loc) · 15.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/* Neon arcade extras on top of Tailwind CDN. */
/* ---------- Type system ---------- Inter for body, Sora for display headings. */
body { font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -webkit-font-smoothing: antialiased; }
.font-display { font-family: 'Sora', 'Inter', system-ui, sans-serif; letter-spacing: -0.015em; }
/* ---------- Glass surfaces ---------- layered cards/panels with depth. */
.glass-card {
background: linear-gradient(180deg, rgba(30, 41, 59, 0.72), rgba(15, 23, 42, 0.72));
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
html[data-mode="light"] .glass-card {
background: linear-gradient(180deg, #ffffff, #eef2f7);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9), 0 1px 2px rgba(15, 23, 42, 0.06);
}
/* Hide number-input spinners so digit fields look clean. */
.digit-input::-webkit-outer-spin-button,
.digit-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.digit-input { -moz-appearance: textfield; }
/* Coin-toss flip. */
@keyframes flip { 0% { transform: rotateX(0); } 50% { transform: rotateX(90deg); } 100% { transform: rotateX(0); } }
.coin-flip { animation: flip 0.4s linear 5; }
/* Prevent accidental text selection / callout on rapid tapping of game cells. */
button { -webkit-tap-highlight-color: transparent; user-select: none; }
/* Keyboard focus: visible outline (was invisible before). */
button:focus-visible, a:focus-visible, select:focus-visible, [tabindex]:focus-visible {
outline: 2px solid var(--acc2, #6366f1); outline-offset: 2px; border-radius: 6px;
}
/* Screen enter transition (applied by show()). */
@keyframes screenEnter { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.screen-enter { animation: screenEnter 0.22s ease-out; }
@media (prefers-reduced-motion: reduce) { .screen-enter { animation: none; } }
/* Staggered card entrance on the home grid (delay set inline per index). */
@keyframes cardIn { from { opacity: 0; transform: translateY(10px) scale(0.98); } to { opacity: 1; transform: none; } }
.card-in { animation: cardIn 0.32s cubic-bezier(0.22, 1, 0.36, 1) both; }
/* Result bar / summary pop-in. */
@keyframes popIn { from { opacity: 0; transform: translateY(8px) scale(0.97); } to { opacity: 1; transform: none; } }
.pop-in { animation: popIn 0.3s cubic-bezier(0.22, 1, 0.36, 1); }
/* Win celebration — confetti fall + banner, held in one #fx-layer that the engine clears on every (re)start. */
@keyframes confettiFall {
0% { transform: translateY(-12vh) rotate(0deg); opacity: 1; }
100% { transform: translateY(112vh) rotate(720deg); opacity: 0; }
}
.fx-layer { position: fixed; inset: 0; z-index: 25; pointer-events: none; overflow: hidden; }
.confetti-piece { position: absolute; top: 0; font-size: 1.4rem; will-change: transform; animation: confettiFall linear forwards; }
@keyframes winBanner {
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
16% { opacity: 1; transform: translate(-50%, -50%) scale(1.08); }
28% { transform: translate(-50%, -50%) scale(1); }
80% { opacity: 1; }
100% { opacity: 0; transform: translate(-50%, -62%) scale(1); }
}
.win-banner { position: absolute; top: 34%; left: 50%; font-family: 'Sora', system-ui, sans-serif; font-weight: 800;
font-size: clamp(1.6rem, 6vw, 2.6rem); color: #fff; text-shadow: 0 2px 16px rgba(0,0,0,0.55); white-space: nowrap;
animation: winBanner 2.4s cubic-bezier(0.22, 1, 0.36, 1) forwards; }
/* ---------- Animated 2048 tile board (shared by 2048 + 2048 Levels) ---------- */
.tb-board { position: relative; width: 100%; max-width: min(26rem, 92vw); aspect-ratio: 1; margin: 0 auto; container-type: inline-size; border-radius: 0.9rem; background: #0f172a; box-shadow: 0 10px 30px -12px rgba(0,0,0,0.6), inset 0 0 0 1px rgba(148,163,184,0.08); touch-action: none; }
.tb-bg, .tb-layer { position: absolute; inset: 0; }
.tb-cell { position: absolute; border-radius: 2.5cqw; background: rgba(148,163,184,0.10); }
.tb-cell.tb-wall { background: #020617; box-shadow: inset 0 0 0 2px rgba(148,163,184,0.28); }
.tb-tile { position: absolute; transition: left 0.1s ease-in-out, top 0.1s ease-in-out; }
.tb-tile-in { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: var(--tb-fs, 8cqw); border-radius: 2.5cqw; color: #f8fafc; line-height: 1; box-shadow: inset 0 -3px 0 rgba(0,0,0,0.14), 0 1px 3px rgba(0,0,0,0.28); }
.tb-tile-sel { outline: 3px solid #fff; outline-offset: -3px; }
.tb-notrans .tb-tile { transition: none !important; }
@keyframes tbPop { 0% { transform: scale(0.1); opacity: 0.2; } 60% { transform: scale(1.08); } 100% { transform: scale(1); opacity: 1; } }
.tb-pop { animation: tbPop 0.16s ease-out; }
@keyframes tbMerge { 0% { transform: scale(1); } 45% { transform: scale(1.18); } 100% { transform: scale(1); } }
.tb-merge { animation: tbMerge 0.16s ease-in-out; }
.tb-frozen { position: relative; filter: saturate(0.5) brightness(0.92); box-shadow: inset 0 0 0 3px rgba(125,211,252,0.75); }
.tb-frozen::after { content: '\2744\FE0E'; position: absolute; top: 2px; right: 4px; font-size: 0.7rem; opacity: 0.85; }
/* ----- 2048 tool effects ----- */
.fx-glow { box-shadow: 0 0 0 3px rgba(255,255,255,0.7), 0 0 22px 6px rgba(125,211,252,0.8) !important; }
@keyframes fxRing { from { transform: translate(-50%, -50%) scale(0.25); opacity: 0.95; } to { transform: translate(-50%, -50%) scale(2.6); opacity: 0; } }
.fx-ring { position: absolute; transform: translate(-50%, -50%); border-radius: 50%; border: 3px solid #fff; pointer-events: none; animation: fxRing 0.52s ease-out forwards; }
@keyframes fxStar { 0% { transform: translate(-50%, -50%) scale(0.3); opacity: 1; } 100% { transform: translate(-50%, -50%) scale(1.3); opacity: 0; } }
.fx-star { position: absolute; transform: translate(-50%, -50%); pointer-events: none; font-size: 0.95rem; animation: fxStar 0.65s ease-out forwards; }
.fx-frag { position: absolute; transform: translate(-50%, -50%); border-radius: 2px; pointer-events: none; }
@keyframes fxAura { 0% { box-shadow: 0 0 0 0 rgba(255,255,255,0.9); } 100% { box-shadow: 0 0 0 16px rgba(255,255,255,0); } }
.fx-aura { animation: fxAura 0.5s ease-out; }
@keyframes fxFlip { 0% { transform: rotateX(0); } 50% { transform: rotateX(85deg); filter: blur(1px); } 100% { transform: rotateX(0); } }
.fx-flip { animation: fxFlip 0.3s ease-in-out; }
@media (prefers-reduced-motion: reduce) { .tb-tile { transition: none; } .tb-pop, .tb-merge { animation: none; } .fx-ring, .fx-star, .fx-frag, .fx-aura, .fx-flip { animation: none !important; display: none; } .fx-glow { box-shadow: none !important; } }
/* Connect Four disc drop (applied to the just-placed cell). */
@keyframes discDrop { 0% { transform: translateY(-450%); } 70% { transform: translateY(0); } 84% { transform: translateY(-11%); } 100% { transform: translateY(0); } }
.disc-drop { animation: discDrop 0.4s cubic-bezier(0.3, 0.7, 0.4, 1); }
@media (prefers-reduced-motion: reduce) {
.card-in, .pop-in, .disc-drop { animation: none; }
.fx-layer { display: none; }
}
/* ---------- Theme accent ---------- recolor the indigo accent per data-theme.
Attribute-scoped selectors outrank Tailwind's plain utilities, so no markup changes. */
html[data-theme="indigo"] { --acc:#4f46e5; --acc2:#6366f1; --acc-t:#818cf8; }
html[data-theme="emerald"] { --acc:#059669; --acc2:#10b981; --acc-t:#34d399; }
html[data-theme="rose"] { --acc:#e11d48; --acc2:#f43f5e; --acc-t:#fb7185; }
html[data-theme="amber"] { --acc:#d97706; --acc2:#f59e0b; --acc-t:#fbbf24; }
html[data-theme="sky"] { --acc:#0284c7; --acc2:#0ea5e9; --acc-t:#38bdf8; }
html[data-theme="violet"] { --acc:#7c3aed; --acc2:#8b5cf6; --acc-t:#a78bfa; }
html[data-theme="teal"] { --acc:#0d9488; --acc2:#14b8a6; --acc-t:#2dd4bf; }
html[data-theme="fuchsia"] { --acc:#c026d3; --acc2:#d946ef; --acc-t:#e879f9; }
html[data-theme="lime"] { --acc:#65a30d; --acc2:#84cc16; --acc-t:#a3e635; }
[data-theme] .bg-indigo-600 { background-color: var(--acc, #4f46e5); }
[data-theme] .hover\:bg-indigo-500:hover { background-color: var(--acc2, #6366f1); }
[data-theme] .text-indigo-400 { color: var(--acc-t, #818cf8); }
[data-theme] .border-indigo-500 { border-color: var(--acc2, #6366f1); }
[data-theme] .hover\:border-indigo-500:hover { border-color: var(--acc2, #6366f1); }
[data-theme] .ring-indigo-500 { --tw-ring-color: var(--acc2, #6366f1); }
[data-theme] .focus\:ring-indigo-500:focus { --tw-ring-color: var(--acc2, #6366f1); }
[data-theme] .hover\:shadow-indigo-500\/60:hover { --tw-shadow-color: var(--acc2, #6366f1); }
/* RTL: keep the mono room code / digit rows visually LTR. */
html[dir="rtl"] .font-mono { direction: ltr; unicode-bidi: embed; }
/* Home hero: subtle animated neon gradient using the current accent. */
.hero-card {
background:
radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--acc2, #6366f1) 35%, transparent), transparent 60%),
radial-gradient(120% 140% at 100% 100%, color-mix(in srgb, var(--acc-t, #818cf8) 22%, transparent), transparent 55%),
#0f172a;
background-size: 180% 180%, 180% 180%, 100% 100%;
animation: heroShift 12s ease-in-out infinite alternate;
}
@keyframes heroShift {
0% { background-position: 0% 0%, 100% 100%, 0 0; }
100% { background-position: 40% 30%, 60% 70%, 0 0; }
}
@media (prefers-reduced-motion: reduce) { .hero-card { animation: none; } }
/* Ambient page aurora: two slow accent-tinted blobs drifting behind everything. Fixed + -z so it
never intercepts taps; alpha kept low so it reads as atmosphere, not clutter. */
body::before {
content: ""; position: fixed; inset: -20%; z-index: -1; pointer-events: none;
background:
radial-gradient(38% 38% at 22% 18%, color-mix(in srgb, var(--acc2, #6366f1) 20%, transparent), transparent 70%),
radial-gradient(42% 42% at 82% 88%, color-mix(in srgb, var(--acc-t, #818cf8) 16%, transparent), transparent 70%);
animation: auroraDrift 26s ease-in-out infinite alternate;
}
@keyframes auroraDrift {
0% { transform: translate3d(-4%, -3%, 0) scale(1); }
100% { transform: translate3d(5%, 4%, 0) scale(1.12); }
}
@media (prefers-reduced-motion: reduce) { body::before { animation: none; } }
/* Horizontal chip row: hide scrollbar. */
#cat-chips::-webkit-scrollbar { display: none; }
#cat-chips { scrollbar-width: none; }
/* ---------- Light mode ---------- remap the core slate surfaces; accent vars still apply.
Attribute-scoped so it outranks Tailwind's plain utilities without touching markup.
Ceiling: individual game boards that hardcode slate may need spot fixes. */
html[data-mode="light"] { color-scheme: light; }
html[data-mode="light"] body,
html[data-mode="light"] .bg-slate-950 { background-color: #f1f5f9; }
html[data-mode="light"] .bg-slate-950\/80 { background-color: rgba(241, 245, 249, 0.85); }
html[data-mode="light"] .bg-slate-900,
html[data-mode="light"] .bg-slate-900\/80 { background-color: #ffffff; }
html[data-mode="light"] .bg-slate-800,
html[data-mode="light"] .bg-slate-800\/70,
html[data-mode="light"] .bg-slate-800\/60 { background-color: #e2e8f0; }
html[data-mode="light"] .bg-slate-700 { background-color: #cbd5e1; }
html[data-mode="light"] .text-slate-100 { color: #0f172a; }
html[data-mode="light"] .text-slate-300,
html[data-mode="light"] .text-slate-300\/80 { color: #334155; }
html[data-mode="light"] .text-slate-400 { color: #475569; }
html[data-mode="light"] .text-slate-500 { color: #64748b; }
html[data-mode="light"] .border-slate-800 { border-color: #e2e8f0; }
html[data-mode="light"] .border-slate-700,
html[data-mode="light"] .border-slate-600 { border-color: #cbd5e1; }
/* ---------- Floating reaction emote ---------- */
@keyframes emoteRise {
0% { transform: translateY(0) scale(0.6); opacity: 0; }
15% { opacity: 1; }
100% { transform: translateY(-160px) scale(1.4); opacity: 0; }
}
.emote-float {
position: absolute; bottom: 20%; z-index: 20;
font-size: 2.75rem; pointer-events: none;
animation: emoteRise 1.6s ease-out forwards;
}
@media (prefers-reduced-motion: reduce) { .emote-float { animation-duration: 0.9s; } }
/* ---------- Winning-cell highlight (ctx.flashWin) ---------- applied after the final paint. */
@keyframes winPulse { 0%, 100% { box-shadow: inset 0 0 0 3px #10b981; } 50% { box-shadow: inset 0 0 0 3px rgba(16, 185, 129, 0.35); } }
.is-win { animation: winPulse 0.75s ease-in-out infinite; border-radius: 0.4rem; z-index: 1; }
@media (prefers-reduced-motion: reduce) { .is-win { animation: none; box-shadow: inset 0 0 0 3px #10b981; } }
/* ---------- Light-mode board surfaces ---------- per-game frame colours the base remap misses. */
html[data-mode="light"] .bg-emerald-900 { background-color: #6ee7b7; }
html[data-mode="light"] .bg-emerald-700 { background-color: #34d399; }
html[data-mode="light"] .bg-amber-950\/40, html[data-mode="light"] .bg-amber-950\/60, html[data-mode="light"] .bg-amber-900\/40 { background-color: #fde68a; }
html[data-mode="light"] .bg-amber-700 { background-color: #f59e0b; }
html[data-mode="light"] .bg-indigo-950 { background-color: #c7d2fe; }
html[data-mode="light"] .bg-slate-900\/60 { background-color: #ffffff; }
/* broader board families so new shades stop breaking light mode */
html[data-mode="light"] #game-root .bg-emerald-800 { background-color: #6ee7b7; }
html[data-mode="light"] #game-root .bg-emerald-600 { background-color: #34d399; }
html[data-mode="light"] #game-root .bg-amber-950, html[data-mode="light"] #game-root .bg-amber-900 { background-color: #fde68a; }
html[data-mode="light"] #game-root .bg-amber-800, html[data-mode="light"] #game-root .bg-amber-600 { background-color: #fbbf24; }
html[data-mode="light"] #game-root .bg-amber-200\/10 { background-color: #f1f5f9; }
html[data-mode="light"] #game-root .bg-indigo-900 { background-color: #c7d2fe; }
/* ---------- Board kit ---------- semantic classes + global depth/animation so all boards feel physical.
Scoped to #game-root so it never touches home/menu chrome. */
/* Every round piece (stone/disc/checker/tile) gets depth + smooth placement/recolour — hits all games. */
#game-root [class*="rounded-full"] {
box-shadow: 0 2px 4px rgba(2, 6, 23, 0.45), inset 0 1px 1px rgba(255, 255, 255, 0.14);
transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.22s ease;
}
/* Cells (buttons) fade their fill instead of snapping. */
#game-root button { transition: background-color 0.16s ease, box-shadow 0.16s ease, transform 0.12s ease; }
#game-root button:active { transform: scale(0.96); }
.board-frame {
border-radius: 1rem;
padding: 0.5rem;
border: 1px solid rgba(148, 163, 184, 0.18);
box-shadow: 0 12px 34px -14px rgba(2, 6, 23, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
html[data-mode="light"] .board-frame { border-color: rgba(15, 23, 42, 0.1); box-shadow: 0 8px 24px -14px rgba(15, 23, 42, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.8); }
.game-piece { box-shadow: 0 2px 5px rgba(2, 6, 23, 0.5), inset 0 1px 1px rgba(255, 255, 255, 0.15); }
@keyframes piecePop { 0% { transform: scale(0.35); opacity: 0; } 70% { transform: scale(1.08); } 100% { transform: scale(1); opacity: 1; } }
.piece-pop { animation: piecePop 0.26s ease-out; }
@keyframes discFlip { 0% { transform: rotateY(0deg); } 50% { transform: rotateY(90deg); } 100% { transform: rotateY(0deg); } }
.disc-flip { animation: discFlip 0.5s ease; }
@media (prefers-reduced-motion: reduce) { .piece-pop, .disc-flip { animation: none; } }