diff --git a/README.md b/README.md index 57e6d16..e2b3959 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Web Accessibility Project -A basic web server template focused on web accessibility practices. +We built this website as a project focused on web accessibility. The goal was to learn how to develop inclusive digital experiences that work for everyone, regardless of their functional ability. +We worked with semantic HTML, proper use of ARIA attributes, clear color contrast, keyboard navigation, and responsive design. +The project is built with HTML, CSS, and JavaScript, and is published via Netlify. -### Installation +# Live site -1. Fork this repository by clicking the "Fork" button at the top right of the GitHub repository page. - -2. Clone your forked repository +https://accessibility-project.netlify.app/ diff --git a/about.html b/about.html index 5adbad6..e2a35e3 100644 --- a/about.html +++ b/about.html @@ -1,10 +1,96 @@ - + About + + + + + + - + + + +
+

About

+ +
+ +
+
+
+ +

+ Hey! I'm Linda, a student at Technigo. I believe + accessibility is essential — not only because the internet should be + available to everyone, but also because it enhances the user + experience for all.

+ Visit Lindas Github +

+
+
+ +

+ Hi! My name is Cathi and I am currently studying + JavaScript at Technigo. I have always dreamed about learning + programming and in January i finally took the chance. I think that + this project, Web Accessibility, that we're working on right now is + super important. The www is for all!

+ Visit Cathis Github +

