A11yAdvent Day 7: Page Title in SPA

Single-page applications (SPA for short) have been all the hype for the last decade or so. The idea is that we can avoid reloading the entire page when navigating within a site and instead update only the moving parts (usually the content area). This comes from a great premise: faster interactions, no unnecessary HTTP roundtrips, less used bandwidth.

The thing we usually don’t think about is that many assistive technologies such as screen-readers have been initially authored with the “original web” in mind and rely on page (re)loads to announce the page context, namely the page title (hold by the element).

When building a SPA—no matter the framework—it is important to do some work to announce the title when following router links. Two things need to happen:

  1. The title of the new view/page needs to be announced.
  2. The focus needs to be preserved or moved to a proper place.

A nice solution is to have a visually hidden element at the top of the page which receives the new title when navigating, and move the focus on that element so the content is read. Ideally, the skip link lives right after that node so the flow goes like this:

  1. Press a link in the content area that causes a router change.
  2. The view gets loaded.
  3. The title for that view gets rendered in the invisible node.
  4. The focus gets move to that node so its content is announced.
  5. Tabbing once gets to the skip link, so getting back to the content area is fast and convenient.

Here is how our HTML should look like:

body>
p tabindex="-1" class="sr-only">p>
a href="#main" class="sr-only sr-only--focusable">Skip to contenta>

body>

And our unflavoured JavaScript. Note that this is no specific framework—it’s just a made-up API to illustrate the concept.

const titleHandler = document.querySelector('body > p')

router.on('page:change', ({ title }) => {
// Render the title of the new page in the


titleHandler.innerText = title
// Focus it—note that it *needs* `tabindex="-1"` to be focusable!
titleHandler.focus()
})

You can find a more in-depth tutorial for React with react-router and react-helmet on this blog. The core concept should be the same no matter the framework.

Note that if you have can guarantee there is always a relevant

element (independently of loading states, query errors and such), another possibly simpler solution would be to skip that hidden element altogether, and focus the

element instead (still with tabindex="-1").

Single-page applications (SPA for short) have been all the hype for the last decade or so. The idea is that we can avoid reloading the entire page when navigating within a site and instead update only the moving parts (usually the content area). This comes from a great premise: faster interactions, no unnecessary HTTP roundtrips, less used bandwidth.

