<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);
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.