-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(expressive-loader): new module #2260
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed everything except for the stories. I'm stopping here because there's likely going to be large changes based on my feedback that will likely impact structure. We can tackle the stories after we've ironed everything else out and the structure is solidified.
dist/tokens/evo-light.css
Outdated
@@ -72,6 +72,10 @@ | |||
--color-data-viz-chart-quinary-stroke: var(--color-teal-5); | |||
--color-data-viz-tooltip-shadow-primary: #00000026; | |||
--color-data-viz-tooltip-shadow-secondary: #0000002b; | |||
--color-expressive-loader-red: #e53238; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to say, I'm feeling more and more uneasy about having very module-specific color definitions, especially non-tokenized (one-offs) inside evo
. While the typical props serve the vast majority of Skin, these are very specific and limited. Having these load with a "global" file seems a bit out of place. Devs who don't use expressive-loader would be loading these module-specific props without ever needing them.
I think these types of light and dark mode props would be better suited to be defined in the module files instead.
<p> | ||
The expressive loader can replace our traditional spinner to feel faster during periods of longer loading. | ||
To portray responsive and quicker loads, the expressive loader's design uses visual momentum to | ||
indicate progression across refreshing, page loading and user actions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing period.
indicate progression across refreshing, page loading and user actions | |
indicate progression across refreshing, page loading and user actions. |
It consists of an animated progress bar in eBay brand colors and an optional set of messages that are cycled into | ||
view. It also supports a reduced motion mode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be best to avoid unnatural line breaks.
It consists of an animated progress bar in eBay brand colors and an optional set of messages that are cycled into | |
view. It also supports a reduced motion mode. | |
It consists of an animated progress bar in eBay brand colors and an optional set of messages that are cycled into view. It also supports a reduced motion mode. |
} | ||
} | ||
|
||
.expressive-line-animation__reduced-motion { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why we need this class since reduced motion should work out of the box in a media query, right?
<div class="demo__inner"> | ||
<div class="expressive-loader"> | ||
<div role="progressbar" aria-label="Loading..." class="expressive-line-animation"> | ||
<div class="expressive-line-animation__reduced-motion"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably not ideal to have different markup for reduced motion since it's a client site setting that is queried in css
. Is it possible to come up with a singular markup structure that would work in both cases - regular and reduced motion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I think this is the main change that is required right now. Getting it all onto a single, flexible markup structure (even if it might end up containing a little bit of redundancy).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ArtBlue, thank you for all your awesome feedback! I've just pushed a new commit.
High level overview of changes
- Removed nesting in less file and simplified BEM naming
- Moved color tokens into the expressive-loader file, made color references more brand agnostic
- Cleaned up DOM structure a bit (see reduced motion notes below)
- Using media queries instead of classes for motion-specific CSS
- General doc improvements, including rearranging examples so that reduced motion & non-reduced motion examples are intermingled
Reduced motion
Structure
I saw your comment about standardizing on the same DOM structure for both reduced and non-reduced motion. I've made them closer to each other, but am not sure I can get them exactly the same given the different types of animations. The reduced motion one is essentially a marquee (oh dear, remember <marquee>
) so its content is a little more rigid.
Here's what I landed on in the commit -- I welcome any suggested improvements:
No reduced motion
<div role="progressbar" aria-label="Loading..." class="expressive-loader__progress">
<div class="expressive-loader__lines">
<!-- dynamically add / remove child lines here -->
</div>
</div>
Reduced motion
<div role="progressbar" aria-label="Loading..." class="expressive-loader__progress">
<div class="expressive-loader__lines">
<!-- Set A lines here -->
</div>
<div class="expressive-loader__lines">
<!-- Set B lines here -->
</div>
<div class="expressive-loader__lines">
<!-- Set A lines here again-->
</div>
<div class="expressive-loader__lines">
<!-- Set B lines here again -->
</div>
</div>
Examples
I initially was using a class (rather than media query), so I could clearly and consistently demonstrate the difference between the two motion modes in the docs. Because the markup & behavior is a little different between the two, the examples in the docs and in Storybook might not render well depending on the viewer's system settings. Is that okay?
src/tokens/evo-core.css
Outdated
@@ -93,6 +93,7 @@ | |||
--font-size-18: 1.125rem; | |||
--font-size-20: 1.25rem; | |||
--font-size-24: 1.5rem; | |||
--font-size-28: 1.75rem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀 Not sure if I should add this here, but our Large Text typography style is size 20 / line height 28.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For typography, this is good. For line height, we use spacing props.
Add an event listener for the element's <code>animationend</code>, which triggers the creation of another line and | ||
removal of the oldest line. | ||
</li> | ||
<li>Prepend the line to the <code>.expressive-loader__lines` container.</code></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<li>Prepend the line to the <code>.expressive-loader__lines` container.</code></li> | |
<li>Prepend the line to the <code>.expressive-loader__lines</code> container.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Skin docs, we typically use <span class="highlight"></span>
to format/highlight certain text. Let's keep it consistent and use that instead of <code>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing another round of review...
Add an event listener for the element's <code>animationend</code>, which triggers the creation of another line and | ||
removal of the oldest line. | ||
</li> | ||
<li>Prepend the line to the <code>.expressive-loader__lines` container.</code></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Skin docs, we typically use <span class="highlight"></span>
to format/highlight certain text. Let's keep it consistent and use that instead of <code>
.
@@ -26,7 +40,7 @@ | |||
color: var(--color-foreground-primary); | |||
font-size: var(--font-size-large-1); | |||
font-weight: var(--font-weight-regular); | |||
line-height: 28px; | |||
line-height: var(--font-size-28, 28px); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For line-height, we use the spacing props in evo-core. Oh, and, you don't need a backup default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our font vars use rem units, our spacing vars use px units. A concern I have about using spacing vars for line height is that if someone were to update their base font size to something larger to make the page mor readable, the line height wouldn't scale appropriately.
@@ -36,141 +50,127 @@ | |||
.expressive-loader--medium .expressive-loader__message-container { | |||
font-size: var(--font-size-medium); | |||
font-weight: var(--font-weight-bold); | |||
line-height: 24px; | |||
line-height: var(--font-size-24); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...spacing props in evo-core.
src/tokens/evo-core.css
Outdated
@@ -93,6 +93,7 @@ | |||
--font-size-18: 1.125rem; | |||
--font-size-20: 1.25rem; | |||
--font-size-24: 1.5rem; | |||
--font-size-28: 1.75rem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For typography, this is good. For line height, we use spacing props.
b721261
to
5b4e8ad
Compare
🦋 Changeset detectedLatest commit: aea7e9d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@ArtBlue @ianmcburnie, major updates since last review:
|
5b4e8ad
to
979cf7e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great overall! Just some minor stuff...
( | ||
( | ||
var(--expressive-loader-line-scale-duration) / | ||
var(--expressive-loader-line-count) | ||
) * 2 | ||
)) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did prettier
insist on the indentations in these blocks? If not, it would be great to do 4 space indentations from previous lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, Prettier keeps forcing this indentation!
<div class="expressive-loader"> | ||
<div class="expressive-loader__message-container"> | ||
<div aria-hidden="true" class="expressive-loader__message">Hang tight.</div> | ||
<div role="status" id="expressive-loader-message-1" class="expressive-loader__message expressive-loader__message--initial"></div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having this 'with Message' section here without a message present seems odd. I know you describe the reason above, but the message should be there statically anyway. We can revise the messaging as we do in other places to indicate the messaging interaction piece is not included in Skin and needs to be added using a library or js
.
{% endhighlight %} | ||
|
||
<h4> | ||
Multiple messages (default behavior) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally, for interactive modules, we actually build a dumbed-down interaction portion as well in Skin using main.js
. For this section, it might be a good idea to do so. You can include a blurb about the interaction piece requiring js
similar to how we've done for other modules.
</p> | ||
|
||
<h4> | ||
Multiple messages (reduced motion behavior) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We typically go from simplest implementation to most complex, so single message should come before multiple messages. In fact, I think top-level sections are probably sufficient:
h3 - Default Expressive Loader
h3 - Expressive Loader with One Message
h3 - Expressive Loader with Multiple Messages
(in this section include a paragraph about reduced motion)
I don't think it's necessary to highlight reduced motion
so much, especially in section titles. It's more of a "transparent" feature for users that devs don't necessarily need to know too much details about.
src/less/expressive-loader/stories/expressive-loader.stories.js
Outdated
Show resolved
Hide resolved
</div> | ||
`; | ||
|
||
export const withMessagesInInitialState = () => ` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we need initial state coverage.
@@ -193,6 +193,55 @@ document.querySelectorAll('.dialog-button').forEach(function (el) { | |||
dialogWidget._el.addEventListener('dialog-cta', logEvent); | |||
}); | |||
|
|||
// EXPRESSIVE-LOADER | |||
// Simple expressive loader implementation that handles rotating messages. | |||
class ExpressiveLoader { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ArtBlue Here's a simple JS implementation of the message rotation. There's more nuance I'd bring to a robust implementation (in ebayui-core and/or makeup-js) around timing, reduced motion enhancements, and start states, but opted to just do message rotation for the Skin examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look great to me! One more small tweak.
Let's see if Andrew and/or Ian have feedback.
Expressive Loader with Multiple Messages | ||
</h3> | ||
<p> | ||
For the following examples, we're using Javascript to rotate through messages. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome stuff! I think this should be good to merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. One last tweak: let's just avoid people possibly assuming that when we say "We're using Javascript..." that the interaction is built into Skin. Being super clear that it's not avoids that risk altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Thanks, @cordeliadillon ! I love where we landed with this.
Address #2162
Description
Introduce styles for an expressive loader pattern, including standard and reduced motion modes.
Notes
The full experience of the expressive loader pattern requires Javascript to rotate through the loading messages. I've noted in the documentation how Javascript could be used to achieve the desired behavior and have also included basic functionality in
main.js
. I have a full Marko implementation in the 2016-expressive-loader branch of ebayui-core, if you'd like to see it in action.There are several potential implementation techniques for line animations, including canvas; I opted for a CSS version since, well, a canvas implementation wouldn't add much to Skin.
Motion approaches
Screen reader experience
The expressive loader consists of two pieces: the message container and the progress bar.
Message container
Expressive loader can have up to 3 messages that loop. As the current message fades out, a new message fades in. The current message has the following accessible properties:
As a live region with role status, the current message container will read out its content whenever it changes.
A new message animating in has
aria-hidden="true"
to hide it from the screen reader. While the new message and the current (outgoing) message are briefly visible on the screen at the same time, the current (outgoing) message is the only one present in the accessibility tree.Progress bar
The progress bar is an indeterminate progressbar. While there's a native
<progress>
HTML element, its style overrides are limited, so we use a div with role progressbar. The progress bar's accessible name defaults to "Loading..." but can be customized, for example to support localization. The progress bar isaria-describedby
the current message, if one exists.We did explore using
aria-roledescription="Loading indicator"
instead of an aria-label, but aria-roledescription has limited support across mobile browsers, confirmed in a brief CodePen test.Screenshots
Standard
Medium text
Long message
Videos
Default (w/ Javascript to handle message changes)
FullAnimation-Default.mov
Reduced motion (w/ Javascript to handle message changes)
FullAnimation-ReducedMotion.mov
Browser limitations: Safari < 14
This CSS-only implementation of expressive loader line animation dynamically updates the order property of the lines to achieve the appearance of infinite lines. Unfortunately, Safari < 14 does not support animating flex order, as I explored in this Codepen: https://codepen.io/cordelia/pen/gOyOePQ
Here is what the default expressive loader looks like on Safari 12:
iOS-Safari-12.0-Default.mov
Here is the same animation on Safari 14:
iOS-Safari-14.0-Default.mov
Checklist
I did a visual regression check of the components impacted by doing a Percy build and approved the buildThis is a net new component