The thing we usually don’t think about is that many assistive technologies such as screen-readers have been initially authored with the “original web” in mind and rely on page (re)loads to announce the page context, namely the page title (hold by the </code> element).</p> <p>When building a SPA—no matter the framework—it is important to do some work to announce the title when following router links. Two things need to happen:</p> <ol> <li>The title of the new view/page needs to be announced.</li> <li>The focus needs to be preserved or moved to a proper place.</li> </ol> <p>A nice solution is to have a <a href="https://hugogiraudel.com/2020/12/03/a11y-advent-hiding-content">visually hidden</a> element at the top of the page which receives the new title when navigating, and move the focus on that element so the content is read. Ideally, the <a href="https://hugogiraudel.com/2020/12/06/a11y-advent-skip-to-content">skip link</a> lives right after that node so the flow goes like this:</p> <ol> <li>Press a link in the content area that causes a router change.</li> <li>The view gets loaded.</li> <li>The title for that view gets rendered in the invisible node.</li> <li>The focus gets move to that node so its content is announced.</li> <li>Tabbing once gets to the skip link, so getting back to the content area is fast and convenient.</li> </ol> <p>Here is how our HTML should look like:</p> <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-1<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#main<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sr-only sr-only--focusable<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Skip to content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><br> <span class="token comment"><!-- Rest of the page --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></code></pre> <p>And our unflavoured JavaScript. Note that this is no specific framework—it’s just a made-up API to illustrate the concept.</p> <pre class="language-js"><code class="language-js"><span class="token keyword">const</span> titleHandler <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'body > p'</span><span class="token punctuation">)</span><br><br>router<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'page:change'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> title <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Render the title of the new page in the <p></span><br> titleHandler<span class="token punctuation">.</span>innerText <span class="token operator">=</span> title<br> <span class="token comment">// Focus it—note that it *needs* `tabindex="-1"` to be focusable!</span><br> titleHandler<span class="token punctuation">.</span><span class="token function">focus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>You can find a more in-depth <a href="https://hugogiraudel.com/2020/01/15/accessible-title-in-a-single-page-react-application/">tutorial for React with <code>react-router</code> and <code>react-helmet</code></a> on this blog. The core concept should be the same no matter the framework.</p> <div class="Info"><p>Note that if you have can guarantee there is <strong>always</strong> a relevant <code><h1></code> element (independently of loading states, query errors and such), another possibly simpler solution would be to skip that hidden element altogether, and focus the <code><h1></code> element instead (still with <code>tabindex="-1"</code>).</p> </div> <br> <style> .ap-print-btn-comment { display:none; } .single .ap-print-btn-comment { display:block; } #respond { visibility:hidden; } .entry-content .ap-print-btn { display:none!important; } .entry-content .singleshow .ap-print-btn { display:block!important; } /* #respond { display:none; } */ </style> <script> function showhidecommentbox() { var x = document.getElementById("respond"); if (x.style.visibility === "visible") { x.style.visibility = "hidden"; } else { x.style.visibility = "visible"; } /* if (x.style.display === "block") { x.style.display = "none"; } else { x.style.display = "block"; } */ } </script> <div class="singleshow"> <a href="?printer_app=1" class="ap-print-btn" style=" padding: 15px; margin-top: 10px; width: 140px; text-align: center; border-radius: 3px; font-size: 14px; box-shadow: none !important; background-color: ; color: #ffffff;float:left; margin-right:10px;text-decoration: none;">Print</a> <a onclick="pop()" class="ap-print-btn ap-print-btn-comment" style="cursor:pointer; padding: 15px; margin-top: 10px; width: 140px; text-align: center; border-radius: 3px; font-size: 14px; box-shadow: none !important; background-color: ; color: #ffffff;float:left; margin-right:10px;text-decoration: none;">Share</a> <a onclick="popcomment()" class="ap-print-btn ap-print-btn-comment" style="cursor:pointer; padding: 15px; margin-top: 10px; width: 140px; text-align: center; border-radius: 3px; font-size: 14px; box-shadow: none !important; background-color: ; color: #ffffff;float:left; margin-right:10px;text-decoration: none;">Comment</a> <a onclick="popcite()" class="ap-print-btn ap-print-btn-comment" style="cursor:pointer; padding: 15px; margin-top: 10px; width: 140px; text-align: center; border-radius: 3px; font-size: 14px; box-shadow: none !important; background-color: ; color: #ffffff;float:left; margin-right:10px;text-decoration: none;">Cite</a> <a onclick="popupload()" class="ap-print-btn ap-print-btn-comment" style="cursor:pointer; padding: 15px; margin-top: 10px; width: 140px; text-align: center; border-radius: 3px; font-size: 14px; box-shadow: none !important; background-color: ; color: #ffffff;float:left; margin-right:10px;text-decoration: none;">Upload</a> <a onclick="poptranslate()" class="ap-print-btn ap-print-btn-comment" style="cursor:pointer; padding: 15px; margin-top: 10px; width: 140px; text-align: center; border-radius: 3px; font-size: 14px; box-shadow: none !important; background-color: ; color: #ffffff;float:left; margin-right:10px;text-decoration: none;">Translate</a> <script> function pop() { var popup = document.getElementById('sharepopup'); popup.classList.toggle('show'); } function popcite() { var popup = document.getElementById('citationarea'); popup.classList.toggle('show'); } function popupload() { var popup = document.getElementById('uploadarea'); popup.classList.toggle('show'); } function poptranslate() { var popup = document.getElementById('translatearea'); popup.classList.toggle('show'); } function popcomment() { var popup = document.getElementById('commentarea'); popup.classList.toggle('show'); var x = document.getElementById("respond"); if (x.style.visibility === "visible") { x.style.visibility = "hidden"; } else { x.style.visibility = "visible"; } } </script> <style> .show{ display: grid !important; grid-template-columns: 70px auto; width: 100%; height: 100%; } #respond { margin-left: -40px; } .popup { display: inline-block; } .popup .popuptext { visibility: hidden; background-color: #000000; color: #fff; border-radius: 3px; padding:10px; position:relative; margin:10px 0; } .popuptext { background-color: #000000; color: #fff; border-radius: 3px; padding:10px; position:relative; margin:10px 0; } .popup { width: 100%; } .popup .show { visibility: visible; } #commentarea{ display: none; } #citationarea { display: none; } #commentarea .show{ display: inline !important; visibility: visible !important; background-color: #ffdb14; } #citationarea { display: none; } #citationarea .show { display: inline !important; visibility: visible !important; background-color: #ffdb14; } #uploadarea { display: none; } #uploadarea .show { display: inline !important; visibility: visible !important; background-color: #ffdb14; } #translatearea { display: none; } #translatearea .show { display: inline !important; visibility: visible !important; background-color: #ffdb14; } .sharedashicons { color: white; text-decoration: none; padding: 5px 0; } #translatearea textarea { max-width: 580px; min-width: 100% !important; } </style> <div class="popup"> <span class="popuptext" id="sharepopup"> <div class="sharelogo" id="emailsharelogo"><a href="mailto:info@example.com?&subject=A11yAdvent Day 7: Page Title in SPA&body=https://www.scien.cx/2020/12/07/a11yadvent-day-7-page-title-in-spa/"><span class="sharedashicons dashicons dashicons-email"></span></a></div> <div class="sharelogo" id="facebooksharelogo"><a href="https://www.facebook.com/sharer/sharer.php?u=https://www.scien.cx/2020/12/07/a11yadvent-day-7-page-title-in-spa/"><span class="sharedashicons dashicons dashicons-facebook"></span></a></div> <div class="sharelogo" id="twittersharelogo"><a href="https://twitter.com/intent/tweet?url=https://www.scien.cx/2020/12/07/a11yadvent-day-7-page-title-in-spa/&text=A11yAdvent Day 7: Page Title in SPA"><span class="sharedashicons dashicons dashicons-twitter"></span></a></div> <div class="sharelogo" id="urlsharelogo"><a href="https://twitter.com/intent/tweet?url=https://www.scien.cx/2020/12/07/a11yadvent-day-7-page-title-in-spa/&text=A11yAdvent Day 7: Page Title in SPA"><span class="sharedashicons dashicons dashicons-admin-links" onclick="myFunction();"></span></a></div> </span> </div> <div class="commentpopup" style"width: 100%;"> <span class="" id="commentarea"> <img src="https://www.radiofree.org/wp-content/plugins/print-app/icon.jpg" width="100%"> <div class="comments-wrapper"> </div><!-- .comments-wrapper --> </span> </div> <div class="citationpopup" style"width: 100%;"> <span class="popuptext" id="citationarea"> CITATION GOES HERE CITATION GOES HERE </span> </div> <div class="citationpopup" style"width: 100%;"> <span class="popuptext" id="uploadarea"> </span> </div> <div class="translatepopup" style"width: 100%;"> <span class="popuptext" id="translatearea"> <form action="/action_page.php"> Select a language: <select data-placeholder="Choose a Language..."> <option value="AF">Afrikaans</option> <option value="SQ">Albanian</option> <option value="AR">Arabic</option> <option value="HY">Armenian</option> <option value="EU">Basque</option> <option value="BN">Bengali</option> <option value="BG">Bulgarian</option> <option value="CA">Catalan</option> <option value="KM">Cambodian</option> <option value="ZH">Chinese (Mandarin)</option> <option value="HR">Croatian</option> <option value="CS">Czech</option> <option value="DA">Danish</option> <option value="NL">Dutch</option> <option value="EN">English</option> <option value="ET">Estonian</option> <option value="FJ">Fiji</option> <option value="FI">Finnish</option> <option value="FR">French</option> <option value="KA">Georgian</option> <option value="DE">German</option> <option value="EL">Greek</option> <option value="GU">Gujarati</option> <option value="HE">Hebrew</option> <option value="HI">Hindi</option> <option value="HU">Hungarian</option> <option value="IS">Icelandic</option> <option value="ID">Indonesian</option> <option value="GA">Irish</option> <option value="IT">Italian</option> <option value="JA">Japanese</option> <option value="JW">Javanese</option> <option value="KO">Korean</option> <option value="LA">Latin</option> <option value="LV">Latvian</option> <option value="LT">Lithuanian</option> <option value="MK">Macedonian</option> <option value="MS">Malay</option> <option value="ML">Malayalam</option> <option value="MT">Maltese</option> <option value="MI">Maori</option> <option value="MR">Marathi</option> <option value="MN">Mongolian</option> <option value="NE">Nepali</option> <option value="NO">Norwegian</option> <option value="FA">Persian</option> <option value="PL">Polish</option> <option value="PT">Portuguese</option> <option value="PA">Punjabi</option> <option value="QU">Quechua</option> <option value="RO">Romanian</option> <option value="RU">Russian</option> <option value="SM">Samoan</option> <option value="SR">Serbian</option> <option value="SK">Slovak</option> <option value="SL">Slovenian</option> <option value="ES">Spanish</option> <option value="SW">Swahili</option> <option value="SV">Swedish </option> <option value="TA">Tamil</option> <option value="TT">Tatar</option> <option value="TE">Telugu</option> <option value="TH">Thai</option> <option value="BO">Tibetan</option> <option value="TO">Tonga</option> <option value="TR">Turkish</option> <option value="UK">Ukrainian</option> <option value="UR">Urdu</option> <option value="UZ">Uzbek</option> <option value="VI">Vietnamese</option> <option value="CY">Welsh</option> <option value="XH">Xhosa</option> </select> <textarea name="paragraph_text" style="::-moz-selection { background: yellow; }; ::selection { background: yellow; };" cols="50" rows="10"><p>Single-page applications (SPA for short) have been all the hype for the last decade or so. The idea is that we can avoid reloading the entire page when navigating within a site and instead update only the moving parts (usually the content area). This comes from a great premise: faster interactions, no unnecessary HTTP roundtrips, less used bandwidth.</p> <p>The thing we usually don’t think about is that many assistive technologies such as screen-readers have been initially authored with the “original web” in mind and rely on page (re)loads to announce the page context, namely the page title (hold by the <code><title></code> element).</p> <p>When building a SPA—no matter the framework—it is important to do some work to announce the title when following router links. Two things need to happen:</p> <ol> <li>The title of the new view/page needs to be announced.</li> <li>The focus needs to be preserved or moved to a proper place.</li> </ol> <p>A nice solution is to have a <a href="https://hugogiraudel.com/2020/12/03/a11y-advent-hiding-content">visually hidden</a> element at the top of the page which receives the new title when navigating, and move the focus on that element so the content is read. Ideally, the <a href="https://hugogiraudel.com/2020/12/06/a11y-advent-skip-to-content">skip link</a> lives right after that node so the flow goes like this:</p> <ol> <li>Press a link in the content area that causes a router change.</li> <li>The view gets loaded.</li> <li>The title for that view gets rendered in the invisible node.</li> <li>The focus gets move to that node so its content is announced.</li> <li>Tabbing once gets to the skip link, so getting back to the content area is fast and convenient.</li> </ol> <p>Here is how our HTML should look like:</p> <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-1<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#main<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sr-only sr-only--focusable<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Skip to content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><br> <span class="token comment"><!-- Rest of the page --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></code></pre> <p>And our unflavoured JavaScript. Note that this is no specific framework—it’s just a made-up API to illustrate the concept.</p> <pre class="language-js"><code class="language-js"><span class="token keyword">const</span> titleHandler <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'body > p'</span><span class="token punctuation">)</span><br><br>router<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'page:change'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> title <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Render the title of the new page in the <p></span><br> titleHandler<span class="token punctuation">.</span>innerText <span class="token operator">=</span> title<br> <span class="token comment">// Focus it—note that it *needs* `tabindex="-1"` to be focusable!</span><br> titleHandler<span class="token punctuation">.</span><span class="token function">focus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>You can find a more in-depth <a href="https://hugogiraudel.com/2020/01/15/accessible-title-in-a-single-page-react-application/">tutorial for React with <code>react-router</code> and <code>react-helmet</code></a> on this blog. The core concept should be the same no matter the framework.</p> <div class="Info"><p>Note that if you have can guarantee there is <strong>always</strong> a relevant <code><h1></code> element (independently of loading states, query errors and such), another possibly simpler solution would be to skip that hidden element altogether, and focus the <code><h1></code> element instead (still with <code>tabindex="-1"</code>).</p> </div> </textarea> <label for="fname">First name:</label><br> <input type="text" id="fname" name="fname" value="John"><br> <label for="lname">Last name:</label><br> <input type="text" id="lname" name="lname" value="Doe"><br><br> <input type="submit" value="Submit"> </form> </span> </div> </div><!-- .entry-content --> <nav class="pagination-single border-color-border"> <a class="previous-post" href="https://www.scien.cx/2020/12/06/static-web-apps-on-devops-labs/"> <span class="arrow">←</span> <span class="title"><span class="title-inner">Static Web Apps on DevOps Labs</span></span> </a> <a class="next-post" href="https://www.scien.cx/2020/12/07/live-first-time-casual-hindi-talks/"> <span class="arrow">→</span> <span class="title"><span class="title-inner">Live First Time : Casual Hindi Talks</span></span> </a> </nav><!-- .single-pagination --> </div><!-- .post-inner --> </article><!-- .post --> <div class="related-posts section-inner"> <h2 class="related-posts-title heading-size-3">Related Posts</h2> <div class="posts"> <div class="posts-grid related-posts-grid grid mcols-1 tcols-2 tlcols-3 dcols-4"> <div class="grid-item"> <article class="preview preview-post post-246218 post type-post status-publish format-standard hentry category-crypto category-decentralized-web category-future-of-nfts category-gaming-metaverse category-music category-music-technology category-nft category-opensource" id="post-246218"> <figure class="wp-block-audio"></figure> <figure class="preview-media"> </figure> <header class="preview-header"> <h2 class="preview-title heading-size-3"><a href="https://www.scien.cx/2021/09/22/the-rising-tempo-an-intro-to-music-nfts/">The Rising Tempo: An Intro to Music NFTs</a></h2> <div class="post-meta-wrapper post-meta-archive"> <ul class="post-meta color-accent"> <li class="post-date"> <a class="meta-wrapper" href="https://www.scien.cx/2021/09/22/the-rising-tempo-an-intro-to-music-nfts/"> <span class="meta-icon"> <span class="screen-reader-text">Post date</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="18" height="19" viewBox="0 0 18 19"><path fill="" d="M4.60069444,4.09375 L3.25,4.09375 C2.47334957,4.09375 1.84375,4.72334957 1.84375,5.5 L1.84375,7.26736111 L16.15625,7.26736111 L16.15625,5.5 C16.15625,4.72334957 15.5266504,4.09375 14.75,4.09375 L13.3993056,4.09375 L13.3993056,4.55555556 C13.3993056,5.02154581 13.0215458,5.39930556 12.5555556,5.39930556 C12.0895653,5.39930556 11.7118056,5.02154581 11.7118056,4.55555556 L11.7118056,4.09375 L6.28819444,4.09375 L6.28819444,4.55555556 C6.28819444,5.02154581 5.9104347,5.39930556 5.44444444,5.39930556 C4.97845419,5.39930556 4.60069444,5.02154581 4.60069444,4.55555556 L4.60069444,4.09375 Z M6.28819444,2.40625 L11.7118056,2.40625 L11.7118056,1 C11.7118056,0.534009742 12.0895653,0.15625 12.5555556,0.15625 C13.0215458,0.15625 13.3993056,0.534009742 13.3993056,1 L13.3993056,2.40625 L14.75,2.40625 C16.4586309,2.40625 17.84375,3.79136906 17.84375,5.5 L17.84375,15.875 C17.84375,17.5836309 16.4586309,18.96875 14.75,18.96875 L3.25,18.96875 C1.54136906,18.96875 0.15625,17.5836309 0.15625,15.875 L0.15625,5.5 C0.15625,3.79136906 1.54136906,2.40625 3.25,2.40625 L4.60069444,2.40625 L4.60069444,1 C4.60069444,0.534009742 4.97845419,0.15625 5.44444444,0.15625 C5.9104347,0.15625 6.28819444,0.534009742 6.28819444,1 L6.28819444,2.40625 Z M1.84375,8.95486111 L1.84375,15.875 C1.84375,16.6516504 2.47334957,17.28125 3.25,17.28125 L14.75,17.28125 C15.5266504,17.28125 16.15625,16.6516504 16.15625,15.875 L16.15625,8.95486111 L1.84375,8.95486111 Z" /></svg> </span> <span class="meta-text"> September 22, 2021 </span> </a> </li> <li class="post-author meta-wrapper"> <span class="meta-icon"> <span class="screen-reader-text">Post author</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="18" height="20" viewBox="0 0 18 20"><path fill="" d="M18,19 C18,19.5522847 17.5522847,20 17,20 C16.4477153,20 16,19.5522847 16,19 L16,17 C16,15.3431458 14.6568542,14 13,14 L5,14 C3.34314575,14 2,15.3431458 2,17 L2,19 C2,19.5522847 1.55228475,20 1,20 C0.44771525,20 0,19.5522847 0,19 L0,17 C0,14.2385763 2.23857625,12 5,12 L13,12 C15.7614237,12 18,14.2385763 18,17 L18,19 Z M9,10 C6.23857625,10 4,7.76142375 4,5 C4,2.23857625 6.23857625,0 9,0 C11.7614237,0 14,2.23857625 14,5 C14,7.76142375 11.7614237,10 9,10 Z M9,8 C10.6568542,8 12,6.65685425 12,5 C12,3.34314575 10.6568542,2 9,2 C7.34314575,2 6,3.34314575 6,5 C6,6.65685425 7.34314575,8 9,8 Z" /></svg> </span> <span class="meta-text"> By <a href="https://www.scien.cx/author/timongty/">timongty</a> </span> </li> <li class="post-categories meta-wrapper"> <span class="meta-icon"> <span class="screen-reader-text">Post categories</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="20" height="19" viewBox="0 0 20 19"><path fill="" d="M2.8,1.85 C2.275329,1.85 1.85,2.27532949 1.85,2.8 L1.85,15.4 C1.85,15.9246705 2.275329,16.35 2.8,16.35 L17.2,16.35 C17.724671,16.35 18.15,15.9246705 18.15,15.4 L18.15,5.5 C18.15,4.97532949 17.724671,4.55 17.2,4.55 L9.1,4.55 C8.8158,4.55 8.550403,4.40796403 8.392757,4.17149517 L6.845094,1.85 L2.8,1.85 Z M17.2,2.85 C18.663555,2.85 19.85,4.03644541 19.85,5.5 L19.85,15.4 C19.85,16.8635546 18.663555,18.05 17.2,18.05 L2.8,18.05 C1.336445,18.05 0.15,16.8635546 0.15,15.4 L0.15,2.8 C0.15,1.33644541 1.336445,0.15 2.8,0.15 L7.3,0.15 C7.5842,0.15 7.849597,0.292035965 8.007243,0.528504833 L9.554906,2.85 L17.2,2.85 Z" /></svg> </span> <span class="meta-text"> In <a href="https://www.scien.cx/category/crypto/" rel="category tag">crypto</a>, <a href="https://www.scien.cx/category/decentralized-web/" rel="category tag">decentralized-web</a>, <a href="https://www.scien.cx/category/future-of-nfts/" rel="category tag">future-of-nfts</a>, <a href="https://www.scien.cx/category/gaming-metaverse/" rel="category tag">gaming-metaverse</a>, <a href="https://www.scien.cx/category/music/" rel="category tag">Music</a>, <a href="https://www.scien.cx/category/music-technology/" rel="category tag">music-technology</a>, <a href="https://www.scien.cx/category/nft/" rel="category tag">nft</a>, <a href="https://www.scien.cx/category/opensource/" rel="category tag">opensource</a> </span> </li> </ul> </div> </header><!-- .preview-header --> </article><!-- .preview --> </div><!-- .grid-item --> <div class="grid-item"> <article class="preview preview-post post-58490 post type-post status-publish format-standard hentry" id="post-58490"> <figure class="wp-block-audio"></figure> <figure class="preview-media"> <iframe width="100%" height="198" src="https://www.youtube.com/embed/F-sFp_AvHc8" frameborder="0" allowfullscreen></iframe> </figure> <header class="preview-header"> <h2 class="preview-title heading-size-3"><a href="https://www.scien.cx/2021/03/16/user-authentication-in-web-apps-passport-js-node-express/">User Authentication in Web Apps (Passport.js, Node, Express)</a></h2> <div class="post-meta-wrapper post-meta-archive"> <ul class="post-meta color-accent"> <li class="post-date"> <a class="meta-wrapper" href="https://www.scien.cx/2021/03/16/user-authentication-in-web-apps-passport-js-node-express/"> <span class="meta-icon"> <span class="screen-reader-text">Post date</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="18" height="19" viewBox="0 0 18 19"><path fill="" d="M4.60069444,4.09375 L3.25,4.09375 C2.47334957,4.09375 1.84375,4.72334957 1.84375,5.5 L1.84375,7.26736111 L16.15625,7.26736111 L16.15625,5.5 C16.15625,4.72334957 15.5266504,4.09375 14.75,4.09375 L13.3993056,4.09375 L13.3993056,4.55555556 C13.3993056,5.02154581 13.0215458,5.39930556 12.5555556,5.39930556 C12.0895653,5.39930556 11.7118056,5.02154581 11.7118056,4.55555556 L11.7118056,4.09375 L6.28819444,4.09375 L6.28819444,4.55555556 C6.28819444,5.02154581 5.9104347,5.39930556 5.44444444,5.39930556 C4.97845419,5.39930556 4.60069444,5.02154581 4.60069444,4.55555556 L4.60069444,4.09375 Z M6.28819444,2.40625 L11.7118056,2.40625 L11.7118056,1 C11.7118056,0.534009742 12.0895653,0.15625 12.5555556,0.15625 C13.0215458,0.15625 13.3993056,0.534009742 13.3993056,1 L13.3993056,2.40625 L14.75,2.40625 C16.4586309,2.40625 17.84375,3.79136906 17.84375,5.5 L17.84375,15.875 C17.84375,17.5836309 16.4586309,18.96875 14.75,18.96875 L3.25,18.96875 C1.54136906,18.96875 0.15625,17.5836309 0.15625,15.875 L0.15625,5.5 C0.15625,3.79136906 1.54136906,2.40625 3.25,2.40625 L4.60069444,2.40625 L4.60069444,1 C4.60069444,0.534009742 4.97845419,0.15625 5.44444444,0.15625 C5.9104347,0.15625 6.28819444,0.534009742 6.28819444,1 L6.28819444,2.40625 Z M1.84375,8.95486111 L1.84375,15.875 C1.84375,16.6516504 2.47334957,17.28125 3.25,17.28125 L14.75,17.28125 C15.5266504,17.28125 16.15625,16.6516504 16.15625,15.875 L16.15625,8.95486111 L1.84375,8.95486111 Z" /></svg> </span> <span class="meta-text"> March 16, 2021 </span> </a> </li> <li class="post-author meta-wrapper"> <span class="meta-icon"> <span class="screen-reader-text">Post author</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="18" height="20" viewBox="0 0 18 20"><path fill="" d="M18,19 C18,19.5522847 17.5522847,20 17,20 C16.4477153,20 16,19.5522847 16,19 L16,17 C16,15.3431458 14.6568542,14 13,14 L5,14 C3.34314575,14 2,15.3431458 2,17 L2,19 C2,19.5522847 1.55228475,20 1,20 C0.44771525,20 0,19.5522847 0,19 L0,17 C0,14.2385763 2.23857625,12 5,12 L13,12 C15.7614237,12 18,14.2385763 18,17 L18,19 Z M9,10 C6.23857625,10 4,7.76142375 4,5 C4,2.23857625 6.23857625,0 9,0 C11.7614237,0 14,2.23857625 14,5 C14,7.76142375 11.7614237,10 9,10 Z M9,8 C10.6568542,8 12,6.65685425 12,5 C12,3.34314575 10.6568542,2 9,2 C7.34314575,2 6,3.34314575 6,5 C6,6.65685425 7.34314575,8 9,8 Z" /></svg> </span> <span class="meta-text"> By <a href="https://www.scien.cx/author/freecodecamp-org/">freeCodeCamp.org</a> </span> </li> </ul> </div> </header><!-- .preview-header --> </article><!-- .preview --> </div><!-- .grid-item --> <div class="grid-item"> <article class="preview preview-post post-20093 post type-post status-publish format-standard hentry" id="post-20093"> <figure class="wp-block-audio"></figure> <figure class="preview-media"> </figure> <header class="preview-header"> <h2 class="preview-title heading-size-3"><a href="https://www.scien.cx/2018/09/18/keeping-a-changelog-in-node-js/">Keeping a Changelog in Node.js</a></h2> <div class="post-meta-wrapper post-meta-archive"> <ul class="post-meta color-accent"> <li class="post-date"> <a class="meta-wrapper" href="https://www.scien.cx/2018/09/18/keeping-a-changelog-in-node-js/"> <span class="meta-icon"> <span class="screen-reader-text">Post date</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="18" height="19" viewBox="0 0 18 19"><path fill="" d="M4.60069444,4.09375 L3.25,4.09375 C2.47334957,4.09375 1.84375,4.72334957 1.84375,5.5 L1.84375,7.26736111 L16.15625,7.26736111 L16.15625,5.5 C16.15625,4.72334957 15.5266504,4.09375 14.75,4.09375 L13.3993056,4.09375 L13.3993056,4.55555556 C13.3993056,5.02154581 13.0215458,5.39930556 12.5555556,5.39930556 C12.0895653,5.39930556 11.7118056,5.02154581 11.7118056,4.55555556 L11.7118056,4.09375 L6.28819444,4.09375 L6.28819444,4.55555556 C6.28819444,5.02154581 5.9104347,5.39930556 5.44444444,5.39930556 C4.97845419,5.39930556 4.60069444,5.02154581 4.60069444,4.55555556 L4.60069444,4.09375 Z M6.28819444,2.40625 L11.7118056,2.40625 L11.7118056,1 C11.7118056,0.534009742 12.0895653,0.15625 12.5555556,0.15625 C13.0215458,0.15625 13.3993056,0.534009742 13.3993056,1 L13.3993056,2.40625 L14.75,2.40625 C16.4586309,2.40625 17.84375,3.79136906 17.84375,5.5 L17.84375,15.875 C17.84375,17.5836309 16.4586309,18.96875 14.75,18.96875 L3.25,18.96875 C1.54136906,18.96875 0.15625,17.5836309 0.15625,15.875 L0.15625,5.5 C0.15625,3.79136906 1.54136906,2.40625 3.25,2.40625 L4.60069444,2.40625 L4.60069444,1 C4.60069444,0.534009742 4.97845419,0.15625 5.44444444,0.15625 C5.9104347,0.15625 6.28819444,0.534009742 6.28819444,1 L6.28819444,2.40625 Z M1.84375,8.95486111 L1.84375,15.875 C1.84375,16.6516504 2.47334957,17.28125 3.25,17.28125 L14.75,17.28125 C15.5266504,17.28125 16.15625,16.6516504 16.15625,15.875 L16.15625,8.95486111 L1.84375,8.95486111 Z" /></svg> </span> <span class="meta-text"> September 18, 2018 </span> </a> </li> <li class="post-author meta-wrapper"> <span class="meta-icon"> <span class="screen-reader-text">Post author</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="18" height="20" viewBox="0 0 18 20"><path fill="" d="M18,19 C18,19.5522847 17.5522847,20 17,20 C16.4477153,20 16,19.5522847 16,19 L16,17 C16,15.3431458 14.6568542,14 13,14 L5,14 C3.34314575,14 2,15.3431458 2,17 L2,19 C2,19.5522847 1.55228475,20 1,20 C0.44771525,20 0,19.5522847 0,19 L0,17 C0,14.2385763 2.23857625,12 5,12 L13,12 C15.7614237,12 18,14.2385763 18,17 L18,19 Z M9,10 C6.23857625,10 4,7.76142375 4,5 C4,2.23857625 6.23857625,0 9,0 C11.7614237,0 14,2.23857625 14,5 C14,7.76142375 11.7614237,10 9,10 Z M9,8 C10.6568542,8 12,6.65685425 12,5 C12,3.34314575 10.6568542,2 9,2 C7.34314575,2 6,3.34314575 6,5 C6,6.65685425 7.34314575,8 9,8 Z" /></svg> </span> <span class="meta-text"> By <a href="https://www.scien.cx/author/thecodebarbarian-com/">TheCodeBarbarian.com</a> </span> </li> </ul> </div> </header><!-- .preview-header --> </article><!-- .preview --> </div><!-- .grid-item --> <div class="grid-item"> <article class="preview preview-post post-148952 post type-post status-publish format-standard hentry category-beginners category-css category-productivity category-webdev" id="post-148952"> <figure class="wp-block-audio"></figure> <figure class="preview-media"> </figure> <header class="preview-header"> <h2 class="preview-title heading-size-3"><a href="https://www.scien.cx/2021/06/09/css-flexbox-cheat-sheet-%f0%9f%94%a5/">CSS Flexbox Cheat Sheet 🔥</a></h2> <div class="post-meta-wrapper post-meta-archive"> <ul class="post-meta color-accent"> <li class="post-date"> <a class="meta-wrapper" href="https://www.scien.cx/2021/06/09/css-flexbox-cheat-sheet-%f0%9f%94%a5/"> <span class="meta-icon"> <span class="screen-reader-text">Post date</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="18" height="19" viewBox="0 0 18 19"><path fill="" d="M4.60069444,4.09375 L3.25,4.09375 C2.47334957,4.09375 1.84375,4.72334957 1.84375,5.5 L1.84375,7.26736111 L16.15625,7.26736111 L16.15625,5.5 C16.15625,4.72334957 15.5266504,4.09375 14.75,4.09375 L13.3993056,4.09375 L13.3993056,4.55555556 C13.3993056,5.02154581 13.0215458,5.39930556 12.5555556,5.39930556 C12.0895653,5.39930556 11.7118056,5.02154581 11.7118056,4.55555556 L11.7118056,4.09375 L6.28819444,4.09375 L6.28819444,4.55555556 C6.28819444,5.02154581 5.9104347,5.39930556 5.44444444,5.39930556 C4.97845419,5.39930556 4.60069444,5.02154581 4.60069444,4.55555556 L4.60069444,4.09375 Z M6.28819444,2.40625 L11.7118056,2.40625 L11.7118056,1 C11.7118056,0.534009742 12.0895653,0.15625 12.5555556,0.15625 C13.0215458,0.15625 13.3993056,0.534009742 13.3993056,1 L13.3993056,2.40625 L14.75,2.40625 C16.4586309,2.40625 17.84375,3.79136906 17.84375,5.5 L17.84375,15.875 C17.84375,17.5836309 16.4586309,18.96875 14.75,18.96875 L3.25,18.96875 C1.54136906,18.96875 0.15625,17.5836309 0.15625,15.875 L0.15625,5.5 C0.15625,3.79136906 1.54136906,2.40625 3.25,2.40625 L4.60069444,2.40625 L4.60069444,1 C4.60069444,0.534009742 4.97845419,0.15625 5.44444444,0.15625 C5.9104347,0.15625 6.28819444,0.534009742 6.28819444,1 L6.28819444,2.40625 Z M1.84375,8.95486111 L1.84375,15.875 C1.84375,16.6516504 2.47334957,17.28125 3.25,17.28125 L14.75,17.28125 C15.5266504,17.28125 16.15625,16.6516504 16.15625,15.875 L16.15625,8.95486111 L1.84375,8.95486111 Z" /></svg> </span> <span class="meta-text"> June 9, 2021 </span> </a> </li> <li class="post-author meta-wrapper"> <span class="meta-icon"> <span class="screen-reader-text">Post author</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="18" height="20" viewBox="0 0 18 20"><path fill="" d="M18,19 C18,19.5522847 17.5522847,20 17,20 C16.4477153,20 16,19.5522847 16,19 L16,17 C16,15.3431458 14.6568542,14 13,14 L5,14 C3.34314575,14 2,15.3431458 2,17 L2,19 C2,19.5522847 1.55228475,20 1,20 C0.44771525,20 0,19.5522847 0,19 L0,17 C0,14.2385763 2.23857625,12 5,12 L13,12 C15.7614237,12 18,14.2385763 18,17 L18,19 Z M9,10 C6.23857625,10 4,7.76142375 4,5 C4,2.23857625 6.23857625,0 9,0 C11.7614237,0 14,2.23857625 14,5 C14,7.76142375 11.7614237,10 9,10 Z M9,8 C10.6568542,8 12,6.65685425 12,5 C12,3.34314575 10.6568542,2 9,2 C7.34314575,2 6,3.34314575 6,5 C6,6.65685425 7.34314575,8 9,8 Z" /></svg> </span> <span class="meta-text"> By <a href="https://www.scien.cx/author/mariana-simon/">Mariana Simon</a> </span> </li> <li class="post-categories meta-wrapper"> <span class="meta-icon"> <span class="screen-reader-text">Post categories</span> <svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="20" height="19" viewBox="0 0 20 19"><path fill="" d="M2.8,1.85 C2.275329,1.85 1.85,2.27532949 1.85,2.8 L1.85,15.4 C1.85,15.9246705 2.275329,16.35 2.8,16.35 L17.2,16.35 C17.724671,16.35 18.15,15.9246705 18.15,15.4 L18.15,5.5 C18.15,4.97532949 17.724671,4.55 17.2,4.55 L9.1,4.55 C8.8158,4.55 8.550403,4.40796403 8.392757,4.17149517 L6.845094,1.85 L2.8,1.85 Z M17.2,2.85 C18.663555,2.85 19.85,4.03644541 19.85,5.5 L19.85,15.4 C19.85,16.8635546 18.663555,18.05 17.2,18.05 L2.8,18.05 C1.336445,18.05 0.15,16.8635546 0.15,15.4 L0.15,2.8 C0.15,1.33644541 1.336445,0.15 2.8,0.15 L7.3,0.15 C7.5842,0.15 7.849597,0.292035965 8.007243,0.528504833 L9.554906,2.85 L17.2,2.85 Z" /></svg> </span> <span class="meta-text"> In <a href="https://www.scien.cx/category/beginners/" rel="category tag">beginners</a>, <a href="https://www.scien.cx/category/css/" rel="category tag">css</a>, <a href="https://www.scien.cx/category/productivity/" rel="category tag">productivity</a>, <a href="https://www.scien.cx/category/webdev/" rel="category tag">webdev</a> </span> </li> </ul> </div> </header><!-- .preview-header --> </article><!-- .preview --> </div><!-- .grid-item --> </div><!-- .posts-grid --> </div><!-- .posts --> </div><!-- .related-posts --> </main><!-- #site-content --> <script type="text/javascript"> jQuery(document).ready(function($){ // using jQuery if( $(document).find('.wp-block-video video').length ) { $(document).find('.wp-block-video video').mediaelementplayer(/* Options */); } if( $(document).find('.wp-block-audio audio').length ) { $(document).find('.wp-block-audio audio').mediaelementplayer(/* Options */); } //$(window).load(function(){ $('.wp-block-audio figcaption').each(function(){ var htm = $(this).html(); $(this).html('<div>' +htm+ '</div>'); }); //}); }); </script> <div class="wdg-bottom"></div> <style> /* ADDING SUPPORT FOR THE NEWSLETTER PLUGIN WIDGET */ .tnp-email::-webkit-input-placeholder::before { content:"Line 1\A";align-content } .tnp-email { top: -0px; position: relative; line-height: 2.0em !important; color: inherit; margin: 0 0 .8rem .8rem; display: flex; justify-content: space-between; width:72% !important; border: 2px; border-color: white; } .tnp-field label { display: none !important; } input.tnp-submit { display: block; top: -40px; position: relative; line-height: 2.0em !important; margin: 0 0 .8rem .8rem; display: flex; justify-content: space-between; width: 26% !important; float: right !important; } .tnp-field.tnp-field-button { top: -20px !important; position: relative; } .menu-modal-inner { box-shadow: 0 0 2rem 0 rgba( 0, 0, 0, .1 ); min-width: 64rem; opacity: 0; transform: translateX(25rem); transition: transform .2s ease-in, opacity .2s ease-in; width: 50%; margin-left: auto; height: 100%; } .t_menu .menu-modal-inner { opacity: 1; transform: none; } .menu-modal { display: block !important; transition: none; } .menu-item ul.sub-menu { position: relative !important; } @media (min-width: 1000px){ .menu-modal { background: rgb(0 0 0 / 30%) !important;} } .tnp-submit { background-color: !important; border-color: !important; } .tnp-email { color: !important; background-color: transparent !important; border-color: !important; } </style> <footer id="site-footer" role="contentinfo"> <div class="footer-inner section-inner has-footer-menu"> <ul class="footer-menu reset-list-style"> </ul><!-- .site-nav --> <!-- .footer-credits --> <div class="left-footer-menu"><ul id="menu-left-footer" class="menu"><li id="menu-item-3230" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-home menu-item-has-children menu-item-3230"><a href="https://www.scien.cx/">Sciencx</a> <ul class="sub-menu"> <li id="menu-item-3256" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-home menu-item-3256"><a href="https://www.scien.cx/">Fuel Innovation</a></li> <li id="menu-item-3233" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-3233"><a href="https://www.scien.cx/about/">About</a></li> <li id="menu-item-3232" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-3232"><a href="https://www.scien.cx/contact/">Contact</a></li> </ul> </li> <li id="menu-item-3231" class="menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-3231"><a href="https://www.scien.cx/blog/">News</a></li> <li id="menu-item-3257" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-3257"><a href="https://secure.actblue.com/donate/sciencx">Donate</a></li> </ul></div> </div><!-- .footer-bottom --> </footer><!-- #site-footer --> <script type='text/javascript' src='https://www.scien.cx/wp-includes/js/wp-embed.min.js?ver=5.8.1' id='wp-embed-js'></script> <script> jQuery(function(){ jQuery('li.menu-item-has-children > a:first-child').on('click',function(event){ event.preventDefault() jQuery(this).parent().find('ul').toggle(300); //Hide menu when clicked outside jQuery(this).parent().find('ul').onclick(function(){ var thisUI = jQuery(this); jQuery('html').click(function(){ thisUI.hide(); jQuery('html').unbind('click'); }); }); }); }); </script> <script> jQuery('.myLinkToTop').click(function () { jQuery('html, body').animate({scrollTop:jQuery(document).height()}, 'slow'); return true; }); jQuery('.myMenuLink').click(function () { jQuery('html, body').animate({scrollTop:0}, 'slow'); return true; }); </script> <script> jQuery(".left-footer-menu .header-toggles").on("click",function(){ jQuery(".left-footer-menu .menu-modal.cover-modal").toggleClass("t_menu") }) jQuery(".left-footer-menu .menu-modal-toggles").on("click",function(){ jQuery(".left-footer-menu .menu-modal.cover-modal").removeClass("t_menu") }) jQuery(".left-footer-menu .menu-item .sub-menu-toggle").on("click",function(){ jQuery(this).toggleClass("active"); jQuery(this).parent().parent().parent().find("ul.sub-menu").toggle() }) /////////////////////////// //this hides images that do not load properly on the homepage $("img").error(function (){ $(this).hide(); // or $(this).css({'display','none'}); }); </script> </body> </html>