From 536ba645b1fbc112c2faa12c3e275be1ad112673 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 8 Nov 2018 14:53:33 -0800
Subject: [PATCH] Update the styling for the Entity Properties window

---
 scripts/system/html/css/edit-style.css     | 403 +++++-----
 scripts/system/html/css/jsoneditor.css     |  10 +-
 scripts/system/html/entityProperties.html  |  13 +
 scripts/system/html/js/entityProperties.js | 868 ++++++++++++---------
 4 files changed, 716 insertions(+), 578 deletions(-)

diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css
index e4b33414ab..c09655b451 100644
--- a/scripts/system/html/css/edit-style.css
+++ b/scripts/system/html/css/edit-style.css
@@ -70,12 +70,11 @@
 }
 
 body {
-    padding: 21px 21px 21px 21px;
 
     color: #afafaf;
     background-color: #404040;
     font-family: Raleway-Regular;
-    font-size: 15px;
+    font-size: 12px;
 
     -webkit-touch-callout: none;
     -webkit-user-select: none;
@@ -425,9 +424,9 @@ input[type=checkbox] {
 }
 input[type=checkbox] + label {
     padding-left: 24px;
-    background-position-y: 6px;
     background-repeat: no-repeat;
     background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACpSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyATkF0KEiQdAzYlMQEIUyicFyDD9+/ePgRxMvsb///4zkIOZ/v0HmkAGHginYjGNGAzS+BpdkAj8mun/3//92DyPD//993cG88nTJ4+Zm5p/BSZeJYb/DEJADEzNOPF7hn8Mk69cvVIPAHN5pyfo70F5AAAAAElFTkSuQmCC);
+    cursor: pointer;
 }
 input[type=checkbox]:enabled + label:hover {
     background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAClSURBVDhPY2hoaGD6//9/6b9//64D8T8gGycASr/7+/dv5/79+1kYgIxKqDjRAKiniRFIv2JgYBAFYlLAE0aQ66AckgDjjx8/yNP44cMH8jS+fPmSPI0PHz4kT+PNmzfJ03jp0iXyNJ46dYo8jYcPHyYnAbxm+vnzZz8wLhlIwd+/f5/BrKSkdExCQuLrnz9/lIBpUAiIQekXF34PTGmTT548WQ8AokXg+rhVtPYAAAAASUVORK5CYII=);
@@ -455,6 +454,15 @@ input[type=checkbox]:checked + label:hover {
     color: #ffffff;
 }
 
+.icon {
+    font-family: HiFi-Glyphs;
+    color: white;
+}
+
+#property-type-icon {
+    font-size: 50px;
+}
+
 .selectable {
     -webkit-touch-callout: text;
     -webkit-user-select: text;
@@ -483,9 +491,11 @@ input[type=checkbox]:checked + label:hover {
 #properties-list {
     display: flex;
     flex-direction: column;
+
+    margin-top: 16px;
 }
 
-#properties-list fieldset {
+#properties-list .fieldset {
     position: relative;
     /* 0.1px on the top is to prevent margin collapsing between this and it's first child */
     margin: 0 -21px 21px -21px;
@@ -495,71 +505,101 @@ input[type=checkbox]:checked + label:hover {
     box-shadow: 0 -1px 0 rgb(37,37,37);
 }
 
-#properties-list fieldset.fstuple, #properties-list fieldset.fsrow {
+#properties-list .fieldset.fstuple, #properties-list .fieldset.fsrow {
     margin-top: 21px;
     border: none;
     box-shadow: none;
 }
 
-#properties-list > fieldset[data-collapsed="true"] + fieldset {
+#properties-list > .fieldset[data-collapsed="true"] + .fieldset {
     margin-top: 0;
 }
 
-#properties-list > fieldset[data-collapsed="true"] > *:not(legend) {
+#properties-list > .fieldset[data-collapsed="true"] > *:not(div.legend) {
      display: none !important;
 }
 
-#properties-list legend + fieldset {
-    margin-top: 0;
-    border: none;
-    box-shadow: none;
-}
-
-#properties-list > fieldset > legend {
-    position: relative;
-    display: table;
-    width: 100%;
-    margin: 21px -21px 0 -21px;
-    padding: 14px 21px 0 21px;
-    font-family: Raleway-Regular;
-    font-size: 12px;
-    color: #afafaf;
-    height: 28px;
-    text-transform: uppercase;
-    outline: none;
-    background-color: #404040;
-    border: none;
+.section-header {
+    padding: 0 16px;
     border-top: 1px rgb(90,90,90) solid;
-    box-shadow: 0 -1px 0 rgb(37,37,37), 0 4px 4px 0 rgba(0,0,0,0.75);
+    box-shadow: 1px -1px 0 rgb(37,37,37);
+    border-bottom: 1px solid rgb(37, 37, 37);
 }
 
 div.section-header, hr {
-    display: table;
-    width: 100%;
-    margin: 21px -21px 0 -21px;
-    padding: 14px 21px 0 21px;
+    display: flex;
+    flex-flow: row nowrap;
+
+    padding: 10px 16px;
     font-family: Raleway-Regular;
     font-size: 12px;
     color: #afafaf;
     height: 28px;
     text-transform: uppercase;
     outline: none;
+    margin-bottom: 10px;
+    align-items: center;
 }
 
