Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's 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 it's URL and the proper URL extention.

+ 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

Save Automatically?

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

              
                .page-content
  h1 Magic circles generator
  h2 What is it ?
  p Originally, a simple css challenge around recursive and semantic rules which became something more complicated than I originally intended. I have a fascination for hermeticism, alchemy and magic, so I thought I might put those in the mix and make a magic circle generator. There is many room for improvements, but I'm quite happy with the results despite some glitches due to the way inner texts are handled.
  p All the figures are plain css. Javascript is used to generate the markup, which should be self-explanatory. Scroll or refresh the page to generate new circles ad nauseam.
              
            
!

CSS

              
                @font-face {
  font-family: Quivira;
  src: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/24822/Quivira.otf") format("opentype");
}

@color: #454647;
@bordercolor: #e74c3c;
@lightcolor: lighten(@color, 20%);
@lightercolor: lighten(@color, 40%);
@lightestcolor: lighten(@color, 50%);
@bgcolor: #fafbfc;
@circleradius: 250px;

* {
  box-sizing: border-box;
}


html, body {
  font: 16px Lato;
  line-height: 1.4em;
  color: @color;
  background: @bgcolor; 
}

a {
  line-height: 1.2;
  display: inline-block;
  
  &:after {
    display: block;
    content: "";
    height: 2px;
    width: 0%;
    background-color: @bordercolor;
    transition: width 0.3s ease-in-out;
  }
  
  &:hover:after, 
  &:focus:after {
    width: 100%;

  }
}

a {
    color: @bordercolor;
    text-decoration: none;
  }

h1 {
  font-size: 4em;
  font-weight: 300;
  line-height: 1.2em;
  margin: 1em 0;
  padding-bottom: 1em;
  text-align: center;
  border-bottom: 1px solid #ccc;
}

h2 {
  font-size: 1.5em;
  font-weight: 300;
  margin: 1em 0;
  line-height: 1.2em;
}

p {
  margin: 1em 0;
  
  &::first-letter {
    font-size: 2.8em;
    font-weight: 300;
    display: block;
    float: left;
    padding-top: 0.25em;
    padding-right: 0.05em; 
  }
}

.page-content {
  max-width: 960px;
  margin: auto;
  text-align: justify;
  padding: 0 20px;
}

.circle-container {
  margin: 100px;
  float: left;
}

/* 
 * Circles
 */
