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.
body
.container
h1
| Plotter Art Month
p.
This is my humble contribution to <a href="https://genuary.art/prompts" target="_blank">Genuary 2024</a>, tailored for a <a href="https://en.wikipedia.org/wiki/Plotter" target="_blank">pen plotter</a> context. I created <strong>one card per day</strong> for a month. There are 31 <strong>"day" cards</strong>, each representing a day's work, and about 10 <strong>"special series" cards</strong>. All of them are linked to a dedicated pen which is tweakable, feel free to explore.
p.
I mostly used <a href="https://github.com/sameer/svg2gcode" target="_blank">svg2gcode</a> for G-code generation. Here is the <a href="https://codepen.io/tfrere/pen/oNOKJwo?editors=0100" target="_blank">boilerplate</a> I used, along with some <a href="https://codepen.io/tfrere/pen/YzbXdBp?editors=0010" target="_blank">helpers</a> I created to manage all the cards. I also used my <a href="https://www.npmjs.com/package/@tfrere/generative-tools" target="_blank">generative-tools</a>, which is a fork of <a href="https://github.com/georgedoescode/generative-utils" target="_blank">georgedoescode generative utils</a>.
If you want to use the boilerplate, here's a <a href="https://codepen.io/tfrere/pen/dyEowzo?editors=0010" target="_blank">2D base example</a> and a <a href="https://codepen.io/tfrere/pen/ExzYKvL" target="_blank">3D base example</a>. <br/><br/>
All pens can also be found into these two collections: <a href="https://codepen.io/collection/QWPOzO" target="_blank">days</a> and <a href="https://codepen.io/collection/dbZkZg" target="_blank">miscs</a>. All the code is under MIT Licence.
h2 Final result ↓
img.main-img(src="https://assets.codepen.io/238916/board-x0.25.jpg")
.mode-toggle-container
button#mode-toggle-button
img#switch-mode-icon(
src="https://assets.codepen.io/238916/switch-mode.svg"
)
svg#grid-icon(viewBox="0 0 24 24", width="24", height="24")
path(
d="M3 3h7v7H3V3zm11 0h7v7h-7V3zm-11 11h7v7H3v-7zm11 0h7v7h-7v-7z"
)
svg#list-icon(
viewBox="0 0 24 24",
width="24",
height="24",
style="display: none"
)
path(d="M3 13h18v-2H3v2zm0 6h18v-2H3v2zM3 7h18V5H3v2z")
#card-container.card-container.grid-view
#list-container.list-container
footer.footer
p.
Made by <a href="https://tfrere.com" target="_blank">Thibaud frere</a>
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
h2 {
margin: 40px 0;
}
body {
display: flex;
flex-direction: column;
align-items: center;
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 20px;
font-family: "Poppins";
margin-bottom: 100px;
margin: 0 50px;
margin-top: 100px;
}
#switch-mode-icon {
position: absolute;
width: 140px;
opacity: 0.3;
right: 60px;
overflow: visible;
object-fit: contain;
top: 40px;
transform: rotate(-3deg);
transition: all 200ms ease;
}
#switch-mode-icon.hidden {
opacity: 0;
}
footer {
margin-top: 100px;
//opacity: 0.1;
margin-bottom: 100px;
}
.container {
max-width: 760px;
margin: 0 auto;
padding: 0 20px;
}
.mode-toggle-container {
width: 100%;
display: flex;
position: fixed;
top: 20px;
right: 20px;
z-index: 999;
justify-content: flex-end;
margin-bottom: 20px;
}
#mode-toggle-button {
padding: 10px 10px;
height: 46px !important;
border: 1px solid grey;
border-radius: 3px;
font-size: 16px;
cursor: pointer;
}
#mode-toggle-button svg {
margin: 0;
padding: 0;
}
.card-container.grid-view {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
width: 100%;
}
.grid-item--full {
grid-column: 1 / -1;
margin-top: 40px;
}
.list-container {
display: none;
width: 100%;
flex-direction: column;
}
.main-img {
width: 100%;
}
.card {
width: 100%;
height: 0;
padding-bottom: calc(100% * 1.47); /* Maintain the 1.47 ratio */
perspective: 1000px;
position: relative;
}
.card-inner {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.6s;
transform-style: preserve-3d;
}
.card:hover .card-inner {
transform: rotateY(180deg);
}
.card-front,
.card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
a {
color: black;
font-weight: 700;
}
.card-front {
display: flex;
align-items: center;
justify-content: center;
}
.card.card--black .card-front {
background-color: black;
}
.card.card--white .card-front {
background-color: white;
}
.card-back {
background-color: white;
color: black;
transform: rotateY(180deg);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 15px;
box-sizing: border-box;
}
.card.card--black .card-back {
background-color: black;
color: white;
}
h1 {
font-size: 4rem;
}
h1 img {
display: inline;
width: 60px;
}
p {
font-size: 1.1rem;
}
.card-back h3 {
margin: 0;
font-weight: 100;
font-size: 3rem;
}
.card-back p {
opacity: 0.3;
}
.card-front img {
width: calc(100% + 4px); /* Increase the width to account for cropping */
height: calc(100% + 4px); /* Increase the height to account for cropping */
clip-path: inset(2px 2px 2px 2px);
object-fit: cover; /* Ensures the image covers the area */
}
.list-container .list-item {
display: flex;
align-items: flex-start;
//margin: 0;
padding: 40px 0;
border-bottom: 1px solid #ddd;
}
.list-container .list-item:first-of-type {
padding-top: 20px;
}
.list-container .list-item__image-container {
width: 33%; /* Ensure the image takes at least 1/3 of the space */
margin-right: 40px;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
display: inline-flex;
clip-path: none;
}
.list-container .list-item img {
width: 100%;
height: auto;
object-fit: contain;
margin: 0;
padding: 0;
}
.list-container .list-item .details {
flex: 1;
position: relative;
}
.list-container .list-item h3 {
margin: 0;
font-size: 1.2rem;
font-weight: 300;
letter-spacing: 0.1rem;
text-transform: uppercase;
}
.list-container .list-item h2 {
margin: 5px 0;
font-weight: 900;
font-size: 1.5rem; /* Make the text larger */
}
.list-container .list-item p span {
margin: 5px 0;
font-weight: 100;
}
.list-container .list-item .keywords {
display: flex;
flex-wrap: wrap;
margin-top: 20px;
}
.list-container .list-item .keywords span {
margin-right: 10px;
border: 1px solid #ddd;
padding: 2px 5px;
border-radius: 3px;
margin-bottom: 3px;
}
@media (max-width: 650px) {
.list-container .list-item {
flex-direction: column;
align-items: center;
text-align: center;
}
.list-container .list-item ul {
list-style-type: none;
}
.list-container .list-item .list-item__image-container {
width: 60%;
border-radius: 10px;
margin: 0 0 20px 0;
}
.list-container .list-item h3 {
position: static;
margin-bottom: 10px;
}
.list-container .list-item .details {
width: 100%;
}
.list-container .list-item .keywords {
justify-content: center;
}
}
ul {
padding-left: 20px;
}
const romanNumerals = [
{
numeral: "I",
title: "Particles. Lots of them.",
url: "https://codepen.io/tfrere/pen/OJYLRRo",
color: "black",
description:
"Generate <b>Perlin noise</b> and identify areas with brightness above 0.4 to create a <b>point cloud</b> with varying density. Distribute points according to the <b>Poisson distribution</b>.<br/><br/><b>Project improvements:</b> Boilerplate update and conduct initial printing tests.",
keywords: ["perlin noise", "point cloud"]
},
{
numeral: "II",
title: "Recursion",
color: "white",
url: "https://codepen.io/tfrere/pen/ExzYpjG",
description:
"Take the largest <b>rectangle</b> (canvas) and randomly divide it horizontally or vertically, recursively up to 4 times. Add a random number of smaller rectangles with decreasing size and randomness.<br/><br/><b>Project improvements:</b> Select final paper and pens.",
keywords: ["rectangle", "canvas"]
},
{
numeral: "III",
title: "Droste effect",
url: "https://codepen.io/tfrere/pen/JjqjGpE",
color: "black",
description:
"Repeat a <b>diamond shape</b> with linear changes in size and <b>rotation</b>. Adjust representation, size, scale, and base angle.<br/><br/><b>Project improvements:</b> Transforming the boilerplate into a well-structured JS class.",
keywords: ["diamond shape", "rotation"]
},
{
numeral: "IV",
title: "Pixels",
url: "https://codepen.io/tfrere/pen/bGyGMrx",
color: "white",
description:
"Create a purely random <b>pixel grid</b> ensuring bi-axial <b>symmetry</b> and using three types of filling density.<br/><br/><b>Project improvements:</b> Add thick borders and finalize the aesthetic definition of a card.",
keywords: ["random pixel grid", "symmetry"]
},
{
numeral: "V",
title: "Vera Molnar",
color: "black",
url: "https://codepen.io/tfrere/pen/dyEPXQy",
description:
"Make a <b>hand-drawn</b> squarish spiral and create a function to transform a path to look hand-drawn, ultimately using <b>rough.js</b>.<br/><br/><b>Project improvements:</b> Establish the first lines and rules of the project.",
keywords: ["hand-drawn", "rough.js"]
},
{
numeral: "VI",
title: "Screensaver",
color: "white",
url: "https://codepen.io/tfrere/pen/MWdYoqG",
description:
"Draw inspiration from the 3D pipes screensaver aesthetic using simple <b>random walkers</b> placed on the <b>inner contour</b> of the canvas, allowing walkers to move randomly in four directions with a slight bias towards the center.<br/><br/><b>Project improvements:</b> Create base templates and improve the boilerplate.",
keywords: ["random walkers", "inner contour"]
},
{
numeral: "VII",
title: "Motion",
color: "black",
url: "https://codepen.io/tfrere/pen/MWdYzqV",
description:
"Attempt to reproduce the <b>rotating snakes</b> optical illusion by filling areas with hatching lines at varying angles. Investigate why the plotter creates zigzags instead of hatches, and find an algorithm to apply hatching patterns within an SVG path.",
keywords: ["rotating snakes", "optical illusion"]
},
{
numeral: "VIII",
title: "Chaotic system",
color: "white",
url: "https://codepen.io/tfrere/pen/VwOLPxO",
description:
"Simulate two inverted pendulums with <b>chaotic movement</b> and implement a 3D path simplification method.<br/><br/><b>Project improvements:</b> Add 3D mode to the boilerplate.",
keywords: ["chaotic movement", "pendulums"]
},
{
numeral: "IX",
title: "ASCII",
color: "black",
url: "https://codepen.io/tfrere/pen/VwOvWWG",
description:
"Begin initial work on plotter and <b>typography</b> using <b>OpenType.js</b> to render typography and create a grid of characters sorted by luminance in a gradient.",
keywords: ["opentype.js", "typography"]
},
{
numeral: "X",
title: "Hexagons",
url: "https://codepen.io/tfrere/pen/vYwKdzG",
color: "white",
description:
"Create a series of hexagons arranged in a <b>circle</b> and overlapping, repeat multiple layers along the <b>Y-axis</b>, and randomize final Y positions between -10 and 10.<br/><br/><b>Project improvements:</b> Explore Matt Deslaurier's boilerplate for further development.",
keywords: ["circle", "y-axis"]
},
{
numeral: "XI",
title: "Anni Albers",
url: "https://codepen.io/tfrere/pen/abrZKRr",
color: "black",
description:
"Create an open <b>labyrinth</b> on the edges, focusing on creating continuous <b>paths</b> for humanization later.<br/><br/><b>Project improvements:</b> Now using svg2gcode from Sameer.",
keywords: ["labyrinth", "paths"]
},
{
numeral: "XII",
title: "Lavalamp",
url: "https://codepen.io/tfrere/pen/pombBgJ",
color: "white",
description:
"Find a way to make an SVG lavalamp shape and implement a <b>ray marching</b> algorithm.",
keywords: ["ray marching", "svg"]
},
{
numeral: "XIII",
title: "Wobble",
url: "https://codepen.io/tfrere/pen/JjqEKaP",
color: "black",
description:
"Create custom noise using <b>sine functions</b> and develop circles based on various sizes of circles using this <b>custom noise</b>.",
keywords: ["custom noise", "sine functions"]
},
{
numeral: "XIV",
title: "1Ko",
url: "https://codepen.io/tfrere/pen/xxNqXWP",
color: "white",
description:
"Generate 1024 <b>random curves</b> and select a <b>visually pleasing</b> result.",
keywords: ["random curves", "visually pleasing"]
},
{
numeral: "XV",
title: "Physics engine",
url: "https://codepen.io/tfrere/pen/vYwxzLK",
color: "black",
description:
"Simulate a <b>ball pit</b> with <b>matter.js</b> ensuring balls do not overlap and distribute as if piled up, then enhance with more shapes. <b>Project improvements:</b> Add an animation loop concept to the boilerplate.",
keywords: ["ball pit", "matter.js"]
},
{
numeral: "XVI",
title: "10000",
url: "https://codepen.io/tfrere/pen/MWdpzjv",
color: "white",
description:
"Distribute 10,000 points in a <b>grid</b> influenced by a <b>sine wave</b>.",
keywords: ["grid", "sine wave"]
},
{
numeral: "XVII",
title: "Islamic art inspired",
url: "https://codepen.io/tfrere/pen/bGyWGYe",
color: "black",
description:
"Create a circular <b>Penrose tiling</b> centered on the <b>canvas</b>.<br/><br/><b>Project improvements:</b> First repair on the pen plotter, a servomotor is dead.",
keywords: ["penrose tiling", "canvas"]
},
{
numeral: "XVIII",
title: "Bauhaus",
url: "https://codepen.io/tfrere/pen/wvbeqpe",
color: "white",
description: "Develop a classic <b>Bauhaus</b> style.",
keywords: ["style", "classic"]
},
{
numeral: "XIX",
title: "Flocking",
url: "https://codepen.io/tfrere/pen/JjqyYqB",
color: "black",
description:
"Simulate a school of fish using a flocking algorithm biased by a <b>flow field</b>, representing them with little oriented arrows.",
keywords: ["school of fish", "flow field"]
},
{
numeral: "XX",
title: "Generative typography",
color: "white",
url: "https://codepen.io/tfrere/pen/OJYxXvd",
description:
"Combine the ASCII card and the card with randomly moving <b>agents</b>. Add continuous movement for smoother lines, dropping random points on the letter contour and letting agents move while avoiding the letter and inner contours.",
keywords: ["agents", "ascii"]
},
{
numeral: "XXI",
title: "Use an unknown library",
url: "https://codepen.io/tfrere/pen/PovOoJB",
color: "black",
description:
"Use <b>ln.js</b> as a replacement for the THREE.js SVGRenderer. Create a simple cube composed of <b>isometric cubes</b>, with many good alternate prints emerging.",
keywords: ["ln.js", "isometric cubes"]
},
{
numeral: "XXII",
title: "Point, line, plane",
url: "https://codepen.io/tfrere/pen/WNBXLPx",
color: "white",
description:
"Use an image input and rasterize it in three passes, illustrating three black density passes with <b>points</b>, <b>lines</b>, and squares (planes).",
keywords: ["points", "lines"]
},
{
numeral: "XXIII",
title: "16x16",
url: "https://codepen.io/tfrere/pen/ExzoLvm",
color: "black",
description:
"Cut a rectangle into 16 parts with <b>Voronoi</b>, place them in a physical simulation and let them settle on a floor. Deflate each polygon 16 times, making them progressively smaller.",
keywords: ["voronoi", "polygons"]
},
{
numeral: "XXIV",
title: "Impossible geometry",
url: "https://codepen.io/tfrere/pen/QWRaMgW",
color: "white",
description:
"Create a generic code for an N-sided <b>Penrose polygon</b> and a Penrose triangle with a gradient based on a progressively dense <b>Poisson distribution</b>.",
keywords: ["penrose polygon", "poisson distribution"]
},
{
numeral: "XXV",
title: "Try to recreate this with code",
url: "https://codepen.io/tfrere/pen/WNBMyWj",
color: "black",
description:
"Recreate a photo of an old stone wall with rounded stones embedded in each other and bound by cement. Use lots of points inside a rectangle then Voronoi cutting, finding an algorithm to make round corners to any polygon. Clip all the shapes inside a rectangle and fill the final shape with hatches.",
keywords: ["stone wall", "voronoi"]
},
{
numeral: "XXVI",
title: "Grow a seed",
url: "https://codepen.io/tfrere/pen/OJYQMNo",
color: "white",
description:
"Implement an <b>L-system</b>, creating leaves on terminal branches and developing a visually pleasing result despite challenges.",
keywords: ["l-system", "leaves"]
},
{
numeral: "XXVII",
title: "Code for one hour",
url: "https://codepen.io/tfrere/pen/NWVyYLM",
color: "black",
description:
"Load an image and place more points where the black intensity is greater. Use <b>Voronoi</b> relaxation on X frames for aesthetic enhancement.",
keywords: ["voronoi", "image"]
},
{
numeral: "XXVIII",
title: "Neumorphism",
url: "https://codepen.io/tfrere/pen/JjqLYKV",
color: "white",
description:
"Simulate UI elements while remaining visually appealing with a <b>neumorphic style</b>. Make 3 primitives, then shadow them on a canvas. Take the final image to distribute points based on the grayscale value.",
keywords: ["ui elements", "grayscale"]
},
{
numeral: "XXIX",
title: "Signed distance functions",
url: "https://codepen.io/tfrere/pen/pomLmBe",
color: "black",
description:
"Create two echoes of <b>circles</b> in the top left and bottom right using signed distance functions on a canvas, rendered using a grid of <b>pixels</b> with two different shapes, cross and squares.",
keywords: ["circles", "pixels"]
},
{
numeral: "XXX",
title: "Shader",
url: "https://codepen.io/tfrere/pen/mdYKPxG",
color: "white",
description:
"Develop a kaleidoscope <b>shader</b>, draw some random black squares, raster image to <b>SVG</b> path, and fill the path.",
keywords: ["kaleidoscope", "svg"]
},
{
numeral: "XXXI",
title: "Generative music",
url: "https://codepen.io/tfrere/pen/pommNMb",
color: "black",
description:
"Use 12 channels of a playing MP3 to influence successive <b>circles</b>, ensuring each circle is smaller than the previous with increasing musical impact. Remove the first two circles at the end due to excessive deformation.",
keywords: ["mp3", "circles"]
}
];
const specialCards = [
{
numeral: "I",
title: "Cycloid",
url: "https://codepen.io/tfrere/pen/BaedrQM",
color: "white",
description: "Implement a <b>cycloid</b>.",
keywords: ["mathematics", "curve"]
},
{
numeral: "II",
title: "Lines",
url: "https://codepen.io/tfrere/pen/vYwJNRQ",
color: "black",
description: "Implement a line influenced by a <b>sinusoid</b>.",
keywords: ["sinusoid", "influence"]
},
{
numeral: "III",
title: "Fillpath",
url: "https://codepen.io/tfrere/pen/MWdVwGo",
color: "white",
description: "Find a way to fill a <b>path</b>.",
keywords: ["path", "fill"]
},
{
numeral: "IV",
title: "Quad tree",
url: "https://codepen.io/tfrere/pen/ExzmMNb",
color: "black",
description: "Experiment with a <b>quad tree</b> data structure.",
keywords: ["data structure", "experiment"]
},
{
numeral: "V",
title: "Moire",
url: "https://codepen.io/tfrere/pen/VwOMzJZ",
color: "white",
description: "Implement a basic <b>moire pattern</b> concept.",
keywords: ["pattern", "visual"]
},
{
numeral: "VI",
title: "Phyllotaxis",
url: "https://codepen.io/tfrere/pen/WNBXpOQ",
color: "black",
description: "Implement a basic <b>phyllotaxis</b> algorithm.",
keywords: ["algorithm", "nature"]
},
{
numeral: "VII",
title: "Flowfield",
url: "https://codepen.io/tfrere/pen/JjqrMVx",
color: "white",
description: "Represent a basic <b>flowfield</b>.",
keywords: ["representation", "vector"]
},
{
numeral: "VIII",
title: "Ln boolean",
url: "https://codepen.io/tfrere/pen/ExzbNbM",
color: "black",
description:
"Learn to use <b>ln.js</b> and apply a <b>boolean operator</b>.",
keywords: ["ln.js", "boolean operator"]
},
{
numeral: "IX",
title: "Sun",
url: "https://codepen.io/tfrere/pen/OJYzQBb",
color: "white",
description: "Experiment with creating a <b>sunset</b>.",
keywords: ["sunset", "experiment"]
},
{
numeral: "X",
title: "Deflate",
url: "https://codepen.io/tfrere/pen/gOJXvQm",
color: "black",
description: "Find a way to deflate <b>polygons</b>.",
keywords: ["polygons", "geometry"]
},
{
numeral: "XI",
title: "Spirograph",
url: "https://codepen.io/tfrere/pen/zYQRvwQ",
color: "white",
description: "Implement a basic <b>spirograph</b> concept.",
keywords: ["pattern", "drawing"]
}
];
const dayToDayTitleText = "Day to day ↓";
const specialEditionTitleText = "Special edition ↓";
const cardContainer = document.getElementById("card-container");
const listContainer = document.getElementById("list-container");
const modeToggleButton = document.getElementById("mode-toggle-button");
const gridIcon = document.getElementById("grid-icon");
const listIcon = document.getElementById("list-icon");
const switchModeIcon = document.getElementById("switch-mode-icon");
let isGridView = true;
let hasClickedToggleButton = false;
const createCard = (data, index, isSpecial = false) => {
const card = document.createElement("div");
card.classList.add(
"card",
data.color === "black" ? "card--black" : "card--white"
);
const cardInner = document.createElement("div");
cardInner.classList.add("card-inner");
const cardFront = document.createElement("div");
cardFront.classList.add("card-front");
//cardFront.style.backgroundColor = index % 2 === 0 ? "black" : "white";
const svg = document.createElement("img");
svg.src = `https://assets.codepen.io/238916/plotter-${
isSpecial ? "special-edition-" : ""
}${data.numeral}.svg`;
svg.alt = data.numeral;
cardFront.appendChild(svg);
const cardBack = document.createElement("div");
cardBack.classList.add("card-back");
const title = document.createElement("h3");
title.textContent = data.numeral;
const text = document.createElement("p");
text.textContent = data.title;
cardBack.appendChild(title);
cardBack.appendChild(text);
cardInner.appendChild(cardFront);
cardInner.appendChild(cardBack);
card.appendChild(cardInner);
card.addEventListener("click", () => window.open(data.url, "_blank"));
card.style.cursor = "pointer";
return card;
};
const createListItem = (data, isSpecial = false) => {
const listItem = document.createElement("div");
listItem.classList.add("list-item");
const imageContainer = document.createElement("div");
imageContainer.classList.add("list-item__image-container");
const img = document.createElement("img");
img.src = `https://assets.codepen.io/238916/plotter-${
isSpecial ? "special-edition-" : ""
}${data.numeral}.svg`;
img.alt = data.title;
imageContainer.appendChild(img);
const details = document.createElement("div");
details.classList.add("details");
const title = document.createElement("h3");
title.textContent = isSpecial
? `SPECIAL ${data.numeral}`
: `Day ${data.numeral}`;
const text = document.createElement("h2");
text.textContent = data.title;
const description = document.createElement("p");
description.innerHTML = data.description;
const keywordsTitle = document.createElement("h4");
keywordsTitle.textContent = "Keywords";
const link = document.createElement("a");
link.href = data.url;
link.textContent = "Go to pen";
const keywordsList = document.createElement("div");
keywordsList.classList.add("keywords");
data.keywords.forEach((keyword) => {
const span = document.createElement("span");
span.textContent = keyword;
keywordsList.appendChild(span);
});
details.appendChild(title);
details.appendChild(text);
details.appendChild(description);
details.appendChild(keywordsList);
listItem.appendChild(imageContainer);
listItem.appendChild(details);
return listItem;
};
const renderGridView = () => {
cardContainer.innerHTML = "";
const normalTitle = document.createElement("h2");
normalTitle.classList.add("grid-item--full");
normalTitle.textContent = dayToDayTitleText;
cardContainer.appendChild(normalTitle);
romanNumerals.forEach((data, index) => {
cardContainer.appendChild(createCard(data, index));
});
const specialTitle = document.createElement("h2");
specialTitle.classList.add("grid-item--full");
specialTitle.textContent = specialEditionTitleText;
cardContainer.appendChild(specialTitle);
specialCards.forEach((data, index) => {
cardContainer.appendChild(createCard(data, index, true));
});
};
const renderListView = () => {
listContainer.innerHTML = "";
const normalTitle = document.createElement("h2");
normalTitle.textContent = dayToDayTitleText;
listContainer.appendChild(normalTitle);
romanNumerals.forEach((data) => {
listContainer.appendChild(createListItem(data));
});
const specialTitle = document.createElement("h2");
specialTitle.textContent = specialEditionTitleText;
listContainer.appendChild(specialTitle);
specialCards.forEach((data) => {
listContainer.appendChild(createListItem(data, true));
});
};
const toggleViewMode = () => {
isGridView = !isGridView;
if (isGridView) {
gridIcon.style.display = "block";
listIcon.style.display = "none";
listContainer.style.display = "none";
cardContainer.style.display = "grid";
renderGridView();
} else {
gridIcon.style.display = "none";
listIcon.style.display = "block";
cardContainer.style.display = "none";
listContainer.style.display = "flex";
renderListView();
}
};
modeToggleButton.addEventListener("click", () => {
toggleViewMode();
hasClickedToggleButton = true;
switchModeIcon.classList.add("hidden");
});
const handleScroll = () => {
if (!hasClickedToggleButton) {
switchModeIcon.classList.toggle("hidden", window.scrollY > 50);
}
};
window.addEventListener("scroll", handleScroll);
// Initial render
toggleViewMode();
toggleViewMode();
renderGridView();
renderListView();
Also see: Tab Triggers