Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86650a1396
|
+424
@@ -0,0 +1,424 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Logo-Generator</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Anton&family=Bowlby+One&family=Bebas+Neue&family=Bungee&family=Passion+One:wght@900&family=Ultra&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
html, body { height: 100%; margin: 0; }
|
||||||
|
body {
|
||||||
|
background: #362582;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: 'Anton', 'Bebas Neue', Impact, sans-serif;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
font-size: 18vw;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #f6d939;
|
||||||
|
line-height: 1.05;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
transform: rotate(-10deg) skewX(-8deg);
|
||||||
|
transform-origin: center;
|
||||||
|
text-align: center;
|
||||||
|
user-select: none;
|
||||||
|
transition: color 0.15s;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: fixed;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
background: rgba(0,0,0,0.72);
|
||||||
|
color: #eee;
|
||||||
|
padding: 14px 18px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 320px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
max-height: 96vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-shadow: 0 6px 30px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
.panel h1 { margin: 0 0 10px; font-size: 14px; color: #fff; }
|
||||||
|
.panel h2 {
|
||||||
|
margin: 14px 0 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #f6d939;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
.panel .hint {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #aaa;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.panel label {
|
||||||
|
display: block;
|
||||||
|
margin: 6px 0 3px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
.panel input[type=range] { width: 100%; margin: 0; }
|
||||||
|
.panel input[type=text],
|
||||||
|
.panel textarea,
|
||||||
|
.panel select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 8px;
|
||||||
|
background: rgba(255,255,255,0.08);
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.panel textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
.panel input:focus, .panel textarea:focus, .panel select:focus {
|
||||||
|
outline: 2px solid #f6d939;
|
||||||
|
outline-offset: 1px;
|
||||||
|
border-color: rgba(246,217,57,0.6);
|
||||||
|
}
|
||||||
|
.toggle-panel:focus-visible,
|
||||||
|
.copy-btn:focus-visible,
|
||||||
|
.reset-btn:focus-visible {
|
||||||
|
outline: 2px solid #f6d939;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.color-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr auto;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
.color-row input[type=color] {
|
||||||
|
width: 36px;
|
||||||
|
height: 28px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
.color-row .swatch-label { font-size: 11px; color: #ccc; }
|
||||||
|
.color-row .hex { font-size: 10px; color: #aaa; text-align: right; width: 60px; }
|
||||||
|
.val { color: #f6d939; font-weight: bold; }
|
||||||
|
.row-2col {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.panel code {
|
||||||
|
display: block;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
padding: 8px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 10px;
|
||||||
|
max-height: 220px;
|
||||||
|
overflow-y: auto;
|
||||||
|
white-space: pre;
|
||||||
|
color: #aaa;
|
||||||
|
font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
|
||||||
|
}
|
||||||
|
.copy-btn, .reset-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.copy-btn {
|
||||||
|
background: rgba(246,217,57,0.15);
|
||||||
|
color: #f6d939;
|
||||||
|
border: 1px solid rgba(246,217,57,0.4);
|
||||||
|
}
|
||||||
|
.copy-btn:hover { background: rgba(246,217,57,0.25); }
|
||||||
|
.copy-btn.copied { background: rgba(120,200,120,0.25); color: #cfc; border-color: rgba(120,200,120,0.5); }
|
||||||
|
.reset-btn {
|
||||||
|
background: rgba(255,255,255,0.05);
|
||||||
|
color: #ccc;
|
||||||
|
border: 1px solid rgba(255,255,255,0.15);
|
||||||
|
}
|
||||||
|
.reset-btn:hover { background: rgba(255,255,255,0.1); }
|
||||||
|
|
||||||
|
.toggle-panel {
|
||||||
|
position: fixed;
|
||||||
|
top: 12px;
|
||||||
|
left: 12px;
|
||||||
|
background: rgba(0,0,0,0.6);
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 11px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
summary.toggle-panel { list-style: none; }
|
||||||
|
summary.toggle-panel::-webkit-details-marker { display: none; }
|
||||||
|
.disclosure { position: static; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="logo" id="logo">LIBRE
|
||||||
|
TECH</div>
|
||||||
|
|
||||||
|
<details class="disclosure" id="panel" open>
|
||||||
|
<summary class="toggle-panel">Panel ein/aus</summary>
|
||||||
|
|
||||||
|
<div class="panel">
|
||||||
|
<h1>Logo-Generator</h1>
|
||||||
|
|
||||||
|
<form id="logoForm">
|
||||||
|
<h2>Inhalt</h2>
|
||||||
|
<p class="hint" id="hint-text">Mehrere Zeilen mit Enter trennen.</p>
|
||||||
|
<textarea id="text" rows="2" aria-label="Logo-Text" aria-describedby="hint-text">LIBRE
|
||||||
|
TECH</textarea>
|
||||||
|
|
||||||
|
<h2>Schrift</h2>
|
||||||
|
<select id="font" aria-label="Schriftart">
|
||||||
|
<option value="Anton">Anton</option>
|
||||||
|
<option value="Bowlby One">Bowlby One</option>
|
||||||
|
<option value="Bebas Neue">Bebas Neue</option>
|
||||||
|
<option value="Bungee">Bungee</option>
|
||||||
|
<option value="Passion One">Passion One</option>
|
||||||
|
<option value="Ultra">Ultra</option>
|
||||||
|
<option value="Impact">Impact (System)</option>
|
||||||
|
</select>
|
||||||
|
<div class="row-2col">
|
||||||
|
<div>
|
||||||
|
<label for="fs">Größe: <span class="val" id="v-fs">18</span>vw</label>
|
||||||
|
<input type="range" id="fs" min="6" max="30" step="0.5" value="18">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="lh">Zeilenabstand: <span class="val" id="v-lh">1.05</span></label>
|
||||||
|
<input type="range" id="lh" min="0.5" max="2" step="0.05" value="1.05">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="ls">Buchstabenabstand: <span class="val" id="v-ls">0.010</span>em</label>
|
||||||
|
<input type="range" id="ls" min="-0.15" max="0.3" step="0.005" value="0.01">
|
||||||
|
|
||||||
|
<h2>Farben</h2>
|
||||||
|
<div class="color-row">
|
||||||
|
<input type="color" id="c-bg" value="#362582" aria-labelledby="lbl-bg">
|
||||||
|
<span class="swatch-label" id="lbl-bg">Hintergrund</span>
|
||||||
|
<span class="hex" id="hex-bg">#362582</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-row">
|
||||||
|
<input type="color" id="c-face" value="#f6d939" aria-labelledby="lbl-face">
|
||||||
|
<span class="swatch-label" id="lbl-face">Face</span>
|
||||||
|
<span class="hex" id="hex-face">#f6d939</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-row">
|
||||||
|
<input type="color" id="c-depth" value="#ea741e" aria-labelledby="lbl-depth">
|
||||||
|
<span class="swatch-label" id="lbl-depth">Tiefe</span>
|
||||||
|
<span class="hex" id="hex-depth">#ea741e</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-row">
|
||||||
|
<input type="color" id="c-shadow" value="#221953" aria-labelledby="lbl-shadow">
|
||||||
|
<span class="swatch-label" id="lbl-shadow">Schatten & Kontur</span>
|
||||||
|
<span class="hex" id="hex-shadow">#221953</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Lichtrichtung</h2>
|
||||||
|
<p class="hint">Steuert alle Versätze gleichzeitig — Schatten geht in Lichtrichtung, Tiefe entgegen.</p>
|
||||||
|
<label for="angle">Winkel: <span class="val" id="v-angle">133</span>° (0=oben, 90=rechts, 180=unten)</label>
|
||||||
|
<input type="range" id="angle" min="0" max="359" value="133">
|
||||||
|
|
||||||
|
<h2>Schichten</h2>
|
||||||
|
<p class="hint">Hinten nach vorn: Schatten · Tiefe · Kontur · Face</p>
|
||||||
|
<label for="shadow">Schatten-Länge: <span class="val" id="v-shadow">30</span>px</label>
|
||||||
|
<input type="range" id="shadow" min="0" max="80" value="30">
|
||||||
|
<label for="depth">Tiefe (in Gegenrichtung): <span class="val" id="v-depth">15</span>px</label>
|
||||||
|
<input type="range" id="depth" min="0" max="30" value="15">
|
||||||
|
<label for="contour">Kontur (vor der Tiefe): <span class="val" id="v-contour">11</span>px</label>
|
||||||
|
<input type="range" id="contour" min="0" max="20" value="11">
|
||||||
|
|
||||||
|
<h2>Komposition</h2>
|
||||||
|
<div class="row-2col">
|
||||||
|
<div>
|
||||||
|
<label for="rot">Drehung: <span class="val" id="v-rot">-10</span>°</label>
|
||||||
|
<input type="range" id="rot" min="-25" max="25" value="-10">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="skew">Schräge: <span class="val" id="v-skew">-8</span>°</label>
|
||||||
|
<input type="range" id="skew" min="-20" max="20" value="-8">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Generiertes CSS</h2>
|
||||||
|
<code id="cssout"></code>
|
||||||
|
<button type="button" class="copy-btn" id="copyBtn">CSS kopieren</button>
|
||||||
|
<button type="reset" class="reset-btn" id="resetBtn">Zurücksetzen</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const $ = id => document.getElementById(id);
|
||||||
|
const logo = $('logo');
|
||||||
|
const cssout = $('cssout');
|
||||||
|
|
||||||
|
// Kompakte text-shadow-Liste mit CSS-Variablen
|
||||||
|
function buildShadows(angle, shadowLen, depthLen, contourLen) {
|
||||||
|
const rad = angle * Math.PI / 180;
|
||||||
|
const dx = Math.sin(rad);
|
||||||
|
const dy = -Math.cos(rad);
|
||||||
|
const round = n => Math.round(n * 100) / 100;
|
||||||
|
const lines = [];
|
||||||
|
|
||||||
|
// Layer "Kontur" (vorn): Stufen in Lichtrichtung, Schatten-Farbe
|
||||||
|
for (let i = 1; i <= contourLen; i++) {
|
||||||
|
lines.push(` ${round(dx * i)}px ${round(dy * i)}px 0 var(--shadow)`);
|
||||||
|
}
|
||||||
|
// Layer "Tiefe" (mitte): in Gegenrichtung
|
||||||
|
for (let i = 1; i <= depthLen; i++) {
|
||||||
|
lines.push(` ${round(-dx * i)}px ${round(-dy * i)}px 0 var(--depth)`);
|
||||||
|
}
|
||||||
|
// Layer "Schatten" (hinten): lang in Lichtrichtung
|
||||||
|
// Kontur und Schatten gehen in dieselbe Richtung; ab contourLen+1 weitermachen,
|
||||||
|
// damit keine doppelten Stufen entstehen
|
||||||
|
for (let i = contourLen + 1; i <= contourLen + shadowLen; i++) {
|
||||||
|
lines.push(` ${round(dx * i)}px ${round(dy * i)}px 0 var(--shadow)`);
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
// Inputs lesen
|
||||||
|
const text = $('text').value || ' ';
|
||||||
|
const font = $('font').value;
|
||||||
|
const fs = +$('fs').value;
|
||||||
|
const lh = +$('lh').value;
|
||||||
|
const ls = +$('ls').value;
|
||||||
|
const cBg = $('c-bg').value;
|
||||||
|
const cFace = $('c-face').value;
|
||||||
|
const cDepth = $('c-depth').value;
|
||||||
|
const cShadow = $('c-shadow').value;
|
||||||
|
const angle = +$('angle').value;
|
||||||
|
const shadowLen = +$('shadow').value;
|
||||||
|
const depthLen = +$('depth').value;
|
||||||
|
const contourLen = +$('contour').value;
|
||||||
|
const rot = +$('rot').value;
|
||||||
|
const skew = +$('skew').value;
|
||||||
|
|
||||||
|
// Anzeigewerte
|
||||||
|
$('v-fs').textContent = fs;
|
||||||
|
$('v-lh').textContent = lh.toFixed(2);
|
||||||
|
$('v-ls').textContent = ls.toFixed(3);
|
||||||
|
$('v-angle').textContent = angle;
|
||||||
|
$('v-shadow').textContent = shadowLen;
|
||||||
|
$('v-depth').textContent = depthLen;
|
||||||
|
$('v-contour').textContent = contourLen;
|
||||||
|
$('v-rot').textContent = rot;
|
||||||
|
$('v-skew').textContent = skew;
|
||||||
|
|
||||||
|
// Hex-Anzeige spiegelt die Color-Inputs (auch nach Reset)
|
||||||
|
$('hex-bg').textContent = cBg;
|
||||||
|
$('hex-face').textContent = cFace;
|
||||||
|
$('hex-depth').textContent = cDepth;
|
||||||
|
$('hex-shadow').textContent = cShadow;
|
||||||
|
|
||||||
|
// Logo: Text als Multi-Line
|
||||||
|
logo.textContent = text;
|
||||||
|
|
||||||
|
// text-shadow direkt mit Hex-Werten anwenden (Browser kennt die var() nicht
|
||||||
|
// automatisch in inline-Styles, daher hier direkte Werte)
|
||||||
|
const rad = angle * Math.PI / 180;
|
||||||
|
const dx = Math.sin(rad);
|
||||||
|
const dy = -Math.cos(rad);
|
||||||
|
const round = n => Math.round(n * 100) / 100;
|
||||||
|
const inlineShadows = [];
|
||||||
|
for (let i = 1; i <= contourLen; i++) {
|
||||||
|
inlineShadows.push(`${round(dx * i)}px ${round(dy * i)}px 0 ${cShadow}`);
|
||||||
|
}
|
||||||
|
for (let i = 1; i <= depthLen; i++) {
|
||||||
|
inlineShadows.push(`${round(-dx * i)}px ${round(-dy * i)}px 0 ${cDepth}`);
|
||||||
|
}
|
||||||
|
for (let i = contourLen + 1; i <= contourLen + shadowLen; i++) {
|
||||||
|
inlineShadows.push(`${round(dx * i)}px ${round(dy * i)}px 0 ${cShadow}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logo.style.textShadow = inlineShadows.length ? inlineShadows.join(', ') : 'none';
|
||||||
|
logo.style.transform = `rotate(${rot}deg) skewX(${skew}deg)`;
|
||||||
|
logo.style.fontFamily = `'${font}', 'Bebas Neue', Impact, sans-serif`;
|
||||||
|
logo.style.color = cFace;
|
||||||
|
logo.style.fontSize = fs + 'vw';
|
||||||
|
logo.style.lineHeight = lh;
|
||||||
|
logo.style.letterSpacing = ls + 'em';
|
||||||
|
document.body.style.background = cBg;
|
||||||
|
|
||||||
|
// CSS-Output mit Variablen — sauber, idiomatisch, modern
|
||||||
|
const shadowLines = buildShadows(angle, shadowLen, depthLen, contourLen);
|
||||||
|
const css = `:root {
|
||||||
|
--bg: ${cBg};
|
||||||
|
--face: ${cFace};
|
||||||
|
--depth: ${cDepth};
|
||||||
|
--shadow: ${cShadow};
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-family: '${font}', sans-serif;
|
||||||
|
font-size: ${fs}vw;
|
||||||
|
line-height: ${lh};
|
||||||
|
letter-spacing: ${ls}em;
|
||||||
|
color: var(--face);
|
||||||
|
transform: rotate(${rot}deg) skewX(${skew}deg);
|
||||||
|
white-space: pre-line;
|
||||||
|
text-shadow:
|
||||||
|
${shadowLines.join(',\n')};
|
||||||
|
}`;
|
||||||
|
cssout.textContent = css;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inputs binden
|
||||||
|
document.querySelectorAll('.panel input, .panel select, .panel textarea').forEach(el => {
|
||||||
|
el.addEventListener('input', update);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('copyBtn').addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(cssout.textContent);
|
||||||
|
const btn = $('copyBtn');
|
||||||
|
btn.textContent = 'Kopiert!';
|
||||||
|
btn.classList.add('copied');
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.textContent = 'CSS kopieren';
|
||||||
|
btn.classList.remove('copied');
|
||||||
|
}, 1500);
|
||||||
|
} catch (err) {
|
||||||
|
alert('Kopieren fehlgeschlagen — bitte manuell aus dem Code-Feld kopieren.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Native <button type="reset"> setzt alle Felder auf ihre value=-Attribute zurück;
|
||||||
|
// danach (nächster Tick) neu rendern.
|
||||||
|
const form = $('logoForm');
|
||||||
|
form.addEventListener('reset', () => setTimeout(update, 0));
|
||||||
|
form.addEventListener('submit', e => e.preventDefault());
|
||||||
|
|
||||||
|
update();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "demo-ecmascript",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://tengo@git.librete.ch:41240/public/demo-ecmascript.git"
|
||||||
|
},
|
||||||
|
"license": "WTFPL",
|
||||||
|
"author": "",
|
||||||
|
"type": "module",
|
||||||
|
"main": "index.html",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user