<div class="wrapper">
  <h1>Gradient angles in CSS</h1>
  
  <div class="figure">
  <div class="rect"></div>
  <div class="vertical fixed"></div>
  <div class="diagonal"></div>
  <div class="circle"></div>
  <div class="topline"></div>
  <div class="bottomline"></div>
  <div class="vertical"></div>
    
  <div class="percent zero">0%</div>
  <div class="percent fifty">50%</div>
  <div class="percent hundred">100%</div>
    
  <svg class="arc" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 160">
    <path class="arc__arc" id="arc1" fill="none" stroke="#fff" stroke-width="10" opacity="0.5"/>
    <rect id="arcAngleNumber" x="60" y="60" width="24" height="10" fill="#fff" rx="5" opacity="0.5" />
    <text id="arcAngleNumberText" text-anchor="middle" x="60" y="110" fill="#003">A°</text>
  </svg>
</div>

  <div class="controls">
  <h2 class="controls__headline">
    Playground:
  </h2>
  <div class="angle-control">
    <input type="range" 
           id="angle-control" 
           min="0" 
           max="360" 
           step="0.5" 
           value="20" 
           list="angles">
    <span id="angle-result" class="result">20.0 deg</span>

    </div>
  <datalist id="angles">
    <!--
    <option value="45">
    <option value="63.5">
    -->
    <option value="90">
    <!-- 
    <option value="116.5">
    <option value="135">
    -->
    <option value="180">
    <!--
    <option value="225">
    <option value="243.5">
    -->
    <option value="270">
    <!--
    <option value="296.5">
    <option value="315">
    -->
  </datalist>
</div>
</div>

<footer>
  <p>
    Want to learn more about gradient angles? Check out my <a href="https://9elements.com/blog/gradient-angles-in-css/" target="_blank">Blog Post</a>.
  </p>
</footer>
:root {
  --angle: 20deg;
  --width: min(880px, 80vw, 60vh);
  --side: var(--width);
  --top: var(--width);
  --diagonal-angle: 45deg;  
  --length: 20px;
  --circle-multiplier: 0.707;
  --circle-width: calc(var(--circle-multiplier) * var(--width));
  
  @media (min-aspect-ratio: 1/1) {
    --width: min(880px, 80vw, 90vh);
    --top: var(--width);
    --side: calc(var(--width) / 2);
    --diagonal-angle: 63.5deg;
    --circle-multiplier: 0.559;
  } 
}

* {
  box-sizing: border-box;
} 

body {
  background: #003;
  overflow-x: hidden;
  color: #fff;
  font-family: 'Raleway', sans-serif;
  min-height: 100vh;
  margin-bottom: 4em;
}

.wrapper {
  min-height: calc(100vh - 4em);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
  margin-bottom: 2vh;
}

h1 {
  z-index: 10;
  text-transform: uppercase;
  font-weight: 900;
  font-size: Min(calc(1em + 3vw), 3em);
  text-align: center;
  padding: 0.25em;
  margin-top: 0.25em;
  margin-bottom: 0.5em;
}


.figure {
  width: var(--top);
  height: var(--side);
  position: relative;
  z-index: 2;
  margin: 1em auto 4em;
  order: 1;
  
  @media screen and (min-width: 30em) {
    order: 0;
  }
}