-div.section-header:first-child {
-    margin-top: -2px;
-    padding-top: 0;
-    background: none;
-    height: auto;
+.section.minor {
+    margin: 0 21px;
+    box-shadow: 1px -1px 0 rgb(37,37,37);
+    border-left: 1px solid #575757;
 }
 
-#properties-list > fieldset > legend span, .section-header span {
-    font-family: HiFi-Glyphs;
+.container.property {
+    padding: 0 16px;
+}
+
+.stretch {
+    width: 100%;
+}
+
+div.section-header .label {
+    width: 100%;
+}
+
+.section.minor div.section-header {
+    border-right: 0;
+}
+
+div.section[collapsed="true"] > .container {
+    display: none;
+}
+
+div.section[collapsed="true"], div.section[collapsed="true"] > .section-header {
+    margin-bottom: 0;
+}
+
+.section.major {
+    margin-bottom: 20px;
+}
+
+.section.minor.last {
+    margin-bottom: 20px;
+    border-bottom: 1px solid rgb(37,37,37);
+}
+
+.section-header {
+    background-color: #373737;
+}
+
+.section-header {
+    cursor: pointer;
+}
+
+.section-header span {
     font-size: 30px;
-    float: right;
-    position: absolute;
-    top: 4px;
-    right: 13px;
+    font-family: HiFi-Glyphs;
+}
+
+.triple-label {
+    text-transform: uppercase;
+    padding: 6px 0;
+}
+
+.triple-item {
+    margin-right: 10px;
 }
 
 .section-header[collapsed="true"] {
@@ -582,9 +622,6 @@ hr {
 }
 
 .property {
-    display: table;
-    width: 100%;
-    margin-top: 21px;
     min-height: 28px;
 }
 
@@ -592,6 +629,10 @@ hr {
     width: auto;
 }
 
+span.indented {
+    padding-left: 16px;
+}
+
 .property label, .number label {
     display: table-cell;
     vertical-align: middle;
@@ -604,13 +645,13 @@ hr {
     font-size: 13px;
 }
 
-.property legend, .number legend {
+.property div.legend, .number div.legend {
     display: table-cell;
     vertical-align: middle;
     font-family: Raleway-SemiBold;
     font-size: 14px;
 }
-.property legend .unit, .number legend .unit {
+.property div.legend .unit, .number div.legend .unit {
     margin-left: 8px;
     font-family: Raleway-Light;
     font-size: 13px;
@@ -623,16 +664,26 @@ hr {
 .value label {
     display: inline-block;
     vertical-align: top;
-    width: 48px;
 }
-.value legend {
+.value div.legend {
     display: inline-block;
     vertical-align: top;
     width: 48px;
 }
 .value span {
-    font-family: FiraSans-SemiBold;
     font-size: 15px;
+    margin-right: 4px;
+}
+
+#placeholder-property-type {
+    display: flex;
+    align-items: center;
+    width: auto;
+    margin-right: 20px;
+}
+
+#placeholder-property-locked {
+    margin-left: 6px;
 }
 
 .checkbox + .checkbox {
@@ -659,33 +710,6 @@ hr {
     height: 1.8rem;
 }
 
-.text label, .url label, .number label, .textarea label, .xy label, .wh label, .rgb label, .xyz label, .pyr label, .dropdown label, .gen label {
-    float: left;
-    margin-left: 1px;
-    margin-bottom: 3px;
-    margin-top: -2px;
-}
-
-.text legend, .url legend, .number legend, .textarea legend, .xy legend, .wh legend, .rgb legend, .xyz legend, .pyr legend, .dropdown legend, .gen legend {
-    float: left;
-    margin-left: 1px;
-    margin-bottom: 3px;
-    margin-top: -2px;
-}
-
-.xy > div, .wh > div, .xyz > div, .pyr > div, .gen > div {
-    clear: both;
-}
-
-.number > input {
-    clear: both;
-    float: left;
-}
-.number > span {
-    clear: both;
-    float: left;
-}
-
 .dropdown {
     position: relative;
     margin-bottom: -17px;
@@ -697,6 +721,7 @@ hr {
 
 .dropdown dl {
     clear: both;
+    cursor: pointer;
 }
 .dropdown dl {
     font-family: FiraSans-SemiBold;
@@ -705,7 +730,7 @@ hr {
     height: 28px;
     padding: 0 28px 0 12px;
     color: #afafaf;
-    background: linear-gradient(#7d7d7d 20%, #686a68 100%);
+    background: #575757;
     position: relative;
 }
 .dropdown dl[dropped="true"] {
@@ -812,27 +837,20 @@ div.refresh input[type="button"] {
 
 .color-picker {
     box-sizing: border-box;
-    float: left;
-    margin-bottom: 21px;
-    width: 36px;
-    height: 36px;
-    border: 4px solid #afafaf;
-    border-radius: 4px;
-    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABTSURBVChTjcxBDoAwCETRwTs33sFjwB6uaCE1Ggvav5qQF7CSqu40dllHjYiOT3gh3yV8Ii+Fb+RNMEP9hm3sKENmBhG5P1aImWMH/EMerSAAOAFgTC/R8ZXSXAAAAABJRU5ErkJggg==);
-    background-position: bottom right;
-    background-repeat: no-repeat;
+    width: 26px;
+    height: 26px;
+    border: 3px solid #2B2B2B;
+    cursor: pointer;
 }
 .color-picker:focus {
     outline: none;
 }
 .color-picker[active="true"] {
     border-color: #000;
-    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABDSURBVChTjcoLCkAhCETRNq0tf97Y5xGZ1gVJ45TH6njThIO+xk2UwhWFcEdH6JCqOuiQiMDi/hcii3crRRb/7ggAPvIMVihQwvSXAAAAAElFTkSuQmCC);
 }
 
 .color-picker[disabled="disabled"] {
     border-color: #afafaf;
-    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABTSURBVChTjcxBDoAwCETRwTs33sFjwB6uaCE1Ggvav5qQF7CSqu40dllHjYiOT3gh3yV8Ii+Fb+RNMEP9hm3sKENmBhG5P1aImWMH/EMerSAAOAFgTC/R8ZXSXAAAAABJRU5ErkJggg==);
 }
 
 .colpick[disabled="disabled"] {
@@ -848,89 +866,15 @@ div.refresh input[type="button"] {
     clear: both;
 }
 
-.rgb legend {
+.rgb div.legend {
     float: left;
     margin-top: 10px;
     margin-left: 21px;
 }
-.rgb legend + * {
+.rgb div.legend + * {
     clear: both;
 }
 
-.tuple div {
-    display: inline-block;
-    position: relative;
-    margin-right: 6px;
-}
-.tuple div:last-child {
-    margin-right: 0;
-}
-
-.tuple label {
-    margin-right: -6px;
-}
-
-.xy .tuple input {
-    padding-left: 25px;
-}
-.wh .tuple input {
-    padding-left: 45px;
-}
-.rgb .tuple input {
-    padding-left: 65px;
-}
-.xyz .tuple input {
-    padding-left: 25px;
-}
-.pyr .tuple input {
-    padding-left: 40px;
-}
-
-.tuple div > label:first-child {
-    float: left;
-}
-.tuple div > label + input {
-    clear: both;
-    float: left;
-}
-.tuple div input + label {
-    display: inline !important;
-    float: none !important;
-    position: absolute;
-    margin-top: 8px;
-    margin-left: 6px;
-    left: 0;
-    font-family: FiraSans-SemiBold;
-    font-size: 12px;
-}
-.tuple .red + label, .tuple .x + label, .tuple .pitch + label, .tuple .width + label {
-    color: #e2334d;
-}
-.tuple .green + label, .tuple .y + label, .tuple .yaw + label, .tuple .height + label {
-    color: #1ac567;
-}
-.tuple .blue + label, .tuple .z + label, .tuple .roll + label {
-    color: #1080b8;
-}
-
-.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus, .tuple .width:focus {
-    outline-color: #e2334d;
-}
-.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus, .tuple .height:focus {
-    outline-color: #1ac567;
-}
-.tuple .blue:focus, .tuple .z:focus, .tuple .roll:focus {
-    outline-color: #1080b8;
-}
-
-.xyz .buttons input {
-    margin-top: 14px;
-}
-.xyz .buttons span {
-    word-wrap: nowrap;
-    white-space: nowrap;
-}
-
 .row .property {
     width: auto;
     display: inline-block;
@@ -984,12 +928,12 @@ div.refresh input[type="button"] {
     width: 50%;
 }
 
-#properties-list fieldset .two-column {
+#properties-list .fieldset .two-column {
     padding-top: 10px;
     display: flex;
 }
 
-#properties-list .two-column fieldset {
+#properties-list .two-column .fieldset {
     width: 50%;
     margin: 0;
     padding: 0;
@@ -1002,7 +946,7 @@ div.refresh input[type="button"] {
     top: -10px;
 }
 
-#properties-list .two-column  fieldset legend {
+#properties-list .two-column  .fieldset div.legend {
     width: 100%;
     margin: 21px -21px 0 -21px;
     padding: 16px 0 0 21px;
@@ -1018,11 +962,11 @@ div.refresh input[type="button"] {
     margin-top: 6px;
 }
 
-fieldset .checkbox-sub-props {
+.fieldset .checkbox-sub-props {
      margin-top: 0;
 }
 
-fieldset .checkbox-sub-props .property:first-child {
+.fieldset .checkbox-sub-props .property:first-child {
     margin-top: 0;
 }
 
@@ -1069,6 +1013,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
 
 body#entity-list-body {
     padding-bottom: 0;
+    margin: 16px;
 }
 
 #entity-list-header {
@@ -1448,7 +1393,6 @@ th#entity-hasScript {
     border-top: none !important;
     box-shadow: none !important;
     margin-bottom: 5px !important;
-    top: -15px;
 }
 
 #properties-base #property-type-icon {
@@ -1467,6 +1411,7 @@ th#entity-hasScript {
     display: inline-block;
 }
 
+/*
 #properties-base #div-property-locked {
     position: absolute;
     top: 6px;
@@ -1487,6 +1432,7 @@ th#entity-hasScript {
 #properties-base #div-property-visible label {
     background-position-y: 1px;
 }
+*/
 
 #properties-base .checkbox label span {
     font-family: HiFi-Glyphs;
@@ -1513,16 +1459,17 @@ th#entity-hasScript {
 }
 
 input#property-scale-button-rescale {
+    margin-top: 6px;
     min-width: 50px;
-    margin-left: 6px;
 }
 input#property-scale-button-reset {
+    margin-top: 6px;
     margin-right: 0;
 }
 
-#property-userData-button-clear,
+#property-userData-button-edit,
 #property-materialData-button-clear {
-    margin-bottom: 10px;
+    margin: 6px 0 6px 0;
 }
 
 #property-userData-static,
@@ -1544,15 +1491,6 @@ input#property-scale-button-reset {
     display: none;
 }
 
-#div-property-serverScripts-status label {
-    position: relative;
-    top: 8px;
-}
-#property-serverScripts-status {
-    position: relative;
-    top: 5px;
-    right: -20px;
-}
 
 #div-property-collisionSoundURL[style*="display: none"] + .property {
     margin-top: 0;
@@ -1637,3 +1575,100 @@ input.rename-entity {
     content: "\e02c";
 }
 
+.container {
+    display: flex;
+    flex-flow: row nowrap;
+    justify-content: space-around;
+    margin-bottom: 8px;
+    min-height: 28px;
+}
+
+.container > label {
+    margin-top: 6px;
+    width: 200px;
+}
+
+.container > div.checkbox {
+    padding-top: 6px;
+}
+
+.container > .value {
+    width: 100%;
+}
+
+.container .row {
+    display: flex;
+    flex-flow: row nowrap;
+}
+
+.container.shrink {
+    width: min-content;
+}
+
+.fstuple {
+    display: flex;
+    flex-flow: row;
+}
+.fstuple input {
+    margin-left: 4px;
+    margin-right: 10px;
+}
+.fstuple label.red, .fstuple label.x {
+    color: #C62147;
+}
+.fstuple label.green, .fstuple label.y {
+    color: #359D85;
+}
+.fstuple label.blue, .fstuple label.z {
+    color: #0093C5;
+}
+
+.xyz.fstuple, .pyr.fstuple {
+    position: relative;
+    left: -12px;
+}
+
+.rgb.fstuple .tuple {
+    display: none;
+}
+
+input.number-slider {
+    background: #575757;
+    border-radius: 4px;
+    color: white;
+}
+
+.fstuple > div {
+    display: flex;
+    align-items: center;
+    justify-content: left;
+}
+
+.flex-row {
+    display: flex;
+    flex-flow: row;
+}
+
+.flex-column {
+    display: flex;
+    flex-flow: column;
+}
+
+.flex-center {
+    align-items: center;
+}
+
+.flex-evenly-spaced {
+    flex: 1;
+}
+
+#property-serverScripts-status {
+    font-family: Raleway-Light;
+    font-size: 14px;
+    margin: 6px 0;
+}
+
+#property-name, #property-id {
+    display: flex;
+    width: 100%;
+}
diff --git a/scripts/system/html/css/jsoneditor.css b/scripts/system/html/css/jsoneditor.css
index ce83b45ff3..eedef60a7f 100644
--- a/scripts/system/html/css/jsoneditor.css
+++ b/scripts/system/html/css/jsoneditor.css
@@ -223,8 +223,6 @@ div.jsoneditor-tree table.jsoneditor-tree {
 
 div.jsoneditor-outer {
   width: 100%;
-  height: 100%;
-  min-height: 150px;
   margin: -35px 0 0 0;
   padding: 0 0 0 0;
   -moz-box-sizing: border-box;
@@ -233,20 +231,18 @@ div.jsoneditor-outer {
   overflow-y: auto;
 }
 
-textarea.jsoneditor-text,
 .ace-jsoneditor {
-  min-height: 150px;
+    min-height: 150px;
+    height: auto !important;
 }
 
 div.jsoneditor-tree {
   width: 100%;
-  height: 100%;
   position: relative;
 }
 
 textarea.jsoneditor-text {
   width: 100%;
-  height: 100%;
   margin: 0;
   -moz-box-sizing: border-box;
   -webkit-box-sizing: border-box;
@@ -931,4 +927,4 @@ table.jsoneditor-search button.jsoneditor-previous {
 
 table.jsoneditor-search button.jsoneditor-previous:hover {
   background-position: -148px -49px;
-}
\ No newline at end of file
+}
diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html
index b56ad346e2..6af8ee992a 100644
--- a/scripts/system/html/entityProperties.html
+++ b/scripts/system/html/entityProperties.html
@@ -28,6 +28,19 @@
 
 <body onload='loaded();'>
     <div id="properties-list">
+        <div class='property container'>
+            <label id='placeholder-property-type'></label>
+            <div class='value'>
+                <div class='row flex-center' style='padding-bottom: 8px;'>
+                    <div id="placeholder-property-name" class="stretch"></div>
+                    <div id="placeholder-property-locked" class="shrink"></div>
+                    <div id="placeholder-property-visible" class="shrink"></div>
+                </div>
+                <div class='row'>
+                    <div id="placeholder-property-id" class="stretch"></div>
+                </div>
+            </div>
+        </div>
         <!-- each property is added at runtime in entityProperties -->
     </div>
 </body>
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js
index bb0c6aa02c..bfd95410ce 100644
--- a/scripts/system/html/js/entityProperties.js
+++ b/scripts/system/html/js/entityProperties.js
@@ -29,7 +29,7 @@ const ICON_FOR_TYPE = {
 
 const DEGREES_TO_RADIANS = Math.PI / 180.0;
 
-const NO_SELECTION = "<i>No selection</i>";
+const NO_SELECTION = "w";
 
 const PROPERTY_SPACE_MODE = {
     ALL: 0,
@@ -46,17 +46,22 @@ const GROUPS = [
                 type: "icon",
                 icons: ICON_FOR_TYPE,
                 propertyID: "type",
+                replaceID: "placeholder-property-type",
             },
             {
                 label: "Name",
                 type: "string",
                 propertyID: "name",
+                placeholder: "Name",
+                replaceID: "placeholder-property-name",
             },
             {
                 label: "ID",
                 type: "string",
                 propertyID: "id",
+                placeholder: "ID",
                 readOnly: true,
+                replaceID: "placeholder-property-id",
             },
             {
                 label: "Description",
@@ -74,16 +79,18 @@ const GROUPS = [
                 propertyID: "parentJointIndex",
             },
             {
-                label: "Locked",
+                label: "",
                 glyph: "&#xe006;",
                 type: "bool",
                 propertyID: "locked",
+                replaceID: "placeholder-property-locked",
             },
             {
-                label: "Visible",
+                label: "",
                 glyph: "&#xe007;",
                 type: "bool",
                 propertyID: "visible",
+                replaceID: "placeholder-property-visible",
             },
         ]
     },
@@ -463,6 +470,7 @@ const GROUPS = [
             {
                 label: "Image",
                 type: "string",
+                placeholder: "URL",
                 propertyID: "image",
             },
         ]
@@ -640,6 +648,7 @@ const GROUPS = [
     {
         id: "particles_emit",
         label: "EMIT",
+        isMinor: true,
         properties: [
             {
                 label: "Emit Rate",
@@ -692,7 +701,7 @@ const GROUPS = [
                 vec3Type: "pyr",
                 step: 0.01,
                 round: 100,
-                subLabels: [ "pitch", "yaw", "roll" ],
+                subLabels: [ "x", "y", "z" ],
                 unit: "deg",
                 propertyID: "emitOrientation",
             },
@@ -706,35 +715,42 @@ const GROUPS = [
     {
         id: "particles_size",
         label: "SIZE",
+        isMinor: true,
         properties: [
             {
+                type: "triple",
                 label: "Size",
-                type: "slider",
-                min: 0,
-                max: 4,
-                step: 0.01,
-                decimals: 2,
-                propertyID: "particleRadius",
-            },
-            {
-                label: "Size Start",
-                type: "slider",
-                min: 0,
-                max: 4,
-                step: 0.01,
-                decimals: 2,
-                propertyID: "radiusStart",
-                fallbackProperty: "particleRadius",
-            },
-            {
-                label: "Size Finish",
-                type: "slider",
-                min: 0,
-                max: 4,
-                step: 0.01,
-                decimals: 2,
-                propertyID: "radiusFinish",
-                fallbackProperty: "particleRadius",
+                properties: [
+                    {
+                        label: "Start",
+                        type: "slider",
+                        min: 0,
+                        max: 4,
+                        step: 0.01,
+                        decimals: 2,
+                        propertyID: "radiusStart",
+                        fallbackProperty: "particleRadius",
+                    },
+                    {
+                        label: "Middle",
+                        type: "slider",
+                        min: 0,
+                        max: 4,
+                        step: 0.01,
+                        decimals: 2,
+                        propertyID: "particleRadius",
+                    },
+                    {
+                        label: "Finish",
+                        type: "slider",
+                        min: 0,
+                        max: 4,
+                        step: 0.01,
+                        decimals: 2,
+                        propertyID: "radiusFinish",
+                        fallbackProperty: "particleRadius",
+                    },
+                ]
             },
             {
                 label: "Size Spread",
@@ -750,24 +766,31 @@ const GROUPS = [
     {
         id: "particles_color",
         label: "COLOR",
+        isMinor: true,
         properties: [
             {
+                type: "triple",
                 label: "Color",
-                type: "color",
-                propertyID: "particleColor",
-                propertyName: "color", // actual entity property name
-            },
-            {
-                label: "Color Start",
-                type: "color",
-                propertyID: "colorStart",
-                fallbackProperty: "color",
-            },
-            {
-                label: "Color Finish",
-                type: "color",
-                propertyID: "colorFinish",
-                fallbackProperty: "color",
+                properties: [
+                    {
+                        label: "Start",
+                        type: "color",
+                        propertyID: "colorStart",
+                        fallbackProperty: "color",
+                    },
+                    {
+                        label: "Middle",
+                        type: "color",
+                        propertyID: "particleColor",
+                        propertyName: "color", // actual entity property name
+                    },
+                    {
+                        label: "Finish",
+                        type: "color",
+                        propertyID: "colorFinish",
+                        fallbackProperty: "color",
+                    },
+                ]
             },
             {
                 label: "Color Spread",
@@ -779,35 +802,42 @@ const GROUPS = [
     {
         id: "particles_alpha",
         label: "ALPHA",
+        isMinor: true,
         properties: [
             {
+                type: "triple",
                 label: "Alpha",
-                type: "slider",
-                min: 0,
-                max: 1,
-                step: 0.01,
-                decimals: 2,
-                propertyID: "alpha",
-            },
-            {
-                label: "Alpha Start",
-                type: "slider",
-                min: 0,
-                max: 1,
-                step: 0.01,
-                decimals: 2,
-                propertyID: "alphaStart",
-                fallbackProperty: "alpha",
-            },
-            {
-                label: "Alpha Finish",
-                type: "slider",
-                min: 0,
-                max: 1,
-                step: 0.01,
-                decimals: 2,
-                propertyID: "alphaFinish",
-                fallbackProperty: "alpha",
+                properties: [
+                    {
+                        label: "Start",
+                        type: "slider",
+                        min: 0,
+                        max: 1,
+                        step: 0.01,
+                        decimals: 2,
+                        propertyID: "alphaStart",
+                        fallbackProperty: "alpha",
+                    },
+                    {
+                        label: "Middle",
+                        type: "slider",
+                        min: 0,
+                        max: 1,
+                        step: 0.01,
+                        decimals: 2,
+                        propertyID: "alpha",
+                    },
+                    {
+                        label: "Finish",
+                        type: "slider",
+                        min: 0,
+                        max: 1,
+                        step: 0.01,
+                        decimals: 2,
+                        propertyID: "alphaFinish",
+                        fallbackProperty: "alpha",
+                    },
+                ]
             },
             {
                 label: "Alpha Spread",
@@ -823,6 +853,7 @@ const GROUPS = [
     {
         id: "particles_acceleration",
         label: "ACCELERATION",
+        isMinor: true,
         properties: [
             {
                 label: "Emit Acceleration",
@@ -847,41 +878,48 @@ const GROUPS = [
     {
         id: "particles_spin",
         label: "SPIN",
+        isMinor: true,
         properties: [
             {
-                label: "Spin",
-                type: "slider",
-                min: -360,
-                max: 360,
-                step: 1,
-                decimals: 0,
-                multiplier: DEGREES_TO_RADIANS,
-                unit: "deg",
-                propertyID: "particleSpin",
-            },
-            {
-                label: "Spin Start",
-                type: "slider",
-                min: -360,
-                max: 360,
-                step: 1,
-                decimals: 0,
-                multiplier: DEGREES_TO_RADIANS,
-                unit: "deg",
-                propertyID: "spinStart",
-                fallbackProperty: "particleSpin",
-            },
-            {
-                label: "Spin Finish",
-                type: "slider",
-                min: -360,
-                max: 360,
-                step: 1,
-                decimals: 0,
-                multiplier: DEGREES_TO_RADIANS,
-                unit: "deg",
-                propertyID: "spinFinish",
-                fallbackProperty: "particleSpin",
+                type: "triple",
+                label: "Alpha",
+                properties: [
+                    {
+                        label: "Start",
+                        type: "slider",
+                        min: -360,
+                        max: 360,
+                        step: 1,
+                        decimals: 0,
+                        multiplier: DEGREES_TO_RADIANS,
+                        unit: "deg",
+                        propertyID: "spinStart",
+                        fallbackProperty: "particleSpin",
+                    },
+                    {
+                        label: "Middle",
+                        type: "slider",
+                        min: -360,
+                        max: 360,
+                        step: 1,
+                        decimals: 0,
+                        multiplier: DEGREES_TO_RADIANS,
+                        unit: "deg",
+                        propertyID: "particleSpin",
+                    },
+                    {
+                        label: "Finish",
+                        type: "slider",
+                        min: -360,
+                        max: 360,
+                        step: 1,
+                        decimals: 0,
+                        multiplier: DEGREES_TO_RADIANS,
+                        unit: "deg",
+                        propertyID: "spinFinish",
+                        fallbackProperty: "particleSpin",
+                    },
+                ]
             },
             {
                 label: "Spin Spread",
@@ -904,51 +942,64 @@ const GROUPS = [
     {
         id: "particles_constraints",
         label: "CONSTRAINTS",
+        isMinor: true,
         properties: [
             {
-                label: "Horizontal Angle Start",
-                type: "slider",
-                min: 0,
-                max: 180,
-                step: 1,
-                decimals: 0,
-                multiplier: DEGREES_TO_RADIANS,
-                unit: "deg",
-                propertyID: "polarStart",
+                type: "triple",
+                label: "Horizontal Angle",
+                properties: [
+                    {
+                        label: "Start",
+                        type: "slider",
+                        min: -180,
+                        max: 0,
+                        step: 1,
+                        decimals: 0,
+                        multiplier: DEGREES_TO_RADIANS,
+                        unit: "deg",
+                        propertyID: "polarStart",
+                    },
+                    {
+                        label: "Finish",
+                        type: "slider",
+                        min: 0,
+                        max: 180,
+                        step: 1,
+                        decimals: 0,
+                        multiplier: DEGREES_TO_RADIANS,
+                        unit: "deg",
+                        propertyID: "polarFinish",
+                    },
+                ],
             },
             {
-                label: "Horizontal Angle Finish",
-                type: "slider",
-                min: 0,
-                max: 180,
-                step: 1,
-                decimals: 0,
-                multiplier: DEGREES_TO_RADIANS,
-                unit: "deg",
-                propertyID: "polarFinish",
-            },
-            {
-                label: "Vertical Angle Start",
-                type: "slider",
-                min: -180,
-                max: 0,
-                step: 1,
-                decimals: 0,
-                multiplier: DEGREES_TO_RADIANS,
-                unit: "deg",
-                propertyID: "azimuthStart",
-            },
-            {
-                label: "Vertical Angle Finish",
-                type: "slider",
-                min: 0,
-                max: 180,
-                step: 1,
-                decimals: 0,
-                multiplier: DEGREES_TO_RADIANS,
-                unit: "deg",
-                propertyID: "azimuthFinish",
-            },
+                type: "triple",
+                label: "Vertical Angle",
+                properties: [
+                    {
+                        label: "Start",
+                        type: "slider",
+                        min: 0,
+                        max: 180,
+                        step: 1,
+                        decimals: 0,
+                        multiplier: DEGREES_TO_RADIANS,
+                        unit: "deg",
+                        propertyID: "azimuthStart",
+                    },
+                    {
+                        label: "Finish",
+                        type: "slider",
+                        min: 0,
+                        max: 180,
+                        step: 1,
+                        decimals: 0,
+                        multiplier: DEGREES_TO_RADIANS,
+                        unit: "deg",
+                        propertyID: "azimuthFinish",
+                    },
+                ]
+            }
         ]
     },
     {
@@ -981,7 +1032,7 @@ const GROUPS = [
                 vec3Type: "pyr",
                 step: 0.1,
                 decimals: 4,
-                subLabels: [ "pitch", "yaw", "roll" ],
+                subLabels: [ "x", "y", "z" ],
                 unit: "deg",
                 propertyID: "rotation",
                 spaceMode: PROPERTY_SPACE_MODE.WORLD,
@@ -992,7 +1043,7 @@ const GROUPS = [
                 vec3Type: "pyr",
                 step: 0.1,
                 decimals: 4,
-                subLabels: [ "pitch", "yaw", "roll" ],
+                subLabels: [ "x", "y", "z" ],
                 unit: "deg",
                 propertyID: "localRotation",
                 spaceMode: PROPERTY_SPACE_MODE.LOCAL,
@@ -1065,6 +1116,7 @@ const GROUPS = [
             },
             {
                 label: "Clone Lifetime",
+                indentedLabel: true,
                 type: "number",
                 unit: "s",
                 propertyID: "cloneLifetime",
@@ -1072,18 +1124,21 @@ const GROUPS = [
             },
             {
                 label: "Clone Limit",
+                indentedLabel: true,
                 type: "number",
                 propertyID: "cloneLimit",
                 showPropertyRule: { "cloneable": "true" },
             },
             {
                 label: "Clone Dynamic",
+                indentedLabel: true,
                 type: "bool",
                 propertyID: "cloneDynamic",
                 showPropertyRule: { "cloneable": "true" },
             },
             {
                 label: "Clone Avatar Entity",
+                indentedLabel: true,
                 type: "bool",
                 propertyID: "cloneAvatarEntity",
                 showPropertyRule: { "cloneable": "true" },
@@ -1120,6 +1175,12 @@ const GROUPS = [
                 buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadServerScripts } ],
                 propertyID: "serverScripts",
             },
+            {
+                label: "Server Script Status",
+                type: "placeholder",
+                indentedLabel: true,
+                propertyID: "serverScriptStatus",
+            },
             {
                 label: "Lifetime",
                 type: "number",
@@ -1146,12 +1207,6 @@ const GROUPS = [
                 inverse: true,
                 propertyID: "collisionless",
             },
-            {
-                label: "Collides With",
-                type: "sub-header",
-                propertyID: "collidesWithHeader", // not actually a property but used for naming/storing this element
-                showPropertyRule: { "collisionless": "false" },
-            },
             {
                 label: "Static Entities",
                 type: "bool",
@@ -1230,7 +1285,7 @@ const GROUPS = [
                 vec3Type: "pyr",
                 multiplier: DEGREES_TO_RADIANS,
                 decimals: 4,
-                subLabels: [ "pitch", "yaw", "roll" ],
+                subLabels: [ "x", "y", "z" ],
                 unit: "deg/s",
                 propertyID: "localAngularVelocity",
             },
@@ -1361,6 +1416,11 @@ var lastEntityID = null;
 var createAppTooltip = new CreateAppTooltip();
 let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL;
 
+function createElementFromHTML(htmlString) {
+    var elTemplate = document.createElement('template');
+    elTemplate.innerHTML = htmlString.trim();
+    return elTemplate.content.firstChild;
+}
 
 /**
  * GENERAL PROPERTY/GROUP FUNCTIONS
@@ -1433,8 +1493,8 @@ function disableProperties() {
 }
 
 function showPropertyElement(propertyID, show) {
-    let elProperty = properties[propertyID].elProperty;
-    elProperty.style.display = show ? "table" : "none";
+    let elProperty = properties[propertyID].elContainer;
+    elProperty.style.display = show ? "flex" : "none";
 }
 
 function resetProperties() {
@@ -1491,7 +1551,6 @@ function resetProperties() {
             }
             case 'icon': {
                 property.elSpan.style.display = "none";
-                property.elLabel.innerHTML = propertyData.label;
                 break;
             }
             case 'texture': {
@@ -1692,23 +1751,23 @@ function createImageURLUpdateFunction(propertyName, isParticleProperty) {
  * PROPERTY ELEMENT CREATION FUNCTIONS
  */
 
-function createStringProperty(property, elProperty, elLabel) {    
+function createStringProperty(property, elProperty) {    
     let propertyName = property.name;
     let elementID = property.elementID;
     let propertyData = property.data;
     
-    elProperty.className = "property text";
+    elProperty.className = "text";
     
-    let elInput = document.createElement('input');
-    elInput.setAttribute("id", elementID);
-    elInput.setAttribute("type", "text"); 
-    if (propertyData.readOnly) {
-        elInput.readOnly = true;
-    }
+    let elInput = createElementFromHTML(`
+        <input id="${elementID}"
+               type="text"
+               ${propertyData.placeholder ? 'placeholder="' + propertyData.placeholder + '"' : ''}
+               ${propertyData.readOnly ? 'readonly' : ''}></input>
+        `)
+
     
     elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
     
-    elProperty.appendChild(elLabel);
     elProperty.appendChild(elInput);
     
     if (propertyData.buttons !== undefined) {
@@ -1718,18 +1777,18 @@ function createStringProperty(property, elProperty, elLabel) {
     return elInput;
 }
 
-function createBoolProperty(property, elProperty, elLabel) {   
+function createBoolProperty(property, elProperty) {   
     let propertyName = property.name;
     let elementID = property.elementID;
     let propertyData = property.data;
     
-    elProperty.className = "property checkbox";
+    elProperty.className = "checkbox";
                         
     if (propertyData.glyph !== undefined) {
-        elLabel.innerText = " " + propertyData.label;
         let elSpan = document.createElement('span');
         elSpan.innerHTML = propertyData.glyph;
-        elLabel.insertBefore(elSpan, elLabel.firstChild);
+        elSpan.className = 'icon';
+        elProperty.appendChild(elSpan);
     }
     
     let elInput = document.createElement('input');
@@ -1737,7 +1796,7 @@ function createBoolProperty(property, elProperty, elLabel) {
     elInput.setAttribute("type", "checkbox");
     
     elProperty.appendChild(elInput);
-    elProperty.appendChild(elLabel);
+    elProperty.appendChild(createElementFromHTML(`<label for=${elementID}>&nbsp;</label>`));
     
     let subPropertyOf = propertyData.subPropertyOf;
     if (subPropertyOf !== undefined) {
@@ -1751,14 +1810,12 @@ function createBoolProperty(property, elProperty, elLabel) {
     return elInput;
 }
 
-function createNumberProperty(property, elProperty, elLabel) { 
+function createNumberProperty(property, elProperty) { 
     let propertyName = property.name;
     let elementID = property.elementID;
     let propertyData = property.data;
     
-    elProperty.className = "property number";
-                        
-    addUnit(propertyData.unit, elLabel);
+    elProperty.className = "number";
     
     let elInput = document.createElement('input');
     elInput.setAttribute("id", elementID);
@@ -1780,7 +1837,6 @@ function createNumberProperty(property, elProperty, elLabel) {
     
     elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty));
     
-    elProperty.appendChild(elLabel);
     elProperty.appendChild(elInput);
     
     if (propertyData.buttons !== undefined) {
@@ -1790,10 +1846,10 @@ function createNumberProperty(property, elProperty, elLabel) {
     return elInput;
 }
 
-function createSliderProperty(property, elProperty, elLabel) {  
+function createSliderProperty(property, elProperty) {  
     let propertyData = property.data;
     
-    elProperty.className = "property range";
+    elProperty.className = "range";
     
     let elDiv = document.createElement("div");
     elDiv.className = "slider-wrapper";
@@ -1843,7 +1899,6 @@ function createSliderProperty(property, elProperty, elLabel) {
         updateProperty(property.name, sliderValue, property.isParticleProperty);
     };
 
-    elDiv.appendChild(elLabel);
     elDiv.appendChild(elSlider);
     elDiv.appendChild(elInput);
     elProperty.appendChild(elDiv);
@@ -1854,26 +1909,23 @@ function createSliderProperty(property, elProperty, elLabel) {
     return elResult;
 }
 
-function createVec3Property(property, elProperty, elLabel) {
+function createVec3Property(property, elProperty) {
     let propertyName = property.name;
     let elementID = property.elementID;
     let propertyData = property.data;
 
-    elProperty.className = "property " + propertyData.vec3Type + " fstuple";
+    elProperty.className = propertyData.vec3Type + " fstuple";
     
-    let elTuple = document.createElement('div');
-    elTuple.className = "tuple";
+    //let elTuple = document.createElement('div');
+    //elTuple.className = "tuple";
     
-    addUnit(propertyData.unit, elLabel);
+    //elProperty.appendChild(elTuple);
     
-    elProperty.appendChild(elLabel);
-    elProperty.appendChild(elTuple);
-    
-    let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], 
+    let elInputX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], 
                                           propertyData.min, propertyData.max, propertyData.step);
-    let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], 
+    let elInputY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], 
                                           propertyData.min, propertyData.max, propertyData.step);
-    let elInputZ = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], 
+    let elInputZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], 
                                           propertyData.min, propertyData.max, propertyData.step);
     
     let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ, 
@@ -1889,19 +1941,16 @@ function createVec3Property(property, elProperty, elLabel) {
     return elResult;
 }
 
-function createVec2Property(property, elProperty, elLabel) {  
+function createVec2Property(property, elProperty) {  
     let propertyName = property.name;
     let elementID = property.elementID;
     let propertyData = property.data;
     
-    elProperty.className = "property " + propertyData.vec2Type + " fstuple";
+    elProperty.className = propertyData.vec2Type + " fstuple";
                         
     let elTuple = document.createElement('div');
     elTuple.className = "tuple";
     
-    addUnit(propertyData.unit, elLabel);
-    
-    elProperty.appendChild(elLabel);
     elProperty.appendChild(elTuple);
     
     let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], 
@@ -1920,11 +1969,11 @@ function createVec2Property(property, elProperty, elLabel) {
     return elResult;
 }
 
-function createColorProperty(property, elProperty, elLabel) {
+function createColorProperty(property, elProperty) {
     let propertyName = property.name;
     let elementID = property.elementID;
     
-    elProperty.className = "property rgb fstuple";
+    elProperty.className = "rgb fstuple";
     
     let elColorPicker = document.createElement('div');
     elColorPicker.className = "color-picker";
@@ -1934,7 +1983,6 @@ function createColorProperty(property, elProperty, elLabel) {
     elTuple.className = "tuple";
     
     elProperty.appendChild(elColorPicker);
-    elProperty.appendChild(elLabel);
     elProperty.appendChild(elTuple);
     
     let elInputR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP);
@@ -1950,7 +1998,7 @@ function createColorProperty(property, elProperty, elLabel) {
     let colorPickerID = "#" + elementID;
     colorPickers[colorPickerID] = $(colorPickerID).colpick({
         colorScheme: 'dark',
-        layout: 'hex',
+        layout: 'rgbhex',
         color: '000000',
         submit: false, // We don't want to have a submission button
         onShow: function(colpick) {
@@ -1980,12 +2028,12 @@ function createColorProperty(property, elProperty, elLabel) {
     return elResult;
 }
 
-function createDropdownProperty(property, propertyID, elProperty, elLabel) { 
+function createDropdownProperty(property, propertyID, elProperty) { 
     let propertyName = property.name;
     let elementID = property.elementID;
     let propertyData = property.data;
     
-    elProperty.className = "property dropdown";
+    elProperty.className = "dropdown";
                         
     let elInput = document.createElement('select');
     elInput.setAttribute("id", elementID);
@@ -2000,24 +2048,17 @@ function createDropdownProperty(property, propertyID, elProperty, elLabel) {
     
     elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
     
-    elProperty.appendChild(elLabel);
     elProperty.appendChild(elInput);
     
     return elInput;
 }
 
-function createTextareaProperty(property, elProperty, elLabel) {   
+function createTextareaProperty(property, elProperty) {   
     let propertyName = property.name;
     let elementID = property.elementID;
     let propertyData = property.data;
     
-    elProperty.className = "property textarea";
-                        
-    elProperty.appendChild(elLabel);
-    
-    if (propertyData.buttons !== undefined) {
-        addButtons(elProperty, elementID, propertyData.buttons, true);
-    }
+    elProperty.className = "textarea";
     
     let elInput = document.createElement('textarea');
     elInput.setAttribute("id", elementID);
@@ -2028,35 +2069,35 @@ function createTextareaProperty(property, elProperty, elLabel) {
     elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
     
     elProperty.appendChild(elInput);
+                        
+    if (propertyData.buttons !== undefined) {
+        addButtons(elProperty, elementID, propertyData.buttons, true);
+    }
     
     return elInput;
 }
 
-function createIconProperty(property, elProperty, elLabel) { 
+function createIconProperty(property, elProperty) { 
     let elementID = property.elementID;
     let propertyData = property.data;
     
-    elProperty.className = "property value";
-    
-    elLabel.setAttribute("id", elementID);
-    elLabel.innerHTML = " " + propertyData.label;
+    elProperty.className = "value";
     
     let elSpan = document.createElement('span');
     elSpan.setAttribute("id", elementID + "-icon");
+    elSpan.className = 'icon';
 
     elProperty.appendChild(elSpan);
-    elProperty.appendChild(elLabel);
     
     let elResult = [];
     elResult[ICON_ELEMENTS.ICON] = elSpan;
-    elResult[ICON_ELEMENTS.LABEL] = elLabel;
     return elResult;
 }
 
-function createTextureProperty(property, elProperty, elLabel) { 
+function createTextureProperty(property, elProperty) { 
     let elementID = property.elementID;
     
-    elProperty.className = "property texture";
+    elProperty.className = "texture";
     
     let elDiv = document.createElement("div");
     let elImage = document.createElement("img");
@@ -2097,9 +2138,8 @@ function createTextureProperty(property, elProperty, elLabel) {
     };
     elInput.onchange = elInput.oninput;
 
-    elProperty.appendChild(elLabel);
-    elProperty.appendChild(elDiv);
     elProperty.appendChild(elInput);
+    elProperty.appendChild(elDiv);
    
     let elResult = [];
     elResult[TEXTURE_ELEMENTS.IMAGE] = elImage;
@@ -2111,11 +2151,10 @@ function createButtonsProperty(property, elProperty, elLabel) {
     let elementID = property.elementID;
     let propertyData = property.data;
     
-    elProperty.className = "property text";
+    elProperty.className = "text";
                         
     let hasLabel = propertyData.label !== undefined;
     if (hasLabel) {
-        elProperty.appendChild(elLabel);
     }
     
     if (propertyData.buttons !== undefined) {
@@ -2130,33 +2169,25 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max,
     
     let elDiv = document.createElement('div');
     let elLabel = document.createElement('label');
-    elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1) + ":";
+    elLabel.className = subLabel;
+    elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1);
     elLabel.setAttribute("for", elementID);
     
     let elInput = document.createElement('input');
-    elInput.className = subLabel;
+    elInput.className = subLabel + " number-slider";
     elInput.setAttribute("id", elementID);
     elInput.setAttribute("type", "number");
     elInput.setAttribute("min", min);
     elInput.setAttribute("max", max);
     elInput.setAttribute("step", step);
     
-    elDiv.appendChild(elInput);
     elDiv.appendChild(elLabel);
+    elDiv.appendChild(elInput);
     elTuple.appendChild(elDiv);
     
     return elInput;
 }
 
-function addUnit(unit, elLabel) {
-    if (unit !== undefined) {
-        let elSpan = document.createElement('span');
-        elSpan.className = "unit";
-        elSpan.innerHTML = unit;
-        elLabel.appendChild(elSpan);
-    }
-}
-
 function addButtons(elProperty, propertyID, buttons, newRow) {
     let elDiv = document.createElement('div');
     elDiv.className = "row";
@@ -2707,32 +2738,125 @@ function updateVisibleSpaceModeProperties() {
             let propertySpaceMode = property.spaceMode;
             if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) {
                 showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode);
+            } else {
+                showPropertyElement(propertyID, true);
             }
         }
     }
 }
 
+function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) {
+    let property = { 
+        data: propertyData, 
+        elementID: propertyElementID, 
+        name: propertyName,
+        elProperty: elProperty,
+    };
+    let propertyType = propertyData.type;
+
+    switch (propertyType) {
+        case 'string': {
+            property.elInput = createStringProperty(property, elProperty);
+            break;
+        }
+        case 'bool': {
+            property.elInput = createBoolProperty(property, elProperty);
+            break;
+        }
+        case 'number': {
+            property.elInput = createNumberProperty(property, elProperty);
+            break;
+        }
+        case 'slider': {
+            let elSlider = createSliderProperty(property, elProperty);
+            property.elSlider = elSlider[SLIDER_ELEMENTS.SLIDER];
+            property.elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT];
+            break;
+        }
+        case 'vec3': {
+            let elVec3 = createVec3Property(property, elProperty);  
+            property.elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT];
+            property.elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT];
+            property.elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT];
+            break;
+        }
+        case 'vec2': {
+            let elVec2 = createVec2Property(property, elProperty);  
+            property.elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT];
+            property.elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT];
+            break;
+        }
+        case 'color': {
+            let elColor = createColorProperty(property, elProperty);  
+            property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER];
+            property.elInputR = elColor[COLOR_ELEMENTS.RED_INPUT];
+            property.elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT];
+            property.elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; 
+            break;
+        }
+        case 'dropdown': {
+            property.elInput = createDropdownProperty(property, propertyID, elProperty);
+            break;
+        }
+        case 'textarea': {
+            property.elInput = createTextareaProperty(property, elProperty);
+            break;
+        }
+        case 'icon': {
+            let elIcon = createIconProperty(property, elProperty);
+            property.elSpan = elIcon[ICON_ELEMENTS.ICON];
+            property.elLabel = elIcon[ICON_ELEMENTS.LABEL];
+            break;
+        }
+        case 'texture': {
+            let elTexture = createTextureProperty(property, elProperty);
+            property.elImage = elTexture[TEXTURE_ELEMENTS.IMAGE];
+            property.elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT];
+            break;
+        }
+        case 'buttons': {
+            property.elProperty = createButtonsProperty(property, elProperty);
+            break;
+        }
+        case 'placeholder':
+        case 'sub-header': {
+            break;
+        }
+        default: {
+            console.log("EntityProperties - Unknown property type " + 
+                propertyType + " set to property " + propertyID);
+            break;
+        }
+    }
+
+    return property;
+}
+
 function loaded() {
     openEventBridge(function() {
         let elPropertiesList = document.getElementById("properties-list");
+
+        let templatePropertyRow = document.getElementById('property-row');
         
-        GROUPS.forEach(function(group) {            
+        GROUPS.forEach(function(group) {
             let elGroup;
             if (group.addToGroup !== undefined) {
                 let fieldset = document.getElementById("properties-" + group.addToGroup);
                 elGroup = document.createElement('div');
                 fieldset.appendChild(elGroup);
             } else {
-                elGroup = document.createElement('fieldset');
-                elGroup.className = "major";
+                elGroup = document.createElement('div');
+                elGroup.className = 'section ' + (group.isMinor ? "minor" : "major");
                 elGroup.setAttribute("id", "properties-" + group.id);
                 elPropertiesList.appendChild(elGroup);
             }       
 
             if (group.label !== undefined) {
-                let elLegend = document.createElement('legend');
+                let elLegend = document.createElement('div');
                 elLegend.className = "section-header";
-                elLegend.innerText = group.label;
+
+                elLegend.appendChild(createElementFromHTML(`<div class="label">${group.label}</div>`));
+
                 let elSpan = document.createElement('span');
                 elSpan.className = ".collapse-icon";
                 elSpan.innerText = "M";
@@ -2748,145 +2872,119 @@ function loaded() {
                 let propertyElementID = "property-" + propertyID;
                 propertyElementID = propertyElementID.replace('.', '-');
                 
-                let elProperty;
-                if (propertyType === "sub-header") {
-                    elProperty = document.createElement('legend');
-                    elProperty.className = "sub-section-header";
-                    elProperty.innerText = propertyData.label;
-                } else {
-                    elProperty = document.createElement('div');
-                    elProperty.setAttribute("id", "div-" + propertyElementID);
-                }
+                let elContainer, elLabel;
                 
-                if (group.twoColumn && propertyData.column !== undefined && propertyData.column !== -1) {
-                    let columnName = group.id + "column" + propertyData.column;
-                    let elColumn = document.getElementById(columnName);
-                    if (!elColumn) {
-                        let columnDivName = group.id + "columnDiv";
-                        let elColumnDiv = document.getElementById(columnDivName);
-                        if (!elColumnDiv) {
-                            elColumnDiv = document.createElement('div');
-                            elColumnDiv.className = "two-column";
-                            elColumnDiv.setAttribute("id", group.id + "columnDiv");
-                            elGroup.appendChild(elColumnDiv);
-                        }
-                        elColumn = document.createElement('fieldset');
-                        elColumn.className = "column";
-                        elColumn.setAttribute("id", columnName);
-                        elColumnDiv.appendChild(elColumn);
+                if (propertyData.replaceID === undefined) {
+                    // Create subheader, or create new property and append it.
+                    if (propertyType === "sub-header") {
+                        elContainer = createElementFromHTML(
+                            `<div class="sub-section-header legend">${propertyData.label}</div>`);
+                    } else {
+                        elContainer = document.createElement('div');
+                        elContainer.setAttribute("id", "div-" + propertyElementID);
+                        elContainer.className = 'property container';
                     }
-                    elColumn.appendChild(elProperty);
-                } else {
-                    elGroup.appendChild(elProperty);
-                }
-                
-                let elLabel = document.createElement('label');
-                elLabel.innerText = propertyData.label;
-                elLabel.setAttribute("for", propertyElementID);
 
-                createAppTooltip.registerTooltipElement(elLabel, propertyID);
-                
-                let property = { 
-                    data: propertyData, 
-                    elementID: propertyElementID, 
-                    name: propertyName,
-                    isParticleProperty: group.id.includes("particles"),
-                    elProperty,
-                    spaceMode: propertySpaceMode,
-                };
-                properties[propertyID] = property;
-                
-                switch (propertyType) {
-                    case 'string': {
-                        properties[propertyID].elInput = createStringProperty(property, elProperty, elLabel);
-                        break;
+                    if (group.twoColumn && propertyData.column !== undefined && propertyData.column !== -1) {
+                        let columnName = group.id + "column" + propertyData.column;
+                        let elColumn = document.getElementById(columnName);
+                        if (!elColumn) {
+                            let columnDivName = group.id + "columnDiv";
+                            let elColumnDiv = document.getElementById(columnDivName);
+                            if (!elColumnDiv) {
+                                elColumnDiv = document.createElement('div');
+                                elColumnDiv.className = "two-column";
+                                elColumnDiv.setAttribute("id", group.id + "columnDiv");
+                                elGroup.appendChild(elColumnDiv);
+                            }
+                            elColumn = document.createElement('fieldset');
+                            elColumn.className = "column";
+                            elColumn.setAttribute("id", columnName);
+                            elColumnDiv.appendChild(elColumn);
+                        }
+                        elColumn.appendChild(elContainer);
+                    } else {
+                        elGroup.appendChild(elContainer);
                     }
-                    case 'bool': {
-                        properties[propertyID].elInput = createBoolProperty(property, elProperty, elLabel);
-                        break;
-                    }
-                    case 'number': {
-                        properties[propertyID].elInput = createNumberProperty(property, elProperty, elLabel);
-                        break;
-                    }
-                    case 'slider': {
-                        let elSlider = createSliderProperty(property, elProperty, elLabel);
-                        properties[propertyID].elSlider = elSlider[SLIDER_ELEMENTS.SLIDER];
-                        properties[propertyID].elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT];
-                        break;
-                    }
-                    case 'vec3': {
-                        let elVec3 = createVec3Property(property, elProperty, elLabel);  
-                        properties[propertyID].elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT];
-                        properties[propertyID].elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT];
-                        properties[propertyID].elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT];
-                        break;
-                    }
-                    case 'vec2': {
-                        let elVec2 = createVec2Property(property, elProperty, elLabel);  
-                        properties[propertyID].elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT];
-                        properties[propertyID].elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT];
-                        break;
-                    }
-                    case 'color': {
-                        let elColor = createColorProperty(property, elProperty, elLabel);  
-                        properties[propertyID].elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER];
-                        properties[propertyID].elInputR = elColor[COLOR_ELEMENTS.RED_INPUT];
-                        properties[propertyID].elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT];
-                        properties[propertyID].elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; 
-                        break;
-                    }
-                    case 'dropdown': {
-                        properties[propertyID].elInput = createDropdownProperty(property, propertyID, elProperty, elLabel);
-                        break;
-                    }
-                    case 'textarea': {
-                        properties[propertyID].elInput = createTextareaProperty(property, elProperty, elLabel);
-                        break;
-                    }
-                    case 'icon': {
-                        let elIcon = createIconProperty(property, elProperty, elLabel);
-                        properties[propertyID].elSpan = elIcon[ICON_ELEMENTS.ICON];
-                        properties[propertyID].elLabel = elIcon[ICON_ELEMENTS.LABEL];
-                        break;
-                    }
-                    case 'texture': {
-                        let elTexture = createTextureProperty(property, elProperty, elLabel);
-                        properties[propertyID].elImage = elTexture[TEXTURE_ELEMENTS.IMAGE];
-                        properties[propertyID].elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT];
-                        break;
-                    }
-                    case 'buttons': {
-                        properties[propertyID].elProperty = createButtonsProperty(property, elProperty, elLabel);
-                        break;
-                    }
-                    case 'sub-header': {
-                        break;
-                    }
-                    default: {
-                        console.log("EntityProperties - Unknown property type " + 
-                                    propertyType + " set to property " + propertyID);
-                        break;
+
+                    elLabel = document.createElement('label');
+                    elLabel.setAttribute("for", propertyElementID);
+                    if (propertyData.indentedLabel || propertyData.showPropertyRule !== undefined) {
+                        let elSpan = document.createElement('span');
+                        elSpan.className = 'indented';
+                        elSpan.innerText = propertyData.label;
+                        elLabel.appendChild(elSpan);
+                    } else {
+                        elLabel.innerText = propertyData.label;
                     }
+                    elContainer.appendChild(elLabel);
+                } else {
+                    elContainer = document.getElementById(propertyData.replaceID);
                 }
-                
-                let showPropertyRule = propertyData.showPropertyRule;
-                if (showPropertyRule !== undefined) {
-                    let dependentProperty = Object.keys(showPropertyRule)[0];
-                    let dependentPropertyValue = showPropertyRule[dependentProperty];
-                    if (properties[dependentProperty] === undefined) {
-                        properties[dependentProperty] = {};
+
+
+                if (elLabel) {
+                    createAppTooltip.registerTooltipElement(elLabel, propertyID);
+                }
+
+                let elProperty = createElementFromHTML('<div style="width: 100%;"></div>');
+                elContainer.appendChild(elProperty);
+
+                if (propertyType == 'triple') {
+                    elProperty.className = 'flex-row';
+                    for (var i = 0; i < propertyData.properties.length; ++i) {
+                        let innerPropertyData = propertyData.properties[i];
+
+                        let elWrapper = createElementFromHTML('<div class="flex-column flex-center triple-item"><div></div></div>');
+
+                        let propertyID = innerPropertyData.propertyID;               
+                        let propertyName = innerPropertyData.propertyName !== undefined ? innerPropertyData.propertyName : propertyID;
+                        let propertyElementID = "property-" + propertyID;
+                        propertyElementID = propertyElementID.replace('.', '-');
+
+                        elWrapper.appendChild(createElementFromHTML(`<div class="triple-label">${innerPropertyData.label}</div>`));
+                        elProperty.appendChild(elWrapper);
+
+                        let property = createProperty(innerPropertyData, propertyElementID, propertyName, propertyID, elWrapper.childNodes[0]);
+                        property.isParticleProperty = group.id.includes("particles");
+                        property.elContainer = elContainer;
+                        property.spaceMode = propertySpaceMode;
+                        
+                        if (property.type !== 'placeholder') {
+                            properties[propertyID] = property;
+                        }
                     }
-                    if (properties[dependentProperty].showPropertyRules === undefined) {
-                        properties[dependentProperty].showPropertyRules = {};
+                } else {
+                    let property = createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty);
+                    property.isParticleProperty = group.id.includes("particles");
+                    property.elContainer = elContainer;
+                    property.spaceMode = propertySpaceMode;
+                    
+                    if (property.type !== 'placeholder') {
+                        properties[propertyID] = property;
+                    }
+                    
+                    let showPropertyRule = propertyData.showPropertyRule;
+                    if (showPropertyRule !== undefined) {
+                        let dependentProperty = Object.keys(showPropertyRule)[0];
+                        let dependentPropertyValue = showPropertyRule[dependentProperty];
+                        if (properties[dependentProperty] === undefined) {
+                            properties[dependentProperty] = {};
+                        }
+                        if (properties[dependentProperty].showPropertyRules === undefined) {
+                            properties[dependentProperty].showPropertyRules = {};
+                        }
+                        properties[dependentProperty].showPropertyRules[propertyID] = dependentPropertyValue;
                     }
-                    properties[dependentProperty].showPropertyRules[propertyID] = dependentPropertyValue;
                 }
             });
             
             elGroups[group.id] = elGroup;
         });
 
+        let minorSections = document.querySelectorAll(".section.minor");
+        minorSections[minorSections.length - 1].className += " last";
+
         updateVisibleSpaceModeProperties();
         
         if (window.EventBridge !== undefined) {
@@ -2925,6 +3023,10 @@ function loaded() {
                         
                         resetProperties();
                         showGroupsForType("None");
+
+                        let elIcon = properties.type.elSpan;
+                        elIcon.innerText = NO_SELECTION;
+                        elIcon.style.display = 'inline-block';
                 
                         deleteJSONEditor();
                         getPropertyInputElement("userData").value = "";
@@ -3114,7 +3216,6 @@ function loaded() {
                                 case 'icon': {
                                     property.elSpan.innerHTML = propertyData.icons[propertyValue];
                                     property.elSpan.style.display = "inline-block";
-                                    property.elLabel.innerHTML = propertyValue;
                                     break;
                                 }
                                 case 'texture': {
@@ -3231,34 +3332,27 @@ function loaded() {
         }
         
         // Server Script Status
-        let serverScriptProperty = properties["serverScripts"];
-        let elServerScript = serverScriptProperty.elInput;
-        let serverScriptElementID = serverScriptProperty.elementID;
-        let serverScriptStatusElementID = serverScriptElementID + "-status";
-        let elDiv = document.createElement('div');
-        elDiv.className = "property";
-        elDiv.setAttribute("id", "div-" + serverScriptStatusElementID);
-        let elLabel = document.createElement('label');
-        elLabel.setAttribute("for", serverScriptStatusElementID);
-        elLabel.innerText = "Server Script Status";
-        createAppTooltip.registerTooltipElement(elLabel, "serverScriptsStatus");
-        let elServerScriptStatus = document.createElement('span');
+        let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus');
+        let elServerScriptStatusContainer = document.getElementById('div-property-serverScriptStatus').childNodes[1];
+        let serverScriptStatusElementID = 'property-serverScripts-status';
+        createAppTooltip.registerTooltipElement(elServerScriptStatusOuter.childNodes[0], "serverScriptsStatus");
+        let elServerScriptStatus = document.createElement('div');
         elServerScriptStatus.setAttribute("id", serverScriptStatusElementID);
-        elDiv.appendChild(elLabel);
-        elDiv.appendChild(elServerScriptStatus);
-        elServerScript.parentNode.appendChild(elDiv);
+        elServerScriptStatusContainer.appendChild(elServerScriptStatus);
         
         // Server Script Error
+        let elServerScripts = getPropertyInputElement("serverScripts");
         elDiv = document.createElement('div');
         elDiv.className = "property";
         let elServerScriptError = document.createElement('textarea');
-        elServerScriptError.setAttribute("id", serverScriptElementID + "-error");
+        let serverScriptErrorElementID = 'property-serverScripts-error';
+        elServerScriptError.setAttribute("id", serverScriptErrorElementID);
         elDiv.appendChild(elServerScriptError);
-        elServerScript.parentNode.appendChild(elDiv);
+        elServerScriptStatusContainer.appendChild(elDiv);
         
         let elScript = getPropertyInputElement("script");
-        elScript.parentNode.className = "property url refresh";
-        elServerScript.parentNode.className = "property url refresh";
+        elScript.parentNode.className = "url refresh";
+        elServerScripts.parentNode.className = "url refresh";
             
         // User Data
         let userDataProperty = properties["userData"];
@@ -3318,7 +3412,7 @@ function loaded() {
         let elCollapsible = document.getElementsByClassName("section-header");
 
         let toggleCollapsedEvent = function(event) {
-            let element = event.target.parentNode.parentNode;
+            let element = this.parentNode;
             let isCollapsed = element.dataset.collapsed !== "true";
             element.dataset.collapsed = isCollapsed ? "true" : false;
             element.setAttribute("collapsed", isCollapsed ? "true" : "false");
@@ -3327,7 +3421,7 @@ function loaded() {
 
         for (let collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) {
             let curCollapsibleElement = elCollapsible[collapseIndex];
-            curCollapsibleElement.getElementsByTagName('span')[0].addEventListener("click", toggleCollapsedEvent, true);
+            curCollapsibleElement.addEventListener("click", toggleCollapsedEvent, true);
         }
         
         // Textarea scrollbars