diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..f10092a Binary files /dev/null and b/.DS_Store differ diff --git a/about.html b/about.html index 5adbad6..bebfbe8 100644 --- a/about.html +++ b/about.html @@ -1,10 +1,174 @@ - + About - + + + + + + +
+
+
+

About Web Accessibility & WCAG

+

+ The internet should work for everyone—but many digital experiences + still fall short when it comes to accessibility. That’s where the + Web Content Accessibility Guidelines (WCAG) come in. +

+
+
+

What is WCAG?

+

+ WCAG is a globally recognized standard developed by the + W3C (World Wide Web Consortium) to help make websites, apps, + and digital content accessible to users of all abilities. These + guidelines are essential for building inclusive interfaces that + consider users with visual, auditory, motor, cognitive, and + neurological impairments.

+ + WCAG is built around four key principles, known as P.O.U.R.: +

+
+
+

WCAG POUR Principles

+ +
+ Diagram illustrating the four WCAG accessibility principles: Perceivable, Operable, Understandable, and Robust, with key characteristics listed under each. +
+
+
+
+

Perceivable

+

+ Users must be able to detect and interpret content, regardless of + how they consume it. +

+
+
+

As a developer, this means:

+
    +
  • Use semantic HTML and provide alt attributes for images.
  • +
  • + Ensure text has sufficient color contrast against backgrounds + (e.g., using WCAG AA or AAA contrast ratios). +
  • +
  • Add captions and transcripts for multimedia content.
  • +
+
+
+ +
+
+

Operable

+

+ All users should be able to navigate and interact with your + interface. +

+
+
+

To support this, you should:

+
    +
  • + Ensure full keyboard functionality (e.g., forms, buttons, + menus). +
  • +
  • Provide visible focus indicators and logical tab order.
  • +
  • + Avoid flashing content that could trigger seizures or + discomfort. +
  • +
+
+
+
+
+

Understandable

+

+ Your content and interface should be predictable, clear, and + helpful. +

+
+
+

This includes:

+
    +
  • Using clear, simple language
  • +
  • Ensuring consistent design and predictable behavior
  • +
  • + Offering helpful error messages and instructions for forms and + interactions +
  • +
+
+
+
+
+

Robust

+

+ Your code should be compatible with a wide range of + technologies—including screen readers and future tools. +

+
+
+

Best practices:

+
    +
  • Use clean, semantic HTML over div-based structures.
  • +
  • + Avoid unnecessary ARIA roles—use native elements where possible. +
  • +
  • + Ensure dynamic content updates are accessible (e.g., using ARIA + live regions). +
  • +
+
+
+
+

+ By following the P.O.U.R. principles, you build more + resilient, inclusive, and future-proof digital experiences. + Accessibility isn’t just a nice-to-have—it’s essential for reaching + all users, and it’s part of writing good code. +

