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

              
                <h1>Skeleton Spots</h1>

<p>
  Used in my post <cite><a href="https://adrianroselli.com/2020/11/more-accessible-skeletons.html">More Accessible Skeletons</a></cite>.
</p>

<p>
  The "Load async content" button will <em>load</em> each piece of real content, visually replacing its UI skeleton counterpart, after a 3 second delay and in successive half-second increments until it does it to all on the page. "Make it all loading" resets the page.
</p>

<aside>
  <fieldset>
    <legend>Change States</legend>
  <button id="DeBusy" class="Busyness" type="button" onclick="setTimeout(NotBusy,3000);">Load async content</button>
  <button id="Busy" class="Busyness" type="button" onclick="Busy();">Make it all loading</button>
  </fieldset>

  <fieldset>
    <legend>Contrast</legend>
    <button id="CDef" type="button" class="Busyness toggleContrast" aria-pressed="true" onclick="toggleContrast(this.id);">Default</button>
    <button id="C3t1" type="button" class="Busyness toggleContrast" aria-pressed="false" onclick="toggleContrast(this.id);">3:1 Contrast</button>
    <button id="C80s" type="button" class="Busyness toggleContrast" aria-pressed="false" onclick="toggleContrast(this.id);">1986</button>
  </fieldset>
  
  <fieldset>
    <legend>Animation</legend>
    <button id="Ani5" type="button toggleAnimation" class="Busyness" aria-pressed="true" onclick="toggleAnimation(this.id);">Stop at 5s</button>
  </fieldset>
</aside>

<h2>Block Links</h2>

<div>

  <article class="default">
    <div class="skeleton link-heading-img-txt">
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <span>Loading</span>
    </div>
    <div aria-busy="true">
      <a href="https://example.com/linkwrap" rel="nofollow">
        <h3>Wrapping Entire Thing</h3>
        <img src="https://www.fillmurray.com/640/360" width="640" height="360" alt="Bill Murray on the award carpet.">
        <p>
          Gastropub sartorial venmo hashtag, franzen actually umami small batch vinyl taiyaki. Seitan organic dreamcatcher
          pinterest, tilde franzen ugh tattooed PBR&B etsy bitters brooklyn yuccie listicle bicycle rights. Keytar
          keffiyeh glossier roof party. YOLO palo santo godard, organic lomo roof party tumeric affogato bicycle rights.
        </p>
      </a>
    </div>
  </article>

  <article class="block">
    <div class="skeleton link-heading-img-txt">
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <span>Loading</span>
    </div>
    <div aria-busy="true">
      <h3><a href="https://example.com/heading" rel="nofollow">Only on Heading</a></h3>
      <img src="https://www.fillmurray.com/580/380" width="600" height="380" alt="Bill Murray in a still from Caddyshack.">
      <p>
        Tumeric prism tattooed, raw denim cloud bread 90's cred plaid shabby chic aesthetic scenester snackwave
        fingerstache gochujang twee. Crucifix readymade hoodie +1 tote bag stumptown iceland umami. Edison bulb plaid
        vegan activated charcoal la croix vape paleo shabby chic slow-carb taiyaki. Pour-over cliche slow-carb unicorn.
      </p>
    </div>
  </article>

  <article class="block">
    <div class="skeleton link-heading-img-txt">
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <span>Loading</span>
    </div>
    <div aria-busy="true">
      <h3 id="A3">Somewhere in the Text</h3>
      <img src="https://www.fillmurray.com/580/320" width="580" height="320" alt="Close-up black-and-white of Bill Murray">
      <p>
        Sriracha actually cred portland beard kickstarter williamsburg direct trade cardigan brooklyn pinterest vape paleo
        readymade iPhone. Narwhal helvetica hammock mixtape aesthetic ramps shaman mustache try-hard small batch bicycle
        rights meh. Green juice tacos twee, offal everyday carry la croix PBR&B. Try-hard roof party flannel. <a href="https://example.com/text" rel="nofollow" aria-describedby="A3">Read more…</a>
      </p>
    </div>
  </article>

</div>

<h2>Block Link Variations</h2>

