Skip to content

Add a "skeletons" component#31859

Merged
mdo merged 5 commits into
twbs:mainfrom
jaumesala:v5-skeletons-component
Aug 3, 2021
Merged

Add a "skeletons" component#31859
mdo merged 5 commits into
twbs:mainfrom
jaumesala:v5-skeletons-component

Conversation

@jaumesala
Copy link
Copy Markdown
Contributor

@jaumesala jaumesala commented Oct 9, 2020

Introduction

Nowadays, webapps use different techniques to improve the UX of the user when some data needs to be loaded. One of those trending techniques is the use of css "skeletons". No matter what javascript tool we use, we should facilitate and encourage developers to use them. Now that the framework is evolving and other components like the spinnerhas been added. I've thought on give a shot and explore the skeletons option.

skeleton example

How it works

Skeletons are composed of .skeleton-lines. Each of them needs one or more .skeleton-dashes.

<span class="skeleton-line">
  <span class="skeleton-dash"></span>
</span>

By default all .skeleton-dashes together will take the full with of the .skeleton-line. Still, you can customize them with custom css widths or even using the utility classes.

Screenshot 2020-10-09 at 18 07 52Screenshot 2020-10-09 at 18 08 03

The way it works is using a hidden empty space to preserve the size of the parent HTML element. This way we can mimic the original element proportion.

Screenshot 2020-10-09 at 18 08 11

For more information on how it works, check the documentation.

Waiting for feedback.

Fixes #31665

Preview: https://deploy-preview-31859--twbs-bootstrap.netlify.app/docs/5.0/components/placeholders/

TODO:

  • fix the empty heading errors properly
  • a11y
  • scss vars in docs like the other components?
  • fix examples
  • use a range in the colors docs?
  • maybe add a button to toggle animation?

@jaumesala jaumesala requested a review from a team as a code owner October 9, 2020 16:21
Copy link
Copy Markdown
Member

@mdo mdo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome! Just a few questions and stuff in a quick review :D.

Comment thread scss/_skeletons.scss Outdated
Comment thread scss/_skeletons.scss Outdated
Comment thread scss/_skeletons.scss Outdated
Comment thread scss/_skeletons.scss Outdated
Comment thread scss/bootstrap.scss Outdated
@patrickhlauke
Copy link
Copy Markdown
Member

to make any kind of sense to assistive tech users, at the very least these will need to have some indication like aria-busy="true" which is then removed when they are actually populated with content, and you'll likely want to have the actual content inside the container aria-hidden="true" so AT users that wander into it don't accidentally heard jibberish.

@jaumesala
Copy link
Copy Markdown
Contributor Author

@patrickhlauke I've added the aria-busy="true" attribute and a hint on how to use it with aria-hidden="true". Take a look and see if it is correct 51d89f00a8c2e5f9db3eb3d5cea3aa4d0447867b

Comment thread site/content/docs/5.0/components/skeletons.md Outdated
Comment thread site/content/docs/5.0/components/skeletons.md Outdated
Comment thread site/content/docs/5.0/components/skeletons.md Outdated
@XhmikosR XhmikosR changed the title Skeletons components Add a "skeletons" component Oct 12, 2020
Comment thread site/content/docs/5.0/components/skeletons.md Outdated
@XhmikosR XhmikosR force-pushed the v5-skeletons-component branch from 9c3c85b to fba3a07 Compare October 12, 2020 06:43
Comment thread site/content/docs/5.0/components/skeletons.md Outdated
@XhmikosR XhmikosR force-pushed the v5-skeletons-component branch from fba3a07 to f33464d Compare October 12, 2020 06:46
ffoodd
ffoodd previously requested changes Oct 12, 2020
Copy link
Copy Markdown
Contributor

@ffoodd ffoodd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from my CSS-part comments, did you ever think of using SVG for this? Using some <rect>s to lay up the pattern would be quite easy too, and it'd be easier to convey alternate text.

Not quite sure about this, but that's how I handled it years ago. I guess using raw markup may be easier to use but still want to consider alternate ways :)

Nice feature idea and overall work though, thanks!

Comment thread scss/_skeletons.scss Outdated
Comment thread scss/_skeletons.scss Outdated
Comment thread scss/_skeletons.scss Outdated
Comment thread scss/_skeletons.scss Outdated
Comment thread scss/bootstrap.scss Outdated
Comment thread scss/_skeletons.scss Outdated
@jacobmllr95
Copy link
Copy Markdown
Contributor

We at BootstrapVue recently introduced sketeton components.
These include text, images, buttons, inputs and even tables.

It would be awesome to have official support for all of these components in Bootstrap v5.
Our implementation may be a good starting point and we could adopt to the official markup in the future.

https://bootstrap-vue.org/docs/components/skeleton

