I've found the solution to this issue today by changing my approach, but I'm still unclear what the problem was with the original setup so I wanted to ask the hive mind. Take this page for example.
When you view this page on a tablet/mobile screen, the page scrolls beyond the bottom of the <body> element. If you open dev tools and enable device preview mode and then resize the viewport, all hell breaks loose. This only happens when the menu is closed. The moment you open the menu, everything sorts itself out.
A simple meme generator that lets users upload a picture can be built with a lightweight tech stack and minimal infrastructure. Here's how you could approach it:
🧰 Core Features
Image Upload Users can drag-and-drop or select an image from their device. Use HTML5 <input type="file"> or a drag-and-drop library like Dropzone.js..
Text Overlay Add top and bottom text (classic meme style). Font color: Black or white Fonts: Arial, Impact, Comic Sans, Times New Roman Live Preview : Real-time rendering of the meme.
Preview & Download Show a live preview of the meme. Let users download the final image as PNG or JPEG and/or share on social media (creates a post on tangled)
🧑💻 Suggested Tech Stack
Frontend HTML, CSS, JavaScript (React or Vanilla JS)
Backend (optional) Node.js , Flask, or serverless (e.g., Vercel Functions)
Hosting GitHub Pages, Netlify, or Vercel
🗃️ Do You Need a Database? No, not necessarily. If you're not storing user data or memes long-term, you can skip a database entirely. Temporary storage: Use in-browser memory or session storage. Cloud upload (optional): If you want to let users share memes, you could integrate: Imgur API (free image hosting) Cloudinary (image hosting + transformations)
Html 1. Image Upload <input type="file" accept="image/*" id="upload" />
🧩 Key Features Upload image Add top and bottom text Choose font (Impact, Arial, Comic Sans, Times New Roman) Choose text color (Black or White) Download meme Share to Twitter or Facebook Tangled
🚀 Hosting Instructions (GitHub Pages) Create a GitHub repo called meme-generator. Push your files (index.html, style.css , script.js) to the repo. Go to Settings > Pages and set the source to main branch and /root. Your meme generator will be live at https://yourusername.github.io/meme-generator.
import React, { useState, useEffect, useCallback } from 'react'; import './PharaohsTrivia.css'; // For professional styling
// Assuming image imports are correctly configured: import Coin20 from './images/enhanced_1762428323273.jpg'; import Coin50 from './images/enhanced_1762430071978.jpg'; import Coin100 from './images/unnamed (9).jpg';
// --- Game Data: Questions and Levels Configuration --- const QUIZ_DATA = [ // Level 1: Easy Questions (Reward: 20 EGP) { question: "What is the capital city of Egypt?", options: ["Alexandria", "Luxor", "Cairo", "Aswan"], answer: "Cairo" }, { question: "Which river is often called the 'Lifeblood' of Egypt?", options: ["Tigris", "Euphrates", "Nile", "Jordan"], answer: "Nile" }, ];
const LEVEL_CONFIG = [ { levelId: 1, minScore: 1, // Need at least 1 correct answer to pass Level 1 reward: { value: 20, image: Coin20, message: "Abu Simbel's Treasure!" } }, { levelId: 2, minScore: 2, // Need at least 2 correct answers to pass Level 2 reward: { value: 50, image: Coin50, message: "Giza Pyramids' Wealth!" } }, { levelId: 3, minScore: 3, reward: { value: 100, image: Coin100, message: "Grand Museum's Gold!" } }, ];
// Handles the player selecting an answer const handleAnswerSelect = useCallback((option) => { if (isAnswerSelected) return;
setIsAnswerSelected(true); setSelectedOption(option); // Check if the answer is correct if (option === QUIZ_DATA[currentQuestion].answer) { setScore(prevScore => prevScore + 1); }
}, [currentQuestion, isAnswerSelected]);
// Handles moving to the next question or checking level completion const handleNext = useCallback(() => { if (!isAnswerSelected) return; // Must select an answer first
const nextQuestion = currentQuestion + 1;
if (nextQuestion < totalQuestions) { // Go to next question setCurrentQuestion(nextQuestion); setIsAnswerSelected(false); setSelectedOption(null); } else { // All questions for the current level answered if (score >= currentLevelConfig.minScore) { setGameState('levelComplete'); } else { setGameState('lost'); // Implement a specific loss state if needed } } }, [currentQuestion, totalQuestions, score, currentLevelConfig, isAnswerSelected]);
// Handles moving to the next level const handleNextLevel = useCallback(() => { const nextLevelIndex = levelIndex + 1;
if (nextLevelIndex < LEVEL_CONFIG.length) { // Reset and advance to the next level setLevelIndex(nextLevelIndex); setCurrentQuestion(0); setScore(0); setGameState('playing'); setIsAnswerSelected(false); setSelectedOption(null); } else { // Game over, all levels finished setGameState('finished'); } }, [levelIndex]);
// Helper function for styling options const getOptionStyle = (option) => { if (!isAnswerSelected || selectedOption !== option) { return {}; // Default style } // Once an answer is selected if (option === QUIZ_DATA[currentQuestion].answer) { return styles.correctOption; } else { return styles.incorrectOption; } };