Created simple web app, added component selection & CSS download
This commit is contained in:
parent
43d4f32049
commit
ae9738d084
10 changed files with 240 additions and 89 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,4 @@
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
.vercel
|
.vercel
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
57
index.html
57
index.html
|
@ -8,7 +8,7 @@
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="content">
|
<div class="content" id="app">
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1>
|
<h1>
|
||||||
|
@ -16,22 +16,61 @@
|
||||||
</h1>
|
</h1>
|
||||||
<p>Simple framework with CSS-only UI components</p>
|
<p>Simple framework with CSS-only UI components</p>
|
||||||
<div class="colorselector">
|
<div class="colorselector">
|
||||||
<input type="color" value="#865e3c" title="Main color">
|
<input type="color" v-model="data.preset.main" @change="applyPreset(data.preset);" title="Main color">
|
||||||
<input type="color" value="#63452c" title="Text color">
|
<input type="color" v-model="data.preset.text" @change="applyPreset(data.preset);" title="Text color">
|
||||||
<input type="color" value="#f9f7f4" title="Background color">
|
<input type="color" v-model="data.preset.bg" @change="applyPreset(data.preset);" title="Background color">
|
||||||
<div class="config" title="Settings">
|
<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>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<aside class="settings">
|
<aside class="settings" :class="{ open: settings }">
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" 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>
|
<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>
|
<b>Presets</b>
|
||||||
|
|
||||||
<div class="presets"></div>
|
<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>
|
</aside>
|
||||||
|
|
||||||
|
@ -103,6 +142,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script src="web/demo.js"></script>
|
<script type="module" src="web/app.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
21
package.json
Normal file
21
package.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "synergy",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"keywords": [],
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"petite-vue": "^0.4.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.0.2",
|
||||||
|
"vite": "^4.4.5"
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: box-shadow .2s;
|
transition: box-shadow .2s;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover {
|
.btn:hover {
|
||||||
|
|
10
tsconfig.json
Normal file
10
tsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2016",
|
||||||
|
"module": "commonjs",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
5
vite.config.ts
Normal file
5
vite.config.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
publicDir: "src/"
|
||||||
|
});
|
49
web/app.ts
Normal file
49
web/app.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
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 = [];
|
||||||
|
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");
|
1
web/declaration.d.ts
vendored
Normal file
1
web/declaration.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
declare module '*.css';
|
|
@ -9,7 +9,8 @@ html, body {color: var(--synergy-text-color); font-family: Cantarell, ui-sans-se
|
||||||
@media screen and (max-width: 700px) {
|
@media screen and (max-width: 700px) {
|
||||||
.grid {grid-template-columns: 1fr; max-width: 500px;}
|
.grid {grid-template-columns: 1fr; max-width: 500px;}
|
||||||
}
|
}
|
||||||
.form {display: flex; flex-direction: column; gap: 20px;}
|
.form {display: flex; flex-direction: column; gap: 15px;}
|
||||||
|
.form > * {margin: 0;}
|
||||||
|
|
||||||
header {margin-bottom: 70px; text-align: center;}
|
header {margin-bottom: 70px; text-align: center;}
|
||||||
header > * {margin: 30px 0;}
|
header > * {margin: 30px 0;}
|
||||||
|
@ -22,11 +23,15 @@ header h1 .color {background-clip: text; -webkit-background-clip: text; backgrou
|
||||||
.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 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;}
|
.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: 15px; z-index: 100; background-color: var(--synergy-bg);}
|
.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.open {right: 0;}
|
||||||
|
|
||||||
|
.settings b {margin-top: 20px;}
|
||||||
|
|
||||||
.presets {display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px;}
|
.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 > * {height: 50px; border-radius: var(--synergy-border-radius); box-shadow: 0 1px 3px #0004; display: flex; overflow: hidden; cursor: pointer;}
|
||||||
.presets > * > * {flex: 1;}
|
.presets > * > * {flex: 1;}
|
||||||
|
|
||||||
.settings .close {width: 64px; height: 64px; cursor: pointer; padding: 20px; position: absolute; right: 0; top: 0; z-index: 10;}
|
.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: 12px; display: block;}
|
|
@ -1,78 +1,94 @@
|
||||||
|
import { reactive } from "petite-vue";
|
||||||
|
|
||||||
var style = document.createElement("style");
|
var style = document.createElement("style");
|
||||||
|
|
||||||
addEventListener("load", () => {
|
addEventListener("load", () => {
|
||||||
|
|
||||||
let cs = document.querySelectorAll(".colorselector > *");
|
document.querySelector("head")!.appendChild(style);
|
||||||
cs.forEach(c => c.addEventListener("change", updateColors));
|
|
||||||
document.querySelector("head").appendChild(style);
|
|
||||||
updateColors();
|
|
||||||
showPresets();
|
|
||||||
const p = presets[Math.floor(Math.random() * presets.length)];
|
|
||||||
applyPreset(p);
|
|
||||||
|
|
||||||
document.querySelector(".colorselector .config").addEventListener("click", toggleSettings);
|
|
||||||
document.querySelector(".settings .close").addEventListener("click", toggleSettings);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateColors() {
|
export namespace Exporter {
|
||||||
let cs = document.querySelectorAll(".colorselector > *");
|
|
||||||
let p = {main: cs[0].value, text: cs[1].value, bg: cs[2].value};
|
export let parts = reactive([
|
||||||
console.log(p);
|
{name: "Buttons", file: "button", enabled: true},
|
||||||
let t = new Theme(p);
|
{name: "Fields", file: "input", enabled: true},
|
||||||
t.apply();
|
{name: "Toggles", file: "toggle", enabled: true},
|
||||||
|
{name: "Checkboxes and radios", file: "checkbox", enabled: true},
|
||||||
|
]);
|
||||||
|
|
||||||
|
interface Result {
|
||||||
|
name: string,
|
||||||
|
css: string,
|
||||||
|
size: number,
|
||||||
|
size_gzip: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSettings() {
|
export async function get(theme: Theme) {
|
||||||
document.querySelector(".settings").classList.toggle("open");
|
|
||||||
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let presets = [
|
async function addResult(results: Result[], name: string, css: string) {
|
||||||
{main: "#2ebdf5", text: "#ffffff", bg: "#040813"},
|
results.push({
|
||||||
{main: "#f5b62e", text: "#ffffff", bg: "#040813"},
|
name,
|
||||||
{main: "#FF6565", text: "#ffffff", bg: "#0f0413"},
|
css,
|
||||||
{main: "#9b8fe4", text: "#cfcef4", bg: "#090818", siteBg: "#100E22"},
|
size: getSize(css),
|
||||||
{main: "#337e2c", text: "#031601", bg: "#f3f7f2"},
|
size_gzip: await getCompressedSize(css)
|
||||||
{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"}
|
|
||||||
];
|
|
||||||
|
|
||||||
function showPresets() {
|
|
||||||
let presetsEl = document.querySelector(".presets");
|
|
||||||
presets.forEach(p => {
|
|
||||||
let el = document.createElement("div");
|
|
||||||
el.innerHTML = `
|
|
||||||
<div style="background-color: ${p.main};"></div>
|
|
||||||
<div style="background-color: ${p.text};"></div>
|
|
||||||
<div style="background-color: ${p.bg};"></div>`;
|
|
||||||
el.addEventListener("click", () => {
|
|
||||||
applyPreset(p);
|
|
||||||
});
|
|
||||||
presetsEl.appendChild(el);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyPreset(p) {
|
async function getCompressedSize(content: string) {
|
||||||
let cs = document.querySelectorAll(".colorselector > *");
|
let ds = new CompressionStream("gzip");
|
||||||
cs[0].value = p.main;
|
let blob = new Blob([content]);
|
||||||
cs[1].value = p.text;
|
let compressedStream = blob.stream().pipeThrough(ds);
|
||||||
cs[2].value = p.bg;
|
return (await new Response(compressedStream).blob()).size;
|
||||||
updateColors();
|
|
||||||
let html = document.querySelector("html");
|
|
||||||
html.style = "";
|
|
||||||
if(p.siteBg) html.style.background = p.siteBg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Color {
|
function getSize(content: string) {
|
||||||
|
return (new TextEncoder().encode(content)).length
|
||||||
|
}
|
||||||
|
|
||||||
r;
|
function minify(value: string) {
|
||||||
g;
|
return value
|
||||||
b;
|
.replace(/([^0-9a-zA-Z\.#])\s+/g, "$1")
|
||||||
a;
|
.replace(/\s([^0-9a-zA-Z\.#]+)/g, "$1")
|
||||||
|
.replace(/;}/g, "}")
|
||||||
|
.replace(/\/\*.*?\*\//g, "");
|
||||||
|
}
|
||||||
|
|
||||||
constructor(r, g, b, a = 1) {
|
}
|
||||||
|
|
||||||
|
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.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
|
@ -83,11 +99,12 @@ class Color {
|
||||||
return new Color(this.r, this.g, this.b, this.a);
|
return new Color(this.r, this.g, this.b, this.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromHex(hex) {
|
static fromHex(hex: string) {
|
||||||
return new Color(...this.hexToRgb(hex));
|
let [r, g, b] = this.hexToRgb(hex);
|
||||||
|
return new Color(r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
static hexToRgb(hex) {
|
static hexToRgb(hex: string) {
|
||||||
hex = hex.replace(/^#/, '');
|
hex = hex.replace(/^#/, '');
|
||||||
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
||||||
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
||||||
|
@ -99,8 +116,8 @@ class Color {
|
||||||
return `rgba(${this.r*255}, ${this.g*255}, ${this.b*255}, ${this.a})`;
|
return `rgba(${this.r*255}, ${this.g*255}, ${this.b*255}, ${this.a})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
contrast(otherColor) {
|
contrast(otherColor: Color) {
|
||||||
const getRelativeLuminance = (rgb) => {
|
const getRelativeLuminance = (rgb: number) => {
|
||||||
const sRGB = rgb / 255;
|
const sRGB = rgb / 255;
|
||||||
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
||||||
};
|
};
|
||||||
|
@ -114,19 +131,19 @@ class Color {
|
||||||
return (contrastRatio*100)-100;
|
return (contrastRatio*100)-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
equals(otherColor) {
|
equals(otherColor: Color) {
|
||||||
return this.r == otherColor.r && this.g == otherColor.g && this.b == otherColor.b && this.a == otherColor.a;
|
return this.r == otherColor.r && this.g == otherColor.g && this.b == otherColor.b && this.a == otherColor.a;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Theme {
|
export class Theme {
|
||||||
|
|
||||||
main;
|
main: Color;
|
||||||
text;
|
text: Color;
|
||||||
bg;
|
bg: Color;
|
||||||
|
|
||||||
constructor(opt) {
|
constructor(opt: Preset) {
|
||||||
|
|
||||||
this.main = Color.fromHex(opt.main);
|
this.main = Color.fromHex(opt.main);
|
||||||
this.text = Color.fromHex(opt.text);
|
this.text = Color.fromHex(opt.text);
|
||||||
|
@ -165,16 +182,16 @@ class Theme {
|
||||||
if(this.main.contrast(this.bg) < .3) alert("Contrast between main color and the background is low!");
|
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!");
|
if(this.bg.contrast(this.text) < .3) alert("Contrast between text color and the background is low!");
|
||||||
|
|
||||||
let styles = [`:root {${variables.join("")}}`];
|
let styles = [`:root {\n${variables.join("\n")}\n}`];
|
||||||
|
|
||||||
let btnColor = this.getBtnColor(this.main, this.text);
|
let btnColor = this.getBtnColor(this.main, this.text);
|
||||||
if(btnColor != this.text) styles.push(`.btn.btn-primary {${this.var("text-color", btnColor.rgbFormat())}}`);
|
if(btnColor != this.text) styles.push(`.btn.btn-primary {\n${this.var("text-color", btnColor.rgbFormat())}\n}`);
|
||||||
|
|
||||||
return styles.join("\n");
|
return styles.join("\n\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getBtnColor(main, text) {
|
getBtnColor(main: Color, text: Color) {
|
||||||
let white = new Color(1, 1, 1);
|
let white = new Color(1, 1, 1);
|
||||||
let black = new Color(0, 0, 0);
|
let black = new Color(0, 0, 0);
|
||||||
let cText = main.contrast(text);
|
let cText = main.contrast(text);
|
||||||
|
@ -182,14 +199,14 @@ class Theme {
|
||||||
return cWhite > .3 ? white : cText > .3 ? text : black;
|
return cWhite > .3 ? white : cText > .3 ? text : black;
|
||||||
}
|
}
|
||||||
|
|
||||||
cArgb(color, alpha = 1) {
|
cArgb(color: Color, alpha: number = 1) {
|
||||||
let c = color.clone();
|
let c = color.clone();
|
||||||
c.a = alpha;
|
c.a = alpha;
|
||||||
return c.rgbFormat();
|
return c.rgbFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
var(name, value) {
|
var(name: string, value: string) {
|
||||||
return `--synergy-${name}: ${value};`;
|
return `\t--synergy-${name}: ${value};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue