discourse/app/views/common/_discourse_splash.html.erb
David Taylor 9c83872a4e
PERF: Refactor and optimize splash screen implementation (#30373)
- Remove JS

- Remove "Loading..." text. This has been been broken for a while due to some conflicting discourse-ai CSS. Also, animating the `content:` property like this requires the browser to repaint/reflow, which cannot be done while JS is executing.

- Replace animated SVG with divs animated via CSS. When JS is executing, browsers pause animations of transform properties inside SVGs. This limitation does not exist on regular CSS animations. So with this change, the animation continues smoothly even you run an infinite loop in JS.

  To ensure the splash screen remains "contentful" for LCP purposes, an SVG background-image is used

There is no change to the visual look of the animation
2024-12-30 11:31:08 +00:00

135 lines
3.5 KiB
Plaintext

<section id="d-splash">
<style>
html {
overflow-y: hidden !important;
}
/* user picked a theme where the "regular" scheme is dark */
<%- if dark_color_scheme? %>
html {
background-color: #<%= ColorScheme.hex_for_name("secondary", scheme_id) %>;
}
#d-splash {
--dot-color: #<%= ColorScheme.hex_for_name("tertiary", scheme_id) %>;
}
<%- else %>
/* user picked a theme a light scheme and also enabled a dark scheme */
/* deal with light scheme first */
@media (prefers-color-scheme: light) {
html {
background-color: #<%= ColorScheme.hex_for_name("secondary", scheme_id) %>;
}
#d-splash {
--dot-color: #<%= ColorScheme.hex_for_name("tertiary", scheme_id) %>;
}
}
/* then deal with dark scheme */
@media (prefers-color-scheme: dark) {
html {
background-color: #<%= ColorScheme.hex_for_name("secondary", dark_scheme_id) %>;
}
#d-splash {
--dot-color: #<%= ColorScheme.hex_for_name("tertiary", dark_scheme_id) %>;
}
}
<%- end %>
#d-splash {
display: grid;
place-items: center;
position: absolute;
left: 0;
top: 0;
width: 100vw;
z-index: 1001;
}
#d-splash .preloader-image {
--splash-dot-size: max(1vw, 25px);
--splash-dot-spacing: calc(var(--splash-dot-size) * 1.5);
width: calc((var(--splash-dot-size) + var(--splash-dot-spacing)) * 5);
height: 100vh;
background-size: cover;
}
@keyframes d-splash-fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.dots {
animation-name: d-splash-loader;
animation-timing-function: ease-in-out;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-delay: calc(var(--n) * 0.15s);
position: absolute;
top: calc(50% - var(--splash-dot-size) / 2);
left: calc((50% - var(--splash-dot-size) / 2) + (var(--n) * var(--splash-dot-spacing)));
transform-origin: calc((var(--splash-dot-spacing) * var(--n) * -1) + var(--splash-dot-size)/2) center;
width: var(--splash-dot-size);
height: var(--splash-dot-size);
border-radius: 50%;
border: 0.5px solid #fff;
background-color: var(--dot-color);
filter: saturate(2) opacity(0.85);
opacity: 0;
}
@keyframes d-splash-loader {
0% {
opacity: 0;
transform: scale(1);
}
45% {
opacity: 1;
transform: scale(0.7);
}
65% {
opacity: 1;
transform: scale(0.7);
}
100% {
opacity: 0;
transform: scale(1);
}
}
</style>
<div class="preloader-image" elementtiming="discourse-splash-visible">
<div class="dots" style="--n:-2;"></div>
<div class="dots" style="--n:-1;"></div>
<div class="dots" style="--n:0;"></div>
<div class="dots" style="--n:1;"></div>
<div class="dots" style="--n:2;"></div>
</div>
<script nonce="<%= csp_nonce_placeholder %>">
const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"><!-- LCP candidate image ${".".repeat(5000)} --></svg>`;
document.querySelector("#d-splash .preloader-image").style.backgroundImage = `url('data:image/svg+xml,${svg}')`
</script>
<noscript>
<style>
html {
overflow-y: revert !important;
}
#d-splash {
display: none;
}
</style>
</noscript>
</section>