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>
<h2 id="choose_a_color">1. Choose a base hue</h2>
<color_wheel>
<swatch class="red" data-color_index="1"></swatch>
<swatch class="red-orange" data-color_index="2"></swatch>
<swatch class="orange" data-color_index="3"></swatch>
<swatch class="orange-yellow" data-color_index="4"></swatch>
<swatch class="yellow" data-color_index="5"></swatch>
<swatch class="yellow-green" data-color_index="6"></swatch>
<swatch class="green" data-color_index="7"></swatch>
<swatch class="green-blue" data-color_index="8"></swatch>
<swatch class="blue" data-color_index="9"></swatch>
<swatch class="blue-purple" data-color_index="10"></swatch>
<swatch class="purple" data-color_index="11"></swatch>
<swatch class="purple-red" data-color_index="12"></swatch>
</color_wheel>
</div>
<div>
<h2>2. Choose a color scheme</h2>
<color_schemes>
<input type="radio" name="color_scheme" id="triadic" data-color_scheme="triadic"><label for="triadic">Triadic</label><br>
<input type="radio" name="color_scheme" id="complementary" data-color_scheme="complementary"><label for="complementary">Complementary</label><br>
<input type="radio" name="color_scheme" id="split-complementary" data-color_scheme="split-complementary"><label for="split-complementary">Split-complementary</label><br>
<input type="radio" name="color_scheme" id="analogous" data-color_scheme="analogous"><label for="analogous">Analogous</label><br>
</color_schemes>
</div>
<div>
<h2>3. Take a screenshot</h2>
<palettes>
<palette id="base_color"></palette>
<palette id="alt_1"></palette>
<palette id="alt_2"></palette>
</palettes>
</div>
// thanks for this mixin whoever wrote it, I'll try to add credit later
@mixin on-circle($item-count, $circle-size, $item-size) {
position: relative;
width: $circle-size;
height: $circle-size;
padding: 0;
border-radius: 50%;
list-style: none;
> * {
display: block;
position: absolute;
top: 50%;
left: 50%;
width: $item-size;
height: $item-size;
margin: -($item-size / 2);
$angle: (360 / $item-count);
$rot: 0;
@for $i from 1 through $item-count {
&:nth-of-type(#{$i}) {
transform: rotate($rot * 1deg) translate($circle-size / 2)
rotate($rot * -1deg);
}
$rot: $rot + $angle;
}
}
transform: rotate(-90deg)
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100vh;
}
body {
display: flex;
font-family:sans-serif;
justify-content: center;
background:#999;
color:#555;
> div {
margin: 1em;
flex:1;
display: flex;
flex-direction: column;
align-items: center;
background:#fff;
padding:1rem;
border-radius:.5rem;
box-shadow:0 0 5px rgba(0,0,0,0.5);
}
}
color_wheel {
@include on-circle($item-count: 12, $circle-size: 8rem, $item-size: 2rem);
}
color_wheel swatch {
cursor: pointer;
border-radius: 100%;
display: block;
}
.red {
background: #ff0000;
}
.red-orange {
background: #ff5300;
}
.orange {
background: #ffa500;
}
.orange-yellow {
background: #ffd200;
}
.yellow {
background: #ffff00;
}
.yellow-green {
background: #80ff00;
}
.green {
background: #0f0;
}
.green-blue {
background: #008080;
}
.blue {
background: #00f;
}
.blue-purple {
background: #4000c0;
}
.purple {
background: #800080;
}
.purple-red {
background: #c00040;
}
h2 {
font-weight:normal;
margin-bottom: 1em;
}
color_schemes {
// margin: 1rem;
}
palettes {
display:flex;
flex-direction: column;
}
palette {
display: grid;
grid-template-columns: repeat(13, auto);
grid-auto-flow: row;
background:#999;
}
palette swatch {
background: #555;
width: 1em;
height: 1em;
display: block;
}
label {
margin-left: .5em;
font-size:1.2rem;
}
.sample_swatch {
width:50px;
height:50px;
display:inline-block;
background:red;
}
.dim {
opacity:.3;
}
.callout {
animation-name: callout;
animation-duration: 1s;
}
@keyframes callout {
from {opacity: .25;}
to {opacity: 1;}
}
// set global variables
var color_index = new Array();
var color_scheme;
var number_steps;
// if they click on a color wheel swatch to choose a base color
$("color_wheel swatch").click(function() {
// get the color index from the swatch and store as the base color
base_color = $(this).data("color_index");
// if a color scheme has been chosen and a color wheel swatch has been clicked
if (color_scheme && color_index.length > 0) {
// update the color index with the new values
update_color_index(base_color);
}
// if the color scheme has been chosen, but a base color has not been clicked yet
else if (color_scheme) {
// add the color index to the color_index array
color_index.push(base_color);
// update the color_index array with the new values
update_color_index(base_color);
}
// otherwise, the color wheel is being clicked for the first time
else {
// add the color index to the color_index array, using shift in case they are clicking more than one color
color_index.shift(base_color);
}
// update the color wheel with the values stored in the color_index array
update_color_wheel(base_color);
});
// if they click on a color scheme option
$("color_schemes input").click(function() {
// get the color scheme from data-color_scheme attribute and store it in the color_scheme var
color_scheme = $(this).data("color_scheme");
// if they click a color scheme before they've chosen a base color. the color_index array will be empty
if (color_index.length == 0) {
// prompt them to choose a base color
$('#choose_a_color').addClass("callout");
} else {
// remove any swatches currently in the palette
$("palette").html("");
// update the color_index array based the new first color in the color_index array
update_color_index(color_index[0]);
}
});
function update_color_index(base_color) {
// remove all values except the first one; the base_color
color_index.length = 1;
// populate the remaining colors based on the chosen color scheme
switch (color_scheme) {
// need the color indexes to always be from 1 to 12 so this is a bit tricky
case "triadic":
// triadic needs to be 1/3 of the way around the wheel
if (base_color < 5) {
color_index.push(base_color + 4, base_color + 8);
} else if (base_color < 9) {
color_index.push(base_color + 4, base_color - 4);
} else {
color_index.push(base_color - 4, base_color - 8);
}
// store the background colors as css. didn't know this was in RGB format
alt_rgb1 = $(
"color_wheel swatch:nth-of-type(" + color_index[1] + ")"
).css("background-color");
// convert the RGB to HSL so the tint/tone levels can be easily created by dividing the L value
alt_hsl1 = RGBToHSL(alt_rgb1);
// generate the tint/tone levels alt color 1
generate_steps(alt_hsl1, "alt_1");
alt_rgb2 = $(
"color_wheel swatch:nth-of-type(" + color_index[2] + ")"
).css("background-color");
alt_hsl2 = RGBToHSL(alt_rgb2);
// generate the tint/tone levels alt color 1
generate_steps(alt_hsl2, "alt_2");
break;
case "complementary":
// repeat for the other color schemes
if (base_color >= 7) {
color_index.push(base_color - 6);
} else {
color_index.push(base_color + 6);
}
alt_rgb1 = $(
"color_wheel swatch:nth-of-type(" + color_index[1] + ")"
).css("background-color");
alt_hsl1 = RGBToHSL(alt_rgb1);
generate_steps(alt_hsl1, "alt_1");
break;
case "split-complementary":
if (base_color < 6) {
color_index.push(base_color + 7, base_color + 5);
} else if (base_color > 7) {
color_index.push(base_color - 7, base_color - 5);
} else {
color_index.push(base_color + 5, base_color - 5);
}
alt_rgb1 = $(
"color_wheel swatch:nth-of-type(" + color_index[1] + ")"
).css("background-color");
alt_hsl1 = RGBToHSL(alt_rgb1);
generate_steps(alt_hsl1, "alt_1");
alt_rgb2 = $(
"color_wheel swatch:nth-of-type(" + color_index[2] + ")"
).css("background-color");
alt_hsl2 = RGBToHSL(alt_rgb2);
generate_steps(alt_hsl2, "alt_2");
break;
case "analogous":
if (base_color == 1) {
color_index.push(12, 2);
} else if (base_color == 12) {
color_index.push(11, 1);
} else {
color_index.push(base_color - 1, base_color + 1);
}
alt_rgb1 = $(
"color_wheel swatch:nth-of-type(" + color_index[1] + ")"
).css("background-color");
alt_hsl1 = RGBToHSL(alt_rgb1);
generate_steps(alt_hsl1, "alt_1");
alt_rgb2 = $(
"color_wheel swatch:nth-of-type(" + color_index[2] + ")"
).css("background-color");
alt_hsl2 = RGBToHSL(alt_rgb2);
generate_steps(alt_hsl2, "alt_2");
break;
}
// reset the color index to contain only the base color. need to do this in case they pick a different swatch
update_color_wheel(color_index[0]);
}
function update_color_wheel(base_color) {
// if they have chosen a scheme
if (color_scheme !== "undefined") {
// need to remove the old base color but keep the length of the array > 1 so it doesn't f up... this needs better explanaition I know. try comenting the next line and test and you'll see. sometimes extra colors get left undimmed in the wheel
color_index.shift();
// add the new base color index
color_index.unshift(base_color);
} else {
// if there's no color scheme yet; i.e., they haven't chosen one, add the base color
color_index.push(base_color);
}
// dim all the color wheel swatches
$("color_wheel swatch").addClass("dim");
// get the RGB value of the color wheel swatch at the first position in the index
base_rgb = $("color_wheel swatch:nth-of-type(" + color_index[0] + ")").css(
"background-color"
);
// convert it to hsl for easy stepping
base_hsl = RGBToHSL(base_rgb);
// generate the steps
generate_steps(base_hsl, "base_color");
// undim the swatches at the locations stored in the color_index
for (var i = 0; i < color_index.length; i++) {
$("color_wheel swatch:nth-of-type(" + color_index[i] + ")").removeClass(
"dim"
);
}
}
// generate the palette swatches
function generate_steps(hsl, palette_id) {
// clear the palette of any existing swatches
$("palette#" + palette_id).html("");
// get the h value from the hsl passed to the function
console.log(hsl);
var h = hsl.split("(").pop().split(",")[0];
// repeat for s. turn the percentage into an integer
var s_string = hsl.split("(").pop().split(",")[1];
s = s_string.slice(0, -1);
// s_rounded = Math.round(parseFloat(s));
console.log("s = " + s);
var s_step = Math.round(s/12);
console.log("s_step = " + s_step);
// repeat for l
// var l = hsl.substring(hsl.lastIndexOf(",") + 1, hsl.lastIndexOf("%"));
var l_string = hsl.split("(").pop().split(",")[2];
l = l_string.slice(0, -2);
// round it
var l_rounded = Math.round(parseFloat(l));
console.log("l_rouded is " + l_rounded);
// create an array for the lightness levels
var lightness = new Array();
// create array for s levels
// create steps for tint and tone
var tone_step = Math.round(l_rounded / 6);
console.log("tone_step is " + tone_step);
var tint_step = Math.round((100 - l_rounded) / 6);
console.log("tint_step is " + tint_step);
var l_step = 0;
// generate the palette swatches. Each color should have 120 colors
for (var i = 0; i < 169; i++) {
var $newSwatch = "<swatch></swatch>";
// add the swatches to the palette id that was passed in the function
$("palette#" + palette_id + "").append($newSwatch);
}
// need to add the original hsl for the middle step. there's always gonna be an odd number of palette swatches for this reason
// tone_level = hsl;
// lightness.push(tone_level);
// generate tints
// generate tones
for (var i = 0; i < 6; i++) {
// reset to full saturation
s = 100;
tone_level = "hsl(" + h + "," + s + "%," + l_step + "%)";
lightness.push(tone_level);
//generate s levels
for (var ii = 0; ii < 12; ii++) {
sat_level = "hsl(" + h + "," + s + "%," + l_step + "%)";
lightness.push(sat_level);
s -= s_step;
}
l_step += tone_step;
// reset to full saturation for the next row
}
// create 6 rows of tint to saturation
// reset lightness
l_step = l_rounded;
for (var i = 0; i < 7; i++) {
s = 100;
l_step += tint_step;
tint_level = "hsl(" + h + "," + s + "%," + l_step + "%)";
lightness.push(tint_level);
// create 12 additional steps
for (var ii = 0; ii < 12; ii++) {
sat_level = "hsl(" + h + "," + s + "%," + l_step + "%)";
lightness.push(sat_level);
s -= s_step;
}
}
// generate core strip?
console.log(lightness);
// lightness.reverse();
// set the background colors of the palette swatches with the ones stored in the lightness levels array
for (var i = 0; i < lightness.length; i++) {
var ntarget = i + 1;
$("palette#" + palette_id + " swatch:nth-of-type(" + ntarget + ")").css(
"background-color",
lightness[i]
);
}
}
// not mine-from css-tricks, add reference
function RGBToHSL(rgb) {
let sep = rgb.indexOf(",") > -1 ? "," : " ";
rgb = rgb
.substr(4)
.split(")")[0]
.split(sep);
for (let R in rgb) {
let r = rgb[R];
if (r.indexOf("%") > -1)
rgb[R] = Math.round(r.substr(0, r.length - 1) / 100 * 255);
}
let r = rgb[0] / 255,
g = rgb[1] / 255,
b = rgb[2] / 255;
let cmin = Math.min(r, g, b),
cmax = Math.max(r, g, b),
delta = cmax - cmin,
h = 0,
s = 0,
l = 0;
if (delta === 0) h = 0;
else if (cmax == r) h = ((g - b) / delta) % 6;
else if (cmax == g) h = (b - r) / delta + 2;
else h = (r - g) / delta + 4;
h = Math.round(h * 60);
if (h < 0) h += 360;
l = (cmax + cmin) / 2;
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
return "hsl(" + h + "," + s + "%," + l + "%)";
}
Also see: Tab Triggers