Initial commit

This commit is contained in:
Filip Znachor 2023-10-05 13:41:55 +02:00
commit c82a789d62
5 changed files with 538 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.vercel

32
demo/demo.css Normal file
View file

@ -0,0 +1,32 @@
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: 20px;}
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: 15px; z-index: 100; background-color: var(--synergy-bg);}
.settings.open {right: 0;}
.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;}

193
demo/demo.js Normal file
View file

@ -0,0 +1,193 @@
var style = document.createElement("style");
addEventListener("load", () => {
let cs = document.querySelectorAll(".colorselector > *");
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() {
let cs = document.querySelectorAll(".colorselector > *");
let p = {main: cs[0].value, text: cs[1].value, bg: cs[2].value};
console.log(p);
let t = new Theme(p);
t.apply();
}
function toggleSettings() {
document.querySelector(".settings").classList.toggle("open");
}
let presets = [
{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"}
];
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) {
let cs = document.querySelectorAll(".colorselector > *");
cs[0].value = p.main;
cs[1].value = p.text;
cs[2].value = p.bg;
updateColors();
let html = document.querySelector("html");
html.style = "";
if(p.siteBg) html.style.background = p.siteBg;
}
class Color {
r;
g;
b;
a;
constructor(r, g, b, a = 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) {
return new Color(...this.hexToRgb(hex));
}
static hexToRgb(hex) {
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() {
return `rgba(${this.r*255}, ${this.g*255}, ${this.b*255}, ${this.a})`;
}
contrast(otherColor) {
const getRelativeLuminance = (rgb) => {
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) {
return this.r == otherColor.r && this.g == otherColor.g && this.b == otherColor.b && this.a == otherColor.a;
}
}
class Theme {
main;
text;
bg;
constructor(opt) {
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.cArgb(this.main, .4)));
variables.push(this.var("border-active", this.cArgb(this.main)));
variables.push(this.var("label", this.cArgb(this.main, .8)));
variables.push(this.var("label-active", this.cArgb(this.main)));
variables.push(this.var("btn-primary-bg", this.cArgb(this.main, .8)));
variables.push(this.var("btn-primary-bg-active", this.cArgb(this.main, .5)));
variables.push(this.var("btn-primary-bg-hover", this.cArgb(this.main)));
let btnBg = new Color(.6, .6, .6);
variables.push(this.var("btn-bg", this.cArgb(btnBg, .3)));
variables.push(this.var("btn-bg-active", this.cArgb(btnBg, .6)));
variables.push(this.var("btn-bg-hover", this.cArgb(btnBg, .4)));
variables.push(this.var("text-color", this.text.rgbFormat()));
variables.push(this.var("bg", this.bg.rgbFormat()));
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 {${variables.join("")}}`];
let btnColor = this.getBtnColor(this.main, this.text);
if(btnColor != this.text) styles.push(`.btn.btn-primary {${this.var("text-color", btnColor.rgbFormat())}}`);
return styles.join("\n");
}
getBtnColor(main, text) {
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;
}
cArgb(color, alpha = 1) {
let c = color.clone();
c.a = alpha;
return c.rgbFormat();
}
var(name, value) {
return `--synergy-${name}: ${value};`;
}
}

91
demo/index.html Normal file
View file

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Synergy UI</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./demo.css">
<link rel="stylesheet" href="../style.css">
</head>
<body>
<div class="content">
<header>
<h1>
<span class="color">Synergy</span> UI
</h1>
<p>Simple framework with CSS-only UI components</p>
<div class="colorselector">
<input type="color" value="#865e3c" title="Main color">
<input type="color" value="#63452c" title="Text color">
<input type="color" value="#f9f7f4" title="Background color">
<div class="config" title="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>
</div>
</header>
<aside class="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>
<b>Presets</b>
<div class="presets"></div>
</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">Check me! I'm a toggle.</label>
<div class="toggle">
<input id="toggle1" type="checkbox">
<div class="indicator"></div>
</div>
</div>
<div class="btn-row">
<button class="btn btn-primary">
Send
</button>
<button type="reset" class="btn">
Reset
</button>
</div>
</form>
</div>
</div>
<script src="./demo.js"></script>
</body>
</html>

221
style.css Normal file
View file

@ -0,0 +1,221 @@
:root {
--synergy-bg: rgba(249, 247, 244, 1);
--synergy-border: rgba(134, 94, 60, 0.4);
--synergy-border-active: rgba(134, 94, 60, 1);
--synergy-border-width: 2px;
--synergy-border-radius: 0.375rem;
--synergy-label: rgba(134, 94, 60, 0.8);
--synergy-label-active: rgba(134, 94, 60, 1);
--synergy-text-color: rgba(99, 69, 44, 1);
--synergy-btn-primary-bg: rgba(134, 94, 60, 0.8);
--synergy-btn-primary-bg-hover: rgba(134, 94, 60, 1);
--synergy-btn-primary-bg-active: rgba(134, 94, 60, 0.5);
--synergy-btn-bg: rgba(153, 153, 153, 0.3);
--synergy-btn-bg-hover: rgba(153, 153, 153, 0.4);
--synergy-btn-bg-active: rgba(153, 153, 153, 0.6);
}
.btn.btn-primary {--synergy-text-color: rgba(255, 255, 255, 1);}
/*
INPUTS
*/
.inp {
position: relative;
width: 100%;
}
/* Label */
.inp label {
position: absolute;
top: 11px;
left: 7px;
font-size: 1rem;
font-weight: 700;
transform-origin: 0 0;
transform: translate3d(0, 0, 0);
transition: all .2s ease;
pointer-events: none;
background: var(--synergy-bg);
padding: 3px 8px;
border-radius: 10px;
line-height: 1;
color: var(--synergy-label);
}
.inp :is(
:is(input, textarea, select):not(:placeholder-shown) + label,
:is(input, textarea):not(:-ms-input-placeholder) + label,
:is(input, textarea):not(:-moz-placeholder-shown) + label
),
.inp :is(input, textarea):focus + label {
color: var(--synergy-label-active);
transform: translate3d(0, -21px, 0) scale(.8);
}
/* Common styles */
.inp :is(input, textarea, select) {
display: block;
width: 100%;
min-height: 47px;
font-family: inherit;
padding: 10px 12px;
font-size: 1rem;
font-weight: 500;
color: var(--synergy-text-color);
transition: border-color 0.15s ease;
border-radius: var(--synergy-border-radius);
border: var(--synergy-border-width) solid var(--synergy-border);
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .1);
background: var(--synergy-bg);
margin: 0px;
}
.inp :is(input, textarea, select):focus {
outline: none;
border-color: var(--synergy-border-active);
}
/* Specific styles */
.inp.select select {
appearance: none;
padding-right: 40px;
}
.inp.select::after {
content: "";
border: 1px solid var(--synergy-text-color);
border-top: 0;
border-left: 0;
display: block;
position: absolute;
top: 50%;
margin-top: -8px;
right: 15px;
padding: 5px;
transform: rotate(45deg);
pointer-events: none;
}
.inp textarea {
height: 100px;
max-width: 100%;
resize: vertical;
}
/*
TOGGLE
*/
.toggle {
border: var(--synergy-border-width) solid var(--synergy-border);
background-color: var(--synergy-bg);
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .1);
border-radius: 100px;
padding: 3px;
display: flex;
max-width: 60px;
height: 30px;
width: 100%;
position: relative;
}
.toggle input {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
z-index: 100;
}
.toggle .indicator {
transition: all .2s;
display: flex;
height: 100%;
}
.toggle .indicator::after {
transition: all .2s;
margin-left: auto;
display: inline-block;
height: 100%;
aspect-ratio: 1 / 1;
background-color: var(--synergy-border);
border-radius: 100%;
content: "";
}
.toggle input:checked + .indicator {
flex: 1;
}
.toggle input:checked + .indicator::after {
background-color: var(--synergy-border-active);
}
/* Toggle with label */
.toggle-text {
display: grid;
align-items: center;
gap: 20px;
grid-template-columns: 1fr 50px;
}
.toggle-text label {
cursor: pointer;
}
/*
BUTTON
*/
.btn-row {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.btn {
background-color: var(--synergy-btn-bg);
padding: 12px 20px;
border: 0;
color: var(--synergy-text-color);
font: inherit;
font-weight: 700;
font-size: 1rem;
border-radius: var(--synergy-border-radius);
transition: .2s background-color;
cursor: pointer;
}
.btn:hover {
background-color: var(--synergy-btn-bg-hover);
}
.btn:active {
background-color: var(--synergy-btn-bg-active);
}
.btn.btn-primary {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .1);
--synergy-btn-bg: var(--synergy-btn-primary-bg);
--synergy-btn-bg-hover: var(--synergy-btn-primary-bg-hover);
--synergy-btn-bg-active: var(--synergy-btn-primary-bg-active);
}