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.
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=0.75">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=UnifrakturMaguntia" />
</head>
<body>
<div id="container">
<div id="content">
<p>
<span id="lorem">L</span>orem ipsum dolor sit amet, consectetur adipiscing elit. Integer sagittis nisi urna, a ultrices est facilisis a. Morbi porttitor vulputate odio, eu lacinia nisi. Suspendisse felis sapien, facilisis nec ex in, blandit tincidunt tellus. Sed at commodo nunc. In nibh lectus, facilisis nec magna nec, bibendum egestas nunc. Nam varius lorem in fringilla cursus. Integer dignissim, lectus vitae sodales molestie, libero purus malesuada arcu, vitae facilisis nunc dolor non mi. In nunc tortor, tempor non pharetra vitae, mattis a purus. Nulla rhoncus vitae metus vel ornare. Nunc augue dui, suscipit ac urna vel, consectetur volutpat ipsum. Nunc ac nulla ut enim laoreet placerat. Sed luctus aliquam purus, sollicitudin blandit dui blandit id. Aenean venenatis risus dolor, at viverra urna aliquam non. Morbi sit amet pellentesque justo, eget viverra augue.
</p>
<p>
Praesent posuere ultricies orci sit amet lacinia. Suspendisse lacinia scelerisque risus, sodales egestas turpis cursus sed. Proin sed mollis mauris, vitae ultricies nibh. Nulla bibendum leo a mauris luctus, sit amet iaculis arcu blandit. Etiam pulvinar, odio et rutrum egestas, elit mi maximus ex, id elementum est tortor id turpis. Duis rhoncus et lorem vel maximus. Aenean at justo sagittis, aliquet eros eget, iaculis magna. Nam non orci congue, dapibus dui eget, sagittis nisl. Phasellus venenatis id est et tempor. Aenean condimentum tristique nibh sit amet varius. Vestibulum et lectus quis eros dapibus consectetur nec auctor dolor. Sed euismod eu felis aliquam fermentum. Donec lacinia fringilla erat, at eleifend velit tempus at.
</p>
<hr>
<p>
Cras justo turpis, vulputate eget venenatis sit amet, bibendum quis dolor. Cras at interdum libero. Quisque convallis rutrum magna in ultrices. Donec ut magna dolor. <img id="tree" src="http://i1155.photobucket.com/albums/p556/browles/3jYNlVu_zpsojxriowp.png">Mauris pulvinar ut sapien a posuere. Sed nisi elit, tincidunt vitae magna eu, dapibus suscipit purus. Maecenas tincidunt mollis eros et dictum. Duis est nulla, rhoncus tincidunt velit at, venenatis elementum velit. Phasellus lobortis sem tellus, id sodales quam dignissim nec. Phasellus pulvinar metus ex, nec gravida nunc elementum vel. Ut mattis varius fringilla. Phasellus imperdiet sit amet risus a elementum. Donec pulvinar ante sit amet massa blandit ullamcorper. Donec vitae malesuada nisl, et laoreet sem.
</p>
<p>
Suspendisse bibendum elit blandit arcu vulputate, nec hendrerit dui vehicula. Vestibulum porta finibus odio vitae maximus. Duis in vulputate risus. Donec mattis turpis ex, vitae semper sem ultrices eu. Aliquam in ex blandit erat ultrices sollicitudin. Vestibulum porta nisl in porttitor rutrum. Integer consectetur porttitor ligula facilisis malesuada. Proin placerat enim sed lacus commodo mollis nec eu arcu. In hac habitasse platea dictumst. Curabitur luctus est risus, sit amet fringilla nunc condimentum vel. Integer mauris lorem, molestie ut nisl sit amet, pellentesque mollis quam. Aliquam posuere purus non nisi molestie semper.
</p>
<hr>
<p>
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris facilisis nisi diam, eu pulvinar ex sollicitudin sed. Maecenas sed eros id quam suscipit ultricies ut tincidunt quam. Donec iaculis, justo at fringilla laoreet, quam sem dapibus urna, ut eleifend odio eros et ligula. Proin urna ante, condimentum vitae sollicitudin sit amet, egestas ac nunc. Aenean sapien velit, porta a eros quis, iaculis dignissim felis. Suspendisse mollis vulputate metus vel interdum. Aliquam hendrerit elementum erat, sit amet commodo velit suscipit et. Sed semper sem at mauris rhoncus, id efficitur arcu molestie. Nam feugiat lorem pretium, consectetur felis et, fringilla dolor. Nunc dui velit, elementum non hendrerit nec, sagittis vitae odio. Curabitur nec leo tincidunt, pellentesque metus at, condimentum risus.
</p>
</div>
</div>
</body>
</html>
body {
background: #333;
overflow: hidden;
}
::-webkit-scrollbar {
display: none;
}
div {
margin: 0;
padding: 0;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
position: absolute;
z-index: 0;
}
#container {
font-family: UnifrakturMaguntia;
width: 350px;
height: 70%;
max-height: 500px;
top: 50%;
left: 50%;
-webkit-perspective: 5000px;
perspective: 5000px;
transform: translate(-50%, -50%) rotateY(20deg);
}
#container p {
padding: 0 5px 0 5px;
}
#container hr {
margin: 0 20px 0 20px;
}
.panel-node {
width: 100%;
}
.panel-cutout {
width: 100%;
height: 100%;
overflow: hidden;
}
#content {
-ms-overflow-style: none;
overflow: -moz-scrollbars-none;
overflow-y: scroll;
height: 100%;
}
#content, .panel-content {
background: #fefee0;
}
.panel-content {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.backface > * {
opacity: 0.25;
}
#lorem {
font-size: 7em;
float: left;
color: red;
border: 1px solid black;
margin-right: 5px;
}
#tree {
float: right;
width: 10em;
height: 10em;
border: 1px solid black;
margin: 0 5px 0 2px;
}
// Hack to detect device scrollbar width.
// When a mouse is plugged in, the contents of an element will shift over to accomodate the scrollbar.
// We will detect this and shift the .panel-content's to the left via padding-right, else the panels and the main content view become out of sync.
(function () {
var div = document.createElement('div');
div.style.width = '100px';
div.style.height = '100px';
div.style.overflow = 'scroll';
div.style.position = 'absolute';
div.style.top = '-9999px';
document.body.appendChild(div);
var scrollBarWidth = div.offsetWidth - div.clientWidth;
document.body.removeChild(div);
var style = document.createElement('style');
document.querySelector('head').appendChild(style);
var selector = '.panel-content';
var rule = `padding-right: ${scrollBarWidth}px`;
if (style.sheet) {
if (style.sheet.insertRule) style.sheet.insertRule(`${selector} {${rule}}`, 0);
else style.sheet.addRule(selector, rule);
}
else if (style.styleSheet) style.styleSheet.addRule(selector, rule);
})();
// Each articulation panel consists of three DOM elements
// - a grandparent for 3d positioning
// - a parent for clipping
// - and a child to hold and 'scroll' content via changing transforms
function panel(html) {
var panelNode = document.createElement('div');
var panelCutoutNode = document.createElement('div');
var panelContentNode = document.createElement('div');
panelNode.classList.add('panel-node');
panelCutoutNode.classList.add('panel-cutout');
panelContentNode.innerHTML = html;
panelContentNode.classList.add('panel-content');
panelCutoutNode.appendChild(panelContentNode);
panelNode.appendChild(panelCutoutNode);
return panelNode;
}
// Keep the content panels in sync by translating them up or down according to the scroll distance
function syncPanelContent(tops, bottoms, scrollTop, containerHeight, panelHeight) {
for (var i = 0; i < tops.length; i++) {
var t = tops[i];
var b = bottoms[i];
var tTop = (i + 1) * panelHeight - scrollTop;
var bTop = -i * panelHeight - scrollTop - containerHeight;
t.style.transform = `translate3d(0,${tTop}px,0)`;
b.style.transform = `translate3d(0,${bTop}px,0)`;
}
}
function transYrotX(y,x) {
return `translate3d(0,${y}px,0) rotateX(${x}rad)`;
}
// Create num top and bottom panels based off the innerHTML of el with articulation angle.
// We nest panels and use `transform-style: preserve-3d` to get the tentacle curl effect.
// Should probably only use this on relatively simple el's, because we are going to need to create 2 * num deep copies of el and attach them to the DOM. Needless to say, this will scale poorly.
function createScrollOverlay(el, panelHeight, num, angle) {
var tops = [];
var bottoms = [];
var topParent = el.parentNode;
var bottomParent = el.parentNode;
var html = el.innerHTML;
var totalTheta = 0;
for (var i = 0; i < num; i++) {
var topPanel = panel(html);
var bottomPanel = panel(html);
topPanel.style.height = `${panelHeight}px`;
bottomPanel.style.height = `${panelHeight}px`;
topPanel.style.transformOrigin = '50% 100% 0';
bottomPanel.style.transformOrigin = '50% 0% 0';
var topPanelContent = topPanel.querySelector('.panel-content');
var bottomPanelContent = bottomPanel.querySelector('.panel-content');
if (i === 0) {
topPanel.style.transform = transYrotX(-panelHeight, 0);
bottomPanel.style.top = '100%';
bottomPanel.style.transform = transYrotX(0, 0);
}
else {
topPanel.style.transform = transYrotX(-panelHeight + 0.25, angle);
bottomPanel.style.transform = transYrotX(panelHeight - 0.25, angle);
totalTheta += angle;
totalTheta %= 2 * Math.PI;
if (Math.PI * (1 / 2) < totalTheta && totalTheta < Math.PI * (3 / 2)) {
topPanelContent.classList.add('backface');
bottomPanelContent.classList.add('backface');
}
}
angle += 0.025;
tops.push(topPanelContent);
bottoms.push(bottomPanelContent);
topParent.appendChild(topPanel);
bottomParent.appendChild(bottomPanel);
topParent = topPanel;
bottomParent = bottomPanel;
}
syncPanelContent(tops, bottoms, 0, container.clientHeight, panelHeight);
function update() {
var scrollTop = el.scrollTop;
var containerHeight = container.clientHeight;
requestAnimationFrame(function() {
syncPanelContent(tops, bottoms, scrollTop, containerHeight, panelHeight);
});
}
el.onscroll = update;
window.onresize = update;
// setInterval(function() {el.scrollTop++}, 32)
}
var theta = 0.3;
var num = 20;
if (/iPhone|Android/.test(navigator.userAgent)) {
theta = 0.45;
num = 10;
}
var $ = document.querySelector.bind(document);
createScrollOverlay($('#content'), 20, num, theta);
Also see: Tab Triggers