JSFiddle - React, Tailwind, and code Playground

Updated container example with external parseCSS working.

by nilloc

HTML

<script src="https://paperclipped.com/js/libraries/parseCSS.js"></script>
<!-- <script src="https://raw.githubusercontent.com/mdevils/node-css-selector-parser/master/lib/css-selector-parser.js" type="text/javascipt"></script> -->

<modkit-container id="editor" row="1" col="1">
  <modkit-container id="toolbar" row="1" col="n">
    toolbar
  </modkit-container>
  <modkit-container id="drawer" row="n" col="1">
    <modkit-style>
      default{
        top:0;
        left:0;
        width:200px;
        bottom:20px; /* could be switched to footer top */
      }
    </modkit-style>
    drawer
  </modkit-container>
  <modkit-container id="handle">
    <modkit-style>
      default{
        top:$('#toolbar').bottom();
        left:$(':prev-sibling()').right();
        right:0;
        bottom:20px;
      }
    </modkit-style>
  </modkit-container>
  <modkit-container id="stage">
    <modkit-style>
      default{
        top:$('#toolbar').bottom();
        left:$('#handle').right();
        right:0;
        bottom:20px;
      }
    </modkit-style>
    stage
    <anchor class="top" contenteditable data-setting="toolbar">toolbar</anchor>
    <anchor class="left" contenteditable data-setting="handle">handle</anchor>
    <anchor class="right" contenteditable data-setting="0">0</anchor>
    <anchor class="bottom" contenteditable data-setting="0">0</anchor>
  </modkit-container>
</modkit-container>

SCSS

*{
  box-sizing:border-box;
}
modkit-container{
  display:block;
  position:relative;
  user-select:none;
}

modkit-style{
  display:none;
}

#editor, #drawer, #handle, #stage{
  position:absolute;
}

#editor{
  background:darkgray;
  top:0;
  left:0;
  bottom:0;
  right:0;
  
  $toolbar-height:60px;
  $drawer-width:200px;
  $handle-width:4px;
  
  #toolbar{
    background:lightgray;
    height:$toolbar-height;
    padding:10px;
    box-shadow:0 2px 3px rgba(0,0,0,0.3);
    z-index:900;
  }
  #drawer{
    background:white;
    top:$toolbar-height;
    left:0;
    width:$drawer-width;
    bottom:0;
  }
  #handle{
    background:#777;
    top:$toolbar-height;
    left:$drawer-width;
    width:$handle-width;
    bottom:0;
    cursor:col-resize;
    z-index:1000;
  }
  #stage{
    background:#eee;
    top:$toolbar-height;
    left:$drawer-width+$handle-width;
    right:0;
    bottom:0;
  }
  
  
  anchor{
    display:block;
    position:absolute;
    height:20px;
    width:60px;
    background:white;
    border:1px solid darkgray;
    border-radius:4px;
    text-align:center;
    outline: none;
    overflow:hidden;
    
    &.top,  &.bottom{left:calc(50% - 30px);}
    &.left, &.right{top:calc(50% - 30px);}
    &.top, &.left, &.right{border-radius:0 0 4px 4px;}
    &.top{top:0;}
    &.bottom{bottom:0;  border-radius:4px 4px 0 0;}
    &.left{   left:-20px; transform:rotate(-90deg); }
    &.right{ right:-20px; transform:rotate(90deg);  }
  }

}

JavaScript

// Add better position methods to $
$.prototype.top = function (margin = false){
return this.offset().top - ((margin === true)? this.css('margin-top'):0);
}
$.prototype.left = function (margin = false){
console.log("left:", this.offset().left)
return this.offset().left - ((margin === true)? this.css('margin-left'):0);
}
$.prototype.right = function (margin = false){
return this.offset().left + this.outerWidth(margin);
}
$.prototype.bottom = function (margin = false){
return this.offset().top + this.outerHeight(margin);
}

$('anchor').on("input", function(){
	console.log($(this).parent(), "changed:", this.textContent);
  var $parent = $(this).parent();
  if(this.textContent.indexOf('%') > -1){
    $parent.css(this.className, this.textContent); // make %
    this.dataset.setting = this.textContent;
  }else if(this.textContent == parseFloat(this.textContent)){
  	$parent.css(this.className, this.textContent+"px"); // make abs
    this.dataset.setting = this.textContent;
  }else{
  	// search for id and attach based on edge
    var $target = $('modkit-container#'+this.textContent);
    if($target.length > 0 ){
    	console.log("found:", $target);
      this.dataset.setting = this.textContent;
/* switch(this.className){
        case 'top':
        console.log($target.offset().top+$target.outerHeight());
        $parent.css('top', $target.offset().top+$target.outerHeight()+"px");
        break;
        case 'left':
        $parent.css('left', $target.offset().left+$target.outerWidth()+"px");
        break;
        case 'right':
        $parent.css('right', $target.offset().left+"px");
        break;
              case 'bottom':
        $parent.css('top', $target.offset().top+"px");
        break;
      }*/
      
      // now class based instead of conditional!
      $parent.css(this.className, $target[this.className]()+"px");
    }
  }
});

$('#handle').on('mousedown', function(){
	$('#editor').on('mousemove.handle', function(evt){
  	//console.log("dragging mouse",...