diff --git a/joke-generator/index.html b/joke-generator/index.html
new file mode 100644
index 000000000..c8c3c2966
--- /dev/null
+++ b/joke-generator/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Random Joke Generator
+
+
+
+
+
+ 😂 Random Joke Generator
+
+ Click the button to get a joke!
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/joke-generator/readme.md b/joke-generator/readme.md
new file mode 100644
index 000000000..633be3c3c
--- /dev/null
+++ b/joke-generator/readme.md
@@ -0,0 +1,42 @@
+# 😂 Random Joke Generator
+
+A simple, beginner-friendly web app that fetches and displays a random joke at the click of a button.
+
+## API Used
+
+This project uses the **[Official Joke API](https://official-joke-api.appspot.com/)**:
+
+- Endpoint: `https://official-joke-api.appspot.com/random_joke`
+- No API key or authentication required
+- Returns a JSON object like:
+ ```json
+ {
+ "id": 1,
+ "type": "general",
+ "setup": "What did the ocean say to the beach?",
+ "punchline": "Nothing, it just waved."
+ }
+ ```
+
+## How to Run
+
+1. Clone or download this folder (`mini-projects/joke-generator/`).
+2. Open `index.html` directly in your browser (double-click it, or right-click → "Open with" your browser).
+3. Click the **Generate Joke** button to fetch a new random joke.
+
+No build tools, installations, or servers required — it's plain HTML, CSS, and JavaScript.
+
+## File Structure
+
+```
+joke-generator/
+├── index.html # Page structure
+├── style.css # Styling
+├── script.js # Fetch logic + button interaction
+└── README.md # This file
+```
+
+## Notes for Contributors
+
+- Code is intentionally kept simple and commented for beginners.
+- Feel free to suggest improvements (e.g., adding joke categories, a "copy to clipboard" button, or dark mode) via a new PR.
\ No newline at end of file
diff --git a/joke-generator/script.js b/joke-generator/script.js
new file mode 100644
index 000000000..839a5090f
--- /dev/null
+++ b/joke-generator/script.js
@@ -0,0 +1,37 @@
+// Grab the elements we need from the page
+const jokeText = document.getElementById("joke-text");
+const jokeBtn = document.getElementById("joke-btn");
+
+// URL of the free joke API we're using (no API key required)
+const API_URL = "https://official-joke-api.appspot.com/random_joke";
+
+// Function to fetch a new joke and display it
+async function getJoke() {
+ // Disable the button and show a loading message while we fetch
+ jokeBtn.disabled = true;
+ jokeText.textContent = "Loading...";
+
+ try {
+ const response = await fetch(API_URL);
+
+ // If the request failed, throw an error to be caught below
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+
+ const data = await response.json();
+
+ // The API returns { setup: "...", punchline: "..." }
+ jokeText.textContent = `${data.setup} — ${data.punchline}`;
+ } catch (error) {
+ // Show a friendly message if something goes wrong
+ jokeText.textContent = "Oops! Couldn't fetch a joke. Please try again.";
+ console.error("Error fetching joke:", error);
+ } finally {
+ // Re-enable the button no matter what happened
+ jokeBtn.disabled = false;
+ }
+}
+
+// Run getJoke() every time the button is clicked
+jokeBtn.addEventListener("click", getJoke);
\ No newline at end of file
diff --git a/joke-generator/style.css b/joke-generator/style.css
new file mode 100644
index 000000000..6eb8a7ea1
--- /dev/null
+++ b/joke-generator/style.css
@@ -0,0 +1,64 @@
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(135deg, #ff9a9e, #fad0c4);
+ font-family: "Segoe UI", Arial, sans-serif;
+}
+
+.card {
+ background: #ffffff;
+ padding: 2.5rem 2rem;
+ border-radius: 16px;
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
+ max-width: 480px;
+ width: 90%;
+ text-align: center;
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 1.6rem;
+ color: #333;
+}
+
+#joke-text {
+ min-height: 80px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.1rem;
+ color: #444;
+ margin: 1.5rem 0;
+ padding: 0 0.5rem;
+}
+
+button {
+ background: #ff6b6b;
+ color: white;
+ border: none;
+ padding: 0.8rem 1.6rem;
+ font-size: 1rem;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background 0.2s ease, transform 0.1s ease;
+}
+
+button:hover {
+ background: #ff4c4c;
+}
+
+button:active {
+ transform: scale(0.97);
+}
+
+button:disabled {
+ background: #ccc;
+ cursor: not-allowed;
+}
\ No newline at end of file