Changed application structure & added detailed settings
This commit is contained in:
parent
82fd7b5a5e
commit
0008b3d1e7
16 changed files with 455 additions and 492 deletions
154
index.html
154
index.html
|
@ -4,159 +4,11 @@
|
||||||
<title>Synergy UI</title>
|
<title>Synergy UI</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="web/demo.css">
|
<link rel="stylesheet" href="web/css/demo.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="content" id="app">
|
<div class="content" id="app"></div>
|
||||||
|
<script type="module" src="web/ts/App.ts"></script>
|
||||||
<header>
|
|
||||||
<h1>
|
|
||||||
<span class="color">Synergy</span> UI
|
|
||||||
</h1>
|
|
||||||
<p>Simple framework with CSS-only UI components</p>
|
|
||||||
<form autocomplete="off" class="colorselector">
|
|
||||||
<input type="color" v-model="data.preset.main" @change="applyPreset(data.preset);" title="Main color">
|
|
||||||
<input type="color" v-model="data.preset.text" @change="applyPreset(data.preset);" title="Text color">
|
|
||||||
<input type="color" v-model="data.preset.bg" @change="applyPreset(data.preset);" title="Background color">
|
|
||||||
<div class="config" title="Settings" @click="settings = !settings;">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M13.875 22h-3.75q-.375 0-.65-.25t-.325-.625l-.3-2.325q-.325-.125-.613-.3t-.562-.375l-2.175.9q-.35.125-.7.025t-.55-.425L2.4 15.4q-.2-.325-.125-.7t.375-.6l1.875-1.425Q4.5 12.5 4.5 12.337v-.674q0-.163.025-.338L2.65 9.9q-.3-.225-.375-.6t.125-.7l1.85-3.225q.175-.35.537-.438t.713.038l2.175.9q.275-.2.575-.375t.6-.3l.3-2.325q.05-.375.325-.625t.65-.25h3.75q.375 0 .65.25t.325.625l.3 2.325q.325.125.613.3t.562.375l2.175-.9q.35-.125.7-.025t.55.425L21.6 8.6q.2.325.125.7t-.375.6l-1.875 1.425q.025.175.025.338v.674q0 .163-.05.338l1.875 1.425q.3.225.375.6t-.125.7l-1.85 3.2q-.2.325-.563.438t-.712-.013l-2.125-.9q-.275.2-.575.375t-.6.3l-.3 2.325q-.05.375-.325.625t-.65.25Zm-1.825-6.5q1.45 0 2.475-1.025T15.55 12q0-1.45-1.025-2.475T12.05 8.5q-1.475 0-2.488 1.025T8.55 12q0 1.45 1.012 2.475T12.05 15.5Z"/></svg>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<aside class="settings" :class="{ open: settings }">
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" @click="settings = false;" class="close" viewBox="0 0 24 24"><path fill="currentColor" d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6L6.4 19Z"/></svg>
|
|
||||||
|
|
||||||
<b>Presets</b>
|
|
||||||
|
|
||||||
<div class="presets">
|
|
||||||
<div v-for="p in data.presets" @click="data.preset = p; applyPreset(p);">
|
|
||||||
<div :style="`background: ${p.main};`"></div>
|
|
||||||
<div :style="`background: ${p.text};`"></div>
|
|
||||||
<div :style="`background: ${p.bg};`"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<b>Component selection</b>
|
|
||||||
|
|
||||||
<form class="form" @submit.prevent="generateCSS">
|
|
||||||
|
|
||||||
<label class="cbox" v-for="p in data.parts">
|
|
||||||
<input type="checkbox" v-model="p.enabled">
|
|
||||||
<span>{{ p.name }}</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="btn-row">
|
|
||||||
<button class="btn btn-primary" type="submit">
|
|
||||||
Generate CSS
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<template v-if="data.results.length">
|
|
||||||
|
|
||||||
<b>CSS download</b>
|
|
||||||
|
|
||||||
<div class="btn-row">
|
|
||||||
<a v-for="r in data.results" class="btn btn-primary btn-download" :download="r.name" :href="'data:text/plain;charset=utf-8,' + encodeURIComponent(r.css)">
|
|
||||||
{{ r.name }}
|
|
||||||
<small>
|
|
||||||
{{ kbSize(r.size_gzip) }} (gzip) · {{ kbSize(r.size) }}
|
|
||||||
</small>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<div class="grid">
|
|
||||||
|
|
||||||
<form class="form">
|
|
||||||
|
|
||||||
<div class="inp">
|
|
||||||
<input type="text" placeholder=" ">
|
|
||||||
<label>Name</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inp">
|
|
||||||
<input type="text" placeholder=" ">
|
|
||||||
<label>Surname</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inp select">
|
|
||||||
<select>
|
|
||||||
<option disabled>Disabled</option>
|
|
||||||
<option>Guest</option>
|
|
||||||
<option>User</option>
|
|
||||||
<option>Administrator</option>
|
|
||||||
</select>
|
|
||||||
<label>Role</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inp">
|
|
||||||
<textarea placeholder=" ">Some very long text...</textarea>
|
|
||||||
<label>Long text</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="toggle-text">
|
|
||||||
<label for="toggle1">Toggle me! I'm a toggle.</label>
|
|
||||||
<div class="toggle">
|
|
||||||
<input id="toggle1" type="checkbox">
|
|
||||||
<div class="indicator"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cbox-row">
|
|
||||||
<label class="cbox">
|
|
||||||
<input type="radio" name="radios" checked>
|
|
||||||
<span>Radio 1</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="cbox">
|
|
||||||
<input type="radio" name="radios">
|
|
||||||
<span>Radio 2</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label class="cbox">
|
|
||||||
<input type="checkbox">
|
|
||||||
<span>Check me!</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="tabs offset">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="tabs" checked>
|
|
||||||
<div>Home</div>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="tabs">
|
|
||||||
<div>Account</div>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="tabs">
|
|
||||||
<div>Settings</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="btn-row">
|
|
||||||
<button class="btn btn-primary">
|
|
||||||
Send
|
|
||||||
</button>
|
|
||||||
<button type="reset" class="btn">
|
|
||||||
Reset
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<script type="module" src="web/app.ts"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
40
package.json
40
package.json
|
@ -1,21 +1,23 @@
|
||||||
{
|
{
|
||||||
"name": "synergy",
|
"name": "synergy",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"petite-vue": "^0.4.1"
|
"@iconify/vue": "^4.1.2",
|
||||||
},
|
"vue": "^3.4.26"
|
||||||
"devDependencies": {
|
},
|
||||||
"typescript": "^5.0.2",
|
"devDependencies": {
|
||||||
"vite": "^4.4.5"
|
"typescript": "^5.0.2",
|
||||||
}
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
|
"vite": "^4.4.5"
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
|
import vuePlugin from '@vitejs/plugin-vue';
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vuePlugin()
|
||||||
|
],
|
||||||
publicDir: "src/"
|
publicDir: "src/"
|
||||||
});
|
});
|
104
web/App.vue
Normal file
104
web/App.vue
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import Settings from "./components/Settings.vue";
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>
|
||||||
|
<span class="color">Synergy</span> UI
|
||||||
|
</h1>
|
||||||
|
<p>Simple framework with CSS-only UI components</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<Settings />
|
||||||
|
|
||||||
|
<div class="preview">
|
||||||
|
|
||||||
|
<form class="form">
|
||||||
|
|
||||||
|
<div class="inp">
|
||||||
|
<input type="text" placeholder=" ">
|
||||||
|
<label>Name</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inp">
|
||||||
|
<input type="text" placeholder=" ">
|
||||||
|
<label>Surname</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inp select">
|
||||||
|
<select>
|
||||||
|
<option disabled>Disabled</option>
|
||||||
|
<option>Guest</option>
|
||||||
|
<option>User</option>
|
||||||
|
<option>Administrator</option>
|
||||||
|
</select>
|
||||||
|
<label>Role</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inp">
|
||||||
|
<textarea placeholder=" ">Some very long text...</textarea>
|
||||||
|
<label>Long text</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="toggle-text">
|
||||||
|
<label for="toggle1">Toggle me! I'm a toggle.</label>
|
||||||
|
<div class="toggle">
|
||||||
|
<input id="toggle1" type="checkbox">
|
||||||
|
<div class="indicator"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cbox-row">
|
||||||
|
<label class="cbox">
|
||||||
|
<input type="radio" name="radios" checked>
|
||||||
|
<span>Radio 1</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="cbox">
|
||||||
|
<input type="radio" name="radios">
|
||||||
|
<span>Radio 2</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="cbox">
|
||||||
|
<input type="checkbox">
|
||||||
|
<span>Check me!</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="tabs offset">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="tabs" checked>
|
||||||
|
<div>Home</div>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="tabs">
|
||||||
|
<div>Account</div>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="tabs">
|
||||||
|
<div>Settings</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-row">
|
||||||
|
<button class="btn btn-primary">
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
<button type="reset" class="btn">
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>./ts/synergy
|
49
web/app.ts
49
web/app.ts
|
@ -1,49 +0,0 @@
|
||||||
import { Exporter, Preset, Theme } from "./lib";
|
|
||||||
import { createApp, reactive } from "petite-vue";
|
|
||||||
|
|
||||||
export function applyPreset(p: Preset) {
|
|
||||||
let theme = new Theme(p);
|
|
||||||
theme.apply();
|
|
||||||
let html = document.querySelector<HTMLElement>("html");
|
|
||||||
if(html) html.style.background = p.siteBg ?? "";
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
export let presets: Preset[] = [
|
|
||||||
// {main: "#2ebdf5", text: "#ffffff", bg: "#040813"},
|
|
||||||
// {main: "#f5b62e", text: "#ffffff", bg: "#040813"},
|
|
||||||
// {main: "#FF6565", text: "#ffffff", bg: "#0f0413"},
|
|
||||||
{main: "#9b8fe4", text: "#cfcef4", bg: "#090818", siteBg: "#100E22"},
|
|
||||||
{main: "#337e2c", text: "#031601", bg: "#f3f7f2"},
|
|
||||||
{main: "#1c71d8", text: "#030e1c", bg: "#ffffff"},
|
|
||||||
{main: "#9141ac", text: "#613583", bg: "#f6edf7"},
|
|
||||||
{main: "#a51d2d", text: "#3d3846", bg: "#f1e9e8"},
|
|
||||||
{main: "#865e3c", text: "#63452c", bg: "#f9f7f4", siteBg: "#ffffff"}
|
|
||||||
];
|
|
||||||
|
|
||||||
let pr = presets[Math.floor(Math.random() * presets.length)];
|
|
||||||
|
|
||||||
let data = reactive({
|
|
||||||
theme: applyPreset(pr),
|
|
||||||
preset: pr,
|
|
||||||
parts: Exporter.parts,
|
|
||||||
presets,
|
|
||||||
results: []
|
|
||||||
});
|
|
||||||
|
|
||||||
applyPreset(pr);
|
|
||||||
|
|
||||||
createApp({
|
|
||||||
applyPreset(p: Preset) {
|
|
||||||
data.results = [];
|
|
||||||
data.theme = applyPreset(p);
|
|
||||||
},
|
|
||||||
async generateCSS() {
|
|
||||||
data.results = await Exporter.get(data.theme);
|
|
||||||
},
|
|
||||||
kbSize(value: number) {
|
|
||||||
return `${Math.round(value/1024*100)/100} kB`;
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
settings: false
|
|
||||||
}).mount("#app");
|
|
53
web/components/Settings.vue
Normal file
53
web/components/Settings.vue
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import { variables } from "../ts/Synergy";
|
||||||
|
import { Icon } from "@iconify/vue";
|
||||||
|
import { Preset } from "../ts/Preset";
|
||||||
|
import { variableValues, applyVariables } from "../ts/Shared";
|
||||||
|
import { setVariables } from "../ts/Styles";
|
||||||
|
|
||||||
|
const variableTypeIcon = {
|
||||||
|
color: "ic:outline-color-lens",
|
||||||
|
number: "mdi:numeric"
|
||||||
|
};
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
setVariables(variableValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="settings">
|
||||||
|
|
||||||
|
<div class="variables">
|
||||||
|
<template v-for="v in variables">
|
||||||
|
<label :for="v.name">
|
||||||
|
<Icon :icon="variableTypeIcon[v.type]" />
|
||||||
|
{{ v.name }}
|
||||||
|
</label>
|
||||||
|
<div v-if="v.type == 'color'">
|
||||||
|
<div class="inp inp-small color">
|
||||||
|
<input v-model="variableValues[v.name]" type="color" :id="v.name" @change="update">
|
||||||
|
</div>
|
||||||
|
<div class="inp inp-small">
|
||||||
|
<input v-model="variableValues[v.name]" type="string" :id="v.name" @change="update">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="inp inp-small">
|
||||||
|
<input v-model="variableValues[v.name]" type="string" :id="v.name" @change="update">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="presets">
|
||||||
|
<div v-for="preset in Preset.presets" @click="applyVariables(preset.getVariables())">
|
||||||
|
<div :style="`background-color: ${c?.hexFormat()}`" v-for="c in preset.colors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>../ts/Synergy../ts/Styles../ts/Shared
|
34
web/css/demo.css
Normal file
34
web/css/demo.css
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
html {background: var(--synergy-site-bg);}
|
||||||
|
html, body {color: var(--synergy-text-color); font-family: Cantarell, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Open Sans, Helvetica Neue, Arial, Noto Sans, Roboto, sans-serif; font-size: 18px; overflow-x: hidden;}
|
||||||
|
|
||||||
|
*, *::before, *::after {box-sizing: border-box;}
|
||||||
|
|
||||||
|
header {margin: 70px 0; text-align: center;}
|
||||||
|
header > * {margin: 30px 0;}
|
||||||
|
header h1 {font-size: 50px;}
|
||||||
|
header h1 .color {background-clip: text; -webkit-background-clip: text; background-image: linear-gradient(10deg, var(--synergy-border-active), var(--synergy-border)); color: transparent;}
|
||||||
|
|
||||||
|
|
||||||
|
.settings {margin: 70px 0; display: grid; grid-template-columns: 1fr 1fr; gap: 25px;}
|
||||||
|
.settings .variables {display: grid; gap: 10px 20px; align-items: center; grid-template-columns: max-content 1fr;}
|
||||||
|
.settings .variables > * {display: flex; gap: 10px; align-items: center;}
|
||||||
|
|
||||||
|
.settings .presets {display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px;}
|
||||||
|
.settings .presets > * {display: flex; border-radius: 10px; overflow: hidden; cursor: pointer; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .1);}
|
||||||
|
.settings .presets > * > * {height: 100px; flex: 1;}
|
||||||
|
|
||||||
|
.content {padding: 0 20px; margin: auto; max-width: 1000px;}
|
||||||
|
|
||||||
|
.preview {margin: 70px 0;}
|
||||||
|
|
||||||
|
.form {display: flex; flex-direction: column; gap: 15px; max-width: 450px;}
|
||||||
|
.form > * {margin: 0;}
|
||||||
|
|
||||||
|
|
||||||
|
/* Synergy expansions */
|
||||||
|
|
||||||
|
.inp.inp-small input {padding: 5px 9px; font-size: .9em;}
|
||||||
|
|
||||||
|
.inp.color {width: 40px;}
|
||||||
|
.inp input[type=color] {padding: 0; border: 0;}
|
||||||
|
::-webkit-color-swatch, ::-moz-color-swatch {border: 0;}
|
6
web/declaration.d.ts
vendored
6
web/declaration.d.ts
vendored
|
@ -1 +1,5 @@
|
||||||
declare module '*.css';
|
declare module '*.css';
|
||||||
|
|
||||||
|
declare interface ComboObject {
|
||||||
|
[U: string]: any
|
||||||
|
};
|
||||||
|
|
37
web/demo.css
37
web/demo.css
|
@ -1,37 +0,0 @@
|
||||||
html {background: var(--synergy-bg);}
|
|
||||||
html, body {color: var(--synergy-text-color); font-family: Cantarell, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Open Sans, Helvetica Neue, Arial, Noto Sans, Roboto, sans-serif; font-size: 18px; overflow-x: hidden;}
|
|
||||||
|
|
||||||
*, *::before, *::after {box-sizing: border-box;}
|
|
||||||
|
|
||||||
.content {padding: 20px; margin: auto; max-width: 1000px;}
|
|
||||||
|
|
||||||
.grid {display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: center;}
|
|
||||||
@media screen and (max-width: 700px) {
|
|
||||||
.grid {grid-template-columns: 1fr; max-width: 500px;}
|
|
||||||
}
|
|
||||||
.form {display: flex; flex-direction: column; gap: 15px;}
|
|
||||||
.form > * {margin: 0;}
|
|
||||||
|
|
||||||
header {margin-bottom: 70px; text-align: center;}
|
|
||||||
header > * {margin: 30px 0;}
|
|
||||||
header h1 {font-size: 50px;}
|
|
||||||
header h1 .color {background-clip: text; -webkit-background-clip: text; background-image: linear-gradient(10deg, var(--synergy-border-active), var(--synergy-border)); color: transparent;}
|
|
||||||
|
|
||||||
.colorselector {display: flex; gap: 10px; justify-content: center;}
|
|
||||||
.colorselector > * {border: 0; padding: 0; cursor: pointer; width: 50px; height: 50px; border-radius: 50%; overflow: hidden; box-shadow: 0 1px 3px #0004;}
|
|
||||||
::-webkit-color-swatch, ::-moz-color-swatch {border: 0;}
|
|
||||||
.colorselector div {display: flex; align-items: center; justify-content: center; box-shadow: 0 1px 3px #0004, inset 0 0 0 2px var(--synergy-border);}
|
|
||||||
.colorselector svg {width: 30px;}
|
|
||||||
|
|
||||||
.settings {position: fixed; right: -600px; top: 0; width: 90%; max-width: 400px; height: 100%; box-shadow: 0 0 0 5px var(--synergy-border-active); border-radius: 40px 0 0 40px; padding: 40px; transition: all .3s; display: flex; flex-direction: column; gap: 20px; z-index: 100; background-color: var(--synergy-bg); overflow-y: auto;}
|
|
||||||
.settings.open {right: 0;}
|
|
||||||
|
|
||||||
.settings b {margin-top: 20px;}
|
|
||||||
|
|
||||||
.presets {display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px;}
|
|
||||||
.presets > * {height: 50px; border-radius: var(--synergy-border-radius); box-shadow: 0 1px 3px #0004; display: flex; overflow: hidden; cursor: pointer;}
|
|
||||||
.presets > * > * {flex: 1;}
|
|
||||||
|
|
||||||
.settings .close {width: 64px; height: 64px; cursor: pointer; padding: 20px; position: absolute; right: 0; top: 0; z-index: 10;}
|
|
||||||
|
|
||||||
.btn.btn-download small {font-weight: 400; font-size: 11px; display: block;}
|
|
235
web/lib.ts
235
web/lib.ts
|
@ -1,235 +0,0 @@
|
||||||
import { reactive } from "petite-vue";
|
|
||||||
|
|
||||||
var style = document.createElement("style");
|
|
||||||
|
|
||||||
addEventListener("load", () => {
|
|
||||||
|
|
||||||
document.querySelector("head")!.appendChild(style);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
export namespace Exporter {
|
|
||||||
|
|
||||||
export let parts = reactive([
|
|
||||||
{name: "Buttons", file: "button", enabled: true},
|
|
||||||
{name: "Fields", file: "input", enabled: true},
|
|
||||||
{name: "Toggles", file: "toggle", enabled: true},
|
|
||||||
{name: "Checkboxes and radios", file: "checkbox", enabled: true},
|
|
||||||
{name: "Tabs", file: "tabs", enabled: true},
|
|
||||||
]);
|
|
||||||
|
|
||||||
interface Result {
|
|
||||||
name: string,
|
|
||||||
css: string,
|
|
||||||
size: number,
|
|
||||||
size_gzip: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function get(theme: Theme) {
|
|
||||||
|
|
||||||
let cssParts = [theme.generate()];
|
|
||||||
for(let p of parts) {
|
|
||||||
if(p.enabled) {
|
|
||||||
let value = await (await fetch(`./${p.file}.css`)).text();
|
|
||||||
value = `/* ${p.name} */\n\n${value}`;
|
|
||||||
cssParts.push(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let css = cssParts.join("\n\n/* ------------------- */\n\n");
|
|
||||||
|
|
||||||
let results: Result[] = [];
|
|
||||||
await addResult(results, "synergy.min.css", minify(css));
|
|
||||||
await addResult(results, "synergy.css", css);
|
|
||||||
|
|
||||||
return results;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addResult(results: Result[], name: string, css: string) {
|
|
||||||
results.push({
|
|
||||||
name,
|
|
||||||
css,
|
|
||||||
size: getSize(css),
|
|
||||||
size_gzip: await getCompressedSize(css)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCompressedSize(content: string) {
|
|
||||||
let ds = new CompressionStream("gzip");
|
|
||||||
let blob = new Blob([content]);
|
|
||||||
let compressedStream = blob.stream().pipeThrough(ds);
|
|
||||||
return (await new Response(compressedStream).blob()).size;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSize(content: string) {
|
|
||||||
return (new TextEncoder().encode(content)).length
|
|
||||||
}
|
|
||||||
|
|
||||||
function minify(value: string) {
|
|
||||||
return value
|
|
||||||
.replace(/([^0-9a-zA-Z\.#])\s+/g, "$1")
|
|
||||||
.replace(/\s([^0-9a-zA-Z\.#]+)/g, "$1")
|
|
||||||
.replace(/;}/g, "}")
|
|
||||||
.replace(/\/\*.*?\*\//g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Preset {
|
|
||||||
main: string,
|
|
||||||
text: string,
|
|
||||||
bg: string,
|
|
||||||
siteBg?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Color {
|
|
||||||
|
|
||||||
r: number;
|
|
||||||
g: number;
|
|
||||||
b: number;
|
|
||||||
a: number;
|
|
||||||
|
|
||||||
constructor(r: number, g: number, b: number, a: number = 1) {
|
|
||||||
this.r = r;
|
|
||||||
this.g = g;
|
|
||||||
this.b = b;
|
|
||||||
this.a = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
clone() {
|
|
||||||
return new Color(this.r, this.g, this.b, this.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromHex(hex: string) {
|
|
||||||
let [r, g, b] = this.hexToRgb(hex);
|
|
||||||
return new Color(r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static hexToRgb(hex: string) {
|
|
||||||
hex = hex.replace(/^#/, '');
|
|
||||||
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
||||||
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
||||||
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
||||||
return [r, g, b];
|
|
||||||
}
|
|
||||||
|
|
||||||
rgbFormat() {
|
|
||||||
let rgb = `${this.r*255}, ${this.g*255}, ${this.b*255}`;
|
|
||||||
return this.a == 1 ? `rgb(${rgb})` : `rgba(${rgb}, ${this.a})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
hexFormat() {
|
|
||||||
let hex = `#${(1 << 24 | (this.r*255) << 16 | (this.g*255) << 8 | (this.b*255)).toString(16).slice(1)}`;
|
|
||||||
return this.a != 1 ? `${hex}${(Math.floor(this.a * 255).toString(16).padStart(2, '0'))}` : hex;
|
|
||||||
}
|
|
||||||
|
|
||||||
contrast(otherColor: Color) {
|
|
||||||
const getRelativeLuminance = (rgb: number) => {
|
|
||||||
const sRGB = rgb / 255;
|
|
||||||
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
|
||||||
};
|
|
||||||
const luminance1 = getRelativeLuminance(this.r) * 0.2126 +
|
|
||||||
getRelativeLuminance(this.g) * 0.7152 +
|
|
||||||
getRelativeLuminance(this.b) * 0.0722;
|
|
||||||
const luminance2 = getRelativeLuminance(otherColor.r) * 0.2126 +
|
|
||||||
getRelativeLuminance(otherColor.g) * 0.7152 +
|
|
||||||
getRelativeLuminance(otherColor.b) * 0.0722;
|
|
||||||
const contrastRatio = (Math.max(luminance1, luminance2) + 0.05) / (Math.min(luminance1, luminance2) + 0.05);
|
|
||||||
return (contrastRatio*100)-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
equals(otherColor: Color) {
|
|
||||||
return this.r == otherColor.r && this.g == otherColor.g && this.b == otherColor.b;
|
|
||||||
}
|
|
||||||
|
|
||||||
mix(color: Color, ratio: number): Color {
|
|
||||||
const r = Math.round(this.r*255 * (1 - ratio) + color.r*255 * ratio);
|
|
||||||
const g = Math.round(this.g*255 * (1 - ratio) + color.g*255 * ratio);
|
|
||||||
const b = Math.round(this.b*255 * (1 - ratio) + color.b*255 * ratio);
|
|
||||||
return new Color(r/255, g/255, b/255);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Theme {
|
|
||||||
|
|
||||||
main: Color;
|
|
||||||
text: Color;
|
|
||||||
bg: Color;
|
|
||||||
|
|
||||||
constructor(opt: Preset) {
|
|
||||||
|
|
||||||
this.main = Color.fromHex(opt.main);
|
|
||||||
this.text = Color.fromHex(opt.text);
|
|
||||||
this.bg = Color.fromHex(opt.bg);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
apply() {
|
|
||||||
style.innerHTML = this.generate();
|
|
||||||
}
|
|
||||||
|
|
||||||
generate() {
|
|
||||||
|
|
||||||
let variables = [];
|
|
||||||
|
|
||||||
variables.push(this.var("border", this.cAlpha(this.main, .4)));
|
|
||||||
variables.push(this.var("border-active", this.cAlpha(this.main)));
|
|
||||||
|
|
||||||
variables.push(this.var("border-width", "2px"));
|
|
||||||
variables.push(this.var("border-radius", ".375rem"))
|
|
||||||
|
|
||||||
variables.push(this.var("focus-highlight", this.cAlpha(this.main, .25)));
|
|
||||||
variables.push(this.var("tab-highlight", this.cAlpha(this.main, .1)));
|
|
||||||
|
|
||||||
variables.push(this.var("label", this.cMix(this.main, this.bg, .8)));
|
|
||||||
variables.push(this.var("label-active", this.cMix(this.main, this.bg)));
|
|
||||||
|
|
||||||
variables.push(this.var("btn-primary-bg", this.cMix(this.main, this.bg, .8)));
|
|
||||||
variables.push(this.var("btn-primary-bg-active", this.cMix(this.main, this.bg, .5)));
|
|
||||||
variables.push(this.var("btn-primary-bg-hover", this.cMix(this.main, this.bg)));
|
|
||||||
|
|
||||||
let btnBg = this.bg.mix(new Color(.6, .6, .6), .8).mix(this.main, .1);
|
|
||||||
variables.push(this.var("btn-bg", this.cMix(btnBg, this.bg, .3)));
|
|
||||||
variables.push(this.var("btn-bg-active", this.cMix(btnBg, this.bg, .6)));
|
|
||||||
variables.push(this.var("btn-bg-hover", this.cMix(btnBg, this.bg, .4)));
|
|
||||||
|
|
||||||
variables.push(this.var("text-color", this.text.hexFormat()));
|
|
||||||
variables.push(this.var("bg", this.bg.hexFormat()));
|
|
||||||
|
|
||||||
if(this.main.contrast(this.bg) < .3) alert("Contrast between main color and the background is low!");
|
|
||||||
if(this.bg.contrast(this.text) < .3) alert("Contrast between text color and the background is low!");
|
|
||||||
|
|
||||||
let styles = [`:root {\n${variables.join("\n")}\n}`];
|
|
||||||
|
|
||||||
let btnColor = this.getBtnColor(this.main, this.text);
|
|
||||||
if(btnColor != this.text) styles.push(`.btn.btn-primary {\n${this.var("text-color", btnColor.hexFormat())}\n}`);
|
|
||||||
|
|
||||||
return styles.join("\n\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
getBtnColor(main: Color, text: Color) {
|
|
||||||
let white = new Color(1, 1, 1);
|
|
||||||
let black = new Color(0, 0, 0);
|
|
||||||
let cText = main.contrast(text);
|
|
||||||
let cWhite = main.contrast(white);
|
|
||||||
return cWhite > .3 ? white : cText > .3 ? text : black;
|
|
||||||
}
|
|
||||||
|
|
||||||
cMix(color: Color, color2: Color, ratio: number = 1) {
|
|
||||||
let c = color2.mix(color, ratio);
|
|
||||||
return c.hexFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
cAlpha(color: Color, alpha: number = 1) {
|
|
||||||
let c = color.clone();
|
|
||||||
c.a = alpha;
|
|
||||||
return c.hexFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
var(name: string, value: string) {
|
|
||||||
return `\t--synergy-${name}: ${value};`;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
4
web/ts/App.ts
Normal file
4
web/ts/App.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { createApp } from "vue";
|
||||||
|
import App from "../App.vue";
|
||||||
|
|
||||||
|
let app = createApp(App).mount("#app");
|
68
web/ts/Color.ts
Normal file
68
web/ts/Color.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
export class Color {
|
||||||
|
|
||||||
|
r: number;
|
||||||
|
g: number;
|
||||||
|
b: number;
|
||||||
|
a: number;
|
||||||
|
|
||||||
|
constructor(r: number, g: number, b: number, a: number = 1) {
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Color(this.r, this.g, this.b, this.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromHex(hex: string) {
|
||||||
|
let [r, g, b] = this.hexToRgb(hex);
|
||||||
|
return new Color(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static hexToRgb(hex: string) {
|
||||||
|
hex = hex.replace(/^#/, '');
|
||||||
|
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
||||||
|
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
||||||
|
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
||||||
|
return [r, g, b];
|
||||||
|
}
|
||||||
|
|
||||||
|
rgbFormat() {
|
||||||
|
let rgb = `${this.r*255}, ${this.g*255}, ${this.b*255}`;
|
||||||
|
return this.a == 1 ? `rgb(${rgb})` : `rgba(${rgb}, ${this.a})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
hexFormat() {
|
||||||
|
let hex = `#${(1 << 24 | (this.r*255) << 16 | (this.g*255) << 8 | (this.b*255)).toString(16).slice(1)}`;
|
||||||
|
return this.a != 1 ? `${hex}${(Math.floor(this.a * 255).toString(16).padStart(2, '0'))}` : hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
contrast(otherColor: Color) {
|
||||||
|
const getRelativeLuminance = (rgb: number) => {
|
||||||
|
const sRGB = rgb / 255;
|
||||||
|
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
||||||
|
};
|
||||||
|
const luminance1 = getRelativeLuminance(this.r) * 0.2126 +
|
||||||
|
getRelativeLuminance(this.g) * 0.7152 +
|
||||||
|
getRelativeLuminance(this.b) * 0.0722;
|
||||||
|
const luminance2 = getRelativeLuminance(otherColor.r) * 0.2126 +
|
||||||
|
getRelativeLuminance(otherColor.g) * 0.7152 +
|
||||||
|
getRelativeLuminance(otherColor.b) * 0.0722;
|
||||||
|
const contrastRatio = (Math.max(luminance1, luminance2) + 0.05) / (Math.min(luminance1, luminance2) + 0.05);
|
||||||
|
return (contrastRatio*100)-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(otherColor: Color) {
|
||||||
|
return this.r == otherColor.r && this.g == otherColor.g && this.b == otherColor.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
mix(color: Color, ratio: number): Color {
|
||||||
|
const r = Math.round(this.r*255 * (1 - ratio) + color.r*255 * ratio);
|
||||||
|
const g = Math.round(this.g*255 * (1 - ratio) + color.g*255 * ratio);
|
||||||
|
const b = Math.round(this.b*255 * (1 - ratio) + color.b*255 * ratio);
|
||||||
|
return new Color(r/255, g/255, b/255);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
85
web/ts/Preset.ts
Normal file
85
web/ts/Preset.ts
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import { Color } from "./Color";
|
||||||
|
|
||||||
|
export class Preset {
|
||||||
|
|
||||||
|
static readonly presets: Preset[] = [
|
||||||
|
new Preset({main: "#337e2c", text: "#031601", bg: "#f3f7f2"}),
|
||||||
|
new Preset({main: "#1c71d8", text: "#030e1c", bg: "#ffffff"}),
|
||||||
|
new Preset({main: "#9141ac", text: "#613583", bg: "#f6edf7"}),
|
||||||
|
new Preset({main: "#a51d2d", text: "#3d3846", bg: "#f1e9e8"}),
|
||||||
|
new Preset({main: "#865e3c", text: "#63452c", bg: "#f9f7f4", "site-bg": "#ffffff"}),
|
||||||
|
new Preset({main: "#F45662", text: "#c4aeae", bg: "#181218"}),
|
||||||
|
new Preset({main: "#E66993", text: "#dddddd", bg: "#18181b"}),
|
||||||
|
new Preset({main: "#ffab9b", text: "#E8C3BC", bg: "#191e23"}),
|
||||||
|
new Preset({main: "#9b8fe4", text: "#cfcef4", bg: "#090818", "site-bg": "#100E22"}),
|
||||||
|
];
|
||||||
|
|
||||||
|
readonly colors: Colors<Color>;
|
||||||
|
|
||||||
|
constructor(colors: Colors<Color | string>) {
|
||||||
|
this.colors = Preset.convertColors(colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private selectColor(colors: ColorType | ColorType[]): Color {
|
||||||
|
let input = Array.isArray(colors) ? colors : [colors];
|
||||||
|
for (let key of input) {
|
||||||
|
let val = this.colors[key];
|
||||||
|
if (val) return val;
|
||||||
|
}
|
||||||
|
return this.colors["main"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getVariables() {
|
||||||
|
const cMain = this.selectColor("main");
|
||||||
|
const cBg = this.selectColor("bg");
|
||||||
|
const cSiteBg = this.selectColor(["site-bg", "bg"]);
|
||||||
|
const cText = this.selectColor("text");
|
||||||
|
const cGray = new Color(0.6, 0.6, 0.6);
|
||||||
|
return {
|
||||||
|
"border": this.cMix(cMain, cBg, 0.4),
|
||||||
|
"border-active": cMain.hexFormat(),
|
||||||
|
"focus-highlight": this.cMix(cMain, cBg, 0.25),
|
||||||
|
"tab-highlight": this.cMix(cMain, cBg, 0.1),
|
||||||
|
"label": this.cMix(cMain, cBg, 0.8),
|
||||||
|
"label-active": this.cMix(cMain, cBg),
|
||||||
|
"btn-primary-bg": this.cMix(cMain, cBg, 0.8),
|
||||||
|
"btn-primary-bg-active": this.cMix(cMain, cBg, 0.5),
|
||||||
|
"btn-primary-bg-hover": this.cMix(cMain, cBg),
|
||||||
|
"btn-bg": this.cMix(cBg.mix(cGray, 0.8).mix(cMain, 0.1), cBg, 0.3),
|
||||||
|
"btn-bg-active": this.cMix(cBg.mix(cGray, 0.8).mix(cMain, 0.1), cBg, 0.6),
|
||||||
|
"btn-bg-hover": this.cMix(cBg.mix(cGray, 0.8).mix(cMain, 0.1), cBg, 0.4),
|
||||||
|
"text-color": cText.hexFormat(),
|
||||||
|
"bg": cBg.hexFormat(),
|
||||||
|
"site-bg": cSiteBg.hexFormat()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public cMix(color: Color, color2: Color, ratio: number = 1) {
|
||||||
|
let c = color2.mix(color, ratio);
|
||||||
|
return c.hexFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static convertColors(input: Colors<Color | string>) {
|
||||||
|
return Object.entries(input).reduce<Partial<Colors<Color>>>((acc, [key, value]) => {
|
||||||
|
acc[key as ColorType] = value instanceof Color ? value : Color.fromHex(value);
|
||||||
|
return acc;
|
||||||
|
}, {}) as Colors<Color>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getRandom() {
|
||||||
|
return this.presets[Math.floor(Math.random() * this.presets.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AcceptedColor = Color | string;
|
||||||
|
|
||||||
|
// TODO: add more colors, use them if available and more specific
|
||||||
|
export interface Colors<T> {
|
||||||
|
main: T,
|
||||||
|
text: T,
|
||||||
|
bg: T,
|
||||||
|
"site-bg"?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
type ColorType = keyof Colors<any>;
|
19
web/ts/Shared.ts
Normal file
19
web/ts/Shared.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { reactive, watch } from "vue";
|
||||||
|
import { getDefaultVariables } from "./Synergy";
|
||||||
|
import { setVariables } from "./Styles";
|
||||||
|
|
||||||
|
let values = getDefaultVariables();
|
||||||
|
|
||||||
|
export let variableValues = reactive<ComboObject>(values);
|
||||||
|
|
||||||
|
watch(() => variableValues, () => {
|
||||||
|
setVariables(variableValues);
|
||||||
|
}, {deep: true});
|
||||||
|
|
||||||
|
setVariables(values);
|
||||||
|
|
||||||
|
export function applyVariables(variables: ComboObject) {
|
||||||
|
Object.keys(variables).forEach(key => {
|
||||||
|
variableValues[key] = variables[key];
|
||||||
|
});
|
||||||
|
}
|
19
web/ts/Styles.ts
Normal file
19
web/ts/Styles.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
let style = document.createElement("style");
|
||||||
|
|
||||||
|
addEventListener("load", () => {
|
||||||
|
document.querySelector("head")!.appendChild(style);
|
||||||
|
});
|
||||||
|
|
||||||
|
function parseVariables(variables: ComboObject) {
|
||||||
|
let vars: string[] = [];
|
||||||
|
Object.keys(variables).forEach(key => {
|
||||||
|
if (variables[key] == null) return;
|
||||||
|
vars.push(`--synergy-${key}: ${variables[key]};`);
|
||||||
|
});
|
||||||
|
return vars.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setVariables(variables: ComboObject) {
|
||||||
|
const vars = parseVariables(variables);
|
||||||
|
style.innerHTML = `:root {${vars}}`;
|
||||||
|
}
|
36
web/ts/Synergy.ts
Normal file
36
web/ts/Synergy.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { Preset } from "./Preset";
|
||||||
|
|
||||||
|
export let variables: Variable[] = [
|
||||||
|
{name: "border", type: "color"},
|
||||||
|
{name: "border-active", type: "color"},
|
||||||
|
{name: "border-width", type: "number"},
|
||||||
|
{name: "border-radius", type: "number"},
|
||||||
|
{name: "focus-highlight", type: "color"},
|
||||||
|
{name: "tab-highlight", type: "color"},
|
||||||
|
{name: "label", type: "color"},
|
||||||
|
{name: "label-active", type: "color"},
|
||||||
|
{name: "btn-primary-bg", type: "color"},
|
||||||
|
{name: "btn-primary-bg-hover", type: "color"},
|
||||||
|
{name: "btn-primary-bg-active", type: "color"},
|
||||||
|
{name: "btn-bg", type: "color"},
|
||||||
|
{name: "btn-bg-hover", type: "color"},
|
||||||
|
{name: "btn-bg-active", type: "color"},
|
||||||
|
{name: "text-color", type: "color"},
|
||||||
|
{name: "bg", type: "color"},
|
||||||
|
{name: "site-bg", type: "color"}
|
||||||
|
];
|
||||||
|
|
||||||
|
export interface Variable {
|
||||||
|
name: string,
|
||||||
|
type: VariableType
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VariableType = "color" | "number";
|
||||||
|
|
||||||
|
export function getDefaultVariables() {
|
||||||
|
return {
|
||||||
|
"border-width": "2px",
|
||||||
|
"border-radius": ".375rem",
|
||||||
|
...Preset.getRandom().getVariables()
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue