Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

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.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

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.

+ add another resource

Packages

Add Packages

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.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                div
  label(class="label label--grade") Grade
  span(id="grade") 0
div 
  label(class="label label--ascender") Ascender Height
  span(id="ascender") 750
div 
  label(class="label label--slant") Slant
  span(id="slant") 0
div 
  label(class="label label--weight") Weight
  span(id="weight") 400
div
  h1 The five boxing wizards jump quickly.
div 
  label(class="label label--width") Width
  span(id="width") 100
div 
  label(class="label label--lowercase-height") Lowercase Height
  span(id="lowercaseHeight") 500
div 
  label(class="label label--descender") Descender Depth
  span(id="descender") -250
div 
  label(class="label label--thin-stroke") Thin Stroke
  span(id="thinStroke") 116
              
            
!

CSS

              
                :root {
  --font-ascender: 750;
  --font-descender: -250;
  --font-grade: 0;
  --font-lowercase-height: 500;
  --font-slant: 0;
  --font-thin-stroke: 116;
  --font-weight: 400;
  --font-width: 100;
}

body {
  font-family: "Roboto Flex";
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  font-size: 16px;
  align-items: flex-start;
  overflow: hidden;
  margin: 0;
  padding: 2em;
  box-sizing: border-box;
  height: 100vh;
  width: 100vw;
  background-color: red;
  color: white;
  cursor: crosshair;
  h1 {
    font-size: 72px; 
    line-height: 1;
    text-align: center;
    font-variation-settings: 
      'YTAS' var(--font-ascender),
      'YTDE' var(--font-descender),
      'GRAD' var(--font-grade),
      'YTLC' var(--font-lowercase-height),
      'slnt' var(--font-slant),
      'YOPQ' var(--font-thin-stroke),
      'wght' var(--font-weight), 
      'wdth' var(--font-width);
  }
  div {
    display: flex;
    flex-direction: column;
    height: 100%;
    // Second column
    &:nth-child(3n+2) {
      align-items: center;
    }
    // Third column
    &:nth-child(3n+3) {
      align-items: flex-end;
    }
    // Second row
    &:nth-child(n+4) {
      justify-content: center;
    }
    // Third row
    &:nth-child(n+7) {
      justify-content: flex-end;
    }
  }
  .label{
    cursor: crosshair;
    &--ascender {
      font-variation-settings: 'YTAS' var(--font-ascender);
    }
    &--descender {
      font-variation-settings: 'YTDE' var(--font-descender);
    }
    &--grade {
      font-variation-settings: 'GRAD' var(--font-grade);
    }
    &--lowercase-height {
      font-variation-settings: 'YTLC' var(--font-lowercase-height);
    }
    &--slant {
      font-variation-settings: 'slnt' var(--font-slant);
    }
    &--thin-stroke {
      font-variation-settings: 'YOPQ' var(--font-thin-stroke);
    }
    &--weight {
      font-variation-settings: 'wght' var(--font-weight);
    }
    &--width {
      font-variation-settings: 'wdth' var(--font-width);
    }
  }
  span {
      font-variation-settings: 'wght' 700;
      cursor: crosshair;
  }
}
              
            
!

JS

              
                const root = document.documentElement;

// Set values associated to each font axis
const ascender = getComputedStyle(root).getPropertyValue("--font-ascender");
const ascenderLabel = document.getElementById('ascender');
const ascenderMin = 649;
const ascenderMax = 854;
const ascenderDiff = ascenderMax - ascenderMin;

const descender = getComputedStyle(root).getPropertyValue("--font-descender");
const descenderLabel = document.getElementById('descender');
const descenderMin = -305;
const descenderMax = -98;
const descenderDiff = descenderMax - descenderMin;

const grade = getComputedStyle(root).getPropertyValue("--font-grade");
const gradeLabel = document.getElementById('grade');
const gradeMin = -200;
const gradeMax = 150;
const gradeDiff = gradeMax - gradeMin;

const lowercaseHeight = getComputedStyle(root).getPropertyValue("--font-lowercase-height");
const lowercaseHeightLabel = document.getElementById('lowercaseHeight');
const lowercaseHeightMin = 416;
const lowercaseHeightMax = 570;
const lowercaseHeightDiff = lowercaseHeightMax - lowercaseHeightMin;

const thinStroke = getComputedStyle(root).getPropertyValue("--font-thin-stroke");
const thinStrokeLabel = document.getElementById('thinStroke');
const thinStrokeMin = 25;
const thinStrokeMax = 135;
const thinStrokeDiff = thinStrokeMax - thinStrokeMin;

const slant = getComputedStyle(root).getPropertyValue("--font-slant");
const slantLabel = document.getElementById('slant');
const slantMin = -10;
const slantMax = 0;
const slantDiff = slantMax - slantMin;

const weight = getComputedStyle(root).getPropertyValue("--font-weight");
const weightLabel = document.getElementById('weight');
const weightMin = 100;
const weightMax = 1000;
const weightDiff = weightMax - weightMin;

