Portal-Vue CSS editor
Use Portal-Vue to render a style tag in <head>
by Simon Herteby
HTML
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/portal-vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/sortable/1.4.2/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/15.0.0/vuedraggable.js"></script>
<div id="vue">
<draggable v-model="styles">
<div v-for="style, i in styles" class="style">
<div>
<input v-model="style.selector">
<button @click="styles.splice(i,1)" tabindex="-1" class="delete">Remove style</button>
</div>
<div v-for="attribute, i in style.attributes">
<input v-model="attribute.key">
<input v-model="attribute.value" @keydown.enter="style.attributes.push({key:'',value:''})">
<button @click="style.attributes.splice(i,1)" tabindex="-1" class="delete">-</button>
</div>
<button @click="style.attributes.push({key:'',value:''})" tabindex="-1">Add attribute</button>
</div>
</draggable>
<button @click="styles.push({selector:'',attributes:[{key:'',value:''}]})" tabindex="-1">Add style</button>
<button @click="reset" class="delete">Reset all</button>
<portal target-el="#style">
<template v-for="style in styles">
{{style.selector}}{
<template v-for="attribute in style.attributes">
{{attribute.key}} : {{attribute.value}} ;
</template>
}
</template>
</portal>
</div>
JavaScript
let styleTag = document.createElement('style')
styleTag.id = 'style'
document.head.appendChild(styleTag)
let defaults = [{"selector":"html","attributes":[{"key":"height","value":"100%"}]},{"selector":"body","attributes":[{"key":"background","value":"linear-gradient(orange,magenta) fixed"}]},{"selector":".style","attributes":[{"key":"background","value":"white"},{"key":"padding","value":"10px"},{"key":"margin-bottom","value":"10px"},{"key":"box-shadow","value":"0px 1px 4px rgba(0,0,0,0.5)"}]},{"selector":".style div","attributes":[{"key":"display","value":"flex"}]},{"selector":".style div:first-child input","attributes":[{"key":"font-weight","value":"bold"}]},{"selector":"input, button","attributes":[{"key":"border-radius","value":"3px"},{"key":"margin","value":"2px"}]},{"selector":"input","attributes":[{"key":"border","value":"none"},{"key":"padding","value":"10px"},{"key":"box-shadow","value":"inset 0px 1px 2px rgba(0,0,0,0.3)"},{"key":"flex-grow","value":"1"}]},{"selector":"button","attributes":[{"key":"background","value":"linear-gradient(#0af,blue)"},{"key":"color","value":"white"},{"key":"border","value":"none"},{"key":"box-shadow","value":"0px 1px 2px rgba(0,0,0,0.3)"},{"key":"padding","value":"10px 15px"}]},{"selector":"button.delete","attributes":[{"key":"background","value":"linear-gradient(red,purple)"}]}]
new Vue({
el: '#vue',
data: {
styles:JSON.parse(localStorage.getItem('styles')) || defaults
},
methods:{
reset(){
this.styles = defaults
localStorage.removeItem('styles')
}
},
watch: {
styles: {
handler(val) {
localStorage.setItem('styles', JSON.stringify(val))
console.log(this.$data.styles)
console.log(JSON.stringify(val))
}, deep: true
}
}
})
console.log(localStorage)