diff --git a/index.html b/index.html index 6378fc6..26d6c6c 100644 --- a/index.html +++ b/index.html @@ -4,159 +4,11 @@ Synergy UI - + -
- -
-

- Synergy UI -

-

Simple framework with CSS-only UI components

-
- - - -
- -
-
-
- - - -
- -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- -
- -
-
-
- -
- - - -
- - - -
- - - -
- -
- - -
- -
- -
- -
- +
+ \ No newline at end of file diff --git a/package.json b/package.json index 68d923c..d1936a6 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,23 @@ { - "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" - } + "name": "synergy", + "version": "1.0.0", + "description": "", + "main": "index.js", + "keywords": [], + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@iconify/vue": "^4.1.2", + "vue": "^3.4.26" + }, + "devDependencies": { + "typescript": "^5.0.2", + "@vitejs/plugin-vue": "^5.0.4", + "vite": "^4.4.5" + } } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index dbbfae1..4024056 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,9 @@ +import vuePlugin from '@vitejs/plugin-vue'; import { defineConfig } from 'vite' export default defineConfig({ + plugins: [ + vuePlugin() + ], publicDir: "src/" }); \ No newline at end of file diff --git a/web/App.vue b/web/App.vue new file mode 100644 index 0000000..c801f62 --- /dev/null +++ b/web/App.vue @@ -0,0 +1,104 @@ + + +./ts/synergy \ No newline at end of file diff --git a/web/app.ts b/web/app.ts deleted file mode 100644 index a9bf52f..0000000 --- a/web/app.ts +++ /dev/null @@ -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("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"); \ No newline at end of file diff --git a/web/components/Settings.vue b/web/components/Settings.vue new file mode 100644 index 0000000..b15c581 --- /dev/null +++ b/web/components/Settings.vue @@ -0,0 +1,53 @@ + + +../ts/Synergy../ts/Styles../ts/Shared \ No newline at end of file diff --git a/web/css/demo.css b/web/css/demo.css new file mode 100644 index 0000000..82ae182 --- /dev/null +++ b/web/css/demo.css @@ -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;} \ No newline at end of file diff --git a/web/declaration.d.ts b/web/declaration.d.ts index 3255cfe..2bba51e 100644 --- a/web/declaration.d.ts +++ b/web/declaration.d.ts @@ -1 +1,5 @@ -declare module '*.css'; \ No newline at end of file +declare module '*.css'; + +declare interface ComboObject { + [U: string]: any +}; diff --git a/web/demo.css b/web/demo.css deleted file mode 100644 index 9dd88e9..0000000 --- a/web/demo.css +++ /dev/null @@ -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;} \ No newline at end of file diff --git a/web/lib.ts b/web/lib.ts deleted file mode 100644 index bc531ea..0000000 --- a/web/lib.ts +++ /dev/null @@ -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};`; - } - -} \ No newline at end of file diff --git a/web/ts/App.ts b/web/ts/App.ts new file mode 100644 index 0000000..2214bf9 --- /dev/null +++ b/web/ts/App.ts @@ -0,0 +1,4 @@ +import { createApp } from "vue"; +import App from "../App.vue"; + +let app = createApp(App).mount("#app"); diff --git a/web/ts/Color.ts b/web/ts/Color.ts new file mode 100644 index 0000000..f652da0 --- /dev/null +++ b/web/ts/Color.ts @@ -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); + } + +} diff --git a/web/ts/Preset.ts b/web/ts/Preset.ts new file mode 100644 index 0000000..b4cc845 --- /dev/null +++ b/web/ts/Preset.ts @@ -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; + + constructor(colors: Colors) { + 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) { + return Object.entries(input).reduce>>((acc, [key, value]) => { + acc[key as ColorType] = value instanceof Color ? value : Color.fromHex(value); + return acc; + }, {}) as Colors; + } + + 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 { + main: T, + text: T, + bg: T, + "site-bg"?: T +} + +type ColorType = keyof Colors; diff --git a/web/ts/Shared.ts b/web/ts/Shared.ts new file mode 100644 index 0000000..fe37e25 --- /dev/null +++ b/web/ts/Shared.ts @@ -0,0 +1,19 @@ +import { reactive, watch } from "vue"; +import { getDefaultVariables } from "./Synergy"; +import { setVariables } from "./Styles"; + +let values = getDefaultVariables(); + +export let variableValues = reactive(values); + +watch(() => variableValues, () => { + setVariables(variableValues); +}, {deep: true}); + +setVariables(values); + +export function applyVariables(variables: ComboObject) { + Object.keys(variables).forEach(key => { + variableValues[key] = variables[key]; + }); +} diff --git a/web/ts/Styles.ts b/web/ts/Styles.ts new file mode 100644 index 0000000..dbcaeaa --- /dev/null +++ b/web/ts/Styles.ts @@ -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}}`; +} diff --git a/web/ts/Synergy.ts b/web/ts/Synergy.ts new file mode 100644 index 0000000..d581140 --- /dev/null +++ b/web/ts/Synergy.ts @@ -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() + }; +}