HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URLs added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<div class="MouseScroll">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" version="1.1">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g fill="rgba(160, 187, 189, 0.8)">
<path d="M40,41.0074017 L40,41.0074017 L40,58.9925983 C40,64.5218355 44.4762336,69 50,69 C55.5234877,69 60,64.5203508 60,58.9925983 L60,41.0074017 C60,35.4781645 55.5237664,31 50,31 C44.4765123,31 40,35.4796492 40,41.0074017 L40,41.0074017 Z M38,41.0074017 C38,34.3758969 43.3711258,29 50,29 C56.627417,29 62,34.3726755 62,41.0074017 L62,58.9925983 C62,65.6241031 56.6288742,71 50,71 C43.372583,71 38,65.6273245 38,58.9925983 L38,41.0074017 L38,41.0074017 Z"></path>
<path d="M49,36 L49,40 C49,40.5522847 49.4477153,41 50,41 C50.5522847,41 51,40.5522847 51,40 L51,36 C51,35.4477153 50.5522847,35 50,35 C49.4477153,35 49,35.4477153 49,36 L49,36 Z"></path>
<path d="M50,81.9929939 L55.4998372,76.4931567 C55.8903615,76.1026324 56.5235265,76.1026324 56.9140508,76.4931567 C57.3045751,76.883681 57.3045751,77.516846 56.9140508,77.9073703 L50.7071068,84.1143143 C50.5118446,84.3095764 50.2559223,84.4072075 50,84.4072075 C49.7440777,84.4072075 49.4881554,84.3095764 49.2928932,84.1143143 L43.038379,77.8598002 C42.6478547,77.4692759 42.6478547,76.8361109 43.038379,76.4455866 C43.4289033,76.0550623 44.0620683,76.0550623 44.4525926,76.4455866 L50,81.9929939 Z" class="MouseScroll--chevron"></path>
</g>
</g>
</svg>
</div>
<section class="Intro CenterAlign">
<div class="Wrapper">
<h1>ScrollTrigger</h1>
<h2>Let your page react to scroll changes</h2>
</div>
</section>
<section class="About">
<div class="Wrapper">
<h3 data-slideInLeft>
Trigger classes based on scroll position
</h3>
<p data-slideInRight>
The most basic usage of ScrollTrigger is to trigger classes based on the current scroll position. E.g. when an element enters the viewport, fade it in. You can add custom offsets per element, or set offsets on the viewport (e.g. always trigger after the
element reaches 20% of the viewport)
</p>
<h3 data-slideInLeft>
Execute callbacks on entering / leaving the viewport
</h3>
<p data-slideInRight>
When using the callbacks ScrollTrigger becomes really powerfull. You can run custom code when an element enters / becomes visible, and even return Promises to halt the trigger if the callback fails. This makes lazy loading images very easy.
</p>
<p></p>
<div data-callback data-slideInBottom class="CenterAlign">
You've scrolled passed this block <span>0</span> times.
</div>
</div>
</section>
<section class="Examples">
<div class="Wrapper">
<h3 data-slideInBottom>Show me some code!</h3>
<p data-slideInBottom>
The easiest way to start is to create a new instance and add some triggers to it, with all default values. This will toggle the 'visible' class when the element comes into the viewport, and toggles the 'invisible' class when it scrolls out of the viewport.
</p>
<code data-slideInBottom>
<span>// Create a new ScrollTrigger instance with default options</span><br />
const trigger = new ScrollTrigger()<br />
<span>// Add all html elements with attribute data-trigger</span><br />
trigger.add('[data-trigger]')<br />
<br />
<span>// Now in your CSS add the following classes, this
fades the [data-trigger] elements in and out</span><br />
.visible, .invisible {<br />
opacity: 0.0;<br />
transition: opacity 0.5s ease;<br />
}<br />
.visible {<br />
opacity: 1.0;<br />
}
</code>
<h3 data-slideInBottom>Now let's add some callbacks and custom classes</h3>
<p data-slideInBottom>
Adding callbacks / different classes can be done globally, this becomes the default for all triggers you add, or you can specify custom configuration when adding a trigger.
</p>
<code data-slideInBottom>
<span>// Create a new ScrollTrigger instance with some custom options</span><br />
const trigger = new ScrollTrigger({<br />
trigger: {<br />
once: true<br />
}<br />
})<br />
<span>// Add all html elements with attribute data-trigger, these elements will only be triggered once</span><br />
trigger.add('[data-trigger]')<br />
<span>// Add all html elements with attribute data-triggerAlways, these elements will always be triggered</span><br />
trigger.add('[data-triggerAlways]', { once: false })<br />
</code>
<p></p>
<p data-slideInBottom>
For more examples, checkout the <span>/demo</span> directory on
<a href="https://github.com/terwanerik/ScrollTrigger/tree/master/demo">GitHub</a>.
</p>
</div>
</section>
<section class="MoreInformation CenterAlign">
<div class="Wrapper">
<h2>More information?</h2>
<h3><a href="https://github.com/terwanerik/ScrollTrigger">Visit GitHub!</a></h3>
</div>
</section>
/* .visible, .invisible {
opacity: 0.0;
transition: opacity 0.5s ease;
}
.visible {
opacity: 1.0;
} */
html, body {
margin: 0;
padding: 0;
}
body {
background: #021517;
color: #a0bbbd;
font-family: 'Montserrat', Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
font-weight: 400;
font-size: 21px;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, p {
margin: 0;
padding: 0.25em 0;
}
h1 {
font-size: 2.5em;
}
h2 {
font-size: 1.875em;
font-weight: normal;
}
h3 {
font-size: 1.5em;
}
a {
text-decoration: none;
font-weight: 500;
color: #689396;
}
section {
display: table;
position: relative;
box-sizing: border-box;
width: 100%;
min-height: 100vh;
padding: 75px 15px;
}
section div.Wrapper {
display: table-cell;
vertical-align: middle;
}
canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
section h3,
section p,
section code {
display: block;
width: 98%;
max-width: 680px;
margin: 0 auto;
padding: 0 0 25px 0;
line-height: 1.4em;
color: inherit;
}
section h3 {
margin: 45px auto 0 auto;
}
section p span {
display: inline-block;
position: relative;
top: -2px;
padding: 0 5px;
background: #689396;
font-family: monospace;
font-size: 14px;
color: #fff;
}
section code {
padding: 15px;
background: #689396;
color: #fff;
font-size: 14px;
}
section code span {
opacity: 0.6;
}
section.Intro {
color: #a0bbbd;
}
section.Intro h2 {
color: rgba(160, 187, 189, 0.8);
}
section.About {
color: #497275;
background: #a0bbbd;
}
section.Examples {
background: #fff;
color: #689396;
}
.CenterAlign {
text-align: center;
}
div.Wrapper {
position: relative;
width: 96%;
max-width: 680px;
margin: 0 auto;
z-index: 1;
}
div.MouseScroll {
position: absolute;
left: 50%;
bottom: 25px;
width: 100px;
height: 100px;
margin: 0 0 0 -50px;
z-index: 2;
}
path.MouseScroll--chevron {
animation: ChevronAnimation 3s ease infinite;
transform: translateY(3px)
}
[data-slideInLeft].visible, [data-slideInLeft].invisible,
[data-slideInRight].visible, [data-slideInRight].invisible ,
[data-slideInBottom].visible, [data-slideInBottom].invisible {
opacity: 1.0;
transform: translate(0, 0);
transition: transform 0.8s ease, opacity 0.8s ease;
}
[data-slideInLeft].invisible {
opacity: 0.0;
transform: translate(10px, 0);
}
[data-slideInRight].invisible {
opacity: 0.0;
transform: translate(-10px, 0);
}
[data-slideInBottom].invisible {
opacity: 0.0;
transform: translate(0, 10px);
}
@keyframes ChevronAnimation {
0% {
transform: translateY(3px);
opacity: 1
}
50% {
transform: translateY(8px);
opacity: 0
}
60% {
transform: translateY(3px);
opacity: 0
}
}
import ScrollTrigger from "https://esm.sh/@terwanerik/scrolltrigger"
((document, window) => {
// This is where the magic happens, start by initializing a ScrollTrigger
// instance. We can set default options for all triggers in the constructor.
//
// We set some default 'trigger' options, and add a custom callback for
// the didScroll method. Also we set the scroll sustain to 800ms.
const trigger = new ScrollTrigger({
// Set custom (default) options for the triggers, these can be overwritten
// when adding new triggers to the ScrollTrigger instance. If you pass
// options when adding new triggers, you'll only need to pass the object
// `trigger`, e.g. { once: false }
trigger: {
// If the trigger should just work one time
once: false,
offset: {
// Set an offset based on the elements position, returning an
// integer = offset in px, float = offset in percentage of either
// width (when setting the x offset) or height (when setting y)
//
// So setting an yOffset of 0.2 means 20% of the elements height,
// the callback / class will be toggled when the element is 20%
// in the viewport.
element: {
x: 0,
y: (trigger, rect, direction) => {
// You can add custom offsets according to callbacks, you
// get passed the trigger, rect (DOMRect) and the scroll
// direction, a string of either top, left, right or
// bottom.
return 0.2
}
},
// Setting an offset of 0.2 on the viewport means the trigger
// will be called when the element is 20% in the viewport. So if
// your screen is 1200x600px, the trigger will be called when the
// user has scrolled for 120px.
viewport: {
x: 0,
y: (trigger, frame, direction) => {
// We check if the trigger is visible, if so, the offset
// on the viewport is 0, otherwise it's 20% of the height
// of the viewport. This causes the triggers to animate
// 'on screen' when the element is in the viewport, but
// don't trigger the 'out' class until the element is out
// of the viewport.
// This is the same as returning Math.ceil(0.2 * frame.h)
return trigger.visible ? 0 : 0.2
}
}
},
toggle: {
// The class(es) that should be toggled
class: {
in: 'visible', // Either a string, or an array of strings
out: ['invisible', 'extraClassToToggleWhenHidden']
},
callback: {
// A callback when the element is going in the viewport, you can
// return a Promise here, the trigger will not be called until
// the promise resolves.
in: null,
// A callback when the element is visible on screen, keeps
// on triggering for as long as 'sustain' is set
visible: null,
// A callback when the element is going out of the viewport.
// You can also return a promise here, like in the 'in' callback.
//
// Here an example where all triggers take 10ms to trigger
// the 'out' class.
out: (trigger) => {
// `trigger` contains the Trigger object that goes out
// of the viewport
return new Promise((resolve, reject) => {
setTimeout(resolve, 10)
})
}
}
},
},
// Set custom options and callbacks for the ScrollAnimationLoop
scroll: {
// The amount of ms the scroll loop should keep triggering after the
// scrolling has stopped. This is sometimes nice for canvas
// animations.
sustain: 200,
// Window|HTMLDocument|HTMLElement to check for scroll events
element: window,
// Callback when the user started scrolling
start: () => {},
// Callback when the user stopped scrolling
stop: () => {},
// Callback when the user changes direction in scrolling
directionChange: () => {}
}
})
function setup() {
// Add the triggers
addTriggers()
}
function addTriggers() {
// Adding triggers can be done in multiple ways, the easiest is to pass
// a querySelector.
trigger.add('[data-slideInLeft]')
.add('[data-slideInRight]')
.add('[data-slideInBottom]')
// Add the trigger for the callback example, also add a custom callback
// when the trigger becomes visible. As an example we pass an HTMLElement
// instead of a querySelector.
const element = document.querySelector('[data-callback]')
trigger.add(element, {
toggle: {
callback: {
in: counterCallback
}
}
})
}
function counterCallback(trigger) {
// In the callback we get passed the Trigger object, from here we have
// access to the responding HTMLElement among other things. You could,
// for instance, change the class it toggles, or attach another callback.
// Check the console for more info.
console.info(trigger)
// For now, we just append the counter
const counterElement = trigger.element.querySelector('span')
const counter = parseInt(counterElement.innerText)
counterElement.innerText = counter + 1
}
setup()
})(document, window)
Also see: Tab Triggers