This is a solution to the Frontend quiz app challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.
Users should be able to:
- Select a quiz subject
- Select a single answer from each question from a choice of four
- See an error message when trying to submit an answer without making a selection
- See if they have made a correct or incorrect choice when they submit an answer
- Move on to the next question after seeing the question result
- See a completed state with the score after the final question
- Play again to choose another subject
- View the optimal layout for the interface depending on their device's screen size
- See hover and focus states for all interactive elements on the page
- Navigate the entire app only using their keyboard
- Change the app's theme between light and dark
- Solution URL: https://github.com/srijanss/frontend-quiz-app/
- Live Site URL: https://srijanss.github.io/frontend-quiz-app/
- Semantic HTML5 markup
- CSS custom properties
- Flexbox
- CSS Grid
- Mobile-first workflow
- Vite - Lightweight frontend tooling
- PostCSS - Tool to transform CSS using plugins
Built this app using basic routing based on Regex filtering and history API based state management. Try to make routing work with SPA without using any libraries.
const routes = {
"/": { component: CategoryPage, name: "home-page" },
":category/question/:id/": { component: QuestionPage, name: "question-page" },
":category/score/": { component: ScorePage, name: "score-page" },
};
function matchRoute(path) {
const matchedRoute = routes["/"].component;
for (const route in routes) {
const re = new RegExp(`^${route.replace(/:\w+/g, "([^/]+)")}$`);
path = path.length > 1 ? path.replace(/^\//, "") : path;
if (!path.endsWith("/")) {
path += "/";
}
const match = path.match(re);
if (match) {
matchedRoute.component = routes[route].component;
const params = match.slice(1);
if (params.length > 0) {
matchedRoute.params = params;
}
return matchedRoute;
}
}
return matchedRoute;
}
export function navigateTo(path, state) {
const newPath = path.includes(store.baseURL) ? path : store.baseURL + path;
window.history.pushState(state || {}, "", newPath);
render();
}
export function navigateByReplace(path, state) {
const newPath = path.includes(store.baseURL) ? path : store.baseURL + path;
window.history.replaceState(state || {}, "", newPath);
render();
}
export function replaceState(path, state) {
const newPath = path.includes(store.baseURL) ? path : store.baseURL + path;
window.history.replaceState(state || {}, "", newPath);
}
export function handleHistoryPopState() {
window.onpopstate = render;
}
There were many challenges to build this multipage app using SPA, and without any routing libraries or fullstack framework. Like when you refresh a page on any URL except the root URL, then it will redirect back to root page.
As a challenge I tried to develop this app without any framework. But in future I will try to build this again with Remix and SvelteKit. It will be fun task.
- Web components - Custom State Set - Very helpful resource about CustomStateSet in web components.
- History API - Very helpful to understand how history stack works. And how to maintain persistent data using history state.
- Website - Srijan Manandhar
- Frontend Mentor - @srijanss