diff --git a/.gitignore b/.gitignore index e3a3069..619f6f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -urls.json \ No newline at end of file +.vscode +urls.json +violations.json \ No newline at end of file diff --git a/index.html b/index.html index eb3ab1e..5cc77dc 100644 --- a/index.html +++ b/index.html @@ -22,6 +22,11 @@

Accessibility Report

+ + +

+
    +
    \ No newline at end of file diff --git a/public/script.js b/public/script.js index ce748cb..38aa97c 100644 --- a/public/script.js +++ b/public/script.js @@ -1,15 +1,50 @@ const app = document.querySelector('#app'); const violations = fetch('violations.json').then(response => response.json()); +// Helper function to convert to sentence case +const toSentenceCase = (string) => { + return string + .toLowerCase() + .split(' ') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +}; +const replaceHyphensWithSpaces = (string) => string.replace(/-/g, ' '); + +// Helper function to escape HTML +const escapeHtml = (unsafe) => { + return unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +}; + +const dialog = document.querySelector('.dialog-violations'); +const violationsList = dialog.querySelector('.dialog-violations__list'); violations.then(data => { const numberOfURLsTested = data.numberOfURLsTested; const allViolations = data.violations; - // Filter for color-contrast violations - const colorContrastViolations = allViolations.filter(violation => violation.id === 'color-contrast'); + // Group violations by type + const violationsByType = allViolations.reduce((acc, violation) => { + if (!acc[violation.id]) { + acc[violation.id] = []; + } + acc[violation.id].push(violation); + return acc; + }, {}); + + // Create variables for each violation type dynamically + const violationTypes = Object.keys(violationsByType); + const violationVariables = violationTypes.reduce((acc, type) => { + acc[type] = violationsByType[type]; + return acc; + }, {}); // Count the number of nodes in color-contrast violations - const numberOfColorContrastNodes = colorContrastViolations.reduce((acc, violation) => acc + violation.nodes.length, 0); + // const numberOfColorContrastNodes = violationVariables['color-contrast'].reduce((acc, violation) => acc + violation.nodes.length, 0); // Get unique URLs with violations const uniqueURLsWithViolations = [...new Set(allViolations.map(violation => violation.url))]; @@ -26,28 +61,55 @@ violations.then(data => { `).join('')}

    Accessibility Violations

    -

    Colour contrast issues

    -

    We found ${numberOfColorContrastNodes} color-contrast issues across the tested pages.

    -

    Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds.

    -

    For more information, see WCAG 2.0 Guideline 1.4.

    -

    For a list of pages with color-contrast issues, see below:

    -
      - ${colorContrastViolations.map(violation => ` -
    1. - ${violation.url} - -
    2. - `).join('')} -
    +
    + ${violationTypes.map(type => ` +
    +

    ${toSentenceCase(replaceHyphensWithSpaces(type))}

    +

    There are ${violationVariables[type].length} ${type} violations across ${uniqueURLsWithViolations.length} URLs.

    +
      + ${violationVariables[type].map((violation, index) => ` +
    1. + ${violation.url} +
        +
      • Description: ${violation.description}
      • +
      • Impact: ${toSentenceCase(violation.impact)}
      • +
      • Instances: ${violation.nodes.length} -
      • +
      +
    2. + `).join('')} +
    +
    + `).join('')} +
    `; + + // Event listener for the view violations buttons + document.querySelectorAll('.view-violations').forEach(button => { + button.addEventListener('click', (event) => { + const type = event.target.getAttribute('data-type'); + const index = event.target.getAttribute('data-index'); + const violation = violationVariables[type][index]; + violationsList.innerHTML = ''; + + violation.nodes.forEach(node => { + console.log(node); + const listItem = document.createElement('li'); + listItem.innerHTML = ` +

    HTML:

    + ${escapeHtml(node.html)} +

    Target: ${node.target.join(' > ')}

    +

    Summary: ${node.failureSummary}

    + `; + violationsList.appendChild(listItem); + }); + + dialog.showModal(); + }); + }); + + // Event listener for the close dialog button + dialog.querySelector('.dialog-close').addEventListener('click', () => { + document.querySelector('.dialog-violations').close(); + }); + }); \ No newline at end of file diff --git a/public/style.css b/public/style.css index 25552c4..809e7a4 100644 --- a/public/style.css +++ b/public/style.css @@ -1,3 +1,13 @@ +:root { + --color-primary: #095dff; + --color-white: #ffffff; + --spacing: 1rem; + --color-grey-lightest: #f2f2f2; + --color-black: #000000; + --transition-time: 0.3s; + --border: 1px solid var(--color-primary); +} + *, *::before, *::after { @@ -12,7 +22,7 @@ body { margin: 0; line-height: 1.5; font-family: Arial, sans-serif; - color: #095dff; + color: var(--color-black); } header, @@ -22,20 +32,90 @@ footer { } a { - color: #095dff; + color: var(--color-primary); +} +a:hover, +a:focus-visible { + text-decoration: none; +} + +a, +button, +input, +select, +textarea { + transition: var(--transition-time); } h2 ~ h2 { margin-block-start: 2rem; padding-block-start: 2rem; - border-top: 1px solid #095dff; + border-top: 1px solid var(--color-primary); } .bg { - background-color: #095dff; + background-color: var(--color-primary); color: #ffffff; } .bg a { color: #ffffff; +} + +.impact { + padding: 0.15rem; + border: var(--border); + color: var(--color-black); + border-color: var(--color-black); +} +.impact--minor { + background-color: #f3ea00; +} +.impact--moderate { + background-color: #d4cd00; +} +.impact--serious { + background-color: #c86e00; + color: var(--color-white); +} +.impact--critical { + color: var(--color-white); + background-color: #dc0000; +} + +dialog::backdrop { + background-color: var(--color-primary); + opacity: 0.9; +} + +button { + padding: 0.25rem 0.5rem; + background-color: var(--color-primary); + color: var(--color-white); + border: var(--border); +} +button:focus-visible, +button:hover { + background-color: var(--color-white); + color: var(--color-primary); + border-color: var(--color-primary); +} + +.violations { + padding: 1rem; + background-color: var(--color-grey-lightest); + border: var(--border); +} + +.violations__list > li { + margin-block-end: 1rem; +} +.violations__list > li li { + margin-block-start: 0.5rem; +} + +.grid { + display: grid; + gap: var(--spacing); + grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); } \ No newline at end of file