.rect {
  width: 100%;
  height: 100%;
  background: #f09;
  background-image: 
    linear-gradient(var(--angle), 
      #f09 0%, 
      #3023AE 50%, 
      #0ff 100%);
  border: 1px solid #fff;
}

.vertical {
  width: 2px;
  height: var(--length);
  background: #fff;
  position: absolute;
  left: calc(50% - 1px);
  top: calc(50% - (var(--length) / 2));
  transform: rotate(var(--angle));
  
  &:before,
  &:after {
    content: "";
    position: absolute;
    width: 0.75em;
    height: 0.75em;
    background: #0ff;
    border: 2px solid #fff;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    top: 0;
    left: 0;
    box-shadow: 0 0 0.5em #000;
  }
  
  &:after {
     top: 100%;
    background: #f09;
  }
  
  &.fixed {
    transform: none;
    background: none;
    border-left: 1px dashed #fff;
    height: calc(var(--side) * 1.4 / 2);
    top: calc(50% - (var(--side) * 0.7));
    top: 50%;
    
    &:before,
    &:after {
      display: none;
    }
  }
}

.diagonal {
  width: 1px;
  height: calc(var(--width) * var(--circle-multiplier) * 2.2);
  border-right: 1px dashed #fff;
  position: absolute;
  left: calc(50% - 1px);
  top: calc(50% - (var(--width) * var(--circle-multiplier) * 1.1));
  transform: rotate(var(--diagonal-angle));
  transition: transform 0.5s ease-out;
  
  .second &,
  .fourth & {
    transform: rotate(calc(180deg - var(--diagonal-angle)));
  }
}

.circle {
  width: calc(var(--width) * 0.559);
  width: var(--circle-width);
  height: calc(var(--width) * 0.559);
  height: var(--circle-width);
  position: absolute;
  right: 0px;
  top: calc(var(--circle-width) / -2);
  border: 1px dashed #fff;
  border-radius: 50%;
  transform-origin: right center;
  transform: rotate( calc((90deg - var(--diagonal-angle)) * -1) );
  transition: top 0.5s ease-out, right 0.5s ease-out, transform-origin 0.5s ease-out, transform 0.5s ease-out;

  .second & {
    top: calc(var(--side) - (var(--circle-width) / 2));
    transform: rotate( calc((90deg - var(--diagonal-angle))) );
  }
  
  .third & {
    top: calc(var(--side) - (var(--circle-width) / 2));
    right: calc(var(--top) - var(--circle-width));
    transform: rotate( calc((90deg - var(--diagonal-angle)) * -1) );
    transform-origin: left center;
  }
  
  .fourth & {
    right: calc(var(--top) - var(--circle-width));
    transform-origin: left center;
    transform: rotate( calc((90deg - var(--diagonal-angle)) * 1) );
  }
}

.topline,
.bottomline {
  width: calc(var(--width) * 1.125) ;
  height: 2px;
  border-bottom: 2px solid #0ff;
  position: absolute;
  left: calc(100% - ((var(--width) * 1.125) / 2));
  top: 0;
  transform: rotate(var(--angle));
  transition: top 0.3s ease-out, left 0.3s ease-out;
}

.topline {
  .second & {
    top: calc(100% - 2px);
  }

  .third & {
    top: calc(100% - 2px);
    left: calc( -1 * ((var(--width) * 1.125) / 2) );
  }

  .fourth & {
    left: calc( -1 * ((var(--width) * 1.125) / 2) );
    top: 0;
  }
}

.bottomline {
  top: calc(100% - 2px);
  left: calc( -1 * ((var(--width) * 1.125) / 2) );
  border-color: #f09;
  
  .second & {
    top: 0;
  }

  .third & {
    left: calc(100% - ((var(--width) * 1.125) / 2));
    top: 0;
  }

  .fourth & {
    left: calc(100% - ((var(--width) * 1.125) / 2)); 
    top: calc(100% - 2px);
  }
}

.percent {
  position: absolute;
  background: #fff;
  color: #003;
  padding: 0.25em 0.5em;
  line-height: 1;
  border-radius: 2em;
  font-weight: 700;
  font-size: 0.875em;
  transition: top 0.4s ease-out, bottom 0.4s ease-out, left 0.4s ease-out, right 0.4s ease-out;
  border: 2px solid #fff;
  box-shadow: 0 0 0.25em rgba(0,0,0,0.5);
}

.zero {
  left: 0;
  bottom: 0;
  transform: translate(-50%, 50%);
  background: #f09;
  color: #fff;
  
  .second &,
  .third & {
    bottom: 100%;
  }
  
  .third &,
  .fourth & {
    left: 100%;
  }
}

.fifty {
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: #3023AE;
  color: #fff;
}

.hundred {
  right: 0;
  top: 0;
  transform: translate(50%, -50%);
  background: #0ff;
  
  .second &,
  .third & {
    top: 100%;
  }
  
  .third &,
  .fourth & {
    right: 100%;
  }
}


.arc {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 40;
  width: calc(var(--width) / 2);
  height: calc(var(--width) / 2);
  
  &__arc {
    transform-origin: 80px 80px;
    transform: rotate(180deg);
  }
  
  text {
    font-size: 6px;
    font-family: 'Raleway', sans-serif;
    font-weight: 700;
  }
}

/* ---------------------------------
   Interactive Controls
--------------------------------- */

.controls {
  font-size: Min(calc(0.5em + 2vw), 1em);
  color: #003;
  background: rgba(255,255,255,0.9);
  z-index: 5;
  border-radius: 0.25em;
  width: Min(var(--width), 34em);
  max-width: 90%;
  margin: 0 auto;
  
  &__headline {
    color: #003;
    margin: 0.75em 1.125em 0.625em;
    font-size: 1em;
    text-align: center;
    font-weight: 700;
  }
}

.angle-control {
  padding: 0.5em 0 0.75em;
  margin: 0 1em;
  font-size: 1em;
  border-top: 2px solid #003;
  display: flex;
  
  > * {
    vertical-align: middle;
  }
  
  span {
    display: inline-block;
    min-width: 8ch;
    margin-left: 1em;
    font-variant-numeric: tabular-nums;
  }
  
  input {
    width: 8em;
    flex-shrink: 1;
    
    @media screen and (min-width: 30em) {
      width: 21em;
      flex-grow: 1;
    }
  }
}

.result {
  text-align: right;
  font-weight: 700;
}

[hidden] {
  display: none;
}

footer {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  text-align: center;
  background: #004;
  z-index: 10;
  border-top: 1px solid rgba(255,255,255,0.1);
  
  p {
    font-size: 0.875em;
    font-weight: 700;
    margin: 0.75em;
  }
  
  a {
    color: #fff;
    text-decoration-color: #f09;
    
    &:hover {
      color: #f09;
    }
  }
}
View Compiled
const angleRange = document.getElementById('angle-control');
const angleResult = document.getElementById('angle-result');
const root = document.querySelector('HTML');


angleRange.addEventListener("input", function() {
  // Show current angle
  angleResult.innerHTML =  parseFloat(angleRange.value).toFixed(1) + " deg";
  
  // Update custom property
  document.documentElement.style.setProperty('--angle', angleRange.value + 'deg');

  // draw the arc describing the current angle
  let arcAngle = angleRange.value;
  if (arcAngle == 360) { arcAngle = 359.9; }
  document.getElementById("arc1").setAttribute("d", describeArc(80, 80, 50, 0, arcAngle));
  
  // Position the Angle Number
  let arcNumber = document.getElementById("arcAngleNumber");
  let arcNumberText = document.getElementById("arcAngleNumberText");
  let numberAngle = angleRange.value / 2;
  let numberPosition = polarToCartesian(80, 80, 30, numberAngle + 180);
  arcNumber.setAttribute("x", numberPosition.x - 12);
  arcNumber.setAttribute("y", numberPosition.y - 5);
  arcNumberText.textContent = (angleRange.value + "°");
  arcNumberText.setAttribute("x", numberPosition.x);
  arcNumberText.setAttribute("y", numberPosition.y + 2);
  
  // Apply classes to root depending on current quadrant
  if (angleRange.value > 90 && angleRange.value <= 180) {
    root.className = 'second';
  }
  else if (angleRange.value > 180 && angleRange.value <= 270 ) {
    root.className = 'third';
  }
  else if (angleRange.value > 270 && angleRange.value <= 360 ) {
    root.className = 'fourth';
  }
  else {
    root.className = '';
  }
  
  // Update Length of the line showing the gradient axis
  getLength();
}, false); 


function getLength() {
  const bodyStyles = window.getComputedStyle(document.body);
  const diagonalAngle = parseFloat(bodyStyles.getPropertyValue('--diagonal-angle'), 10);
  
  // calculate the Hypotenuse
  const rect = document.querySelector('.rect');
  const rectWidth = Math.max(rect.offsetWidth, rect.offsetHeight);
  const circleMultiplier = bodyStyles.getPropertyValue('--circle-multiplier');
  const c = rectWidth * circleMultiplier;
  
  // calculate alpha depending on the current quadrant
  let angle = angleRange.value;
  let quadrant = parseInt(angle / 90);
  let alpha = Math.abs(diagonalAngle - (angle - 90 * quadrant));
  
  if (quadrant % 2 !== 0) {
   alpha = Math.abs(90 - diagonalAngle - (angle - 90 * quadrant));
  }

  // calculate adjacent leg
  const alphaRadians = alpha * Math.PI / 180.0;
  const adjacentLeg = Math.cos(alphaRadians) * c;
  
  // update custom property
  document.documentElement.style.setProperty('--length', (adjacentLeg * 2) + 'px');
}


// Stuff to draw the arc for the current Angle
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
    ].join(" ");

    return d;       
}


// Initial call
document.getElementById("arc1").setAttribute("d", describeArc(80, 80, 50, 0, 20));
  // Position the Angle Number
  let arcNumber = document.getElementById("arcAngleNumber");
  let arcNumberText = document.getElementById("arcAngleNumberText");
  let numberAngle = angleRange.value / 2;
  let numberPosition = polarToCartesian(80, 80, 30, 200);
  arcNumber.setAttribute("x", numberPosition.x - 12);
  arcNumber.setAttribute("y", numberPosition.y - 5);
  arcNumberText.textContent = (angleRange.value + "°");
  arcNumberText.setAttribute("x", numberPosition.x);
  arcNumberText.setAttribute("y", numberPosition.y + 2);
getLength();

// Update diagonal-axis after resizing the window
window.addEventListener('resize', getLength);


Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.