<div id="controls">
<div>
<h2>Element-Based Offsets<br /><small>Where's #target?</small></h2>
<dl>
<dt><label for="edge">Edge</label></dt>
<dd>
<select name="edge" id="edge">
<option value="start">start</start>
<option value="end" selected>end</option>
</select>
</dd>
<dt><label for="threshold">Threshold</label></dt>
<dd>
<input type="range" min="0" max="1" step="0.01" name="threshold" id="threshold" value="0" />
</dd>
</dl>
<pre><code>selector(#target) <output for="edge">end</output> <output for="threshold">0.00</output></code></pre>
</div>
</div>
<div id="browser">
<main>
<div class="box" data-edge="end" data-threshold="0">
<p><output>#target</output> is positioned at the <output for="edge">end</output> edge, and has an intersection threshold of <output for="threshold">0.00</output></p>
</div>
</main>
<aside id="scrollbar">
<div id="thumb"></div>
</aside>
</div>
<details>
<summary>âšī¸</summary>
<dialog open>
<h1>About this Pen</h1>
<p>âšī¸ Scroll-Linked Animations with Element-Based offsets take a somewhat quirky syntax. This tool helps you visualize each selected offset.</p>
<p>đ¤ Want to learn more about Scroll-Linked Animations / Scroll-Timeline? Check out these two articles:</p>
<ul>
<li><a href="https://brm.us/scroll-linked-animations-pt1" target="_top">Scroll-Linked Animations with <code>@scroll-timeline</code> (Part 1) â Introduction</a></li>
<li><a href="https://brm.us/scroll-linked-animations-pt2" target="_top">Scroll-Linked Animations with <code>@scroll-timeline</code> (Part 2) â Element-Based Offsets</a></li>
</ul>
<p>đ More animations/visualizations: <a href="https://codepen.io/collection/AYoego" target="_top">https://codepen.io/collection/AYoego</a></p>
<p>đ Built by Bramus! â <a href="https://www.twitter.com/bramus" target="_top">@bramus</a> - <a href="https://www.bram.us/" target="_top">https://www.bram.us/</a> </p>
</dialog>
</details>
:root {
--scrollbox-border-size: 1rem;
--scrollbox-height: 60vh;
--scrollbox-width: 60vw;
--box-height: 10vh;
--box-gap: 5rem;
--thumb-size: 10vh;
--scrollbar-width: 0.5rem;
}
html, body {
width: 100vw;
height: 100vh;
background: #fff;
}
body {
display: grid;
place-items: center;
}
#browser {
width: var(--scrollbox-width);
height: var(--scrollbox-height);
border: var(--scrollbox-border-size) solid lightblue;
display: grid;
grid-template-columns: 1fr var(--scrollbar-width);
grid-template-rows: 1fr;
grid-template-areas: "content scrollbar";
position: relative;
}
main {
display: flex;
gap: var(--box-gap);
flex-direction: row;
position: relative;
background: #efefef;
}
main::before, main::after {
white-space: nowrap;
display: inline-block;
position: absolute;
right: -1em;
text-transform: uppercase;
font-size: 0.8rem;
}
main::before {
content: 'start edge';
top: 0;
transform: translate3d(calc(1em + var(--scrollbox-border-size) + 100%), calc(-50% - var(--scrollbox-border-size) / 2), 0);
}
main::after {
content: 'end edge';
bottom: 0;
transform: translate3d(calc(1em + var(--scrollbox-border-size) + 100%), calc(50% + var(--scrollbox-border-size) / 2), 0);
}
#scrollbar {
grid-area: scrollbar;
height: 100%;
align-self: start;
background: #ccc;
}
#scrollbar #thumb {
margin-top: 14vh;
height: var(--thumb-size);
width: 100%;
background: #333;
border-radius: 9999px;
}
.box {
height: var(--box-height);
flex: 1;
--shaded-size: 2px;
--shaded-color: rgba(0 0 0 / 0.12);
background: repeating-linear-gradient(
-45deg,
var(--shaded-color),
var(--shaded-color) var(--shaded-size),
transparent var(--shaded-size),
transparent calc(var(--shaded-size) * 2)
);
border: 1px solid #333;
display: grid;
place-items: center;
text-align: center;
position: absolute;
left: 0;
right: 0;
--threshold: 0;
}
.box p {
margin: 0;
padding: 0 0.5em;
}
.box[data-edge="start"] {
top: calc((var(--box-height) + var(--scrollbox-border-size)) * ((1 - var(--threshold)) * -1));
}
.box[data-edge="end"] {
bottom: calc((var(--box-height) + var(--scrollbox-border-size)) * ((1 - var(--threshold)) * -1));
}
#controls {
width: var(--scrollbox-width);
display: flex;
justify-content: space-evenly;
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
z-index: 2;
}
#controls label {
cursor: pointer;
text-align: right;
}
#controls input, #controls select {
width: 100%;
margin: 0;
padding: 0;
}
.box output {
font-weight: 700;
}
pre {
padding: 1em;
border: 1px solid #ccc;
}
/* DL GRID @src https://codepen.io/bramus/pen/POEaXg*/
dl {
display: grid;
grid-template: auto / 10em 1fr;
}
dt {
grid-column: 1;
text-align: right;
}
dd {
grid-column: 2;
}
dt, dd {
margin: 0;
padding: .3em .5em;
}
/** Poor man's infobox */
details {
position: fixed;
bottom: 1em;
right: 1em;
font-size: clamp(1.25rem, 2vw, 2rem);
z-index: 9999;
}
dialog[open] {
position: fixed;
top: 10vmin;
right: 10vmin;
bottom: 10vmin;
left: 10vmin;
width: auto;
height: auto;
/* Extra CSS for Safari âĻ */
background: #eee;
border: 0.25em solid lightblue;
padding: 1em;
overflow: auto;
}
details[open] > summary::after {
content: '';
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
background: rgba(0 0 0 / 0.5);
display: block;
}
/* Hide summary arrow âĻ*/
details summary::-webkit-details-marker {
display: none;
}
details summary {
display: inline-block;
}
details summary::marker {
display: none;
}
const $target = document.querySelector('.box');
document.querySelector('#edge').addEventListener('change', (e) => {
const edge = e.target.value;
document.querySelectorAll('output[for="edge"]').forEach((el) => el.innerHTML = edge);
$target.setAttribute('data-edge', edge);
});
document.querySelector('#threshold').addEventListener('input', (e) => {
const threshold = parseFloat(e.target.value).toFixed(2);
document.querySelectorAll('output[for="threshold"]').forEach((el) => el.innerHTML = threshold);
$target.style.setProperty('--threshold', threshold);
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.