.magic-circle {
  display: block;
  position: relative;
  width: @circleradius;
  height: @circleradius;
  border: 1px solid @lightcolor;
  border-radius: @circleradius * 0.5;
  
  &.lighter {
    border: 1px solid @lightercolor;
  }
  
  &.lightest {
    border: 1px solid @lightestcolor;
  }
  
  &.dotted {
    border-style: dotted; 
  }
  
  &.doubled {
    &:after {
      display: block; 
      content: '';
      width: 108%;
      height: 108%;
      border: 1px solid @lightcolor;
      border-radius: 100%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
    
    &.dotted:after { 
      border-style: dotted; 
    }
  }
  
  > .formula {
    font-family: Quivira;
    transform: translate(47%,0);
    height: 100%;
    font-size: 0.8em;
    text-transform: uppercase;
    color: @lightcolor;
    
    span {
      height: 50%;
      position: absolute;
      width: 1em;
      padding-top: .25em;
      left: 0;
      top: 0;
      transform-origin: bottom center;
    }
    
    .generate-chars(72);
    
    .generate-chars(@n, @i: 1) when (@i =< @n) {
      .char@{i} {
        transform: rotate(@i * 5deg); 
      }
      .generate-chars(@n, (@i + 1));
    }
  }
  
  > .magic-circle {
    width: 48%;
    height: 48%;
    position: absolute;
    left: 26%;
    top: 26%;
    font-size: 0.6em;
    
    &.top {
      top: -23%;
    }
    &.bottom {
      top: 77%;
    }
    &.left {
      left: -23%;
    }
    &.right {
      left: 77%;
    }
    &.top-left {
      top: -11%;
      left: -11%; 
    }
    &.top-right {
      top: -11%;
      left: 62%; 
    }
    &.bottom-left {
      top: 62%; 
      left: -11%; 
    }
    &.bottom-right {
      top: 62%;
      left: 62%;
    }
  }
  
  /*
   * Symbols
   */
  
  .symbol {
    position: absolute;
    font-family: Quivira;
    font-size: 3em;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: @lightcolor;

    
    &.lighter {
      color: @lightercolor;
    }
    
    &.lightest {
      color: @lightestcolor;
    }
  }
  
  /*
   * Squares
   */
  .square {
    position: absolute;
    border: 1px solid @lightcolor;
    width: 70%;
    height: 70%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    
    &.lighter {
      border: 1px solid @lightercolor;
    }

    &.lightest {
      border: 1px solid @lightestcolor;
    }
    
    &.dotted {
      border-style: dotted;
    }
    
    > .formula {
      font-family: Quivira;
      font-size: 0.8em; 
      text-align: center;
      text-transform: uppercase;
      color: @lightcolor;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      
      &.up {
        top: 0%;
        transform: translate(-50%, 0%);
      }
      
      &.down {
        top: 100%;
        margin-top: -1em;
        transform: translate(-50%, 0%) rotate(180deg);
      }
      
      &.right {
        left: 100%;
        transform: translate(-70%, -50%) rotate(90deg);
      }
      
      &.left {
        left: 0%;
        transform: translate(-35%, -50%) rotate(-90deg);
      }
    }
    
    > .magic-circle {
      width: 100%;
      height: 100%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
    
    > .triangle {
      width: 100%;
      left: 0;
      top: 100%;
      
      &:before {
        width: 112%;
        transform: rotate(-63.3deg);
        
      }
      
      &:after {
        width: 112%;
        transform: rotate(63.3deg);
        left: -11.5%;
      }
      
      &.reverse {
        width: 100%;
        left: 0;
        top: -0.6%; 

        &:before {
          width: 112%;
          transform: rotate(-63.3deg);
        }

        &:after {
          width: 112%;
          transform: rotate(63.3deg);
          left: -11.5%;
        }
      }
      
      > .square {
        width: @circleradius * 0.353;
        height: @circleradius * 0.353; 
        position: absolute;
        left: 50%;
        transform: translate(-50%, -100%);
        border: 1px solid @lightcolor;
        
        > .triangle {
          width: 100%;
          left: 0;
          top: 100%;

          &:before {
            width: 112%;
            transform: rotate(-63.3deg);

          }

          &:after {
            width: 112%;
            transform: rotate(63.3deg);
            left: -11.5%;
          }

          &.reverse {
            width: 100%;
            left: 0;
            top: 0%;

            &:before {
              width: 112%;
              transform: rotate(-63.3deg);
            }

            &:after {
              width: 112%;
              transform: rotate(63.3deg);
              left: -11.5%;
            }
          }
        }
      }
    }
  }
  
  /*
   * Triangles
   */
  .triangle {
    position: absolute;
    width: 88%;
    height: 0;
    border-width: 1px 0 0 0;
    border-style: solid;
    border-color: @lightcolor;
    top: 75.5%;
    left: 6%;
    
    &.reverse {
      transform: rotate(180deg);
      top: 24.5%;
    }
    
    &:before {
      
      content: ''; 
      position: absolute;
      width: 100%;
      height: 0;
      border-width: 1px 0 0 0;
      border-style: solid;
      border-color: @lightcolor;
      transform-origin: 0% 50%;
      transform: rotate(-60deg);
      
    }
    
    &:after {
      
      content: '';
      position: absolute;
      width: 100%;
      height: 0;
      border-width: 1px 0 0 0;
      border-style: solid;
      border-color: @lightcolor;
      transform-origin: 100% 50%;
      transform: rotate(60deg);
      
    }
    
    &.lighter {
      
      background: @lightercolor;
      
      &:before, &:after {
        
        background: @lightercolor;
        
      }
      
    }
    
    &.lightest {
      background: @lightestcolor;
      
      &:before, &:after {
        background: @lightestcolor;
      }
    }
    
    &.dotted {
      border-style: dotted;
      
      &:before, &:after {
        border-style: dotted; 
      }
    }
    
    > .formula {
      font-family: Quivira;
      font-size: 0.8em; 
      text-align: center;
      text-transform: uppercase;
      color: @lightcolor;
      position: absolute;
      top: -@circleradius * 0.27;
      left: 50%;
      transform: translate(-50%, -50%);
      
      &.base {
        top: 0%;
        margin-top: -1.5em;
        transform: translate(-50%, 0%);
      }
      
      &.right {
        top: 0;
        left: 0;
        width: 100%;
        text-align: center;
        transform-origin: 100% 50%;
        transform: rotate(60deg) translate(0, .5em);
      }
      
      &.left {
        top: 0;
        left: 0;
        width: 100%;
        text-align: center;
        transform-origin: 0% 50%;
        transform: rotate(-60deg) translate(0, .5em);
      }
    }
    
    > .symbol {
      transform: translate(-50%, -450%); 
    }
    
    > .square {
      width: @circleradius * 0.4;
      height: @circleradius * 0.4; 
      position: absolute;
      left: 50%;
      transform: translate(-50%, -100%);
      border: 1px solid @lightcolor;
    }
    
    > .magic-circle {
      width: @circleradius * 0.5; 
      height: @circleradius * 0.5;
      position: absolute;
      left: 50%;
      transform: translate(-50%, -100%);
      
      > .triangle > .magic-circle {
        width: @circleradius * 0.25; 
        height: @circleradius * 0.25;
      }
      
      > .triangle > .magic-circle > .triangle > .magic-circle {
        width: @circleradius * 0.125; 
        height: @circleradius * 0.125;
      }
    }
    
    > .triangle {
      width: @circleradius * 0.44;
      left: 50%;
      top: -@circleradius * 0.125;
      transform: translate(-50%, -50%);
      
      &.reverse {
        transform: translate(-50%, -50%) rotate(180deg);
        top: -@circleradius * 0.38;
      }
      
      > .triangle {
        width: @circleradius * 0.21;
        left: 50%;
        top: -@circleradius * 0.07;
        transform: translate(-50%, -50%);

        &.reverse {
          transform: translate(-50%, -50%) rotate(180deg);
          top: -@circleradius * 0.19;
        }
      }
    }
  }
}
              
            
!

JS

              
                var maxChildsPerElements = 2; 
var maxNumberOfFirstChilds = 4;
var minNumberOfFirstChilds = 2;
var minNumberOfSubcircles = 0;
var maxNumberOfSubcircles = 6;
var chanceForFormulas = 0.5;

var longformulas = 
    [
      'Visita Interiora Terrae Rectificando Invenies Occultum Lapidem Veram Medicinam.',
      'Caelum non animum mutant qui trans mare currunt',
      'cessante ratione legis cessat ipsa lex',
      'contra vim mortis non crescit herba in hortis',
      'crescat scientia vita excolatur',
      'cuius est solum eius est usque ad coelum et ad inferos',
      'cuncti adsint meritaeque expectent praemia palmae'
      
    ]
var formulas = 
    [
      'admirandum naturæ operationem', 
      'caput mortuum',
      'carpe noctem',
      'clavis aurea',
      'camera obscura',
      'capax infiniti',
      'caput inter nubila',
      'anima magica abscondita',
      'causa mortis',
      'ceteris paribus',
      'compos mentis',
      'concilio et labore',
      'concordia cum veritate',
      'concordia parvae res crescunt',
      'condicio sine qua non',
      'contemptus mundi',
      'contraria contrariis curantur',
      'cor ad cor loquitur',
      'cor aut mors',
      'cor unum',
      'corpus vile',
      'corruptio optimi pessima',
      'corvus oculum corvi non eruit',
      'crescente luce',
      'cupio dissolvi',
      'auctus ex dimicatione',
      'voluntas vincit omnia',
      'concordia magica'
    ];
var shortformulas = 
    [
      'Creo',
      'Rego', 
      'Perdo', 
      'Intellego',
      'Muto',
      'Ignem',
      'Aqua',
      'Terram',
      'Auram',
      'Herbam',
      'Corpus',
      'Mentem',
      'Vim',
      'Animal',
      'Imagonem',
      'Vitae',
      'Nocte',
      'Diem'
    ]; 

function makeMagicCircle()
{
  /* creates the base circle */
  var container = $('<div class="circle-container" />');
  var circle = $('<div class="magic-circle" />');
  container.append(circle);
  $('body').append(container); 
  
  /* generates random elements */
  var dice = minNumberOfFirstChilds + Math.floor(Math.random() * (maxNumberOfFirstChilds - minNumberOfFirstChilds));
  for(i = 0; i < dice; i++)
    appendRandomElement(circle, maxChildsPerElements);
  
  /* generates subcircles */
  dice = minNumberOfSubcircles + Math.floor(Math.random() * (maxNumberOfSubcircles - minNumberOfSubcircles));
  for(i = 0; i < dice; i++)
    appendCircle(circle);

  /*
   * is doubled ?
   */
  if(Math.random() > 0.75) circle.addClass('doubled');
  
  /*
   * Random weight
   */
  dice = Math.floor((Math.random() * 2));
  if(dice == 1) circle.addClass('lighter');
  else if(dice == 2) circle.addClass('lighter');
  
  appendSymbol(circle);
}

function appendRandomElement(element, maxChilds)
{
  /*
   * Random type
   */
  var dice = Math.floor((Math.random() * 3));
  var child;
  
  switch(dice)
    {
      case 0: child = appendCircle(element, 2); break;
      case 1: child = appendSquare(element); break;
      case 2: child = appendTriangle(element); break;
    } 
  
  /*
   * Make childs
   */
  if(dice != 3) { 
    dice = Math.floor((Math.random() * maxChilds));
    if(dice > 0)
      for(i = 0; i < dice; i++) 
        appendRandomElement(child, maxChilds);
  }
  
  /*
   * Random weight
   */
  dice = Math.floor((Math.random() * 3));
  if(dice == 1) child.addClass('lighter');
  else if(dice == 2) child.addClass('lighter'); 
  
  /*
   * Random style
   */
  dice = Math.floor((Math.random() * 2)); 
  if(dice == 1) child.addClass('dotted'); 
  

}

function appendCircle(element, maxSubcircles)
{
  var circle = $('<div class="magic-circle" />');
  element.append(circle);
  
  /*
   * Random position
   */
  var dice = Math.random();
  if(dice > 0.9) circle.addClass('top');
  else if(dice > 0.8) circle.addClass('top-right');
  else if(dice > 0.7) circle.addClass('right');
  else if(dice > 0.6) circle.addClass('bottom-right');
  else if(dice > 0.5) circle.addClass('bottom');
  else if(dice > 0.4) circle.addClass('bottom-left');
  else if(dice > 0.3) circle.addClass('left');
  else if(dice > 0.2) circle.addClass('top-left');
  
  /*
   * is doubled ?
   */
  if(Math.random() > 0.75) circle.addClass('doubled');
  
  /*
   * make an inner symbol ?
   */
  appendSymbol(circle);
  
  /*
   * has formula ?
   */
  if(Math.random() > chanceForFormulas)
  {
    var formula = $('<div class="formula">' + getRandomLongFormula() + '</div>'); 
    circle.append(formula);
  }
  
  /* generates subcircles */
  if(maxSubcircles > 0) {
    var dice = Math.floor(Math.random() * (maxSubcircles));
    for(i = 0; i < dice; i++)
      appendCircle(circle, maxSubcircles - 1);
  }
  
  return circle;
}

function appendSquare(element)
{
  var square = $('<div class="square" />');
  element.append(square);
  
  /*
   * Has formulas ?
   */
  if(Math.random() > chanceForFormulas)
  {
    var dice = Math.floor(Math.random() * 2);
    if(dice == 0) // simple formula
    {
      var formula = $('<div class="formula">' + getRandomFormula() + '</div>'); 
      square.append(formula);
    }
    else if(dice == 1) // side formulas
    {
        var formula1 = $('<div class="formula up">' + getRandomShortFormula() + '</div>'); 
        var formula2 = $('<div class="formula right">' + getRandomShortFormula() + '</div>'); 
        var formula3 = $('<div class="formula down">' + getRandomShortFormula() + '</div>'); 
        var formula4 = $('<div class="formula left">' + getRandomShortFormula() + '</div>'); 
        square.append(formula1);
        square.append(formula2);
        square.append(formula3);
        square.append(formula4); 
    }
  }
  

  return square;
}

function appendTriangle(element)
{
  var triangle = $('<div class="triangle" />');
  if(Math.random() > 0.5) 
    triangle.addClass('reverse');
  element.append(triangle);
  
    /*
   * Has formulas ?
   */
  if(Math.random() > chanceForFormulas)
  {
    var dice = Math.floor(Math.random() * 2);
    if(dice == 0) // simple formula
    {
      var formula = $('<div class="formula">' + getRandomFormula() + '</div>');
      triangle.append(formula);
    }
    else if(dice == 1) // side formulas
    {
        var formula1 = $('<div class="formula base">' + getRandomShortFormula() + '</div>'); 
        var formula2 = $('<div class="formula right">' + getRandomShortFormula() + '</div>'); 
        var formula3 = $('<div class="formula left">' + getRandomShortFormula() + '</div>');
        triangle.append(formula1);
        triangle.append(formula2);
        triangle.append(formula3);
    }
  }
    
  return triangle;
}

function appendSymbol(element)
{
  /*
   * symbols goes from 128768 to 128883
   */
  var code = (128768 + Math.floor((Math.random() * 114)))
  var symbol = $('<div class="symbol">&#' + code + '</div>');
  element.append(symbol);
  
  /*
   * Random weight
   */
  var dice = Math.floor((Math.random() * 3)); 
  if(dice == 1) symbol.addClass('lighter');
  else if(dice == 2) symbol.addClass('lighter'); 
  
  return symbol;
}

function getRandomLongFormula()
{
  var dice = Math.floor((Math.random() * longformulas.length)); 
  return longformulas[dice];
}

function getRandomFormula()
{
  var dice = Math.floor((Math.random() * formulas.length)); 
  return formulas[dice];
}

function getRandomShortFormula()
{
  var dice = Math.floor((Math.random() * shortformulas.length)); 
  return shortformulas[dice];
}

function moreCircles()
{
  for(var i = 0; i < 10; i++)
    makeMagicCircle();
  $('.magic-circle > .formula').lettering();
}

$(function () {
  moreCircles();
});


$(window).scroll(function() {
  if($(window).scrollTop() + $(window).height() == $(document).height()) {
    moreCircles();
  }
});
              
            
!
999px

Console