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 Skypack, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ES6 import
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.
<h1>Sass Color Contrast Functions</h1>
<p><a href="https://codepen.io/giana/project/full/ZWbGzD" target="_parent">Read the documentation</a>. Note: Colors are randomized, so this page is going to be ugly. Run it in edit mode for new colors or set your own.</p>
<section>
<div class="color-block">
<h2>Settings</h2>
<div class="color color1">#1</div>
<div class="color color2">#2</div>
<br />
<code>Ratio: <span class="ratio"></span> </code>
<code>Balance: <span class="balance"></span></code>
</div>
</section>
<section>
<div class="color-block fix-color">
<h2>fix-color()</h2>
<div class="color">Fix #1</div>
<div class="color">Fix #2</div>
</div>
<div class="color-block fix-contrast">
<h2>fix-contrast()</h2>
<div class="color">Fix both</div>
<div class="color">Fix both</div>
</div>
<div class="color-block best-contrast">
<h2>best-contrast()</h2>
<div class="color">#3</div>
<div class="color">Fix #3</div>
</div>
</section>
<section>
<div class="color-block scale-luminance">
<h2>scale-luminance()</h2>
<div class="color">Match #1<br>with #2</div>
</div>
<div class="color-block check-contrast">
<h2>check-contrast()</h2>
<code class="result"></code>
</div>
<div class="color-block luminance">
<h2>luminance()</h2>
<code class="result">#1, #2</code>
</div>
</section>
<footer><p>Note: Pseudo elements are being used to generate text for this demo. This isn't their purpose, and it can be an <a href="http://tink.uk/accessibility-support-for-css-generated-content/">accessibility concern</a>.</p></footer>
/*
WCAG color contrast formula
https://www.w3.org/TR/2016/NOTE-WCAG20-TECHS-20161007/G18#G18-procedure
This pen uses the non-standard Sass pow() function
https://css-tricks.com/snippets/sass/power-function/
Using it outside of CodePen requires you provide your own pow() function with support for decimals
To generate random colors, we're also using a two-variable random() function includded with compass.
*/
//== Helper functions
@import 'compass';
// Check if value is not a number, eg, NaN or Infinity
@function is-nan($value) {
@return $value / $value != 1;
}
// Constrain number between two values
@function clip($value, $min : 0.0001, $max : 0.9999) {
@return if($value > $max, $max, if($value < $min, $min, $value));
}
// Checks if value is within specified bounds, inclusive
@function in-bounds($value, $min : 0, $max : 1) {
@return if($value >= $min and $value <= $max, true, false);
}
//== Step one: Convert
// Returns an RGB channel processed as XYZ... or partly at least
// See w3.org link for formula
@function xyz($channel) {
$channel: $channel / 255;
@return if($channel <= 0.03928, $channel / 12.92, pow((($channel + 0.055) / 1.055), 2.4));
}
// Reverse of xyz(). Returns XYZ value to RGB channel
// https://en.wikipedia.org/wiki/SRGB
@function srgb($channel) {
@return 255 * if($channel <= 0.0031308, $channel * 12.92, 1.055 * pow($channel, 1/2.4) - 0.055);
}
//== Step two: Measure brightness
// Returns relative luminance of color
// See w3.org link for formula
@function luminance($color) {
$red: xyz(red($color));
$green: xyz(green($color));
$blue: xyz(blue($color));
@return $red * 0.2126 + $green * 0.7152 + $blue * 0.0722;
}
//== Step three: Check contrast
// Checks if two colors pass minimum contrast requirements, option to return ratio instead of true/false
// See w3.org link for formula
@function check-contrast($color1, $color2 : #fff, $min-ratio : 'AA', $return-ratio : false) {
// Accept keywords for ratio
@if($min-ratio == 'AA' or $min-ratio == 'AAALG') { $min-ratio: 4.5; }
@elseif($min-ratio == 'AALG') { $min-ratio: 3; }
@elseif($min-ratio == 'AAA') { $min-ratio: 7; }
// Check brightness of each color
$lum1: luminance($color1);
$lum2: luminance($color2);
// Measure contrast ratio
$ratio: (max($lum1, $lum2) + 0.05) / (min($lum1, $lum2) + 0.05);
// Return ratio if option set
@if($return-ratio) { @return $ratio; }
// Else return boolean
@return if($ratio >= $min-ratio, true, false);
}
//== Step four: Scale luminance and lightness
// Takes color, scales luminance, spits out new color
@function scale-luminance($color, $target-luminance) {
// First, scale the channels by the required amount
$scale: $target-luminance / luminance($color);
// And clip them, so we don't end up dividing by zero... among other things I forget
$red: clip(xyz(red($color))) * $scale;
$green: clip(xyz(green($color))) * $scale;
$blue: clip(xyz(blue($color))) * $scale;
// Sometimes, that's not enough and one channel hits #ff or #00. We'll need to scale the other channels to compensate
$red-passes: in-bounds($red);
$green-passes: in-bounds($green);
$blue-passes: in-bounds($blue);
@if(not $red-passes or not $green-passes or not $blue-passes) {
// First, pick a channel to be a baseline, so the rest can be expressed as ratios
$baseline: min($red, $green, $blue);
// Then set up the variables expressed in terms of the baseline
$r: $red / $baseline;
$g: $green / $baseline;
$b: $blue / $baseline;
// Subtract any channel no longer in bounds
//-- TODO This needs to DRY. how to dry. help
@if(not $red-passes) {
$target-luminance: $target-luminance - 0.2126;
$r: 0;
}
@if(not $green-passes) {
$target-luminance: $target-luminance - 0.7152;
$g: 0;
}
@if (not $blue-passes) {
$target-luminance: $target-luminance - 0.0722;
$b: 0;
}
// Now get the required difference by using the luminance() formula
$x: $target-luminance / ($r * 0.2126 + $g * 0.7152 + $b * 0.0722);
// And multiply the channels by this new per-channel luminance
@if($red-passes) { $red: $r * $x; }
@if($green-passes) { $green: $g * $x; }
@if($blue-passes) { $blue: $b * $x; }
}
// Return the new color
@return rgb(srgb($red), srgb($green), srgb($blue));
}
// Scales lightness by 0.1% while checking contrast ratio. This is just a last-ditch effort to correct rounding errors
@function scale-light($color1, $color2, $min-ratio, $operation, $iterations) {
// Loop this function for however many iterations are passed
@for $n from 1 through $iterations {
// Return color unchanged if it passes contrast check
@if(check-contrast($color1, $color2, $min-ratio)) {
@return $color1;
} @else {
// Otherwise use the built-in lighten() and darken() functions, which change the lightness channel (ie, the L in HSL)
// Our previous scale-luminance() function changes both saturation and lightness
$color1: if($operation == lighten, lighten($color1, 0.1%), darken($color1, 0.1%));
}
}
// Return the best color we've got
@return $color1;
}
//== Step six: Fix colors
// Tries to fix contrast by adjusting $color1
@function fix-color($color1, $color2 : #fff, $min-ratio : 'AA', $iterations : 5) {
// Accept keywords for ratio
@if($min-ratio == 'AA' or $min-ratio == 'AAALG') { $min-ratio: 4.5; }
@elseif($min-ratio == 'AALG') { $min-ratio: 3; }
@elseif($min-ratio == 'AAA') { $min-ratio: 7; }
// If check fails, begin conversion
@if(not check-contrast($color1, $color2, $min-ratio)) {
// First get both luminances and clip so #fff and #000 don't break anything
$lum1: clip(luminance($color1));
$lum2: clip(luminance($color2));
// Defaults we'll set later
$target-luminance: $lum1;
$operation: '';
// If the same luminance is passed, lighten/darken one to make conversion possible
@if($lum1 == $lum2) {
// Darken light colors and lighten dark colors, so we have more room to scale them (eg, we won't hit #fff or #000 before we can fix them)
@if($lum1 > 0.5) {
$color1: darken($color1, 1%);
$lum1: luminance($color1);
} @else {
$color1: lighten($color1, 1%);
$lum1: luminance($color1);
}
}
// Now let's get the target luminance. This basically reverses check-contrast(), so we know what luminance to aim for
@if(max($lum1, $lum2) == $lum1) {
$target-luminance: (($lum2 + 0.05) * $min-ratio - 0.05);
$operation: lighten;
} @else {
$target-luminance: (($lum2 + 0.05) / $min-ratio - 0.05);
$operation: darken;
}
// Skip the whole conversion if we just need #fff or #000
@if($target-luminance >= 1) { @return #fff; }
@elseif ($target-luminance <= 0) { @return #000; }
@else {
// Scale color by calculated difference to arrive at target luminance
$color1: scale-luminance($color1, $target-luminance);
// Try to fix any rounding errors by lightening or darkening
$color1: scale-light($color1, $color2, $min-ratio, $operation, $iterations);
}
}
// Tada
@return $color1;
}
// Tries to fix contrast of both colors by weighted balance (0–100)
// 0 = don't change first color, change second color;
// 100 = change first color, don't change second color
@function fix-contrast($color1, $color2, $min-ratio : 'AA', $balance : 50) {
@if(not check-contrast($color1, $color2, $min-ratio)) {
// Fix colors
$color-fixed-1: fix-color($color1, $color2, $min-ratio);
$color-fixed-2: fix-color($color2, $color1, $min-ratio);
// We're just fixing both colors, then mixing back the original color using the native Sass function. Easy-peasy
$color1: mix($color-fixed-1, $color1, $balance);
$color2: mix($color2, $color-fixed-2, $balance);
// If the current configuration doesn't work, try to fix it
@if (not check-contrast($color1, $color2, $min-ratio)) {
// This happens if, again, we reach #fff or #000 before we want to
@if(not in-bounds(luminance($color-fixed-2), 0.00002, 0.99936)) {
// So we scale the opposite color to compensate
$color1: fix-color($color1, $color2, $min-ratio);
@warn "Your settings didn't work. Modifying first color in an attempt to fix."
}
@if(not in-bounds(luminance($color-fixed-1), 0.00002, 0.99936)) {
$color2: fix-color($color2, $color1, $min-ratio);
@warn "Your settings didn't work. Modifying second color in an attempt to fix."
}
}
}
// Returns a list with both colors, use nth($result, 1) and nth($result, 2) to get colors. See below for example
@return $color1, $color2;
}
// Get the best contrast when given three colors
@function best-contrast($color, $color1, $color2, $ratio1 : 'AA', $ratio2 : $ratio1) {
@if(not check-contrast($color, $color1, $ratio1) or not check-contrast($color, $color2, $ratio2)) {
// First get the luminance of the two static colors
$lum1: luminance(fix-color($color1, $color1, $ratio1));
$lum2: luminance(fix-color($color2, $color2, $ratio2));
// Average the luminance together to get the maximum difference
$average-lum: ($lum1 + $lum2) / 2;
// Then set changing color to this luminance
$color: scale-luminance($color, $average-lum);
// Warn if it fails contrast check
@if(not check-contrast($color, $color1, $ratio1)) {
@warn 'Your color fails to contrast with #{$color1}';
}
@if(not check-contrast($color, $color2, $ratio2)) {
@warn 'Your color fails to contrast with #{$color2}';
}
}
@return $color;
}
//====== Helper functions
@function randomColor() {
$color: hsl(random(0,360), random(0,100), random(0,100));
@return $color;
}
@mixin show-color($color) {
background: $color;
color: if(luminance($color) > 0.55, #000, #fff);
&::after {
content: '#{$color}';
}
}
// All my pens that used compass's random() function are now extremely broken. I've fixed some of them; others remaining bafflingly stubborn
@function rand($min, $max) {
@return floor(random() * ($max - $min) + $min);
}
//====== Put in your own settings here
$ratio: rand(3,7); // A number between 1 and 21
$balance: rand(0, 100); // A number between 0 and 100
// Any valid color
$color1: randomColor();
$color2: scale-luminance(randomColor(), luminance($color1) + 0.1);
$color3: randomColor();
.ratio::after { content: '#{$ratio}'; }
.balance::after { content: '#{$balance}'; }
.color-block .color1 { @include show-color($color1); }
.color-block .color2 { @include show-color($color2); }
.fix-color {
.color:nth-child(2) { @include show-color(fix-color($color1, $color2, $ratio)); }
.color:nth-child(3) { @include show-color(fix-color($color2, $color1, $ratio)); }
}
.fix-contrast {
.color:nth-child(2) { @include show-color(nth(fix-contrast($color1, $color2, $ratio, $balance),1)); }
.color:nth-child(3) { @include show-color(nth(fix-contrast($color1, $color2, $ratio, $balance),2)); }
}
.best-contrast {
.color:nth-child(2) { @include show-color($color3); }
.color:nth-child(3) { @include show-color(best-contrast($color3, $color1, $color2, $ratio, $ratio)); }
}
.scale-luminance {
.color:nth-child(2) { @include show-color(scale-luminance($color1, luminance($color2))); }
}
.check-contrast {
.result::after { content: '#{check-contrast($color1, $color2, $ratio)}' ;}
}
.luminance {
.result::after { content: '#{luminance($color1), luminance($color2)}' ;}
}
//====== Page styling, ignore
body {
padding: 1rem;
margin: 0 auto;
max-width: 900px;
text-align: center;
}
section {
padding-top: 1rem;
padding-bottom: 1rem;
}
.color-block {
display: inline-block;
padding: 0 1rem;
vertical-align: top;
}
.color,
.result {
display: inline-flex;
flex-wrap: wrap;
align-content: center;
align-items: center;
justify-content: center;
background: #eee;
box-sizing: border-box;
border: 1px solid #ddd;
margin: 0.5rem;
position: relative;
height: 100px;
width: 100px;
&::after {
display: block;
font-weight: bolder;
width: 100%;
line-height: 1;
}
}
.color {
box-shadow: inset 0 0 0 8px #fff;
}
.result {
padding: 0.5rem;
}
br { clear: both; }
Also see: Tab Triggers