-
Notifications
You must be signed in to change notification settings - Fork 6
ENABLE-140 Component Cookie Banner #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 14 commits
4e7f484
c6cafe8
3aa5997
45c7c2f
fee7b17
8759730
3fdcca0
485b882
73a7ff7
c308ec3
9ac6d9c
eb06df1
f91b786
4b74c83
47e15d5
5c82c30
e72b019
cb4e8c2
88cf056
3fda260
77007be
8bd5749
9af7456
553cb0f
77968eb
83dc65e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| <p> | ||
| Cookie banners are ubiquitous and are often the first element on a page demanding your attention. They appear almost | ||
| instantaneously and requires a prompt decision to a lengthy explanation as to why cookies are necessary for the | ||
| webpage. | ||
| </p> | ||
|
|
||
| <p> | ||
| Unfortunately, many cookie banner implementations do not have the explanation interactive as text, nor do they | ||
| automatically announce that the user is now interacting with a cookie banner. Instead, screen reader users are often | ||
| guided immediately to one of the buttons—accept or reject—without knowing what it is they are accepting or rejecting. | ||
| </p> | ||
|
|
||
| <p> | ||
| The instructions on this page walk through how to implement an accessible cookie banner using the | ||
| <code>dialog</code> HTML element. | ||
| </p> | ||
|
|
||
| <h2>Modal Cookie Banner</h2> | ||
|
|
||
| <p> | ||
| Using a modal dialog for cookie banners causes the contents of the webpage to be unavailable—often covered by a dark | ||
| overlay—until the visitor of the webpage makes a decision on their cookie preferences. The contents of the webpage | ||
| cannot be interacted with until the modal dialog is dismissed, this is true for both sighted users and screen readers. | ||
| </p> | ||
|
|
||
| <br /> | ||
|
|
||
| <button id="show-modal-button">Show Modal Banner</button> | ||
|
|
||
| <h2>Non-Modal Cookie Banner</h2> | ||
|
|
||
| <p> | ||
| Using a non-modal dialog for cookie banners allows the contents of the webpage to still be interactive. However, focus | ||
| often immediately shifts to the action buttons—accept or reject—without first announcing the contents of the dialog. | ||
| The implementation below announces the contents while also automatically focuses on the action buttons. | ||
| </p> | ||
|
|
||
| <br /> | ||
|
|
||
| <button id="show-non-modal-button">Show Non-Modal Banner</button> | ||
|
|
||
| <div id="cookie-banner-container"> | ||
| <dialog id="cookie-banner" class="cookie-banner"> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because this is a non-modal, I think it better to not use the dialog tag to encapsulate the cookie banner in the non-modal case. I think would be better (witharia-labeledby pointing to the title of the banner.
You should use for the modal case, but make sure you follow all the instructions in dialog.php.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @zoltan-dulac , could you share why the <aside> tag would be appropriate for a cookie banner. If we trade <dialog> in favor of <aside> then we also lose the following benefits:
We'd have to program these functionalities ourselves when using the <aside> tag, and also convince users of Enable to do the same.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zoltan: "Will check with Allison regarding the |
||
| <form class="cookie-banner-form" method="dialog" aria-labelledby="cookie-banner-title"> | ||
| <button id="cookie-banner-close-button" class="cookie-banner-close-button" autofocus> | ||
| <img class="a11y-modal__button--close-image" src="images/close-window.svg" alt="close dialog"> | ||
| </button> | ||
|
|
||
| <div role="document" tabindex="0"> | ||
| <h2 id="cookie-banner-title">Cookie Notice</h2> | ||
| <p id="cookie-banner-message"> | ||
| We use strictly necessary cookies to make our Sites work. In addition, if you consent, we will use optional | ||
| functional, performance and targeting cookies to help us understand how people use our website, to improve your | ||
| user experience and to provide you with targeted advertisements. You can accept all cookies, or click to review | ||
| your cookie preferences. | ||
| </p> | ||
| </div> | ||
|
|
||
| <div class="cookie-banner-action-buttons"> | ||
| <button id="cookie-banner-accept-button">Accept</button> | ||
| <button id="cookie-banner-reject-button" class="cookie-banner-reject-button">Reject</button> | ||
| </div> | ||
| </form> | ||
| </dialog> | ||
| </div> | ||
|
|
||
| <?php includeShowcode("cookie-banner-container"); ?> | ||
|
|
||
| <script type="application/json" id="cookie-banner-container-props"> | ||
| { | ||
| "replaceHtmlRules": {}, | ||
| "steps": [ | ||
| { | ||
| "label": "Use a form and ensure it has its aria-labelledby attribute set", | ||
| "highlight": "aria-labelledby", | ||
| "notes": "Using a form is good practice—<a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#usage_notes\">MDN docs</a>—to have the dialog close automatically when a button within it is pressed. Using aria-labelledby along with using a div element with a role of document (next step) ensures it's read out when the dialog is opened." | ||
| }, | ||
| { | ||
| "label": "Use the document role to enclose the cookie explanation", | ||
| "highlight": "role", | ||
| "notes": "This presents the content in reading mode for screen readers." | ||
| } | ||
| ] | ||
| } | ||
| </script> | ||
|
|
||
| <p> | ||
| With the HTML set up, use the built-in methods <code>.showModal()</code> or <code>.show()</code> for the | ||
| <code>dialog</code> HTML tag to show a modal or a non-modal dialog, respectively. | ||
| </p> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| <script id="cookie-banner-js" type="module"> | ||
| import cookieBanner from "./js/modules/cookiebanner.js" | ||
| import showCode from "./js/enable-libs/showcode.js"; | ||
| cookieBanner.init(); | ||
| showCode.addJsObj('cookie banner', cookieBanner); | ||
| </script> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| <link id="cookiebanner-css" rel="stylesheet" type="text/css" href="css/cookiebanner.css" > |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| .cookie-banner { | ||
| position: fixed; | ||
| bottom: 0; | ||
| z-index: 1000; | ||
| } | ||
| .cookie-banner-form { | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
| .cookie-banner-close-button { | ||
| position: absolute; | ||
| top: 0; | ||
| right: 0; | ||
| max-width: 32px; | ||
| max-height: 32px; | ||
| } | ||
| .cookie-banner-action-buttons { | ||
| display: flex; | ||
| flex-direction: row; | ||
| margin-left: auto; | ||
| } | ||
| .cookie-banner-reject-button { | ||
| margin-left: 16px; | ||
| } |
ocjadan marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| 'use-strict' | ||
|
|
||
| const cookieBanner = new function() { | ||
| let cookieBanner; | ||
|
|
||
| this.init = function() { | ||
| cookieBanner = document.getElementById('cookie-banner'); | ||
| setUpModalButton(); | ||
| setUpNonModalButton(); | ||
| } | ||
|
|
||
| function setUpModalButton() { | ||
| const showModalButton = document.getElementById('show-modal-button'); | ||
| showModalButton.addEventListener('click', () => { | ||
| cookieBanner.showModal(); | ||
| }); | ||
| } | ||
|
|
||
| function setUpNonModalButton() { | ||
| const showNonModalButton = document.getElementById('show-non-modal-button'); | ||
| showNonModalButton.addEventListener('click', () => { | ||
| cookieBanner.show(); | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| export default cookieBanner; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| 'use-strict' | ||
|
|
||
| const cookieBanner = new (function() { | ||
| let cookieBanner; | ||
|
|
||
| this.init = function() { | ||
| cookieBanner = document.getElementById('cookie-banner'); | ||
| setUpModalButton(); | ||
| setUpNonModalButton(); | ||
| } | ||
|
|
||
| function setUpModalButton() { | ||
| const showModalButton = document.getElementById('show-modal-button'); | ||
| showModalButton.addEventListener('click', () => { | ||
| cookieBanner.showModal(); | ||
| }); | ||
| } | ||
|
|
||
| function setUpNonModalButton() { | ||
| const showNonModalButton = document.getElementById('show-non-modal-button'); | ||
| showNonModalButton.addEventListener('click', () => { | ||
| cookieBanner.show(); | ||
| }) | ||
| } | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import config from './test-config'; | ||
|
|
||
| let cookieBanner; | ||
|
|
||
| beforeAll(async () => { | ||
| await page.goto(`${config.BASE_URL}/cookiebanner.php`); | ||
| cookieBanner = await page.$('.cookie-banner'); | ||
| }); | ||
|
|
||
| describe('Tests for cookie banner', () => { | ||
| it('Contains div with a document role', async () => { | ||
| let result = await cookieBanner.$('div[role="document"]'); | ||
| expect(result).not.toBeNull(); | ||
| }); | ||
|
|
||
| it('Contains close button', async () => { | ||
| let result = await getCloseButton(); | ||
| console.log(result); | ||
| expect(result).not.toBeNull(); | ||
| }); | ||
|
|
||
| it('Has autofocus set to the close button', async () => { | ||
| let closeButton = await getCloseButton(); | ||
| let result = await page.evaluate((closeButton) => closeButton.hasAttribute('autofocus'), closeButton); | ||
| expect(result).toBe(true); | ||
| }); | ||
|
|
||
| async function getCloseButton() { | ||
| return await cookieBanner.$('button[id$="close-button"]'); | ||
| } | ||
|
|
||
| it('Contains form with method of dialog', async () => { | ||
| let result = await getForm(); | ||
| expect(result).not.toBeNull(); | ||
| }); | ||
|
|
||
| it('Has aria-labelledby set for form', async () => { | ||
| let form = await getForm(); | ||
| let result = await page.evaluate((form) => form.hasAttribute('aria-labelledby'), form); | ||
| expect(result).toBe(true); | ||
| }); | ||
|
|
||
| it('Contains element for aria-labelledby', async () => { | ||
| let form = await getForm(); | ||
| let ariaLabelledBy = await page.evaluate((el) => el.getAttribute('aria-labelledby'), form); | ||
| let result = await cookieBanner.$(`[id$="${ariaLabelledBy}"]`); | ||
| expect(result).not.toBeNull(); | ||
| }); | ||
|
|
||
| async function getForm() { | ||
| return await cookieBanner.$('form[method="dialog"]'); | ||
| } | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| .cookie-banner { | ||
ocjadan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| position: fixed; | ||
| bottom: 0; | ||
| z-index: 1000; // Ensure the non-modal dialog is above other content | ||
| } | ||
|
|
||
| .cookie-banner-form { | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
|
|
||
| .cookie-banner-close-button { | ||
| position: absolute; | ||
| top: 0; | ||
| right: 0; | ||
| max-width: 32px; | ||
| max-height: 32px; | ||
| } | ||
|
|
||
| .cookie-banner-action-buttons { | ||
| display: flex; | ||
| flex-direction: row; | ||
| margin-left: auto; | ||
| } | ||
|
|
||
| .cookie-banner-reject-button { | ||
| margin-left: 16px; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.