const width = getComputedStyle(root).getPropertyValue("--font-width");
const widthLabel = document.getElementById('width');
const widthMin = 25;
const widthMax = 151;
const widthDiff = widthMax - widthMin;


// Get the current viewport dimensions
function getViewport() {
  let width = root.clientWidth;
  let height = root.clientHeight;
  let widthHalf = width / 2;
  let heightHalf = height / 2;
  let diagonal = Math.hypot(widthHalf, heightHalf);  
  // Calulate the per-pixel increment for each font axis
  // This works by dividing the amount of possible values for each axis by the size of half the viewport for the direction the axis is bound to
  let ascenderPixels = heightHalf / ascenderDiff;
  let descenderPixels = heightHalf / descenderDiff;
  let gradePixels = diagonal / gradeDiff;
  let lowercaseHeightPixels = diagonal / lowercaseHeightDiff;
  let slantPixels = diagonal / slantDiff;
  let thinStrokePixels = diagonal / thinStrokeDiff;
  let weightPixels = widthHalf / weightDiff;
  let widthPixels = widthHalf / widthDiff;
  return {width, height, weightPixels, widthPixels, ascenderPixels, descenderPixels, slantPixels, thinStrokePixels, gradePixels, lowercaseHeightPixels}
}

// Update values 
function updateDisplay(event) {
  const client = getViewport();
  //  Calculate cursor distance...
  let topOffset = event.pageY; // from top
  let leftOffset = event.pageX; // from left
  let rightOffset = client.width - event.pageX; // from right
  let bottomOffset = client.height - event.pageY; // from bottom
  let topLeftOffset = Math.hypot(topOffset, leftOffset); // from top left
  let topRightOffset = Math.hypot(topOffset, rightOffset); // from top right
  let bottomLeftOffset = Math.hypot(bottomOffset, leftOffset); //from bottom left
  let bottomRightOffset = Math.hypot(bottomOffset, rightOffset); // from bottom right
  
  // Calculate the font axis values for the distance from edge
  let ascenderCalc = (topOffset / client.ascenderPixels) + ascenderMin;
  let descenderCalc = (bottomOffset / client.descenderPixels) + descenderMin;
  let gradeCalc = (topLeftOffset / client.gradePixels) + gradeMin;
  let lowercaseHeightCalc = (bottomLeftOffset / client.lowercaseHeightPixels) + lowercaseHeightMin;
  let slantCalc = (topRightOffset / client.slantPixels) + slantMin;
  let thinStrokeCalc = (bottomRightOffset / client.thinStrokePixels) + thinStrokeMin;
  let weightCalc = (leftOffset / client.weightPixels) + weightMin;
  let widthCalc = (rightOffset / client.widthPixels) + widthMin;
  
  // Round values to nearest whole number, prevent from going past min and max
  let ascenderTotal =  Math.round(Math.min(Math.max(ascenderCalc, ascenderMin), ascenderMax));
  let descenderTotal =  Math.round(Math.min(Math.max(descenderCalc, descenderMin), descenderMax));
  let gradeTotal = Math.round(Math.min(Math.max(gradeCalc, gradeMin), gradeMax));
  let lowercaseHeightTotal = Math.round(Math.min(Math.max(lowercaseHeightCalc, lowercaseHeightMin), lowercaseHeightMax));
  let slantTotal =  Math.round(Math.min(Math.max(slantCalc, slantMin), slantMax));
  let thinStrokeTotal =  Math.round(Math.min(Math.max(thinStrokeCalc, thinStrokeMin), thinStrokeMax));
  let weightTotal = Math.round(Math.min(Math.max(weightCalc, weightMin), weightMax));
  let widthTotal = Math.round(Math.min(Math.max(widthCalc, widthMin), widthMax));

  // Set CSS variables for each font axes
  root.style.setProperty("--font-ascender", ascenderTotal);
  root.style.setProperty("--font-descender", descenderTotal);
  root.style.setProperty("--font-grade", gradeTotal);
  root.style.setProperty("--font-lowercase-height", lowercaseHeightTotal);
  root.style.setProperty("--font-slant", slantTotal);
  root.style.setProperty("--font-thin-stroke", thinStrokeTotal);
  root.style.setProperty("--font-weight", weightTotal);
  root.style.setProperty("--font-width", widthTotal);
  
  // Display totals on the page 
  ascenderLabel.innerHTML = ascenderTotal;
  descenderLabel.innerHTML = descenderTotal;
  gradeLabel.innerHTML = gradeTotal;
  lowercaseHeightLabel.innerHTML = lowercaseHeightTotal;
  slantLabel.innerHTML = slantTotal;
  thinStrokeLabel.innerHTML = thinStrokeTotal;
  weightLabel.innerHTML = weightTotal;
  widthLabel.innerHTML = widthTotal;
}

// Update values on mouse move
document.addEventListener("mousemove", updateDisplay);

// Recalculate viewport values when window is resized
document.addEventListener('resize', getViewport)

getViewport();

              
            
!
999px

Console