@ffoodd
Copy link
Copy Markdown
Contributor

ffoodd commented Oct 12, 2020

The cursor: wait; declaration is a good idea. The overall implementation is pretty much the same as this PR, from a fast and superficial review, isn't it?

@jaumesala
Copy link
Copy Markdown
Contributor Author

We at BootstrapVue recently introduced sketeton components.
These include text, images, buttons, inputs and even tables.

It would be awesome to have official support for all of these components in Bootstrap v5.
Our implementation may be a good starting point and we could adopt to the official markup in the future.

https://bootstrap-vue.org/docs/components/skeleton

@jackmu95 I didn't know of the existence of this (component)!
But looking at your examples, I also had in mind to add a skeleton for images, skeleton-figure for instance, not sure if it is needed though...

Also, I was plaing with mask-image + linear gradient to create another effect like your wave effect, but for the first PR I stopped prototyping it.

IMO I'd say those two effects "glow" and "wave" are a must for the Bootstrap version. 😋

@XhmikosR

This comment has been minimized.

@jaumesala

This comment has been minimized.

@XhmikosR XhmikosR force-pushed the v5-skeletons-component branch from bf58e52 to 061eb51 Compare October 12, 2020 12:00
@XhmikosR

This comment has been minimized.

Comment thread site/content/docs/5.0/components/skeletons.md Outdated
@XhmikosR XhmikosR force-pushed the v5-skeletons-component branch from 061eb51 to 77f6625 Compare October 13, 2020 13:38
This was linked to issues Oct 16, 2020
@shahoob
Copy link
Copy Markdown

shahoob commented Oct 21, 2020

Just to tell you:
https://bootstrap-vue.org/docs/components/skeleton
Bootstrap Vue has it already implemented.

@MartijnCuppens
Copy link
Copy Markdown
Member

The markup can be simplified quite a bit if we're using display: inline-block for the skeleton/placeholders. Made a codepen here, what do you think? https://codepen.io/MartijnCuppens/pen/ExyNKYq?editors=1100

@jaumesala
Copy link
Copy Markdown
Contributor Author

I may have some free time this weekend. Let me know if I can help with anything.

@patrickhlauke
Copy link
Copy Markdown
Member

i should be able to spend some time this weekend on this as well, yes

@patrickhlauke
Copy link
Copy Markdown
Member

is netlify updating its preview or is it just not running due to bundlewatch failure?

@patrickhlauke
Copy link
Copy Markdown
Member

assuming https://deploy-preview-31859--twbs-bootstrap.netlify.app/docs/5.0/components/placeholders/ is still the latest version:

  • out of all browser/AT combinations, it seems only JAWS (with Chrome/Firefox) actually respects aria-busy="true" and effectively skips any content marked as busy. NVDA and VoiceOver (with various browsers) still read out the content without any indication that it's "busy".
  • focusable elements inside placeholder containers also still receive keyboard focus (with or without assistive technologies running)

I'd say two things at least should happen:

  • in addition to aria-busy="true", there should also be aria-hidden="true" on the placeholder container. arguably, this makes the aria-busy="true" redundant as it hides the element that is set to be busy, but until browser/AT support for this is improved, that attribute is pointless anyway (but makes sense to still keep there, regardless, as at least it shows the intent). arguably - though more involved - you could even say the aria-busy="true" should be on the placeholder container, and then any direct child element should be given an explicit aria-hidden="true". and this should then programmatically be removed when the placeholder becomes active/live (and the aria-busy-"true" is removed or set to "false")
  • any focusable element inside the placeholder should be given an explicit tabindex="-1" to prevent it being keyboard focusable (and again, this should be removed when the element becomes active)

@mdo
Copy link
Copy Markdown
Member

mdo commented May 5, 2021

@jaumesala Hey there! It's been awhile, but we're finally shipping v5.0.0 and are ready to look at our next set of features for v5.1.0. Would you be willing to jump back in here and help us ship this? If not we can try to tackle ourselves :). Thanks!

@jaumesala
Copy link
Copy Markdown
Contributor Author

Hey @mdo! Yeah, glad to help you guys! I've reread the last comments and commited some changes regarding "a11y" and SCSS. I've also cleaned up some examples in the documentation. Let me know what you think. Btw, It looks like Bundlewatch is still complainging about the size.

@patrickhlauke
Copy link
Copy Markdown
Member

circling back to this, as aria-busy support is still poor...i think the simplest and most sensible approach overall is simply to use aria-hidden="true" on any container/element that's in a skeleton/busy state (as there's no point doing both aria-busy="true" and aria-hidden="true" on the same element, since even if it was used in a browser/AT combo that supported busy, it would not be announced as it's hidden).

note also that any focusable elements need to be explicitly made non-focusable with tabindex="-1" or (where supported, e.g. on form controls/buttons) disabled).

