Improve FOUCE reduction utility, docs fixes, :state(wa-defined) (#643)

* Utility layout

* Split out, improve & document FOUCE utility
This commit is contained in:
Lea Verou
2025-01-31 08:19:30 -08:00
committed by GitHub
parent ce1ce6caca
commit 404d59d9d6
11 changed files with 179 additions and 19 deletions

View File

@@ -3,14 +3,14 @@
<wa-tab panel="css">In CSS</wa-tab>
<wa-tab-panel name="html">
Simply add the following code to the `<head>` of your page:
Add the following code to the `<head>` of your page:
```html
<link rel="stylesheet" href="{% cdnUrl stylesheet %}" />
```
</wa-tab-panel>
<wa-tab-panel name="css">
Simply add the following code at the top of your CSS file:
Add the following code at the top of your CSS file:
```css
@import url('{% cdnUrl stylesheet %}');
```

19
docs/_layouts/utility.njk Normal file
View File

@@ -0,0 +1,19 @@
{% extends '../_layouts/block.njk' %}
{% block afterContent %}
{% if file %}
{% markdown %}
## Opting In
If you want to use this utility **only** without [all others](../), you can include the following CSS file from the Web Awesome CDN.
{% set stylesheet = file %}
{% include 'import-stylesheet-code.md.njk' %}
Want them all?
Follow the [instructions on the Utilities overview page](../) to get all Web Awesome utilities.
{% endmarkdown %}
{% endif %}
{% endblock %}

View File

@@ -6,6 +6,7 @@ snippets:
- .wa-outlined
- .wa-filled
- .wa-plain
file: styles/utilities/appearance.css
---
Some Web Awesome components, like `<wa-button>`, allow you to change their overall style by using an `appearance` attribute:

View File

@@ -8,6 +8,7 @@ snippets:
- .wa-success
- .wa-warning
- .wa-danger
file: styles/utilities/variants.css
---
Some Web Awesome components, like `<wa-button>`, allow you to change the color by using a `variant` attribute:

View File

@@ -0,0 +1,131 @@
---
title: Reduce FOUCE
description: Utility to improve the loading experience by hiding non-prerendered custom elements until they are registered.
file: styles/utilities/fouce.css
icon: spinner
---
{% markdown %}
No class is needed to use this utility, it will be applied automatically as long as it its CSS is included.
Here is a comparison of the loading experience with and without this utility,
with a simulated slow loading time:
{% endmarkdown %}
<div class="wa-split wa-align-items-end">
<strong>Normal loading</strong>
<wa-button onclick="document.querySelectorAll('iframe').forEach(iframe => iframe.srcdoc = iframe.srcdoc)">
<wa-icon name="refresh"></wa-icon>
Refresh
</wa-button>
<strong>With FOUCE reduction</strong>
</div>
{% set sample_card %}
<link id="theme-stylesheet" rel="stylesheet" href="/dist/styles/themes/default.css" render="blocking" fetchpriority="high">
<link rel="stylesheet" href="/dist/styles/webawesome.css">
<link rel="stylesheet" href="/dist/styles/forms.css">
<script type=module>
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const loadScript = src => new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = src;
script.type = "module";
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
await delay(500);
await loadScript("/dist/components/button/button.js");
await delay(500);
await loadScript("/dist/components/card/card.js");
await delay(500);
await loadScript("/dist/components/rating/rating.js");
</script>
<wa-card with-footer with-image class="card-overview">
<img
slot="image"
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
alt="A kitten sits patiently between a terracotta pot and decorative grasses."
/>
<strong>Mittens</strong><br />
This kitten is as cute as he is playful. Bring him home today!<br />
<small>6 weeks old</small>
<div slot="footer">
<wa-button variant="brand" pill>More Info</wa-button>
<wa-rating></wa-rating>
</div>
</wa-card>
<style>
.card-overview small {
color: var(--wa-color-text-quiet);
}
.card-overview [slot=footer] {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
{% endset %}
<div class="iframes">
<iframe srcdoc='<body class="wa-fouce-off">{{ sample_card }}</body>'></iframe>
<iframe srcdoc='{{ sample_card }}'></iframe>
</div>
<style>
.iframes {
display: flex;
gap: var(--wa-space-m);
margin-top: var(--wa-space-l);
iframe {
flex: 1;
height: 60ch;
}
}
</style>
{% markdown %}
## How does it work?
The utility consists of a timeout (`2s` by default) and a fade duration (`200ms` by default).
- If the element is _ready_ before the timeout, it will appear immediately.
- If it takes longer than _timeout_ + _fade_, it will fade in over the fade duration.
- If it takes somewhere between _timeout_ and _timeout_ + _fade_, you will get an interrupted fade.
An element is considered ready when both of these are true:
1. Either It has been registered or has a `did-ssr` attribute (indicating it was pre-rendered)
2. If its a Web Awesome component, its contents are also ready
## Customization
You can use the following CSS variables to customize the behavior:
| Variable | Description | Default |
| --- | --- | --- |
| `--wa-fouce-fade` | The transition duration for the fade effect. | `200ms` |
| `--wa-fouce-timeout` | The timeout after which elements will appear even if not registered | `2s` |
The fade duration cannot be longer than the timeout.
This means that you can disable FOUCE reduction on an element by setting `--wa-fouce-timeout: 0s`.
For example, if instead of `did-ssr` you used an `ssr` attribute to mark elements that were pre-rendered, you can do this to get them to appear immediately:
```css
[ssr] {
--wa-fouce-timeout: 0s;
}
```
You can also opt-out from FOUCE reduction for an element and its contents by adding the `.wa-fouce-off` class to it.
Applying this class to the root element will disable the utility for the entire page.
{% endmarkdown %}

View File

@@ -2,6 +2,7 @@
title: Gap
description: Gap utilities set the gap property of flex and grid containers, like other Web Awesome layout utilities.
tags: ["utilities", "layout"]
file: styles/utilities/gap.css
---
<style>

View File

@@ -1,4 +1,4 @@
{
"layout": "block",
"layout": "utility",
"tags": ["utilities"]
}

View File

@@ -26,6 +26,8 @@ export default class WebAwesomeElement extends LitElement {
console.error('Element internals are not supported in your browser. Consider using a polyfill');
}
this.toggleCustomState('wa-defined');
let Self = this.constructor as typeof WebAwesomeElement;
for (let [property, spec] of Self.elementProperties) {
if (spec.default === 'inherit' && spec.initial !== undefined && typeof property === 'string') {

View File

@@ -29,22 +29,6 @@ body {
-webkit-text-size-adjust: none;
}
@keyframes wa-fade-in {
from {
opacity: 0;
}
}
/* Show custom elements only after they're registered */
:where(:not(:defined, [did-ssr])) {
/*
* if an element gets defined earlier than 800ms, the animation stops applying so it just appears (no fade)
* If it takes somewhere between 800 and 1000 ms, then you may get an interrupted fade
* If an element takes longer than 1000ms to get defined, it fades in over 200ms
*/
animation: 200ms 800ms wa-fade-in both;
}
/* Content flow */
address,
audio,

View File

@@ -1,3 +1,4 @@
@import url('utilities/fouce.css');
@import url('utilities/visually-hidden.css');
@import url('utilities/scroll-lock.css');
@import url('utilities/align-items.css');

View File

@@ -0,0 +1,20 @@
/*
* Utility to minimize FOUCE and show custom elements only after they're registered
*/
@keyframes wa-fade-in {
from {
opacity: 0;
}
}
:not(:defined),
:state(wa-defined):has(:not(:defined)) {
/* The clamp() ensures that if --wa-fouce-timeout is set to 0s, the whole effect is disabled */
--wa-fouce-animation: clamp(0s, var(--wa-fouce-fade, 200ms), var(--wa-fouce-timeout, 2s)) var(--wa-fouce-timeout, 2s)
wa-fade-in both;
&:where(:not([did-ssr], .wa-fouce-off, .wa-fouce-off *)) {
animation: var(--wa-fouce-animation);
}
}