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.
import zim from "https://zimjs.org/cdn/017/zim";
// REFERENCES for ZIM at https://zimjs.com
// see https://zimjs.com/intro.html for an intro example
// see https://zimjs.com/learn.html for video and code tutorials
// see https://zimjs.com/docs.html for documentation
// see https://codepen.io/topic/zim/ for ZIM on CodePen
// ~~~~~~~~~~~~~~~~ FRAME ~~~~~~~~~~
// will use FULL mode
// most of the CodePen ZIM apps are in FIT mode
// which means we do not have to worry about scaling
// With FULL mode we will scale things at the bottom in a resize event
// For more options, also see the Layout class which is made to scale things in FULL mode
// but this app just had a simple top and bottom bar
// We have not scaled to content... but might want to for smaller mobile
// but the circles are not that big so it fits well on all devices
const frame = new Frame({
scaling:FULL,
color:dark,
assets:{font:"reuben", src:"Reuben.otf"},
path:"https://assets.codepen.io/2104200/"
});
frame.on("ready", () => {
const stage = frame.stage;
let stageW = frame.width;
let stageH = frame.height;
// ~~~~~~~~~~~~~~~~ TOP BAR ~~~~~~~~~~
// here is the top bar - we will scale the backing in the resize event at the bottom
const topBar = new Container(stageW, 70).addTo();
topBar.backing = new Rectangle(stageW, 70, darker).addTo(topBar).sha(null,0,5,15)
frame.makeIcon(null,darker).scaleTo(topBar,null,80).pos(30,0,LEFT,CENTER,topBar).tap(() => {
zgo("https://zimjs.com", "_blank")
});
const updates = new Button({
label:"UPDATES",
width:240,
backgroundColor:purple,
rollBackgroundColor:pink,
corner:[40,0,40,0]
}).sca(.4).alp(.7).tap(()=>{zgo("https://zimjs.com/updates.html", "_black");}); // will position in resize
// used place then commented it out
new Label("HISTORY", 20, "reuben", light).loc(76, 41, topBar); //.place();
// ~~~~~~~~~~~~~~~~ HAPPENINGS ~~~~~~~~~~
// We call the events happenings (or circles)
// We are going to spread them along a ZIM Squiggle() using ZIM Beads()
// Each bead will be made by calling the happening function
// Here we prepare the data for the happenings
// The colors will be in a series so they repeat
// we could use an array then use color = colors[i%colors.length]
// but ZIM has a series to make that easier
// every time we call colors() it gets the next one in the series
const colors = series(pink.darken(.3), green.darken(.3), orange.darken(.3));
// We will use real date data for the different versions of ZIM
// month is 0 indexed
const dates = [
new Date(2014,10,24), // one
new Date(2015,11,23), // duo
new Date(2016,3,16), // tri
new Date(2016,8,15), // 4th
new Date(2017,5,5), // vee
new Date(2017,8,9), // six
new Date(2018,1,6), // hep
new Date(2018,6,28), // oct
new Date(2018,8,25), // nio
new Date(2019,1,10), // ten
new Date(2020,4,1), // cat
new Date(2021,6,21), // nft
new Date(2022,3,11), // zim
new Date(2023,5,30), // 014
new Date(2023,10,11), // 015
new Date(2024,0,24), // 016
new Date(2024,9,2) // 017
]
// convert the dates to percents which is what the beads use
const percents = [];
// dates can be converted to seconds from 1970 with the JS getTime()
const firstTime = dates[0].getTime();
const lastTime = dates[dates.length-1].getTime();
const range = lastTime-firstTime;
loop(dates, date=>{
percents.push((date.getTime()-firstTime)/range*100);
});
const names = ["ONE","DUO","TRI","4TH","VEE","SIX","HEP","OCT","NIO","TEN","CAT","NFT","ZIM","014","015","016","017"]
let count = 0;
function happening() {
let circle = new Circle(100,colors(),lighter,5);
STYLE = {align:CENTER, valign:CENTER, color:lighter}
new Label("ZIM\n"+names[count], null, "reuben").centerReg(circle).mov(0,-20);
new Label(dates[count].toLocaleString('en-us',{month:'short', year:'numeric'}), 20).centerReg(circle).mov(0,40)
STYLE = {}
// will turn this on when the happening is selected
circle.highLight = new Circle(circle.radius+20,white).addTo(circle).bot().alp(0)
circle.cur().tap((e) => {
go(null,e.target.beadNum);
});
count++;
return circle; // gets added as a bead
}
// ~~~~~~~~~~~~~~~~ PATH AND BEADS ~~~~~~~~~~
// Here is the path that the Beads will follow
// made at https://zimjs.com/paths
const points = [[34.6,23.4,0,0,-12.8,18.7,12.8,-18.7],[79.9,-10.8,0,0,-24.6,-3,24.6,3],[118.4,15.6,0,0,-21.7,-1,21.7,1],[153,-9.8,0,0,-20.7,4.9,20.7,-4.9],[189.5,5.7,0,0,-17.7,0,17.7,0],[219.1,-27.6,0,0,-16.7,6.9,16.7,-6.9],[258.6,-15.9,0,0,-17.7,0,17.7,0],[289.2,-39.4,0,0,0,0,0,0]];
const path = new Squiggle({points:points, thickness:10})
.transformPoints("scaleX", 12) // scales points
.transformPoints("scaleY", 9)
.approximateBounds()
.sca(3) // scales everything
const beads = new Beads({
path:path,
obj:happening,
count:names.length,
percents:percents,
interactive:false,
clone:false // IMPORTANT - so Beads keep the circle made in function
}).center();
// make a white line follow the path - just for the look!
const line = path.clone().loc(path,null,beads).bot().ord(1);
line.thickness = 2;
line.color = white;
// ~~~~~~~~~~~~~~~~ SWIPER ~~~~~~~~~~
// normally we use drag() to drag things
// but here we use a couple swipers to easily move the happenings around
const swiperX = new Swiper(stage, beads, "x");
const swiperY = new Swiper(stage, beads, "y", null, VERTICAL);
// ~~~~~~~~~~~~~~~~ BOTTOM BAR ~~~~~~~~~~
// prepare the controls for the bottom bar
const left = new Arrow().rot(180).tap(()=>{go(LEFT);});
left.activate(false);
const right = new Arrow().tap(()=>{go(RIGHT);});
const indicator = new Indicator({
width:440,
height:30,
num:dates.length,
interactive:true,
foregroundColor:blue,
selectedIndex:-1
}).change(() => {
go(null,indicator.selectedIndex)
});
STYLE = {valign:CENTER}
const botBar = new Tile([left,indicator,right],3,1,0,0,true);
STYLE = {}
botBar.widthStart = botBar.width;
// ~~~~~~~~~~~~~~~~ GO FUNCTION ~~~~~~~~~~
let lastCircle; // will use to deselect last selected
let animating = false; // do not want to interupt animation
let waiting = null; // will remember if want to go again
// we might be going left or right - or pass in a specific happening
function go(dir, num) {
if (animating) { // do not interupt animation
waiting = dir; // we check this once animation is done
return;
}
waiting = null;
animating = true;
swiperX.enabled = false; // do not swipe while animating
swiperY.enabled = false;
indicator.enabled = false;
let location = getLocation(dir, num); // find out where to animate
beads.animate({
props:location,
ease:"quintInOut",
// make for shorter time with shorter distance, etc.
time:constrain(Math.max(Math.abs(location.x),Math.abs(location.y))/500, 0, 3),
call:() => {
// swipers use damping
// set immediate or they will try and go to last remembered location
swiperX.immediate(beads.x);
swiperX.enabled = true;
swiperY.immediate(beads.y);
swiperY.enabled = true;
indicator.enabled = true;
testEnds(); // set the arrows if past the ends
animating = false;
// here is where we help the user experience
// otherwise, it is a pain to keep waiting for the animation to finish
// to make the next move - this at least seems to help a little
// so the user can keep clicing and it flows quickly from one to the next
if (waiting) go(waiting);
}
});
}
// ~~~~~~~~~~~~~~~~ TESTING ARROWS ~~~~~~~~~~
swiperX.on("swipestop", testEnds);
function testEnds() {
// circles are inside beads which moves around
// so use localToGlobal() on the first and last centers 0,0 for a circle
// and compare to stageW/2 to see if we need to grey out arrows
let point1 = beads.items[0].localToGlobal(0,0);
let point2 = beads.items[beads.items.length-1].localToGlobal(0,0);
left.activate(point1.x < stageW/2-5);
right.activate(point2.x > stageW/2+5);
}
// ~~~~~~~~~~~~~~~~ CALCULATING LOCATIONS ~~~~~~~~~~
function getLocation(dir, num) {
if (num==null) {
if (dir==null) dir = RIGHT;
num = dir==LEFT?beads.items.length-1:0;
// loop through the beads to find the next one to go to
// if going right then it will be the next circle past the center
// if going left then it will be the next circle before the center
let val = beads.beads.loop((bead, i)=>{
// circles are in beads so this locates their center on the stage
let point = bead.localToGlobal(0,0);
// if this is the next in the direction we are going return it to val
// returning a value stops the loop - like break in for loop
// if we wanted to go to the next loop (like continue) we would just return (with no value)
if (dir==RIGHT && point.x > stageW/2+5) return i;
else if (dir==LEFT && point.x < stageW/2-5) return i;
}, dir==LEFT); // if direction is LEFT then loop backwards
if (val===true) { // this is if no circle is found
let point1 = beads.items[0].localToGlobal(0,0);
if (point1.x > stageW/2-5) num = 0;
let point2 = beads.items[beads.items.length-1].localToGlobal(0,0);
if (point2.x < stageW/2+5) num = beads.items.length-1;
} else if (val>=0) num = val;
}
testEnds();
indicator.selectedIndex = num;
// animate out and in a selection
if (lastCircle) lastCircle.highLight.animate({alpha:0},.5);
beads.items[num].highLight.animate({alpha:.2},.5);
lastCircle = beads.items[num]; // remember what is selected for next time
// finally calculate the relative distance to the center
// relative distances are passed in to animate as strings so convert to strings
// usually animation is to an absolute postion (numbers) but here we chose relative
const point = beads.items[num].localToGlobal(0,0);
return {x:String(stageW/2-point.x), y:String(stageH/2-point.y)};
}
topBar.top(); // make top layer
go(null,0); // go to the first happening
// ~~~~~~~~~~~~~~~~ RESIZE ~~~~~~~~~~
frame.on("resize", () => {
stageW = frame.width; // get the new stageW and stageH
stageH = frame.height;
topBar.backing.widthOnly = stageW; // widthOnly not width as width keeps aspect ratio
updates.pos(30,33,RIGHT,TOP);
// here is how we can set min and max sizes but still scale if needed
botBar.width = constrain(stageW*.8, 200, botBar.widthStart)
// keep the bottom bar centered on the bottom
botBar.pos(0,30,CENTER,BOTTOM)
});
stage.update(); // this is needed to show any changes
// DOCS FOR ITEMS USED
// https://zimjs.com/docs.html?item=Frame
// https://zimjs.com/docs.html?item=Container
// https://zimjs.com/docs.html?item=Circle
// https://zimjs.com/docs.html?item=Rectangle
// https://zimjs.com/docs.html?item=Squiggle
// https://zimjs.com/docs.html?item=Label
// https://zimjs.com/docs.html?item=Indicator
// https://zimjs.com/docs.html?item=tap
// https://zimjs.com/docs.html?item=change
// https://zimjs.com/docs.html?item=drag
// https://zimjs.com/docs.html?item=animate
// https://zimjs.com/docs.html?item=loop
// https://zimjs.com/docs.html?item=cur
// https://zimjs.com/docs.html?item=sha
// https://zimjs.com/docs.html?item=pos
// https://zimjs.com/docs.html?item=loc
// https://zimjs.com/docs.html?item=mov
// https://zimjs.com/docs.html?item=top
// https://zimjs.com/docs.html?item=bot
// https://zimjs.com/docs.html?item=ord
// https://zimjs.com/docs.html?item=alp
// https://zimjs.com/docs.html?item=rot
// https://zimjs.com/docs.html?item=sca
// https://zimjs.com/docs.html?item=scaleTo
// https://zimjs.com/docs.html?item=addTo
// https://zimjs.com/docs.html?item=centerReg
// https://zimjs.com/docs.html?item=center
// https://zimjs.com/docs.html?item=place
// https://zimjs.com/docs.html?item=Arrow
// https://zimjs.com/docs.html?item=Tile
// https://zimjs.com/docs.html?item=Beads
// https://zimjs.com/docs.html?item=Swiper
// https://zimjs.com/docs.html?item=constrain
// https://zimjs.com/docs.html?item=series
// https://zimjs.com/docs.html?item=darken
// https://zimjs.com/docs.html?item=transformPoints
// https://zimjs.com/docs.html?item=zgo
// https://zimjs.com/docs.html?item=STYLE
}); // end of ready
Also see: Tab Triggers