<div>

  <article class="block reorder">
    <div class="skeleton link-img-heading-txt">
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <span>Loading</span>
    </div>
    <div aria-busy="true">
      <h3><a href="https://example.com/re-ordered" rel="nofollow">Art Visually Above Heading</a></h3>
      <img src="https://placekitten.com/680/380" width="680" height="380" alt="Kitten resting against a scratching post.">
      <p>
        This looks perfect. Just Photoshop out the dog, add a baby, and make the curtains blue. There are more projects
        lined up; charge extra the next time. Can you make the logo bigger? Yes, bigger. Bigger still. The logo is too
        big. You can get my logo from Facebook. The target audience is males and females aged zero and up, so make it
        original. Can you rework to make the pizza look more delicious?
      </p>
    </div>
  </article>

  <article class="block reorder extracta">
    <div class="skeleton link-img-heading-txt">
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <span>Loading</span>
    </div>
    <div aria-busy="true">
      <h3><a href="https://example.com/extraCTA" rel="nofollow">With Another Call to Action</a></h3>
      <img src="https://placekitten.com/600/320" width="600" height="320" alt="Kitten staring into camera.">
      <p>
        What do you feel you would bring to the table if you were hired for this position? Core competencies? Blue sky
        wheelhouse. Product market fit shelfware performance review. Please use "solutionise" instead of solution ideas!
        Proceduralize. Can I just chime in on that one, get all your ducks in a row, so corporate synergy.
      </p>
      <p>
        <a href="https://example.com/" rel="nofollow">Order Your Own!</a>
      </p>
    </div>
  </article>

</div>

<h2>Block Buttons</h2>

<div>

  <section class="default">
    <div class="skeleton button-heading-txt-btn">
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <span>Loading</span>
    </div>
    <div aria-busy="true">
      <button type="button" onclick="alert('You look nice in those shoes.');">
        <h3>Wrapping Entire Thing</h3>
        <p>
          Hundreds of thousands how far away not a sunrise but a galaxyrise paroxysm of global death gathered by gravity
          preserve and cherish that pale blue dot?
        </p>
        <p>
          <span>Settings</span>
        </p>
      </button>
    </div>
  </section>

  <section class="block">
    <div class="skeleton button-heading-txt-btn">
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <div aria-hidden="true"></div>
      <span>Loading</span>
    </div>
    <div aria-busy="true">
      <h3>Only on Settings Text</h3>
      <p>
        Hundreds of thousands how far away not a sunrise but a galaxyrise paroxysm of global death gathered by gravity
        preserve and cherish that pale blue dot?
      </p>
      <button type="button" onclick="alert('You look nice in that shirt.');">
        <span>Settings</span>
      </button>
    </div>
  </section>

</div>

<h2>Generic</h2>

<p>
  The following paragraph uses <code>aria-busy="true"</code>, which should have the effect of hiding the content completely from screen readers.
</p>

<p aria-busy="true">
  This content should not be announced nor exposed to screen reader users. However, only JAWS (without IE11) honors it.
</p>

<p>
  Cards stolen from my blog post <cite><a href="https://adrianroselli.com/2020/02/block-links-cards-clickable-regions-etc.html">Block Links, Cards, Clickable Regions, Etc.</a></cite>. Do not use as is.
</p>

<!-- https://vuetifyjs.com/en/components/skeleton-loaders/#accessibility -->
              
            
!

CSS

              
                body {
  font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto,
    Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
  line-height: 1.4;
  /*   line-height: 1.5; */
  /*   letter-spacing: 0.12em; */
  /*   word-spacing: 0.16em; */
  color: #333;
  background: #efefef;
}

body > div {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
}

body > div > * {
  padding: .5em 1em;
  margin: 0 0.5em 1em 0.5em;
  flex: 0 1 20em;
  border: .2em solid rgba(0,0,0,.15);
  border-radius: 1em;
  background-color: #fff;
}

img {
  max-width: 100%;
  height: auto;
}

h3 {
  margin: .2em 0;
  line-height: 1.2;
}

p {
/*   margin: .2em 0; */
}

