├── .env.example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png └── manifest.json └── src ├── App.js ├── components ├── Error.js ├── FinishScreen.js ├── Footer.js ├── Header.js ├── Loader.js ├── Main.js ├── NextButton.js ├── Progress.js ├── Questions.js ├── StartScreen.js ├── Timer.js └── index.js ├── data └── questions.json ├── index.js └── styles.css /.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_ENV=development 2 | REACT_APP_API_URL_DEV=http://localhost:5000/questions 3 | REACT_APP_API_URL_PROD= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Quiz App 2 | 3 | Welcome to the React Quiz App! This web application allows users to participate in interactive quizzes on **React**. Built using React, this app offers an engaging way for users to test their knowledge and learn new facts. 4 | 5 | Experience the React Quiz App for yourself by visiting [https://react-quizzler.netlify.app](https://react-quizzler.netlify.app). 6 | 7 | ## Features 8 | 9 | - **Multiple Choice Questions**: Each quiz consists of multiple-choice questions to challenge participants. 10 | - **Timer**: A countdown timer adds excitement to the quizzes, encouraging quick thinking and engagement. 11 | - **Instant Feedback**: Users receive immediate feedback on their answers, with correct answers highlighted. 12 | - **Score Tracking**: The app tracks and displays the user's score as they progress through the quiz. 13 | - **Responsive Design**: The application is designed to work seamlessly on different devices and screen sizes. 14 | 15 | ## Installation 16 | 17 | 1. Clone the repository: `git clone https://github.com/krishandeep17/react-quiz-app.git` 18 | 2. Navigate to the project directory: `cd react-quiz-app` 19 | 3. Install the dependencies: `npm install` 20 | 21 | ## Usage 22 | 23 | 1. Start the server: `npm run server` 24 | 2. Start the application: `npm start` 25 | 3. Open your web browser and visit `http://localhost:3000`. 26 | 4. Choose a quiz from the available options. 27 | 5. Read each question carefully and select your answer. 28 | 6. After answering all the questions, view your score and the high score. 29 | 30 | ## Customization 31 | 32 | - **Adding Quizzes**: You can easily add new quizzes in the `src/data/questions.json` file. Each quiz should follow the provided format. 33 | - **Styling**: Customize the styling by modifying the CSS file `src/styles.css` to match your desired look and feel. 34 | 35 | ## Contributing 36 | 37 | Contributions are welcome! If you have suggestions, bug reports, or feature requests, please open an issue on the [GitHub repository](https://github.com/krishandeep17/react-quiz-app/issues). 38 | 39 | ## Contact 40 | 41 | For any inquiries or questions, please reach out to [krishandeep17@gmail.com](mailto:krishandeep17@gmail.com). 42 | 43 | Feel free to modify this `README.md` file to fit your specific project and add any additional sections or information as needed. Enjoy using the React Quiz App for fun and learning! 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-quiz-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.17.0", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "json-server": "^0.17.3", 10 | "react": "^18.2.0", 11 | "react-dom": "^18.2.0", 12 | "react-scripts": "5.0.1", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject", 20 | "server": "json-server --watch src/data/questions.json --port 5000" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krishandeep17/react-quiz-app/82b331dc48b0daad2cab9536b78144060ad437aa/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | React Quiz 24 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krishandeep17/react-quiz-app/82b331dc48b0daad2cab9536b78144060ad437aa/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krishandeep17/react-quiz-app/82b331dc48b0daad2cab9536b78144060ad437aa/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useReducer } from "react"; 2 | 3 | import { 4 | Error, 5 | FinishScreen, 6 | Footer, 7 | Header, 8 | Loader, 9 | Main, 10 | NextButton, 11 | Progress, 12 | Questions, 13 | StartScreen, 14 | Timer, 15 | } from "./components"; 16 | 17 | const SECS_PER_QUESTION = 30; 18 | const URL = 19 | process.env.REACT_APP_ENV === "production" 20 | ? process.env.REACT_APP_API_URL_PROD 21 | : process.env.REACT_APP_API_URL_DEV; 22 | 23 | const initialState = { 24 | questions: [], 25 | status: "loading", // 'loading', 'error', 'ready', 'active', 'finished' 26 | index: 0, 27 | answer: null, 28 | points: 0, 29 | highScore: Number(localStorage.getItem("highScore")), 30 | secondsRemaining: null, 31 | }; 32 | 33 | const reducer = (state, action) => { 34 | switch (action.type) { 35 | case "received_data": { 36 | return { ...state, questions: action.payload, status: "ready" }; 37 | } 38 | case "failed": { 39 | return { ...state, status: "error" }; 40 | } 41 | case "start": { 42 | return { 43 | ...state, 44 | status: "active", 45 | secondsRemaining: state.questions.length * SECS_PER_QUESTION, 46 | }; 47 | } 48 | case "new_answer": { 49 | const question = state.questions[state.index]; 50 | 51 | return { 52 | ...state, 53 | answer: action.payload, 54 | points: 55 | action.payload === question.correctOption 56 | ? state.points + question.points 57 | : state.points, 58 | }; 59 | } 60 | case "next_question": { 61 | return { ...state, answer: null, index: state.index + 1 }; 62 | } 63 | case "finish": { 64 | return { 65 | ...state, 66 | status: "finished", 67 | highScore: 68 | state.points > state.highScore ? state.points : state.highScore, 69 | }; 70 | } 71 | case "tick": { 72 | return { 73 | ...state, 74 | secondsRemaining: state.secondsRemaining - 1, 75 | status: state.secondsRemaining === 0 ? "finished" : state.status, 76 | }; 77 | } 78 | case "restart": { 79 | return { 80 | ...initialState, 81 | questions: state.questions, 82 | status: "ready", 83 | highScore: state.highScore, 84 | }; 85 | } 86 | 87 | default: 88 | throw new Error(`Unknown action ${action.type}`); 89 | } 90 | }; 91 | 92 | const App = () => { 93 | const [state, dispatch] = useReducer(reducer, initialState); 94 | 95 | const { 96 | questions, 97 | status, 98 | index, 99 | answer, 100 | points, 101 | highScore, 102 | secondsRemaining, 103 | } = state; 104 | 105 | const numQuestions = questions.length; 106 | const maxPoints = questions.reduce((acc, cur) => acc + cur.points, 0); 107 | 108 | useEffect(() => { 109 | fetch(URL) 110 | .then((res) => res.json()) 111 | .then((data) => dispatch({ type: "received_data", payload: data })) 112 | .catch((err) => dispatch({ type: "failed" })); 113 | }, []); 114 | 115 | useEffect(() => { 116 | localStorage.setItem("highScore", highScore); 117 | }, [highScore]); 118 | 119 | return ( 120 |
121 |
122 |
123 | {status === "loading" && } 124 | {status === "error" && } 125 | {status === "ready" && ( 126 | 127 | )} 128 | 129 | {status === "active" && ( 130 | <> 131 | 138 | 143 |
144 | {answer !== null && ( 145 | 150 | )} 151 | 152 | 153 |
154 | 155 | )} 156 | 157 | {status === "finished" && ( 158 | 164 | )} 165 |
166 |
167 | ); 168 | }; 169 | 170 | export default App; 171 | -------------------------------------------------------------------------------- /src/components/Error.js: -------------------------------------------------------------------------------- 1 | const Error = () => { 2 | return ( 3 |

4 | 💥 There was an error fetching questions. 5 |

6 | ); 7 | }; 8 | 9 | export default Error; 10 | -------------------------------------------------------------------------------- /src/components/FinishScreen.js: -------------------------------------------------------------------------------- 1 | const FinishScreen = ({ points, maxPoints, highScore, dispatch }) => { 2 | const percentage = Math.ceil((points / maxPoints) * 100); 3 | 4 | let emoji; 5 | if (percentage === 100) emoji = "🏅"; 6 | if (percentage >= 80 && percentage < 100) emoji = "🎉"; 7 | if (percentage >= 50 && percentage < 80) emoji = "🙃"; 8 | if (percentage >= 0 && percentage < 50) emoji = "🤨"; 9 | if (percentage === 0) emoji = "😞"; 10 | 11 | return ( 12 | <> 13 |

14 | {emoji} You scored {points} out of{" "} 15 | {maxPoints} ({percentage}%) 16 |

17 |

(High Score: {highScore} points)

18 | 24 | 25 | ); 26 | }; 27 | 28 | export default FinishScreen; 29 | -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | const Footer = ({ children }) => { 2 | return ; 3 | }; 4 | 5 | export default Footer; 6 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | const Header = () => { 2 | return ( 3 |
4 | React logo 5 |

React Quiz

6 |
7 | ); 8 | }; 9 | 10 | export default Header; 11 | -------------------------------------------------------------------------------- /src/components/Loader.js: -------------------------------------------------------------------------------- 1 | const Loader = () => { 2 | return ( 3 |
4 |
5 |

Loading Questions

6 |
7 | ); 8 | }; 9 | 10 | export default Loader; 11 | -------------------------------------------------------------------------------- /src/components/Main.js: -------------------------------------------------------------------------------- 1 | const Main = ({ children }) => { 2 | return
{children}
; 3 | }; 4 | 5 | export default Main; 6 | -------------------------------------------------------------------------------- /src/components/NextButton.js: -------------------------------------------------------------------------------- 1 | const NextButton = ({ dispatch, index, numQuestions }) => { 2 | if (index === numQuestions - 1) { 3 | return ( 4 | 10 | ); 11 | } 12 | 13 | return ( 14 | 20 | ); 21 | }; 22 | 23 | export default NextButton; 24 | -------------------------------------------------------------------------------- /src/components/Progress.js: -------------------------------------------------------------------------------- 1 | const Progress = ({ index, numQuestions, points, maxPoints, answer }) => { 2 | return ( 3 |
4 | 5 |

6 | Question {index + 1} / {numQuestions} 7 |

8 |

9 | {points} / {maxPoints} 10 |

11 |
12 | ); 13 | }; 14 | 15 | export default Progress; 16 | -------------------------------------------------------------------------------- /src/components/Questions.js: -------------------------------------------------------------------------------- 1 | const Questions = ({ questions, answer, dispatch }) => { 2 | const { question, options, correctOption } = questions; 3 | 4 | const hasAnswer = answer !== null; 5 | 6 | const correctClass = (i) => 7 | hasAnswer && (i === correctOption ? "correct" : "wrong"); 8 | 9 | const answerClass = (i) => i === answer && "answer"; 10 | 11 | return ( 12 |
13 |

{question}

14 | 15 |
16 | {options.map((option, i) => ( 17 | 25 | ))} 26 |
27 |
28 | ); 29 | }; 30 | 31 | export default Questions; 32 | -------------------------------------------------------------------------------- /src/components/StartScreen.js: -------------------------------------------------------------------------------- 1 | const StartScreen = ({ numQuestions, dispatch }) => { 2 | return ( 3 |
4 |

Welcome to The React Quiz!

5 |

{numQuestions} questions to test your React mastery

6 | 9 |
10 | ); 11 | }; 12 | 13 | export default StartScreen; 14 | -------------------------------------------------------------------------------- /src/components/Timer.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | const Timer = ({ dispatch, secondsRemaining }) => { 4 | const mins = Math.floor(secondsRemaining / 60); 5 | const seconds = secondsRemaining % 60; 6 | 7 | useEffect(() => { 8 | const id = setInterval(function () { 9 | dispatch({ type: "tick" }); 10 | }, 1000); 11 | 12 | return () => clearInterval(id); 13 | }, [dispatch]); 14 | 15 | return ( 16 |
17 | {mins < 10 && "0"} 18 | {mins}:{seconds < 10 && "0"} 19 | {seconds} 20 |
21 | ); 22 | }; 23 | 24 | export default Timer; 25 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import Error from "./Error"; 2 | import FinishScreen from "./FinishScreen"; 3 | import Footer from "./Footer"; 4 | import Header from "./Header"; 5 | import Loader from "./Loader"; 6 | import Main from "./Main"; 7 | import NextButton from "./NextButton"; 8 | import Progress from "./Progress"; 9 | import Questions from "./Questions"; 10 | import StartScreen from "./StartScreen"; 11 | import Timer from "./Timer"; 12 | 13 | export { 14 | Error, 15 | FinishScreen, 16 | Footer, 17 | Header, 18 | Loader, 19 | Main, 20 | NextButton, 21 | Progress, 22 | Questions, 23 | StartScreen, 24 | Timer, 25 | }; 26 | -------------------------------------------------------------------------------- /src/data/questions.json: -------------------------------------------------------------------------------- 1 | { 2 | "questions": [ 3 | { 4 | "question": "Which is the most popular JavaScript framework?", 5 | "options": ["Angular", "React", "Svelte", "Vue"], 6 | "correctOption": 1, 7 | "points": 10 8 | }, 9 | { 10 | "question": "Which company invented React?", 11 | "options": ["Google", "Apple", "Netflix", "Facebook"], 12 | "correctOption": 3, 13 | "points": 10 14 | }, 15 | { 16 | "question": "What's the fundamental building block of React apps?", 17 | "options": ["Components", "Blocks", "Elements", "Effects"], 18 | "correctOption": 0, 19 | "points": 10 20 | }, 21 | { 22 | "question": "What's the name of the syntax we use to describe the UI in React components?", 23 | "options": ["FBJ", "Babel", "JSX", "ES2015"], 24 | "correctOption": 2, 25 | "points": 10 26 | }, 27 | { 28 | "question": "How does data flow naturally in React apps?", 29 | "options": [ 30 | "From parents to children", 31 | "From children to parents", 32 | "Both ways", 33 | "The developers decides" 34 | ], 35 | "correctOption": 0, 36 | "points": 10 37 | }, 38 | { 39 | "question": "How to pass data into a child component?", 40 | "options": ["State", "Props", "PropTypes", "Parameters"], 41 | "correctOption": 1, 42 | "points": 10 43 | }, 44 | { 45 | "question": "When to use derived state?", 46 | "options": [ 47 | "Whenever the state should not trigger a re-render", 48 | "Whenever the state can be synchronized with an effect", 49 | "Whenever the state should be accessible to all components", 50 | "Whenever the state can be computed from another state variable" 51 | ], 52 | "correctOption": 3, 53 | "points": 30 54 | }, 55 | { 56 | "question": "What triggers a UI re-render in React?", 57 | "options": [ 58 | "Running an effect", 59 | "Passing props", 60 | "Updating state", 61 | "Adding event listeners to DOM elements" 62 | ], 63 | "correctOption": 2, 64 | "points": 20 65 | }, 66 | { 67 | "question": "When do we directly \"touch\" the DOM in React?", 68 | "options": [ 69 | "When we need to listen to an event", 70 | "When we need to change the UI", 71 | "When we need to add styles", 72 | "Almost never" 73 | ], 74 | "correctOption": 3, 75 | "points": 20 76 | }, 77 | { 78 | "question": "In what situation do we use a callback to update state?", 79 | "options": [ 80 | "When updating the state will be slow", 81 | "When the updated state is very data-intensive", 82 | "When the state update should happen faster", 83 | "When the new state depends on the previous state" 84 | ], 85 | "correctOption": 3, 86 | "points": 30 87 | }, 88 | { 89 | "question": "If we pass a function to useState, when will that function be called?", 90 | "options": [ 91 | "On each re-render", 92 | "Each time we update the state", 93 | "Only on the initial render", 94 | "The first time we update the state" 95 | ], 96 | "correctOption": 2, 97 | "points": 30 98 | }, 99 | { 100 | "question": "Which hook to use for an API request on the component's initial render?", 101 | "options": ["useState", "useEffect", "useRef", "useReducer"], 102 | "correctOption": 1, 103 | "points": 10 104 | }, 105 | { 106 | "question": "Which variables should go into the useEffect dependency array?", 107 | "options": [ 108 | "Usually none", 109 | "All our state variables", 110 | "All state and props referenced in the effect", 111 | "All variables needed for clean up" 112 | ], 113 | "correctOption": 2, 114 | "points": 30 115 | }, 116 | { 117 | "question": "An effect will always run on the initial render.", 118 | "options": [ 119 | "True", 120 | "It depends on the dependency array", 121 | "False", 122 | "In depends on the code in the effect" 123 | ], 124 | "correctOption": 0, 125 | "points": 30 126 | }, 127 | { 128 | "question": "When will an effect run if it doesn't have a dependency array?", 129 | "options": [ 130 | "Only when the component mounts", 131 | "Only when the component unmounts", 132 | "The first time the component re-renders", 133 | "Each time the component is re-rendered" 134 | ], 135 | "correctOption": 3, 136 | "points": 20 137 | } 138 | ] 139 | } 140 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | 4 | import "./styles.css"; 5 | import App from "./App"; 6 | 7 | const root = createRoot(document.getElementById("root")); 8 | 9 | root.render( 10 | 11 | 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-darkest: #343a40; 3 | --color-dark: #495057; 4 | --color-medium: #ced4da; 5 | --color-light: #f1f3f5; 6 | 7 | --color-theme: #1098ad; 8 | --color-accent: #ffa94d; 9 | } 10 | 11 | * { 12 | margin: 0; 13 | padding: 0; 14 | box-sizing: border-box; 15 | } 16 | 17 | html { 18 | font-size: 62.5%; 19 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 20 | "Ubuntu", "Cantarell", "Open Sans", "Helvetica Neue", sans-serif; 21 | } 22 | 23 | body { 24 | min-height: 100vh; 25 | min-height: 100svh; 26 | color: var(--color-light); 27 | background-color: var(--color-darkest); 28 | padding: 3.2rem; 29 | } 30 | 31 | .app { 32 | display: flex; 33 | flex-direction: column; 34 | align-items: center; 35 | justify-content: center; 36 | } 37 | 38 | .main { 39 | max-width: 50rem; 40 | width: 100%; 41 | } 42 | 43 | /* HEADER */ 44 | 45 | .header { 46 | margin-bottom: 4rem; 47 | display: flex; 48 | align-items: center; 49 | gap: 3.2rem; 50 | } 51 | 52 | .header img { 53 | width: 12.8rem; 54 | max-width: 100%; 55 | animation: spin infinite 20s linear; 56 | } 57 | 58 | @keyframes spin { 59 | from { 60 | transform: rotate(0deg); 61 | } 62 | to { 63 | transform: rotate(360deg); 64 | } 65 | } 66 | 67 | h1 { 68 | font-family: "Codystar", cursive; 69 | font-size: 5.6rem; 70 | } 71 | 72 | /* LOADER */ 73 | 74 | .loader-container { 75 | display: flex; 76 | flex-direction: column; 77 | align-items: center; 78 | margin-top: 4rem; 79 | gap: 1.6rem; 80 | color: var(--color-medium); 81 | font-size: 14px; 82 | } 83 | 84 | .loader { 85 | width: 50px; 86 | height: 24px; 87 | background: radial-gradient(circle closest-side, currentColor 90%, #0000) 0% 88 | 50%, 89 | radial-gradient(circle closest-side, currentColor 90%, #0000) 50% 50%, 90 | radial-gradient(circle closest-side, currentColor 90%, #0000) 100% 50%; 91 | background-size: calc(100% / 3) 12px; 92 | background-repeat: no-repeat; 93 | animation: loader 1s infinite linear; 94 | } 95 | 96 | @keyframes loader { 97 | 20% { 98 | background-position: 0% 0%, 50% 50%, 100% 50%; 99 | } 100 | 40% { 101 | background-position: 0% 100%, 50% 0%, 100% 50%; 102 | } 103 | 60% { 104 | background-position: 0% 50%, 50% 100%, 100% 0%; 105 | } 106 | 80% { 107 | background-position: 0% 50%, 50% 50%, 100% 100%; 108 | } 109 | } 110 | 111 | /* ERROR */ 112 | 113 | .error { 114 | text-align: center; 115 | font-size: 1.8rem; 116 | font-weight: 500; 117 | padding: 2rem; 118 | background-color: var(--color-dark); 119 | border-radius: 100px; 120 | } 121 | 122 | /* START SCREEN */ 123 | 124 | .start { 125 | display: flex; 126 | flex-direction: column; 127 | align-items: center; 128 | text-align: center; 129 | } 130 | 131 | .start h2 { 132 | font-size: 3.6rem; 133 | margin-bottom: 2.4rem; 134 | } 135 | 136 | .start h3 { 137 | font-size: 2.4rem; 138 | font-weight: 600; 139 | margin-bottom: 4rem; 140 | } 141 | 142 | /* BUTTON */ 143 | 144 | .btn { 145 | display: block; 146 | font-family: inherit; 147 | color: var(--color-light); 148 | font-size: 2rem; 149 | border: 2px solid var(--color-dark); 150 | background-color: var(--color-dark); 151 | padding: 1.2rem 2.4rem; 152 | cursor: pointer; 153 | border-radius: 100px; 154 | transition: 0.3s; 155 | user-select: none; 156 | } 157 | 158 | .btn:not([disabled]):hover { 159 | background-color: var(--color-darkest); 160 | } 161 | 162 | .btn[disabled]:hover { 163 | cursor: not-allowed; 164 | } 165 | 166 | .btn-right { 167 | float: right; 168 | } 169 | 170 | /* PROGRESS */ 171 | 172 | .progress { 173 | margin-bottom: 4rem; 174 | display: grid; 175 | justify-content: space-between; 176 | gap: 1.2rem; 177 | grid-template-columns: auto auto; 178 | font-size: 1.8rem; 179 | color: var(--color-medium); 180 | } 181 | 182 | progress { 183 | -webkit-appearance: none; 184 | appearance: none; 185 | width: 100%; 186 | height: 1.2rem; 187 | grid-column: 1 / -1; 188 | } 189 | 190 | ::-webkit-progress-bar { 191 | background-color: var(--color-medium); 192 | border-radius: 100px; 193 | } 194 | 195 | ::-webkit-progress-value { 196 | background-color: var(--color-theme); 197 | border-radius: 100px; 198 | } 199 | 200 | /* QUESTIONS */ 201 | 202 | h4 { 203 | font-size: 2.2rem; 204 | font-weight: 600; 205 | margin-bottom: 2.4rem; 206 | } 207 | 208 | .options { 209 | display: flex; 210 | flex-direction: column; 211 | gap: 1.2rem; 212 | margin-bottom: 3.2rem; 213 | } 214 | 215 | .btn-option { 216 | width: 100%; 217 | text-align: left; 218 | } 219 | 220 | .btn-option:not([disabled]):hover { 221 | transform: translateX(1.2rem); 222 | } 223 | 224 | .options .correct { 225 | background-color: var(--color-theme); 226 | border: 2px solid var(--color-theme); 227 | color: var(--color-light); 228 | } 229 | 230 | .options .wrong { 231 | background-color: var(--color-accent); 232 | border: 2px solid var(--color-accent); 233 | color: var(--color-darkest); 234 | } 235 | 236 | .options .answer { 237 | transform: translateX(1.8rem); 238 | } 239 | 240 | /* TIMER */ 241 | 242 | .timer { 243 | float: left; 244 | font-size: 1.8rem; 245 | color: var(--color-medium); 246 | border: 2px solid var(--color-dark); 247 | padding: 1.35rem 2.8rem; 248 | border-radius: 100px; 249 | } 250 | 251 | /* FINISH SCREEN */ 252 | 253 | .result { 254 | background-color: var(--color-theme); 255 | color: var(--color-light); 256 | border-radius: 100px; 257 | text-align: center; 258 | padding: 2rem 0; 259 | font-size: 2rem; 260 | font-weight: 500; 261 | margin-bottom: 1.6rem; 262 | } 263 | 264 | .result span { 265 | font-size: 2.2rem; 266 | } 267 | 268 | .high-score { 269 | font-size: 1.8rem; 270 | text-align: center; 271 | margin-bottom: 4.8rem; 272 | } 273 | 274 | /* RESPONSIVE QUERIES */ 275 | 276 | @media (max-width: 38em) { 277 | html { 278 | font-size: 56.25%; 279 | } 280 | .header img { 281 | width: 9.8rem; 282 | } 283 | h1 { 284 | font-size: 4.4rem; 285 | } 286 | } 287 | 288 | @media (max-width: 31em) { 289 | html { 290 | font-size: 50%; 291 | } 292 | .header { 293 | gap: 2.4rem; 294 | } 295 | .header img { 296 | width: 8.6rem; 297 | } 298 | h1 { 299 | font-size: 3.6rem; 300 | } 301 | .start h2 { 302 | font-size: 3rem; 303 | } 304 | .start h3 { 305 | font-size: 2rem; 306 | } 307 | } 308 | --------------------------------------------------------------------------------