Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <a class=skip_lnk href="#main_h1">Skip to content</a>

<nav class=fakeNav>
  <ul class=fakeNav_lst>
    <li class=fakeNav_li><a class=fakeNav_lnk href>dummy nav link</a></li>
    <li class=fakeNav_li><a class=fakeNav_lnk href>dummy nav link</a></li>
  </ul>
</nav>

<main class=main>

  <!-- tabindex = -1 is required for iOS with JS unavailable -->
  <h1 class=main_h1 id=main_h1>Accessible skip-to-content link for desktop and mobile</h1>
  
  <p class=main_p>Assistive technologies struggle with skip-links on mobile and touch devices.</p>
  
  <p class=main_p>Activating the skip-link anchor (only available via tab key) moves the focus to its <code class="main_code language-html">href</code> which is problematic on iOS and Android devices.</p>

  <p class=main_p>I've written a futherence to this piece to include accessible <a class=fancyLnk rel="me noopener" target=_blank title="[new window]" href="https://codepen.io/2kool2/pen/JjYGdmZ">In-page anchors for desktop &amp; mobile</a> (includes skip-to-content).</p>
  
  <p class=main_p>These solutions correct experienced issues across desktop, iOS and Android.</p>
  
  <h2 class=main_h2>Skip-link to the content heading</h2>
  
  <figure>
    <figcaption>HTML</figcaption>
    <pre><code class=language-markup spellcheck=false contenteditable>&lt;a class=skip_lnk href="#main_title"&gt;Skip to main content&lt;/a&gt;

&lt;!-- Nav, etc --&gt;

&lt;h1 class="main_title" id="main_title" tabindex="-1"&gt;Page heading&lt;/h1&gt;
</code></pre>
  </figure>
  
  <p class=main_p><code class="main_code language-html">tabindex="-1"</code> resolves iOS issues.</p>
  
  <p class=main_p>Note: I'm using the <code class="main_code language-html">h1</code> as the focus point.<br>Wherever possible avoid moving focus onto blocks of content, such as <code class="main_code language-html">main</code>, <strong>especially</strong> if it contains focusable elements: links, inputs, video, etc.</p>

  <figure>
    <figcaption>CSS</figcaption>
    <pre><code class=language-css spellcheck=false contenteditable>.skip_lnk {
  position: absolute;
  padding: .5rem 1rem;
  color: #fff;
  background-color: #000;
  text-decoration: none;
  font-weight: bold;
  z-index: 10;
  transform: translate3d(.125rem, -5rem, 0);
  transition: transform .3s ease-out;
}
.skip_lnk:focus {
  transform: translate3d(.125rem, .125rem, 0);
  outline: #fff solid .125rem;
}
@media print {
  .skip_lnk {
    display: none;
  }
}
</code></pre>
  </figure>
  
  <p class=main_p>JavaScript is required to resolve Android issues.</p>

  <figure>
    <figcaption>JavaScript (ES6)</figcaption>
    <pre><code class=language-javascript spellcheck=false contenteditable>(() => {
  const skip_lnk = document.querySelector('.skip_lnk');
  if (!skip_lnk) return false;
  skip_lnk.addEventListener('click', (e) => {
    e.preventDefault();
    const to_obj = document.getElementById(skip_lnk.href.split('#')[1]);
    if (to_obj) to_obj.focus();
  });
})();
</code></pre>
  </figure>

  <figure>
    <figcaption>JavaScript (ES5) - Google closure compiled 194 bytes</figcaption>
    <pre><code class=language-javascript spellcheck=false contenteditable>(function(){var a=document.querySelector(".skip_lnk");if(!a)return!1;a.addEventListener("click",function(b){b.preventDefault();(b=document.getElementById(a.href.split("#")[1]))&&b.focus()})})();
</code></pre>
  </figure>


  <h2 class=main_h2>Skip-link to a content block</h2>

  <p class=main_p>If you cannot link to the main heading, and have to link to a content block (which may contain focusable elements), then avoid the use of <code class="main_code language-html">tabindex</code> in the HTML.</p>
  <p class=main_p>Instead add and remove it upon demand via JavaScript.</p>

  <figure>
    <figcaption>HTML</figcaption>
    <pre><code class=language-markup spellcheck=false contenteditable>&lt;a class=skip_lnk href="#main"&gt;Skip to main content&lt;/a&gt;

&lt;!-- Nav, etc --&gt;

&lt;main class="main" id="main"&gt;
  &lt;!-- May contain focusable elements --&gt;