+
+
+
+ + + + diff --git a/assets/banner.png b/assets/banner.png new file mode 100644 index 0000000..459a3ed Binary files /dev/null and b/assets/banner.png differ diff --git a/assets/cathi.jpg b/assets/cathi.jpg new file mode 100644 index 0000000..4fa2e1e Binary files /dev/null and b/assets/cathi.jpg differ diff --git a/assets/linda.JPG b/assets/linda.JPG new file mode 100644 index 0000000..538ce9f Binary files /dev/null and b/assets/linda.JPG differ diff --git a/css/styles.css b/css/styles.css index e69de29..6cb9b8e 100644 --- a/css/styles.css +++ b/css/styles.css @@ -0,0 +1,465 @@ +:root { + --primary-color: #f9f9f9; + --background-color: #fca788; + --accent-color: #f77f5b; + --error-color: #801024; + --success-color: #274a34; + --focus-outline: 3px solid #000; + --focus-ring-color: #282459; + --text-color: #2c2c2c; + --border-color: black; + --transition-speed: 0.3s; + --high-contrast-bg: #282459; + --high-contrast-text: #000000; + --button_radius: 0.75em; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +body { + font-family: "Noto Sans", sans-serif; + background-color: var(--background-color); + color: var(--text-color); + margin: 0; + padding: 0; +} + +header { + display: flex; + flex-direction: column; + justify-content: space-between; + flex-wrap: wrap; + padding-top: 2rem; + padding-bottom: 2rem; + align-items: center; + position: relative; +} + +nav { + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; + gap: 1rem; + margin-bottom: 1rem; +} + +nav ul { + display: flex; + font-family: Rubik, sans-serif; + font-weight: bold; + font-size: 20px; + color: var(--text-color); + gap: 1rem; + list-style: none; + padding: 0; + margin: 0.2rem; +} + +h1 { + display: flex; + padding: 0; + justify-content: center; + font-family: Rubik, sans-serif; + font-size: 25px; +} + +h2 { + font-family: Rubik, sans-serif; + color: var(--text-color); + margin-bottom: 0.2rem; +} + +a, +a:link, +a:visited { + color: var(--text-color); + text-decoration: none; + transition: color var(--transition-speed); +} + +a:hover { + color: var(--accent-color); +} + +.skip-link { + position: absolute; + top: -40px; + left: 0; + background: var(--primary-color); + color: var(--text-color); + padding: 8px; + z-index: 100; + transition: top 0.3s; +} + +.skip-link:focus { + top: 0; + outline: var(--focus-outline); +} + +.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; +} + +/* Tooltip container */ +.tooltip { + position: relative; + display: inline-block; +} + +/* Tooltip text */ +.tooltip .tooltiptext { + visibility: hidden; + width: 7rem; + background-color: #555; + color: #fff; + text-align: center; + padding: 0.3rem 0; + border-radius: 0.2rem; + /* Position the tooltip text */ + position: absolute; + z-index: 10; + top: 130%; + left: 50%; + margin-left: -60px; + + /* Fade in tooltip */ + opacity: 0; + transition: opacity 0.3s; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +/* Full-width banner */ +#banner { + margin: -20px -20px 2rem; + width: 100vw; + position: relative; + left: 50%; + right: 50%; + margin-left: -50vw; + margin-right: -50vw; + overflow: hidden; +} + +#banner img { + width: 100%; + height: 200px; + object-fit: cover; +} + +#introduction { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + max-width: 600px; + margin: 0 auto; + padding: 1rem; +} + +section { + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + flex-wrap: wrap; +} + +form { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + width: 100%; + margin: 0 auto; + padding: 1rem; +} + +#toggle-element { + position: absolute; + top: 1rem; + right: 1rem; + z-index: 999; +} + +/* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; + margin-left: 2rem; +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +/* The slider */ +.slider { + position: absolute; + border-radius: 34px; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +/* The actual moveable "button" */ +.slider:before { + position: absolute; + border-radius: 50%; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: var(--primary-color); + -webkit-transition: 0.4s; + transition: 0.4s; +} + +/* When the slider is activated */ +input:checked + .slider { + background-color: var(--success-color); +} + +/* Focus effect */ +.switch input:focus-visible + .slider { + box-shadow: 0 0 0 3px var(--text-color); +} + +/* When the button is activated - move the slider */ +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* High contrast mode styles */ +.high-contrast { + background-color: var(--high-contrast-bg); + color: var(--primary-color); +} + +.high-contrast h1, +.high-contrast h2 { + color: var(--primary-color); +} + +.high-contrast a:link, +.high-contrast a:visited { + color: var(--primary-color); +} + +.high-contrast a:hover { + color: var(--high-contrast-text); +} + +.high-contrast .skip-link { + background: var(--high-contrast-text); + color: var(--primary-color); +} + +.high-contrast .tooltip .tooltiptext { + background-color: var(--primary-color); + color: var(--text-color); +} + +.high-contrast .switch input:focus-visible + .slider { + box-shadow: 0 0 0 3px var(--primary-color); +} + +.high-contrast fieldset { + color: var(--high-contrast-text); +} + +.high-contrast fieldset legend, +.high-contrast fieldset label, +.high-contrast fieldset p, +.high-contrast fieldset span { + color: var(--high-contrast-text); +} + +.high-contrast #submit-button { + background-color: var(--high-contrast-bg); + color: var(--primary-color); + border: 1px solid var(--high-contrast-text); + border-right: 3px solid var(--high-contrast-text); + border-bottom: 3px solid var(--high-contrast-text); +} + +.high-contrast #submit-button:hover { + background-color: var(--high-contrast-text); + color: var(--primary-color); + border: 3px solid var(--high-contrast-bg); +} + +/* Quiz form styles*/ +fieldset { + border: 1px solid var(--border-color); + border-right: 4px solid var(--border-color); + border-bottom: 5px solid var(--border-color); + width: 100%; + max-width: 18rem; + margin: 0.5rem; + border-radius: 0.5rem; + background-color: var(--primary-color); +} + +legend { + background-color: var(--primary-color); + border: 1px solid var(--border-color); + border-right: 3px solid var(--border-color); + border-bottom: 3px solid var(--border-color); + padding: 1em; + margin-bottom: 0.5rem; + border-radius: 0.5rem; + font-weight: bold; +} + +label { + display: flex; + gap: 0.5rem; + padding: 0.5rem; +} + +label input { + margin: 0; +} + +#submit-button { + font-size: 17px; + font-weight: bold; + border: 1px solid var(--border-color); + border-right: 3px solid var(--border-color); + border-bottom: 3px solid var(--border-color); + background: var(--accent-color); + border-radius: var(--button_radius); + padding: 0.8rem; + margin: 0.8rem; +} + +#submit-button:hover { + background: var(--primary-color); +} + +.error-feedback { + display: inline-block; + border-bottom: 3px solid var(--error-color); + padding-bottom: 0.2em; +} + +.success-feedback { + display: inline-block; + border-bottom: 3px solid var(--success-color); + padding-bottom: 0.2em; +} + +#result-container { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + background-color: var(--primary-color); +} + +#results-container h2 { + margin: 1rem 0; + padding: 0; +} + +#results-container p { + margin: 0; + padding: 0; +} + +/* About-page styling*/ +.team { + justify-content: center; + flex-direction: row; +} + +.presentation { + display: flex; + flex-direction: column; + align-items: center; + padding: 1rem; +} + +.presentation img { + max-height: 200px; + max-width: 200px; + border-radius: 150px; + margin-bottom: 10px; +} + +.presentation h2, +p { + margin: 0.5rem; + padding: 0; +} + +footer { + display: flex; + flex-direction: row; + justify-content: center; + padding-top: 2rem; + padding-bottom: 2rem; + align-items: center; +} + +svg { + width: 18px; + height: auto; +} + +@media (min-width: 600px) { + .banner img { + width: 100%; + object-fit: cover; + } + + h1 { + font-size: 2.5rem; + } + + #introduction { + max-width: 600px; + } + + .presentation { + max-width: 600px; + flex-direction: row; + gap: 0.8rem; + } + + fieldset { + max-width: 600px; + } +} diff --git a/index.html b/index.html index a9a33db..c26ae0f 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,177 @@ - + - Title + Web Accessibility Quiz + + + + + + + -

This is the starting point.

+ +
+

Web Accessibility Quiz

+ +
+ +
+ +
+

Accessibility Quiz

+

+ The quiz was made accessible using semantic HTML elements, enabling + smooth navigation with assistive technologies like screen readers. You + can navigate through the quiz using your keyboard: Use Tab to move + between elements, press Spacebar to select an answer, press Enter to + submit the quiz. +

+
+ +
+
+
+ What is the most common form of color blindness? + + + +
+
+
+ + Which assistive technology is commonly used by people with visual + impairments? + + + + +
+
+
+ Who was Louis Braille? + + + +
+
+ +
+
+ +
+ + +
+
+ + + + diff --git a/js/main.js b/js/main.js index e69de29..a9dc8f9 100644 --- a/js/main.js +++ b/js/main.js @@ -0,0 +1,45 @@ +document.addEventListener("DOMContentLoaded", () => { + const toggle = document.getElementById("toggle") + if (!toggle) { + console.error("Toggle element not found!") + return + } + const body = document.body + + // Ensure localStorage has a default value for highContrast + if (localStorage.getItem("highContrast") === null) { + localStorage.setItem("highContrast", "false") + } + + // Restore the toggle state from localStorage so the state doesn't change on reloading the page + const isHighContrast = localStorage.getItem("highContrast") === "true" + if (isHighContrast) { + toggle.classList.add("active") + toggle.checked = true + body.classList.add("high-contrast") + } else { + toggle.classList.remove("active") + toggle.checked = false // To make the toggle visually reflect the state + body.classList.remove("high-contrast") + } + + // Add event listener to the toggle element + toggle.addEventListener("click", () => { + toggleContrast() // Call the toggleContrast function when the button is clicked + }) +}) + +const toggleContrast = () => { + const body = document.body + const toggle = document.getElementById("toggle") + + if (!toggle.classList.contains("active")) { + toggle.classList.add("active") + body.classList.add("high-contrast") + localStorage.setItem("highContrast", "true") // Save preference to localStorage + } else { + toggle.classList.remove("active") + body.classList.remove("high-contrast") + localStorage.setItem("highContrast", "false") // Save preference to localStorage + } +} diff --git a/js/quiz.js b/js/quiz.js new file mode 100644 index 0000000..0c8852b --- /dev/null +++ b/js/quiz.js @@ -0,0 +1,90 @@ +const announcer = document.getElementById("announcer"); +const submitButton = document.getElementById("submit-button"); + +submitButton.addEventListener("click", (event) => { + event.preventDefault(); // Prevent the form from reloading the page + submitAnswers(); +}); + +const submitAnswers = (event) => { + // Call the function to compare answers + compareAnswers(); +}; + +const compareAnswers = () => { + const correctAnswers = ["answer1", "answer2", "answer3"]; // Question 1's correct answer is [0], question 2's is [1], etc.). + + const userAnswers = []; // Array to store user-selected answers + const numberOfQuestions = 3; // Number of questions in the quiz - in case we want to add more questions in the future - just change this variable + + // Retrieve user answers + for (let i = 1; i <= numberOfQuestions; i++) { + const question = document.querySelector( + `input[name="question${i}"]:checked` + ); + if (question) { + userAnswers.push(question.value); // Add the selected answer to the userAnswers array + } else { + userAnswers.push(null); // If no answer is selected, push null + } + } + + let totalCorrectAnswers = 0; + + // Compare user answers with correct answers + for (let i = 0; i < correctAnswers.length; i++) { + if (userAnswers[i] === correctAnswers[i]) { + totalCorrectAnswers++; // Increment the count of correct answers + } + } + displayResults(totalCorrectAnswers, correctAnswers, userAnswers); +}; + +// Loop through each question to display individual feedback (correct/incorrect) +// Get the question number, the user's selected answer, and the correct answer +const displayUserFeedback = (userAnswers, correctAnswers) => { + for (let i = 0; i < correctAnswers.length; i++) { + const questionNumber = i + 1; + const userAnswer = userAnswers[i]; + const correctValue = correctAnswers[i]; + + const feedbackDiv = document.getElementById( + `feedback-question${questionNumber}` + ); + const correctInput = document.querySelector( + `input[name="question${questionNumber}"][value="${correctValue}"]` + ); + + const correctText = correctInput.parentElement.textContent.trim(); // Get the text of the correct answer option + + // Display feedback based on the user's answer + if (userAnswer === correctValue) { + feedbackDiv.classList.add("success-feedback"); + feedbackDiv.innerHTML = `

Correct! "${correctText}" is the right answer!

`; + } else if (userAnswer === null) { + feedbackDiv.classList.add("error-feedback"); + feedbackDiv.innerHTML = `

You forgot to answer!

`; + } else { + feedbackDiv.classList.add("error-feedback"); + feedbackDiv.innerHTML = `

Wrong! The right answer is "${correctText.toLowerCase()}".

`; + } + } +}; + +const displayResults = (totalCorrectAnswers, correctAnswers, userAnswers) => { + const resultsContainer = document.getElementById("results-container"); + resultsContainer.innerHTML = `

Your results:

`; + if (totalCorrectAnswers >= 2) { + resultsContainer.innerHTML = `

Your results:

Great job! You answered ${totalCorrectAnswers} out of ${correctAnswers.length} questions correctly. You know something about accessibility!

`; + announcer.innerHTML = `

Your results:

Great job! You answered ${totalCorrectAnswers} out of ${correctAnswers.length} questions correctly. You know something about accessibility!

`; // Add announcement for screen readers + } else { + resultsContainer.innerHTML = `

Your results:

You answered ${totalCorrectAnswers} out of ${correctAnswers.length} questions correctly. You could learn more about accessibility!

`; + announcer.innerHTML = `

Your results:

You answered ${totalCorrectAnswers} out of ${correctAnswers.length} questions correctly. You could learn more about accessibility!

`; // Add announcement for screen readers + } + resultsContainer.setAttribute("tabindex", "-1"); // Make results section focusable + resultsContainer.focus(); // Set focus to the results section + setTimeout(() => { + resultsContainer.focus(); + }, 300); + displayUserFeedback(userAnswers, correctAnswers); // Call the function to display feedback for each question +}; diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..b705663 --- /dev/null +++ b/styles.css @@ -0,0 +1,30 @@ +/* Toggle switch styles */ +#toggle { + width: 50px; + height: 25px; + background-color: gray; + border-radius: 25px; + position: relative; + cursor: pointer; + transition: background-color 0.3s ease; +} + +#toggle.active { + background-color: yellow; +} + +#toggle::before { + content: ""; + width: 20px; + height: 20px; + background-color: white; + border-radius: 50%; + position: absolute; + top: 2.5px; + left: 2.5px; + transition: left 0.3s ease; +} + +#toggle.active::before { + left: 27.5px; +}