diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..1dd1d96 Binary files /dev/null and b/.DS_Store differ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..aef8443 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/about.html b/about.html index 5adbad6..90aad7a 100644 --- a/about.html +++ b/about.html @@ -1,10 +1,94 @@ - - - - - About - - - + + + + + + Web Accessibility Quiz + + + + + + + + + + +
+ + +
+

The team

+

We're a team of developers that are passioned about web accessibility. +
We truly belive that everyone should have an engaging and fair access to all content on the world wide + web. +

+
+ + +
+ + + + + + \ No newline at end of file diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/celebration.gif b/assets/celebration.gif new file mode 100644 index 0000000..fd75cce Binary files /dev/null and b/assets/celebration.gif differ diff --git a/assets/descriptive-image.png b/assets/descriptive-image.png new file mode 100644 index 0000000..b940776 Binary files /dev/null and b/assets/descriptive-image.png differ diff --git a/assets/img1.jpg b/assets/img1.jpg new file mode 100644 index 0000000..0c65285 Binary files /dev/null and b/assets/img1.jpg differ diff --git a/assets/img2.jpg b/assets/img2.jpg new file mode 100644 index 0000000..0ab5b52 Binary files /dev/null and b/assets/img2.jpg differ diff --git a/assets/placeholder.png b/assets/placeholder.png new file mode 100644 index 0000000..3232056 Binary files /dev/null and b/assets/placeholder.png differ diff --git a/css/styles.css b/css/styles.css index e69de29..1ed86bf 100644 --- a/css/styles.css +++ b/css/styles.css @@ -0,0 +1,415 @@ +*, +html { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: "Lexend", Arial, Helvetica, sans-serif; + font-weight: 300; + font-optical-sizing: auto; +} + +body { + display: flex; + flex-direction: column; + background-color: #F4F7F7; + color: #235558; + min-height: 100vh; +} + +h1 { + font-size: 27px; +} + + +h1, +h2, +h3 { + font-weight: 400; + margin-bottom: 1rem; +} + +.header-content h1 { + padding: 10px; +} + + +p { + max-width: 600px; + margin: 0 auto; + font-size: 16px; + line-height: 1.6; +} + +.skip-link { + position: absolute; + top: -50px; + right: 0; + text-decoration: none; + padding: 10px; + background: #235558; + color: white; + cursor: pointer; + z-index: 100; + transition: top 0.3s; +} + +.skip-link:focus-visible { + top: 0; + outline-offset: 2px; +} + +#header { + background: #E7F1F1; + padding-bottom: 10px; +} + +.nav-list { + display: flex; + gap: 1rem; + text-decoration: none; + list-style: none; +} + +a { + text-decoration: none; + font-size: 20px; + font-weight: 400; + color: #235558; + padding: 10px; +} + +a:hover { + background-color: #CCE3E4; +} + + +#main-content { + flex-grow: 1; +} + +#introduction { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin: 0 auto; + text-align: center; + margin-bottom: 4rem; +} + + + + +.text-container { + position: absolute; + width: 100%; + padding: 1rem; +} + +.img-container { + position: relative; + background-color: #E9F1F1; + background: -webkit-linear-gradient(#CCE3E4cc, #F5F7F7cc); + top: 0; + z-index: -1; + width: 100%; + height: 37vh; + overflow: hidden; +} + +.decorative-img { + max-width: 100%; + width: 100%; + height: auto; + object-fit: cover; + opacity: 0.3; + +} + +#about { + text-align: center; + margin: 4rem auto; + margin-bottom: 4rem; +} + +.navigation-info { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap-reverse; + padding: 10px; + margin-bottom: 4rem; + text-align: center; +} + +.descriptive-img { + max-width: 600px; + width: 100%; + height: 100%; + margin-top: 2rem; +} + +/*quiz styling*/ + +#quiz { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin: 2rem auto; +} + +.quiz-progress { + margin-bottom: 1rem; +} + +.progress-bar { + background-color: #E7F1F1; + border-radius: 8px; + height: 20px; + overflow: hidden; + position: relative; +} + +.progress-fill { + background-color: #235558; + height: 100%; + width: 0%; + transition: width 0.3s ease; + border-radius: 8px; +} + +legend { + display: flex; + justify-content: center; + align-items: center; + font-size: 30px; + /* color: #235558; */ + background-color: #BFF6D4; + padding: 10px; + border: 2px solid #bababa; + border-radius: 50%; + width: 50px; + height: 50px; +} + +.question { + display: none; + /* color: #235558; */ + flex-direction: column; + align-items: flex-start; + border: 2px solid #bababa; + border-radius: 15px; + width: auto; + max-width: 500px; + padding: 10px; + margin: 5px; +} + +.answer-options { + display: flex; + flex-direction: column; + gap: 15px; +} + +input[type="radio"] { + appearance: none; +} + +.abc-index { + display: inline-flex; + justify-content: center; + align-items: center; + padding: 4px; + height: 25px; + width: 25px; + border-radius: 50%; + background-color: #F4F7F7; + border: #6FB686 solid 2px; + box-sizing: border-box; +} + +.submit-btn { + align-self: flex-end; + margin-top: 1rem; +} + +label.btn, +button.submit-btn, +#start-button, +.continue-btn { + display: inline-block; + color: #235558; + font-size: 16px; + background-color: #BFF6D4; + padding: 10px 10px; + border-radius: 25px; + border: #6FB686 solid 2px; + cursor: pointer; +} + +label.btn:hover, +button.submit-btn:hover, +#start-button:hover, +.continue-btn:hover { + background-color: #b2daff; + border: #004280 solid 2px; + box-shadow: #235558 0px 0px 10px; +} + +/* style buttons when in focus */ +button.submit-btn:focus-visible, +#start-button:focus-visible, +.continue-btn:focus-visible { + outline: 2px solid #235558; + outline-offset: 5px; +} + + +input[type="radio"]:checked+label { + background-color: #00ff62; + border-color: #00ff62; +} + +/* style label when radio button is in focus */ +input[type="radio"]:focus-visible+label { + outline: 2px solid #235558; + outline-offset: 5px; +} + + +.submit-btn { + align-self: flex-end; +} + +main { + flex-grow: 1; +} + +footer { + /* position: fixed; */ + /* bottom: 0; */ + margin-top: auto; + background: #E7F1F1; + /* height: 50px; */ + width: 100%; +} + +footer p { + padding: 10px; + align-items: left; + font-size: 12px; + margin: 0; +} + +.error { + color: red; + font-size: 16px; + padding: 15px 10px 10px 10px; +} + +.message-container { + display: none; + /* color: #235558; */ + flex-direction: column; + align-items: flex-end; + gap: 15px; + border: 2px solid #bababa; + border-radius: 15px; + width: auto; + max-width: 500px; + padding: 20px; + margin: 10px 5px; +} + +.results-container { + display: none; + flex-direction: column; + align-items: center; + gap: 30px; + border: 2px solid #bababa; + border-radius: 15px; + width: auto; + max-width: 500px; + padding: 50px; + margin: auto; + margin-bottom: 50px; + text-align: center; +} + +.results-container p { + font-size: 24px; +} + +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.quiz-gif { + max-width: 100%; + width: 200px; + height: auto; + margin-top: 1rem; + border-radius: 12px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); +} + +.results-container h2 { + font-size: 28px; + color: #235558; + text-align: center; + margin-bottom: 1rem; +} + +/* +- fix the shadows on answer btn when hoovering +- maybe other colors idk +- fix the font size of the question? +- fix the font size of the submit button +- fix legend size and centering +*/ + + +/* LEXEND font class for a variable style + +// : Use a value from 100 to 900 +// : Use a unique and descriptive class name + +.lexend- { + font-family: "Lexend", sans-serif; + font-optical-sizing: auto; + font-weight: ; + font-style: normal; +} + */ + +@media screen and (min-device-width: 320px) and (max-device-width: 1200) { + body { + font-size: 1rem; + } +} + +@media (min-width: 768px) { + body { + font-size: 1.2rem; + } + +} + +@media (min-width: 1024px) { + body { + font-size: 1.4rem; + } + + .navigation-info { + gap: 4rem; + } +} \ No newline at end of file diff --git a/index.html b/index.html index a9a33db..58cbde9 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,369 @@ - - - - - Title - - - -

This is the starting point.

- - + + + + + + Web Accessibility Quiz + + + + + + + + + + + + +
+ +
+
+

Welcome to our quiz

+

We designed this quiz to be easy for everyone to use. It's keyboard friendly, screen reader ready and built + with clear text and strong contrast for better visibility.

+
+
+ +
+
+ +
+

Accessibility Quiz

+ +
+
+
+
+

+
+ + + + + + + + +
+ + + +
+ +
+ +
+

2025 Web Accessibility Quiz

+
+ + + + + + + + + + + \ No newline at end of file diff --git a/js/main.js b/js/main.js index e69de29..0ed069d 100644 --- a/js/main.js +++ b/js/main.js @@ -0,0 +1,251 @@ +// correct answers with messages +const correctAnswers = ['c', 'b', 'b', 'a'] +const correctMessages = [ + 'Well done! The Web Content Accessibility Guidelines (WCAG) are a series of recommendations for making web content accessible to people with disabilities.', + 'Correct! Level AA tackles the biggest and most common barriers for disabled users. This is often the target for many websites.', + 'Exactly! ARIA landmarks are a way to classify and label sections of the HTML code that is conveyed visually through layout to be represented programmatically. ARIA landmark roles are used for example to make it easy for or assistive technology users to understand the meaning of the layout of a page.', + 'Great job! A keyboard trap occurs when a user is unable to navigate away from an element using only the keyboard, which can be frustrating for people who rely on keyboard navigation.' +] + +// DOM selectors +const quiz = document.getElementById('quiz-form') +const startQuizButton = document.getElementById('start-button') +const questions = document.querySelectorAll('.question') +const submitButtons = document.querySelectorAll('.submit-btn') +const messageContainer = document.getElementById('message-container') +const resultsContainer = document.getElementById('results-container') +const progressFill = document.querySelector('.progress-fill') +const progressText = document.querySelector('.progress-text') + +// initialize variables +let currentQuestionIndex = 0 // current question counter +let score = 0 // quiz score + +// function to set the active question +const setActiveQuestion = (index) => { + // show the active question - hide the others + questions.forEach((question, i) => { + if (i === index) { + question.style.display = 'inline-flex' + question.hidden = false + // set focus on first option + question.querySelector('input[type="radio"]').focus() + question.scrollIntoView({ behaviour: 'smooth' }) + } else { + question.style.display = 'none' + question.hidden = true + } + + }); +} + +// function to start Quiz +const startQuiz = () => { + // show quiz + quiz.hidden = false + // set first question + setActiveQuestion(currentQuestionIndex) + // initialize progress bar + updateProgress() + // hide start button + startQuizButton.style.display = 'none' + startQuizButton.hidden = true + //announce to the screen reader + announcer.textcontent = 'Moved to first question' +} + +// function to check selected answer +const checkAnswer = () => { + // get radio group of current question + const radioGroup = questions[currentQuestionIndex].querySelectorAll('input[type="radio"]') + // loop through radio group to find selected answer + let answer + radioGroup.forEach(option => { + option.checked ? (answer = option.value) : null + }) + // check if selected answer is correct + if (answer) { + // clear error message if any + clearError(questions[currentQuestionIndex].querySelector('.error')) + // get correct answer + const correctOption = correctAnswers[currentQuestionIndex] + if (answer === correctOption) { + // add 1 point to score + score += 1 + // display feedback message + displayFeedback(correctMessages[currentQuestionIndex]) + } else { + // display feedback message + const wrongMessage = `Nice try! But the correct answer is ${correctOption.toUpperCase()}.` + displayFeedback(wrongMessage) + } + } else { + // if no option is selected - show eror message to user to select an option + showError(questions[currentQuestionIndex].querySelector('.error'), 'Please select your answer') + // set focus to first option + questions[currentQuestionIndex].querySelector('input[type="radio"]').focus() + } +} + +// show error message if no option is selected +const showError = (errorElement, message) => { + errorElement.textContent = message + errorElement.hidden = false +} + +// clear error message +const clearError = (errorElement) => { + errorElement.textContent = '' + errorElement.hidden = true +} + +// show feedback message +const displayFeedback = (message) => { + // display message + messageContainer.innerHTML = ` +

${message}

+ ` + messageContainer.style.display = 'flex' + messageContainer.hidden = false + // continue button + // if last question - show results + if (currentQuestionIndex === (questions.length - 1)) { + messageContainer.innerHTML += ` + + ` + } else { + // else - next question + messageContainer.innerHTML += ` + + ` + } + // set focus to continue button + const continueButton = messageContainer.querySelector('.continue-btn') + continueButton.focus() + // set event listener on the continue button based on the the message type + continueButton.addEventListener('click', () => { + // clear feedback message + messageContainer.innerHTML = '' + messageContainer.style.display = 'none' + messageContainer.hidden = true + // if last question - end quiz + if (currentQuestionIndex === (questions.length - 1)) { + endQuiz() + //announce to the screen reader + announcer.textContent = 'Moved to results section' + } else { + // else - go to next question + const nextQuestionIndex = (currentQuestionIndex + 1) + setActiveQuestion(nextQuestionIndex) + currentQuestionIndex = nextQuestionIndex + // update progress + updateProgress() + //announce to the screen reader + announcer.textContent = 'Moved to next question' + } + }) +} + +// update progress function +const updateProgress = () => { + const totalQuestions = (questions.length) + const answeredQuestions = currentQuestionIndex + 1 + const percentage = (answeredQuestions / totalQuestions) * 100 + // update progress bar + progressFill.style.width = `${percentage}%` + // update progress text + progressText.textContent = `Question ${answeredQuestions} of ${totalQuestions}` + // announce to screen reader + announcer.textContent = `Question ${answeredQuestions} of ${totalQuestions}` +} + +// end quiz +const endQuiz = () => { + // hide quiz + quiz.hidden = true + // show results + resultsContainer.innerHTML = ` +

🎉 Congratulations! 🎉

+ celebration image +

Thank you for taking the quiz! We hope you learned something about web accessibility.

+

Your score: ${score} of ${questions.length}

+ + ` + resultsContainer.style.display = 'flex' + resultsContainer.hidden = false + resultsContainer.scrollIntoView({ behaviour: 'smooth' }) + // set focus to button + const continueButton = resultsContainer.querySelector('.continue-btn') + continueButton.focus() + continueButton.scrollIntoView({ behaviour: 'smooth' }) + // set event listener to take the quiz again + continueButton.addEventListener('click', () => { + // reset current question index + currentQuestionIndex = 0 + // reset results + score = 0 + // clear container + resultsContainer.innerHTML = '' + resultsContainer.style.display = 'none' + // uncheck all radio options + questions.forEach((question) => { + const radios = question.querySelectorAll('input[type="radio"]') + radios.forEach((option) => { + option.checked = false + }) + }) + // start quiz + startQuiz() + }) + + +} + +// start quiz when start button is selected +startQuizButton.addEventListener('click', startQuiz) + +// check answer when submit button is selected +submitButtons.forEach(button => { + button.addEventListener('click', (e) => { + e.preventDefault() // prevent page reload + checkAnswer() + }) +}) + +// event listener to handle keyboard input for questions +questions.forEach((question) => { + const radioGroup = question.querySelector('.answer-options') + // add event listener on the radio group + radioGroup.addEventListener('keydown', (event) => { + const optionInFocus = radioGroup.querySelector(":focus") + // let the user select option by pressing a,b, or c + // enter or space to select the option in focus + switch (event.key) { + case 'a': + case 'A': + event.preventDefault() + question.querySelector('input[value="a"]').checked = true + question.querySelector('input[value="a"]').focus() + break + case 'b': + case 'B': + event.preventDefault() + question.querySelector('input[value="b"]').checked = true + question.querySelector('input[value="b"]').focus() + break + case 'c': + case 'C': + event.preventDefault() + question.querySelector('input[value="c"]').checked = true + question.querySelector('input[value="c"]').focus() + break + case 'Enter': + event.preventDefault() + optionInFocus.checked = true + break + } + + }) + +}) + diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..4998662 --- /dev/null +++ b/todo.md @@ -0,0 +1,6 @@ +# To do + +## Branches CSS: + +header & footes: +introduction \ No newline at end of file