+
+
+
+ + + diff --git a/css/styles.css b/css/styles.css index e69de29..d03cb13 100644 --- a/css/styles.css +++ b/css/styles.css @@ -0,0 +1,418 @@ +/* ----- Overall styling ----- */ + +/* Colors */ +:root { + --header-background-color: #335c67; + --main-background-color: #ffffff; + --body-background-color: #ffeedd; + --primary-color: #b7410e; + --error-color: #e74c3c; + --success-color: #27ae60; + --progress-bg: #f5f5f5; + --border-color: #e2e8f0; + --primary-color-dark: #540b0e; + + --hover-bg: #f5f5f5; + --focus-outline: 3px solid var(--accent-color); + --focus-ring-color: rgba(0, 86, 179, 0.5); +} + +/* Apply box-sizing border-box to all elements */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Body */ +body { + margin: 0; + background: var(--body-background-color); + min-height: 100vh; + font-family: "Roboto", sans-serif; +} + +/* --- Typography --- */ +h1 { + font-size: 40px; + font-weight: 700; +} + +h2 { + font-size: 32px; + font-weight: 700; +} + +h3, +legend { + font-size: 24px; + font-weight: 700; +} + +h4 { + font-size: 20px; + font-weight: 700; +} + +p { + font-size: 16px; + font-weight: 400; +} + +a { + text-decoration: none; + font-size: 18px; + font-weight: 700px; + color: white; +} + +/* --- Buttons --- */ + +.primary-button, +.secondary-button { + padding: 8px 12px; + border-radius: 5px; + cursor: pointer; + font-size: 16px; + font-weight: 800; +} + +.primary-button { + background: var(--primary-color); + color: white; + border: 3px solid var(--primary-color); +} + +.secondary-button { + background-color: white; + color: var(--primary-color); + border: 3px solid var(--primary-color); +} + +.primary-button:hover, +.secondary-button:hover { + background: var(--primary-color-dark); + color: white; + border: 3px solid var(--primary-color-dark); +} + +/* --- Accessibility --- */ +.skip-link { + position: absolute; + top: -40px; + left: 0; + background: var(--primary-color); + color: white; + padding: 8px; + z-index: 100; + transition: top 0.3s; +} + +.skip-link:focus { + top: 0; + outline: var(--focus-outline); + outline-offset: 2px; +} + +.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; +} + +/* Images */ +.decorative-img { + height: 80px; +} + +.educational-images { + display: flex; + justify-content: center; +} +.educational-images img { + width: 100%; + max-width: 500px; + height: auto; + padding: 16px; +} + +/* ----- Layout components ----- */ + +/* ---Header--- */ +header { + background: var(--header-background-color); + padding: 36px 64px; + height: 250px; + color: white; + width: 100%; + position: relative; + z-index: 1; +} + +header ul { + list-style-type: none; + display: flex; + gap: 24px; + padding: 16px; +} + +header ul li { + position: relative; + text-align: center; + font-weight: 600; + padding: 0 12px; +} + +li.active::after { + position: absolute; + content: ""; + height: 3px; + width: 100%; + background: var(--primary-color); + left: 0; + bottom: -10px; +} + +/* --- Main --- */ +main { + margin: 16px 64px; + padding: 24px; + background: var(--main-background-color); + border-radius: 15px; + position: relative; + top: -60px; + max-width: 800px; + z-index: 2; +} + +/* --- Quiz form --- */ +.quiz-section form { + margin-top: 32px; +} + +.quiz-section fieldset { + padding: 16px; + margin-bottom: 16px; + border: none; +} + +.radio-group { + display: flex; + flex-direction: column; + gap: 12px; + margin: 24px 0; +} +.radio-option { + border: 2px solid var(--border-color); + border-radius: 15px; + padding: 4px; + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; +} + +.radio-option:hover { + background: var(--hover-bg); +} + +.quiz-section fieldset label { + cursor: pointer; + width: 95%; + height: 100%; + display: inline-block; + padding: 16px 8px; +} + +/* Form controls */ +.form-controls { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.result-controls { + display: flex; + flex-direction: row; + gap: 16px; +} + +/* Progress bar */ +.progress-bar { + background-color: var(--progress-bg); + border-radius: 8px; + height: 20px; + overflow: hidden; + position: relative; +} + +.progress-fill { + background-color: var(--primary-color); + height: 100%; + transition: width 0.3s ease; + border-radius: 8px; +} + +/* Error message */ +.error-message { + padding: 16px; + background-color: var(--error-color); + border-radius: 15px; + color: black; +} + +/* --- Results Section --- */ +.result-content { + margin: 24px 0; + padding: 16px; + background-color: var(--hover-bg); + border-radius: 8px; + font-size: 19px; +} + +.result-content p { + margin: 0; + font-size: 20px; +} + +.result-content strong { + color: #d35400; + font-weight: 700; +} + +/* Question summary */ +.feedback-details { + margin: 32px 0; +} + +.feedback-details h3 { + margin-bottom: 24px; + border-bottom: 2px solid #f0f0f0; + padding-bottom: 8px; +} + +.feedback-details ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +.feedback-details li { + margin-bottom: 24px; + padding-bottom: 24px; + border-bottom: 1px solid #eaeaea; +} + +.feedback-details li:last-child { + border-bottom: none; + margin-bottom: 0; +} + +/* Feedback details */ +.feedback-details p { + margin: 8px 0; + line-height: 1.5; +} + +.feedback-details strong { + font-weight: 600; +} + +.correct { + color: #27ae60; + font-weight: 600; + position: relative; + padding-left: 22px; +} + +.correct::before { + content: "✓"; + position: absolute; + left: 0; + color: #27ae60; +} + +.incorrect { + color: #e74c3c; + font-weight: 600; + position: relative; + padding-left: 22px; +} + +.incorrect::before { + content: "✗"; + position: absolute; + left: 0; + color: #e74c3c; +} + +.feedback-details li p:last-child { + color: #27ae60; + font-weight: 500; +} + +/* --- Footer --- */ +footer { + margin: 0; + background-color: var(--header-background-color); + height: 150px; +} + +.footer-content { + padding-top: 24px; +} +footer ul { + list-style: none; + margin: 24px 64px; + display: flex; + flex-direction: column; + gap: 16px; +} + +footer a { + color: white; + text-decoration: none; +} + +/* Media QUERIES */ +@media (max-width: 667px) { + /* Header */ + header { + padding: 24px 16px; + height: auto; + } + + /* Main */ + main { + margin: 0; + padding: 24px 16px; + border-radius: 0px; + top: 0px; + } + + /* Footer */ + footer ul { + margin: 24px 16px; + } + + /* Quiz form */ + .quiz-section fieldset { + padding: 0px; + } + + .form-controls, + .result-controls { + flex-wrap: wrap; + gap: 12px; + } + + button { + width: 100%; + } +} diff --git a/img/.DS_Store b/img/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/img/.DS_Store differ diff --git a/img/WCAG-POUR.png b/img/WCAG-POUR.png new file mode 100644 index 0000000..6afe3c9 Binary files /dev/null and b/img/WCAG-POUR.png differ diff --git a/img/intro-icon.png b/img/intro-icon.png new file mode 100644 index 0000000..97097fd Binary files /dev/null and b/img/intro-icon.png differ diff --git a/img/party-popper-emoji.png b/img/party-popper-emoji.png new file mode 100644 index 0000000..961e223 Binary files /dev/null and b/img/party-popper-emoji.png differ diff --git a/img/quiz-icon.png b/img/quiz-icon.png new file mode 100644 index 0000000..12f043c Binary files /dev/null and b/img/quiz-icon.png differ diff --git a/index.html b/index.html index a9a33db..b53b4ce 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,165 @@ - + - Title + Quizy + -

This is the starting point.

+ + + + +
+
+ +

Is Your Code Truly Accessible? Test Your Knowledge

+

+ Creating an inclusive digital experience is not just an option—it is a + responsibility. Web accessibility ensures that everyone, including + people with disabilities, can navigate, understand, and interact with + online content. Adhering to the + WCAG (Web Content Accessibility Guidelines) helps make the web + more usable for all.

How well do you understand + accessibility best practices? Whether you are an experienced developer + or just beginning to explore inclusive design, this quiz will assess + your knowledge and provide valuable insights to help you improve. +

+ Are you ready to enhance your accessibility skills? Start the quiz + now! +

+
+ +
+
+ + + + + +
+
+ + + + diff --git a/js/accordion.js b/js/accordion.js new file mode 100644 index 0000000..3a74738 --- /dev/null +++ b/js/accordion.js @@ -0,0 +1,73 @@ +// function initAccordion() { +// const accordion = document.querySelector('.accordion') +// if (!accordion) return; + +// const buttons = accordion.querySelectorAll('.accordion-button') + +// buttons.forEach((button, index) => { +// button.addEventListener('keydown', (event) => { +// const targetId = button.getAttribute('aria-controls'); +// const targetPanel = document.getElementById(targetId) + +// switch (event.key) { +// case 'AerrowDown': +// case 'ArrowRight': +// event.preventDefault(); +// const nextButton = buttons[(index + 1) % buttons.length] +// nextButton.focus(); +// break; +// case 'ArrowUp': +// case 'ArrowLeft': +// event.preventDefault(); +// const prevButton = +// buttons[(index - 1 + buttons.length) % buttons.length] +// prevButton.focus(); +// break; +// case 'Home': +// event.preventDefault(); +// buttons[0].focus(); +// break; +// case 'End': +// event.preventDefault(); +// buttons[buttons.length - 1].focus(); +// break; +// case ' ': +// case 'Enter': +// event.preventDefault(); +// togglePanel(button, targetPanel); +// break; +// } +// }); + +// button.addEventListener('click', () => { +// const targetId = button.getAttribute('aria-controls') +// const targetPanel = document.getElementById(targetId) +// togglePanel(button, targetPanel); +// }); +// }); + +// function togglePanel(button, panel) { +// const isExpanded = button.getAttribute('aria-expanded') === 'true'; +// const newExpandedState = !isExpanded; + +// button.setAttribute('aria-expanded', newExpandedState); + +// if (newExpandedState) { +// panel.hidden = false; + +// requestAnimationFrame(() => { +// panel.style.maxHeight = panel.scrollHeight + 'px'; +// }); +// } else { +// panel.style.maxHeight = '0'; + +// panel.addEventListener('transitioned', () => { +// panel.hidden = true; +// }, +// { once: true } +// ); +// } +// } +// } + +// document.addEventListener('DOMContentLoaded', initAccordion); diff --git a/js/main.js b/js/main.js index e69de29..5bc66b7 100644 --- a/js/main.js +++ b/js/main.js @@ -0,0 +1,361 @@ +// Quiz State Management +let currentQuestionIndex = 0; +let userAnswers = []; +let questionsData = []; + +// DOM Elements - Organized by section +const sections = { + intro: document.getElementById("introduction"), + quiz: document.getElementById("quiz"), + results: document.getElementById("results"), +}; + +const elements = { + // Buttons + startButton: document.getElementById("start-quiz-button"), + nextButton: document.getElementById("nextQuestion"), + previousButton: document.getElementById("previousQuestion"), + submitButton: document.getElementById("submitQuiz"), + restartButton: document.getElementById("restartQuiz"), + homeButton: document.getElementById("backToHome"), + + // Quiz elements + quizForm: document.getElementById("quiz-form"), + questionContainer: document.getElementById("question-container"), + questionContent: document.querySelector(".question-content"), + + // Progress indicators + progressText: document.querySelector(".progress-text"), + progressFill: document.querySelector(".progress-fill"), + + // Results elements + resultContent: document.getElementById("result-content"), + feedbackDetails: document.getElementById("feedback-details"), + + // Accessibility and error handling + announcer: document.getElementById("announcer"), + questionError: document.getElementById("question-error"), + errorMessage: document.querySelector(".error-message"), +}; + +// Initialize the quiz +document.addEventListener("DOMContentLoaded", () => { + // Import quiz questions from external file + questionsData = questions || []; + + // Set up event listeners + setupEventListeners(); + + // Initialize user answers array + resetUserAnswers(); +}); + +// Set up all event listeners +function setupEventListeners() { + elements.startButton.addEventListener("click", startQuiz); + elements.nextButton.addEventListener("click", goToNextQuestion); + elements.previousButton.addEventListener("click", goToPreviousQuestion); + elements.quizForm.addEventListener("submit", handleQuizSubmit); + elements.restartButton.addEventListener("click", restartQuiz); + elements.homeButton.addEventListener("click", goToHomepage); +} + +// Reset user answers to initial state +function resetUserAnswers() { + userAnswers = new Array(questionsData.length).fill(null); +} + +// Start the quiz +function startQuiz() { + showSection("quiz"); + window.location.hash = "#quiz"; + + // Reset quiz state + currentQuestionIndex = 0; + resetUserAnswers(); + + // Load the first question + loadQuestion(currentQuestionIndex); + + // Announce restart for screen readers + announceMessage("Quiz started. First question loaded."); +} + +// Load a question by index +function loadQuestion(index) { + // Update progress indicators + updateProgress(index); + + const currentQuestion = questionsData[index]; + + // Clear previous question content + elements.questionContent.innerHTML = ""; + + // Update fieldset legend + updateQuestionLegend(currentQuestion, index); + + // Create and add radio options + createRadioOptions(currentQuestion, index); + + // Hide error message + hideError(); + + // Update navigation buttons + updateNavigationButtons(); +} + +// Update the question legend in the fieldset +function updateQuestionLegend(question, index) { + const fieldset = elements.questionContent.closest("fieldset"); + const legendId = `question-${index}`; + + // Remove existing legend if present + const existingLegend = fieldset.querySelector("legend"); + if (existingLegend) { + fieldset.removeChild(existingLegend); + } + + // Create and add new legend + const legend = document.createElement("legend"); + legend.id = legendId; + legend.textContent = question.question; + fieldset.insertBefore(legend, fieldset.firstChild); +} + +// Create radio options for the current question +function createRadioOptions(question, questionIndex) { + const radioGroup = document.createElement("div"); + radioGroup.className = "radio-group"; + radioGroup.setAttribute("role", "radiogroup"); + + question.options.forEach((option, optionIndex) => { + const radioOption = document.createElement("div"); + radioOption.className = "radio-option"; + + const input = document.createElement("input"); + input.type = "radio"; + input.name = `question-${questionIndex}`; + input.value = optionIndex; + input.id = `option-${questionIndex}-${optionIndex}`; + + // Check if user has already answered this question + if (userAnswers[questionIndex] === optionIndex) { + input.checked = true; + } + + const label = document.createElement("label"); + label.setAttribute("for", `option-${questionIndex}-${optionIndex}`); + label.textContent = option.text; + + // Add event listener to capture answers + input.addEventListener("change", () => { + userAnswers[questionIndex] = optionIndex; + hideError(); + updateNavigationButtons(); + updateProgress(currentQuestionIndex); + }); + + radioOption.appendChild(input); + radioOption.appendChild(label); + radioGroup.appendChild(radioOption); + }); + + elements.questionContent.appendChild(radioGroup); +} + +// Update progress indicators +function updateProgress(index) { + const totalQuestions = questionsData.length; + const currentQuestionNum = index + 1; + + // Update text progress + elements.progressText.innerHTML = `Question ${currentQuestionNum} of ${totalQuestions}`; + + // Count answered questions and update progress bar + const answeredCount = userAnswers.filter((answer) => answer !== null).length; + const progressPercentage = (answeredCount / totalQuestions) * 100; + elements.progressFill.style.width = `${progressPercentage}%`; +} + +// Update navigation buttons based on current state +function updateNavigationButtons() { + const isLastQuestion = currentQuestionIndex === questionsData.length - 1; + const isFirstQuestion = currentQuestionIndex === 0; + + // Show/hide previous button + elements.previousButton.hidden = isFirstQuestion; + + // Show/hide next and submit buttons + elements.nextButton.hidden = isLastQuestion; + elements.submitButton.hidden = !isLastQuestion; +} + +// Navigate to next question +function goToNextQuestion() { + // Ensure current question is answered + if (userAnswers[currentQuestionIndex] === null) { + showError("Please select an answer before continuing."); + return; + } + + // Move to next question if not at the end + if (currentQuestionIndex < questionsData.length - 1) { + currentQuestionIndex++; + loadQuestion(currentQuestionIndex); + + // Focus on first radio button for accessibility + focusOnFirstRadioButton(); + announceMessage(`Question ${currentQuestionIndex + 1} loaded.`); + } +} + +// Navigate to previous question +function goToPreviousQuestion() { + if (currentQuestionIndex > 0) { + currentQuestionIndex--; + loadQuestion(currentQuestionIndex); + + // Focus on first radio button for accessibility + focusOnFirstRadioButton(); + announceMessage(`Question ${currentQuestionIndex + 1} loaded.`); + } +} + +// Focus on the first radio button for keyboard navigation +function focusOnFirstRadioButton() { + setTimeout(() => { + const firstRadio = elements.questionContent.querySelector( + 'input[type="radio"]' + ); + if (firstRadio) { + firstRadio.focus(); + } + }, 10); +} + +// Show error message +function showError(message) { + elements.questionError.hidden = false; + elements.errorMessage.textContent = message; + elements.announcer.textContent = message; +} + +// Hide error message +function hideError() { + elements.questionError.hidden = true; +} + +// Handle quiz submission +function handleQuizSubmit(event) { + event.preventDefault(); + + // Ensure final question is answered + if (userAnswers[currentQuestionIndex] === null) { + showError("Please select an answer before submitting."); + return; + } + + showResults(); +} + +// Calculate and display results +function showResults() { + showSection("results"); + + // Calculate score + const result = calculateScore(); + + // Display score summary + elements.resultContent.innerHTML = ` +

You scored ${result.correct} out of ${result.total} questions correctly.

`; + + // Generate detailed feedback + elements.feedbackDetails.innerHTML = generateFeedbackHTML(result); + + // Announce results for screen readers + announceMessage( + `Quiz completed. You scored ${result.correct} out of ${result.total} questions correctly.` + ); +} + +// Calculate quiz score +function calculateScore() { + let correctAnswers = 0; + const totalQuestions = questionsData.length; + + userAnswers.forEach((answer, index) => { + if (answer !== null && questionsData[index].options[answer].correct) { + correctAnswers++; + } + }); + + return { + correct: correctAnswers, + total: totalQuestions, + percentage: Math.round((correctAnswers / totalQuestions) * 100), + }; +} + +// Generate HTML for feedback details +function generateFeedbackHTML(result) { + let feedbackHTML = "

Question Summary

"; +} + +// Restart the quiz +function restartQuiz() { + // Reset state + currentQuestionIndex = 0; + resetUserAnswers(); + + // Show quiz section + showSection("quiz"); + + // Load first question + loadQuestion(currentQuestionIndex); + + // Announce restart for screen readers + announceMessage("Quiz restarted. First question loaded."); +} + +// Go to homepage +function goToHomepage() { + showSection("intro"); + announceMessage("Returned to homepage."); +} + +// Helper function to show a specific section and hide others +function showSection(sectionName) { + Object.keys(sections).forEach((key) => { + sections[key].hidden = key !== sectionName; + }); +} + +// Screen reader announcement utility +function announceMessage(message) { + elements.announcer.textContent = message; +} diff --git a/js/quiz.js b/js/quiz.js new file mode 100644 index 0000000..42e9f9d --- /dev/null +++ b/js/quiz.js @@ -0,0 +1,53 @@ +const questions = [ + { + question: "What does the 'Robust' principle in WCAG focus on?", + options: [ + { text: "Making sure your design looks modern", correct: false }, + { + text: "Ensuring content works with various assistive technologies", + correct: true, + }, + { text: "Using animations that enhance UX", correct: false }, + { text: "Providing user-friendly navigation", correct: false }, + ], + }, + { + question: "Which of the following supports the 'Perceivable' principle?", + options: [ + { text: "Making all text bold", correct: false }, + { text: "Providing alt text for images", correct: true }, + { text: "Using JavaScript-only buttons", correct: false }, + { text: "Removing focus outlines", correct: false }, + ], + }, + { + question: "Which practice improves 'Operable' accessibility?", + options: [ + { text: "Adding hover-only navigation", correct: false }, + { text: "Ensuring the site works without JavaScript", correct: false }, + { text: "Enabling all functionality via keyboard", correct: true }, + { text: "Center-aligning all text", correct: false }, + ], + }, + { + question: "What does the 'Understandable' principle emphasize?", + options: [ + { text: "Making sure your app loads quickly", correct: false }, + { text: "Consistent layout and helpful error messages", correct: true }, + { text: "Adding more animations for clarity", correct: false }, + { text: "Using icons without labels to simplify UI", correct: false }, + ], + }, + { + question: "Why is semantic HTML important for accessibility?", + options: [ + { text: "It makes code easier to minify", correct: false }, + { + text: "It helps screen readers interpret content correctly", + correct: true, + }, + { text: "It boosts website animations", correct: false }, + { text: "It avoids needing JavaScript", correct: false }, + ], + }, +];