&lt;/main&gt;
</code></pre>
  </figure>

  <figure>
    <figcaption>JavaScript (ES6)</figcaption>
    <pre><code class=language-javascript spellcheck=false contenteditable>(_ => {
  const skip_lnk = document.querySelector('.skip_lnk');
  if (!skip_lnk) return false;
  skip_lnk.addEventListener('click', e => {
    e.preventDefault();
    const to_obj = document.getElementById(skip_lnk.href.split('#')[1]);
    if (to_obj) {
      to_obj.setAttribute('tabindex', '-1');
      to_obj.addEventListener('blur', e => {
        to_obj.removeAttribute('tabindex');
      }, {once: true});
      to_obj.focus();
    }
  });
})();
</code></pre>
  </figure>


  <p class=main_p>For improved accessibility, the on-demand addition and removal of <code class="main_code language-html">tabindex</code> should be applied to <strong>all page anchors</strong>, not just skip-to-content links.</p>
  <p class=main_p>I've rewritten this piece to do exactly that in an accessible manner: <a class=fancyLnk rel="me noopener" target=_blank title="[new window]" href="https://codepen.io/2kool2/pen/JjYGdmZ">In-page anchors for desktop &amp; mobile</a></p>

  
  <h2 class=main_h2>Credits</h2>

  <p class=main_p>For a full description of the issue faced on mobile devices, and who it affects, please see the original article by Hampus Sethfors: <a class=fancyLnk rel=noreferrer target=_blank href="https://axesslab.com/skip-links/">Your skip links are broken</a>.<br>The code here replaces the article's jQuery version with vanilla JavaScript and was extended to include on-demand tabindexing.</p>

</main>