a:focus, a:hover {
  text-decoration: none;
}

.default a:link {
  text-decoration: none;
  color: #333;
}

.default a:focus h3, .default a:hover h3 {
  color: #00f;
  text-decoration: underline;
}

button {
  font: inherit;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  padding: 0;
  margin: 0;
}

button:focus, button:hover {
  outline: none;
}

button span {
  display: block;
  text-align: center;
  padding: .5em 1em;
  margin: .5em 1em;
  border: .1em solid #00c;
  border-radius: .25em;
  background-color: #00c;
  color: #fff;
}

button:focus span, button:hover span {
  color: #00c;
  background-color: #fff;
}

/* Move the image above the heading */

.reorder div {
  display: flex;
  flex-direction: column;
  padding-top: .5em;
}

.reorder img {
/*  see comment on post  */
  order: -1;
}

/* .reorder h3 {
  order: 2;
}

.reorder p {
  order: 3;
} */

/* Block links */

.block {
  position: relative;
}

.block a[href]::after, .block button::after {
  content: "";
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

.block:hover, .block:focus-within, .default:hover, .default:focus-within {
  border-color: rgba(0,0,255,.5);
  box-shadow: 0 0 .5em rgba(0,0,0,.25);
}

/* Help out IE */

.block a[href]:hover, .block a[href]:focus {
  outline: .4em solid rgba(0,0,255,.25);
  outline-offset: .25em;
}

/* Undo the IE */

.block:hover a[href]:hover, .block:hover a[href]:focus, .block:hover button:focus, .block:hover button:hover, .block:focus-within a[href]:hover, .block:focus-within a[href]:focus, .block:focus-within button:focus, .block:focus-within button:hover {
  outline: none;
}

/* ------------------------------------ */

/* Now with another link in the block thinger */

.block.extracta a[href]::after {
  bottom: 3.75em;
}

.block.extracta p a[href]::after {
  content: none;
}

.block.extracta p a[href] {
  display: block;
  text-align: center;
  margin: 1em 2em 0 2em;
  padding: .5em 1em;
  border: .1em solid #00c;
  border-radius: .25em;
  text-decoration: none;
  color: #fff;
  background-color: #00c;
}

.block.extracta p a[href]:focus, .block.extracta p a[href]:hover {
  color: #00c;
  background-color: #fff;
}





/* ================================== */
/* Skeletons and asynchronous content */
/* ================================== */

aside {
  display: flex;
  flex-wrap: wrap;
}

aside > * {
  margin: .5em;
}

.Busyness {
  border: .2em solid #33f;
  border-radius: .25em;
  padding: .25em .5em;
  color: #fff;
  background: #33f;
  display: inline-block;
  width: auto;
  text-shadow: 1px 1px 1px #000;
  font: inherit;
  font-size: 1rem;
  margin: .2em .1em;
}

.Busyness:focus, .Busyness:hover {
  color: #33f;
  background: #fff;
  text-shadow: none;
}

.Busyness[aria-pressed]::before {
  content: "⊘ ";
}

.Busyness[aria-pressed="true"]::before {
  content: "✔ ";
}

.Busyness[aria-pressed="true"] {
  border-color: #000;
}

.skeleton {
  cursor: progress;
}

.skeleton div {
  height: 2em;
  margin: .5em 0;
  padding: 0;
  border-radius: .25em;
  background: linear-gradient(-60deg, rgba(238,238,238,1) 0, rgba(238,238,238,1) 120px, rgba(250,250,250,1) 150px, rgba(238,238,238,1) 180px, rgba(238,238,238,1) 100%);
}

.skeleton.C3t1 div {
/* WCAG 2.1 SC 1.411 3:1 contrast ratio compliance */
  background: linear-gradient(-60deg, #949494 0, #949494 100px, rgba(255,255,255,1) 150px, #949494 200px, #949494 100%);
}

.skeleton.C80s div {
/* Full neon */
  background: linear-gradient(-60deg, #000 0, #000 130px, #A43348 146px, #f00 147px, #FF69B4 149px, #FFDBED 150px, #FF69B4 151px, #f00 153px, #A43348 154px, #000 170px, #000 100%);
}

@keyframes gradientBG {
	from { background-position: 0px; }
	to { background-position: 315px; }
}

@media (prefers-reduced-motion: no-preference) {
  .skeleton div {
/*  setting animation-iteration-count to 2.5    */
/*  so it does not exceed 5s per WCAG 2.2.2    */
    animation: gradientBG 2s ease 2.5;
  }
  .skeleton.animate div{
    animation: gradientBG 2s ease infinite;
  }
}

.skeleton.link-heading-img-txt div:nth-child(2),
.skeleton.link-heading-img-txt div:nth-child(3) {
  height: 10em;
}

.skeleton.link-img-heading-txt div:nth-child(1),
.skeleton.link-img-heading-txt div:nth-child(3) {
  height: 10em;
}

.skeleton.button-heading-txt-btn div:nth-child(2) {
  height: 6em;
}

.skeleton.button-heading-txt-btn div:nth-child(3) {
  height: 3em;
}

section *[aria-busy=true], article *[aria-busy=true], .skeleton[aria-hidden=true] {
/* Only throwing !important in here so I do not have to unwire all my styles above */
  display: none !important;
}

.visually-hidden, .skeleton > span {
  position: absolute;
  top: auto;
  overflow: hidden;
  clip: rect(1px, 1px, 1px, 1px);
  width: 1px;
  height: 1px;
  white-space: nowrap;
}

/*
[aria-busy="true"] {
  outline: 3px solid #f00;
}

[aria-busy="false"] {
  outline: 3px solid #0f0;
}
*/

/* Forced colors mode */
@media screen and (-ms-high-contrast: active),
  screen and (forced-colors: active) {
    .skeleton div {
      -ms-high-contrast-adjust: none;
      forced-color-adjust: none;
      background: linear-gradient(-60deg, Canvas 0, Canvas 130px, CanvasText 146px, ButtonText 149px, ButtonText 151px, CanvasText 154px, Canvas 170px, Canvas 100%);
    }
}
              
            
!

JS

              
                async function NotBusy() {
  wrappers = document.querySelectorAll("[aria-busy='true']");
  skeletons = document.querySelectorAll(".skeleton");
  for (var i = 0; i < wrappers.length; i++) {
    skeletons[i].setAttribute("aria-hidden", "true");
    wrappers[i].setAttribute("aria-busy", "false");
    await sleep(500);
  }
}

function Busy() {
  wrappers = document.querySelectorAll("[aria-busy='false']");
  skeletons = document.querySelectorAll(".skeleton");
  for (var i = 0; i < wrappers.length; i++) {
    skeletons[i].removeAttribute("aria-hidden");
    wrappers[i].setAttribute("aria-busy", "true");
  }
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function toggleContrast(btnID) {
  var deToggle = document.querySelectorAll("button.toggleContrast[aria-pressed]");
  for (var i = 0; i < deToggle.length; i++) {
    deToggle[i].setAttribute("aria-pressed", "false");
  }
  var theButton = document.getElementById(btnID);
  theButton.setAttribute("aria-pressed", "true");
  changeSkeletonClass(btnID);
}

function changeSkeletonClass(bClass) {
    skeletons = document.querySelectorAll(".skeleton");
  for (var i = 0; i < skeletons.length; i++) {
    skeletons[i].classList.remove("CDef", "C3t1", "C80s");
    skeletons[i].classList.add(bClass);
  }
}

function toggleAnimation(btnID) {
  var theButton = document.getElementById(btnID);
  if (theButton.getAttribute("aria-pressed") == "true") {
    theButton.setAttribute("aria-pressed", "false");
    changeSkeletonAnimation("animate");
  } else {
    theButton.setAttribute("aria-pressed", "true");
    changeSkeletonAnimation("static");
  }
}

function changeSkeletonAnimation(bClass) {
    skeletons = document.querySelectorAll(".skeleton");
  for (var i = 0; i < skeletons.length; i++) {
    skeletons[i].classList.remove("animate", "static");
    skeletons[i].classList.add(bClass);
  }
}
              
            
!
999px

Console