@jaumesala
Copy link
Copy Markdown
Contributor Author

jaumesala commented Jun 10, 2021

Ok @patrickhlauke! Just to be sure I understand your comment. Using the example as a reference, it is enough to add aria-hidden="true" on the the direct descendants elements (see below)? Or, should we add it on the h5, span, p, a, too?

<div class="card" aria-busy="true" style="width: 18rem;">
    <img aria-hidden="true" src="..." class="card-img-top" alt="...">
    <div aria-hidden="true" class="card-body">
      <h5 class="card-title placeholder-glow">
        <span class="placeholder col-6"></span>&#8232;
      </h5>
      <p class="card-text placeholder-glow">
        <span class="placeholder col-7"></span>
        <span class="placeholder col-4"></span>
        <span class="placeholder col-4"></span>
        <span class="placeholder col-6"></span>
        <span class="placeholder col-8"></span>
      </p>
      <a href="#" tabindex="-1" class="btn btn-primary disabled placeholder col-6">
        &nbsp;<!-- needed to give the element some height -->
      </a>
    </div>
  </div>

@patrickhlauke
Copy link
Copy Markdown
Member

patrickhlauke commented Jun 11, 2021

i think currently, seeing that AT with one exception don't actually do anything useful/announce anything to users, we can probably completely forego aria-busy for generic containers, particularly if they don't have any additional text (like a "Loading...") or similar that gives at least some information to AT users that something is supposed to be there. [edit: see for instance https://adrianroselli.com/2020/11/more-accessible-skeletons.html where there is actual "Loading" text which is visually hidden but present inside the skeleton - this is text that at least will be announced, regardless of whether or not the AT knows what to do with aria-busy]

in the case of the card, just hiding the entire card with aria-hidden="true" and neutralising the link with tabindex="-1" is enough

<div class="card" aria-hidden="true" style="width: 18rem;">
    <img src="..." class="card-img-top" alt="...">
    <div class="card-body">
      <h5 class="card-title placeholder-glow">
        <span class="placeholder col-6"></span>&#8232;
      </h5>
      <p class="card-text placeholder-glow">
        <span class="placeholder col-7"></span>
        <span class="placeholder col-4"></span>
        <span class="placeholder col-4"></span>
        <span class="placeholder col-6"></span>
        <span class="placeholder col-8"></span>
      </p>
      <a href="#" tabindex="-1" class="btn btn-primary disabled placeholder col-6">
        &nbsp;<!-- needed to give the element some height -->
      </a>
    </div>
  </div>

it gets a bit trickier/nuanced if it's a very specific element with a role and accessible name that is currently being updated ... for instance, a link on its own that is being updated seems to be announced as "busy" - for instance, the example given of the busy link is indeed announced as "busy, link" by Chrome/NVDA. but i think that's more an edge case. i'd be ok with the whole link just being hidden.

long story short: just hiding the element that is set as a skeleton is probably sufficient at this stage. It gets more case-specific depending how authors will actually use the placeholder styles, how they plan to update things, etc. Might be worth just focusing on the styling here, showing a basic example with just aria-hidden, and then hinting at the fact that more may need to be done for AT users. I can have a think about the latter part...

@XhmikosR
Copy link
Copy Markdown
Member

Does anybody have any ideas how we could skip the &nbsp; part? It should simplify our markup.

@jaumesala
Copy link
Copy Markdown
Contributor Author

I've updated the code with @patrickhlauke recommendations and rewritten the callout information regarding the loading implementation. Take a look, it might need better words. 😋

@mdo mdo requested a review from ffoodd June 24, 2021 01:47
@mdo mdo force-pushed the v5-skeletons-component branch 2 times, most recently from 35bda77 to 86db844 Compare July 26, 2021 02:40
@mdo
Copy link
Copy Markdown
Member

mdo commented Jul 26, 2021

Pushed some changes and squashed everything into a single commit here. The main change was to add some pseudo-class styles for .btn::before that negates the need for &nbsp; characters in the HTML. Made a change to the primary example, too, so there is no handing space in an empty heading that'll fail the linter.

Co-Authored-By: XhmikosR <xhmikosr@gmail.com>
Co-Authored-By: Jaume Sala <jaumesala@gmail.com>
@mdo mdo force-pushed the v5-skeletons-component branch from 86db844 to 50fcd54 Compare July 26, 2021 02:53
@XhmikosR
Copy link
Copy Markdown
Member

Pinging @patrickhlauke for another look at a11y and @ffoodd for the CSS changes :)

@mdo mdo dismissed ffoodd’s stale review August 3, 2021 15:53

Changes addressed

@mdo mdo merged commit 39b7c75 into twbs:main Aug 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Content loading placeholder Skeleton loading screens

9 participants