<!-- Footer codepen include -->
[[[https://codepen.io/2kool2/pen/mKeeGM]]]
              
            
!

CSS

              
                html {box-sizing: border-box;}
*,
*::before,
*::after {box-sizing: inherit;}
body {

  /* Fix Safari bug with viewport units in calc() */
  min-height: 0vw;

  /* Fluidly variable between: 16px @ 320px and 32px @ 1920px */
  --space: calc(1rem + ((1vw - 0.2em) * 1));

  margin: 0;
  color: #666;
  font-size: var(--space);
  font-family: sans-serif;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: hsl(269,19%,30%);
  background-color: hsla(32,100%,85%,.35);
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg width='100%25' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.4'/%3E%3C/filter%3E%3C/defs%3E%3C!-- %3Cpath filter='url(%23a)' opacity='.3' d='M0 0h1200v256H0z'/%3E--%3E%3Crect filter='url(%23a)' opacity='.3' width='100%25' height='100%25'/%3E%3C/svg%3E");
}
::-moz-selection {
  color: #fff;
  background-color: #000;
}
::selection {
  color: #fff;
  background-color: #000;
}


/* Skip link */

.skip_lnk {
  position: absolute;
  padding: .5rem 1rem;
  color: #fff;
  background-color: #3B2D4A;
  text-decoration: none;
  font-weight: bold;
  z-index: 10;
  transform: translate3d(.125rem, -5rem, 0);
  transition: transform .3s ease-out;
}
.skip_lnk:focus {
  transform: translate3d(.125rem, .125rem, 0);
  outline: #fff solid .125rem;
}
@media print {
  .skip_lnk {
    display: none;
  }
}


/* Fake nav */
.fakeNav {
  color: #fff;
  background-color: #463b4c;
  border: 2px solid #463b4c;
}
.fakeNav_lst {
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: space-around;
  list-style: none;
}
.fakeNav_li {}
.fakeNav_lnk {
  display: block;
  font-weight: bold;
  color: #fff;
  padding: .5em;
}
.fakeNav_lnk:focus {
  outline: #fff solid .125rem;
}


/* Main */
.main {padding: 0 1rem;}
.main_h1 {
  font-weight: 100;
  line-height: 1.3;
  text-align: center;
  margin-top: 2rem;
}
.main_h2 {
  font-size: 2rem;
  font-weight: 100;
  line-height: 1.3;
  text-align: center;
  margin-top: 3rem;
}

/* Just so you can see where the focus lands */
[tabindex="-1"]:focus {
  color: red;
  outline: #fff solid .125rem;
}

.main_h1 + .main_p {
  font-size: larger;
  line-height: 1.3;
  text-align: center;
}
.main_p {
  text-align: left;
  max-width: 65ch;
  margin: 1rem auto;
}
.main_lnk,
.main_lnk:visited {
  white-space: nowrap;
  color: #236ECE;
}
.main_lnk:hover,
.main_lnk:focus {
  color: #014cac;
}
.main_code {font-size:1rem}




/* Prism - is used for code highlighting from an external pen: https://codepen.io/2kool2/pen/MEbeEg */

figure {
  max-width: 65ch;
  margin: 1.5rem auto;
}
figure + figure {
  margin-top: 2rem;
}
figcaption {
  font-weight: 700;
  padding: 0 0 0 .5rem;
}
pre[class*="language-"] {
/*   background-position: top -1.2rem left -.25rem; */
}
pre,
pre > code {
  background-color: transparent !important;
}
pre:focus {
  outline: #fff solid .125rem;
}
pre > code {
  display:block;
  margin: 0 0 0.75rem;
  padding-left: 0.5rem;
}
code[class*="language-"] {
  line-height: 2rem;
}

/* Fancier text links */
body {
  --fancyColor: hsla(214,72%,48%,1);
  --fancyColorHover: hsla(214,72%,20%,1);
  --fancyBgHover: hsla(214,100%,80%,1);
  --fancyUnderline: hsla(214,72%,48%,1);
}
@media (prefers-color-scheme: dark) {
  body:not([data-lightMode="light"]) {
    --fancyColor: hsla(214,100%,72%,1);
    --fancyColorHover: #fff;
    --fancyBgHover: hsla(214,100%,25%,1);
    --fancyUnderline: hsla(214,100%,72%,.4);
  }
}
body[data-lightMode="dark"] {
  --fancyColor: hsla(214,100%,72%,1);
  --fancyColorHover: #fff;
  --fancyBgHover: hsla(214,100%,25%,1);
  --fancyUnderline: hsla(214,100%,72%,.4);
}
.fancyLnk:link,
.fancyLnk:visited {
  color: var(--fancyColor);
  white-space: nowrap;
  position: relative;
  text-decoration: none;
  border-bottom: 1px solid var(--fancyUnderline);
  transition: color .3s ease-out;
}
.fancyLnk:hover,
.fancyLnk:focus {
  color: var(--fancyColorHover);
}
.fancyLnk:focus {
  outline: none;
}
.fancyLnk::before,
.fancyLnk::after {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
  opacity: .5;
  transform: scaleX(0);
  transition: none;
  /* Set to the delayed-on time, or as close as possible */
  transition: all .15s linear;
}
.fancyLnk::before {
  background-color: var(--fancyBgHover);
  outline-offset: -.25rem;
  outline: var(--fancyBgHover) solid .5rem;
  height: 100%;
  bottom: 0;
  z-index: -1;
}
.fancyLnk::after {
  background-color: var(--fancyColor);
  bottom: -0.1rem;
  height: 0.1rem;
}
.fancyLnk:hover::before,
.fancyLnk:focus::before,
.fancyLnk:hover::after,
.fancyLnk:focus::after {
  opacity: 1;
  transform: scaleX(1);
  /* Delayed to reduce annoying unintentional activations */
  transition: all .3s ease-in .15s;
}
@media (prefers-reduced-motion: reduce) {
  .fancyLnk::before,
  .fancyLnk::after {
    transition: none;
  }
}
              
            
!

JS

              
                // JS required to resolve Android issues

// (_ => {
//   const skip_lnk = document.querySelector('.skip_lnk');
//   if (!skip_lnk) return false;
//   skip_lnk.addEventListener('click', e => {
//     e.preventDefault();
//     const to_obj = document.getElementById(skip_lnk.href.split('#')[1]);
//     if (to_obj) to_obj.focus();
//   });

// })();


// ES5 version
/*
var skip_to_content_link = (function () {
  var skip_lnk = document.querySelector('.skip_lnk');
  if (!skip_lnk) return false;
  skip_lnk.addEventListener('click', function(e) {
    e.preventDefault();
    var to_obj = document.getElementById(skip_lnk.href.split('#')[1]);
    if (to_obj) to_obj.focus();
  });
})();
*/


// ES5 version - Google closure compiled 175 bytes gzipped (194 bytes uncompressed)
/*
(function(){var a=document.querySelector(".skip_lnk");if(!a)return!1;a.addEventListener("click",function(b){b.preventDefault();(b=document.getElementById(a.href.split("#")[1]))&&b.focus()})})();
*/


// ES6 version for content blocks (writes tabindex on demand)
(_ => {
  const skip_lnk = document.querySelector('.skip_lnk');
  if (!skip_lnk) return false;
  skip_lnk.addEventListener('click', e => {
    e.preventDefault();
    const to_obj = document.getElementById(skip_lnk.href.split('#')[1]);
    if (to_obj) {
      to_obj.setAttribute('tabindex', '-1');
      to_obj.addEventListener('blur', _ => {
        to_obj.removeAttribute('tabindex');
      }, {once: true});
      to_obj.focus();
    }
  });
})();



/* Prism - is used for code highlighting from an external pen: https://codepen.io/2kool2/pen/MEbeEg */
// Force Prism to use light mode 
document.body.setAttribute('data-lightmode', 'light');
              
            
!
999px

Console