Liquid_UI / app.py
DxrkMonteva's picture
Update app.py
209f324 verified
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
HTML_PAGE = """<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LiquidUI v2.0 — 40+ Жидких Эффектов</title>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;700;800&family=DM+Sans:ital,wght@0,300;0,400;1,300&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #060811; --bg2: #0c1020; --surface: rgba(255,255,255,0.04);
--border: rgba(255,255,255,0.08); --text: #e8e8f0;
--muted: rgba(232,232,240,0.45); --accent: #7c6dff;
--accent2: #00d4ff; --accent3: #ff5fa0; --accent4: #ffaa00;
--accent5: #00ff88; --accent6: #ff0044;
}
html { scroll-behavior: smooth; }
body {
background: var(--bg); color: var(--text);
font-family: 'DM Sans', sans-serif; font-weight: 300;
line-height: 1.6; overflow-x: hidden;
}
header {
min-height: 100vh; display: flex; flex-direction: column;
align-items: center; justify-content: center; position: relative;
overflow: hidden; text-align: center; padding: 60px 24px;
}
.hero-bg {
position: absolute; inset: 0; pointer-events: none;
background:
radial-gradient(ellipse 80% 60% at 50% 0%, rgba(124,109,255,0.18) 0%, transparent 70%),
radial-gradient(ellipse 60% 40% at 80% 80%, rgba(0,212,255,0.12) 0%, transparent 60%),
radial-gradient(ellipse 50% 50% at 20% 60%, rgba(255,95,160,0.1) 0%, transparent 60%);
}
.hero-title {
font-family: 'Syne', sans-serif; font-weight: 800;
font-size: clamp(3.5rem, 10vw, 8rem); letter-spacing: -0.03em;
line-height: 0.9; position: relative; z-index: 1;
background: linear-gradient(135deg, #fff 0%, rgba(124,109,255,0.9) 50%, #00d4ff 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text; margin-bottom: 0.3em;
}
.hero-sub {
font-size: clamp(1rem, 2.5vw, 1.35rem); color: var(--muted);
max-width: 600px; position: relative; z-index: 1;
margin-bottom: 2.5rem; font-style: italic;
}
.hero-badge {
display: inline-flex; align-items: center; gap: 8px;
background: rgba(124,109,255,0.12); border: 1px solid rgba(124,109,255,0.3);
border-radius: 100px; padding: 6px 18px; font-size: 0.78rem;
letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent);
margin-bottom: 2rem; position: relative; z-index: 1;
}
.scroll-hint {
position: absolute; bottom: 40px; left: 50%; transform: translateX(-50%);
display: flex; flex-direction: column; align-items: center; gap: 8px;
color: var(--muted); font-size: 0.75rem; letter-spacing: 0.15em;
text-transform: uppercase; animation: float 2s ease-in-out infinite;
z-index: 1;
}
main { max-width: 1200px; margin: 0 auto; padding: 0 24px 120px; }
.section { margin-bottom: 100px; }
.section-label {
font-size: 0.7rem; letter-spacing: 0.2em; text-transform: uppercase;
color: var(--accent); margin-bottom: 12px; display: block;
}
.section-title {
font-family: 'Syne', sans-serif; font-size: clamp(1.6rem, 4vw, 2.4rem);
font-weight: 700; color: var(--text); margin-bottom: 10px; line-height: 1.15;
}
.section-desc { color: var(--muted); max-width: 520px; font-size: 0.95rem; margin-bottom: 40px; }
.divider {
width: 60px; height: 2px;
background: linear-gradient(90deg, var(--accent), var(--accent2));
border-radius: 2px; margin-bottom: 24px;
}
.btn-row { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }
.btn {
display: inline-flex; align-items: center; gap: 8px;
padding: 14px 32px; border-radius: 100px; border: none;
cursor: pointer; font-family: 'Syne', sans-serif;
font-weight: 700; font-size: 0.9rem; letter-spacing: 0.05em;
position: relative; overflow: hidden; transition: transform 0.2s;
text-decoration: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--accent), var(--accent2));
color: #fff; box-shadow: 0 8px 32px rgba(124,109,255,0.35);
}
.btn-secondary { background: transparent; border: 1.5px solid var(--border); color: var(--text); }
.btn-glow { background: var(--accent3); color: #fff; box-shadow: 0 0 20px rgba(255,95,160,0.4); }
.btn-gold { background: linear-gradient(135deg, #ffaa00, #ff6600); color: #fff; }
.btn-cyber { background: linear-gradient(135deg, #00ff88, #0088ff); color: #000; }
.btn-dark { background: #1a1a2e; border: 2px solid #333; color: #fff; }
/* Grid layouts */
.effects-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }
.mini-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 16px; }
.demo-card {
background: var(--surface); border: 1px solid var(--border); border-radius: 20px;
padding: 32px 24px; display: flex; flex-direction: column; align-items: center;
gap: 14px; text-align: center; position: relative; overflow: hidden;
}
.demo-card .tag {
font-size: 0.68rem; letter-spacing: 0.15em; text-transform: uppercase;
color: var(--muted); margin-top: auto; font-family: 'Syne', sans-serif;
}
.glass-panel {
background: rgba(255,255,255,0.04);
backdrop-filter: blur(24px) saturate(1.5);
border: 1px solid rgba(255,255,255,0.12);
border-radius: 24px; padding: 40px; position: relative; overflow: hidden;
}
.code-block {
background: rgba(0,0,0,0.5); border: 1px solid rgba(255,255,255,0.07);
border-radius: 14px; padding: 24px 28px; font-family: 'Courier New', monospace;
font-size: 0.82rem; color: #a8c8ff; line-height: 1.8; overflow-x: auto;
}
/* ========== 40+ EFFECTS ========== */
/* 1-5: ELASTIC */
.elastic-btn { transition: transform 0.1s cubic-bezier(0.34,1.56,0.64,1); transform-origin: center; }
.elastic-btn:hover { transform: scale(1.05, 0.95); }
.elastic-btn:active { transform: scale(0.95, 1.05); }
.bounce-btn { animation: bounce 2s infinite; }
@keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
.pulse-btn { animation: pulse-scale 2s ease-in-out infinite; }
@keyframes pulse-scale { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
.wobble-btn:hover { animation: wobble 0.8s ease; }
@keyframes wobble { 0% { transform: rotate(0); } 25% { transform: rotate(-5deg) scale(1.1); } 75% { transform: rotate(5deg) scale(0.9); } 100% { transform: rotate(0); } }
.jelly-intense { filter: url('#goo'); transition: all 0.3s; }
/* 6-10: MORPH */
.morph-blob { animation: morph 4s ease-in-out infinite; border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; }
@keyframes morph { 0%, 100% { border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; } 50% { border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%; } }
.morph-square { animation: morph-square 5s ease-in-out infinite; }
@keyframes morph-square { 0%, 100% { border-radius: 20px; } 50% { border-radius: 50% 20% 40% 60%; } }
.liquid-shape { animation: liquid-morph 6s ease-in-out infinite; }
@keyframes liquid-morph { 0%, 100% { clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); } 25% { clip-path: polygon(60% 10%, 90% 40%, 40% 90%, 10% 60%); } 50% { clip-path: polygon(40% 20%, 80% 30%, 60% 80%, 20% 70%); } 75% { clip-path: polygon(70% 0%, 100% 60%, 30% 100%, 0% 40%); } }
.amoeba { animation: amoeba 8s ease-in-out infinite; filter: url('#goo'); }
@keyframes amoeba { 0%, 100% { border-radius: 40% 60% 70% 30% / 40% 50% 60% 50%; transform: rotate(0deg); } 33% { border-radius: 60% 40% 30% 70% / 60% 30% 40% 60%; transform: rotate(120deg); } 66% { border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%; transform: rotate(240deg); } }
/* 11-15: GLASS */
.glass-frost { backdrop-filter: blur(20px) saturate(180%); background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.2); }
.glass-dark { backdrop-filter: blur(30px); background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.1); }
.glass-gradient { backdrop-filter: blur(25px); background: linear-gradient(135deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05)); }
.glass-chrome { backdrop-filter: blur(15px) brightness(1.2); background: linear-gradient(135deg, rgba(255,255,255,0.2), rgba(255,255,255,0.05), rgba(255,255,255,0.15)); border: 2px solid rgba(255,255,255,0.3); }
.glass-neon { backdrop-filter: blur(20px); background: rgba(124,109,255,0.15); border: 2px solid var(--accent); box-shadow: 0 0 30px rgba(124,109,255,0.3), inset 0 0 20px rgba(124,109,255,0.1); }
/* 16-20: MAGNETIC */
.magnetic { transition: transform 0.2s cubic-bezier(0.22,1,0.36,1); }
.magnetic-strong { transition: transform 0.15s cubic-bezier(0.22,1,0.36,1); }
.magnetic-elastic { transition: transform 0.4s cubic-bezier(0.68,-0.55,0.265,1.55); }
.magnetic-smooth { transition: transform 0.6s cubic-bezier(0.16,1,0.3,1); }
.magnetic-snap { transition: transform 0.1s; }
/* 21-25: RIPPLE */
.ripple { position: relative; overflow: hidden; }
.ripple-dark { position: relative; overflow: hidden; }
.ripple-color { position: relative; overflow: hidden; }
.ripple-glow { position: relative; overflow: hidden; }
.ripple-multi { position: relative; overflow: hidden; }
/* 26-30: TILT */
.tilt-3d { transform-style: preserve-3d; transition: transform 0.15s; }
.tilt-glare { transform-style: preserve-3d; transition: transform 0.15s; position: relative; }
.tilt-glare::after { content: ''; position: absolute; inset: 0; background: linear-gradient(135deg, rgba(255,255,255,0.4) 0%, transparent 50%, transparent 100%); opacity: 0; transition: opacity 0.3s; pointer-events: none; }
.tilt-glare:hover::after { opacity: 1; }
.tilt-parallax { transform-style: preserve-3d; transition: transform 0.2s; }
.tilt-parallax .parallax-layer { transform: translateZ(20px); }
.tilt-mirror { transform-style: preserve-3d; transition: transform 0.15s; }
/* 31-35: PARTICLES */
.particle-float { position: relative; }
.particle-float::before { content: ''; position: absolute; width: 4px; height: 4px; background: var(--accent); border-radius: 50%; animation: particle-rise 3s infinite; }
@keyframes particle-rise { 0% { transform: translateY(100px) scale(0); opacity: 0; } 50% { opacity: 1; } 100% { transform: translateY(-100px) scale(0); opacity: 0; } }
.particle-orbit { position: relative; }
.particle-orbit::before, .particle-orbit::after { content: ''; position: absolute; width: 6px; height: 6px; background: var(--accent2); border-radius: 50%; animation: orbit 4s linear infinite; }
@keyframes orbit { 0% { transform: rotate(0deg) translateX(30px) rotate(0deg); } 100% { transform: rotate(360deg) translateX(30px) rotate(-360deg); } }
.particle-trail { position: relative; overflow: hidden; }
.particle-sparkle { position: relative; }
.particle-sparkle::after { content: '✦'; position: absolute; color: var(--accent3); animation: sparkle 2s ease-in-out infinite; }
@keyframes sparkle { 0%, 100% { opacity: 0; transform: scale(0) rotate(0deg); } 50% { opacity: 1; transform: scale(1) rotate(180deg); } }
.particle-dust { position: relative; }
/* 36-40: SPECIAL */
.glitch-hover:hover { animation: glitch 0.3s infinite; }
@keyframes glitch { 0% { transform: translate(0); } 20% { transform: translate(-2px, 2px); } 40% { transform: translate(-2px, -2px); } 60% { transform: translate(2px, 2px); } 80% { transform: translate(2px, -2px); } 100% { transform: translate(0); } }
.scanline { position: relative; }
.scanline::after { content: ''; position: absolute; inset: 0; background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,0.1) 2px, rgba(0,0,0,0.1) 4px); pointer-events: none; }
.crt { position: relative; animation: flicker 0.15s infinite; }
@keyframes flicker { 0%, 100% { opacity: 1; } 50% { opacity: 0.98; } }
.crt::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,0.4) 100%); pointer-events: none; }
.hologram { position: relative; background: linear-gradient(135deg, rgba(0,255,255,0.1), rgba(255,0,255,0.1)); border: 2px solid rgba(0,255,255,0.5); animation: hologram-flicker 4s infinite; }
@keyframes hologram-flicker { 0%, 100% { opacity: 1; } 50% { opacity: 0.8; } 75% { opacity: 0.95; } }
.neon-pulse { animation: neon-glow 2s ease-in-out infinite alternate; }
@keyframes neon-glow { 0% { box-shadow: 0 0 5px var(--accent), 0 0 10px var(--accent), 0 0 20px var(--accent); } 100% { box-shadow: 0 0 10px var(--accent), 0 0 20px var(--accent), 0 0 40px var(--accent), 0 0 80px var(--accent); } }
/* 41-45: TEXT EFFECTS */
.text-wave span { display: inline-block; animation: wave 2s ease-in-out infinite; }
@keyframes wave { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
.text-shake:hover { animation: shake 0.5s; }
@keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } }
.text-gradient { background: linear-gradient(90deg, var(--accent), var(--accent2), var(--accent3), var(--accent)); background-size: 300% 100%; -webkit-background-clip: text; -webkit-text-fill-color: transparent; animation: gradient-shift 3s ease infinite; }
@keyframes gradient-shift { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
.text-glow { text-shadow: 0 0 10px currentColor, 0 0 20px currentColor, 0 0 40px currentColor; }
.text-3d { text-shadow: 1px 1px 0 #000, 2px 2px 0 #000, 3px 3px 0 #000, 4px 4px 0 #000, 5px 5px 0 #000; }
/* 46-50: INTERACTIVE */
.drag-elastic { cursor: grab; user-select: none; }
.drag-elastic:active { cursor: grabbing; }
.squeeze-hover:hover { animation: squeeze 0.4s; }
@keyframes squeeze { 0%, 100% { transform: scale(1, 1); } 50% { transform: scale(1.1, 0.9); } }
.flip-hover { transition: transform 0.6s; transform-style: preserve-3d; }
.flip-hover:hover { transform: rotateY(180deg); }
.rotate-hover:hover { animation: rotate-once 0.6s ease; }
@keyframes rotate-once { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } }
.skew-hover:hover { transform: skew(-10deg, 5deg); transition: transform 0.3s; }
/* SVG Filters */
svg.filters { position: absolute; width: 0; height: 0; }
</style>
</head>
<body>
<svg class="filters">
<defs>
<filter id="goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur"/>
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9" result="goo"/>
<feComposite in="SourceGraphic" in2="goo" operator="atop"/>
</filter>
<filter id="noise">
<feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/>
</filter>
</defs>
</svg>
<header>
<div class="hero-bg"></div>
<div class="hero-badge">v2.0 · 40+ Effects · Zero Dependencies</div>
<h1 class="hero-title">LiquidUI</h1>
<p class="hero-sub">Первая в мире библиотека жидких интерфейсов. От эластичных кнопок до голограмм — просто добавь класс.</p>
<div class="btn-row" style="justify-content:center; position:relative; z-index:1;">
<button class="btn btn-primary elastic-btn" onclick="document.getElementById('effects').scrollIntoView()">Исследовать эффекты</button>
<button class="btn btn-secondary bounce-btn">Прыгающая кнопка</button>
<button class="btn btn-glow neon-pulse">Неоновый пульс</button>
</div>
<div class="scroll-hint">scroll</div>
</header>
<main>
<!-- ========== ELASTIC & BOUNCE ========== -->
<section class="section" id="effects">
<span class="section-label">01-05 — Elastic Family</span>
<h2 class="section-title">Эластичные элементы</h2>
<p class="section-desc">Физика упругости: сжатие, растяжение, пружинный отскок.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card">
<button class="btn btn-primary elastic-btn">Elastic Press</button>
<div class="tag">elastic-btn</div>
</div>
<div class="demo-card">
<button class="btn btn-secondary bounce-btn">Bounce</button>
<div class="tag">bounce-btn</div>
</div>
<div class="demo-card">
<button class="btn btn-glow pulse-btn">Pulse Scale</button>
<div class="tag">pulse-btn</div>
</div>
<div class="demo-card">
<button class="btn btn-gold wobble-btn">Wobble</button>
<div class="tag">wobble-btn</div>
</div>
<div class="demo-card" style="filter: url('#goo');">
<button class="btn btn-cyber jelly-intense">Intense Jelly</button>
<div class="tag">jelly-intense + goo</div>
</div>
</div>
</section>
<!-- ========== MORPH SHAPES ========== -->
<section class="section">
<span class="section-label">06-10 — Morphing Shapes</span>
<h2 class="section-title">Живые формы</h2>
<p class="section-desc">Органичная трансформация границ через CSS и clip-path.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card morph-blob" style="background: linear-gradient(135deg, var(--accent), var(--accent2)); min-height: 150px;">
<span style="font-size: 2rem;">🫧</span>
<div class="tag">morph-blob</div>
</div>
<div class="demo-card morph-square" style="background: linear-gradient(135deg, var(--accent3), var(--accent4)); min-height: 150px;">
<span style="font-size: 2rem;">🔷</span>
<div class="tag">morph-square</div>
</div>
<div class="demo-card liquid-shape" style="background: linear-gradient(135deg, var(--accent5), var(--accent)); min-height: 150px; width: 150px; margin: 0 auto;">
<span style="font-size: 2rem;">💧</span>
<div class="tag">liquid-shape</div>
</div>
<div class="demo-card amoeba" style="background: linear-gradient(135deg, var(--accent6), var(--accent3)); min-height: 150px; filter: url('#goo');">
<span style="font-size: 2rem;">🦠</span>
<div class="tag">amoeba + goo</div>
</div>
</div>
</section>
<!-- ========== GLASS FAMILY ========== -->
<section class="section">
<span class="section-label">11-15 — Glass Effects</span>
<h2 class="section-title">Стеклянные панели</h2>
<p class="section-desc">Backdrop-filter магия: frosted, chrome, neon glass.</p>
<div class="divider"></div>
<div style="background: linear-gradient(135deg, #1a0b2e, #0d1832); border-radius: 24px; padding: 40px;">
<div class="effects-grid">
<div class="demo-card glass-frost">
<h3 style="font-family: 'Syne', sans-serif;">Frosted</h3>
<p style="font-size: 0.8rem; color: var(--muted);">Размытие + насыщенность</p>
<div class="tag">glass-frost</div>
</div>
<div class="demo-card glass-dark">
<h3 style="font-family: 'Syne', sans-serif;">Dark Glass</h3>
<p style="font-size: 0.8rem; color: var(--muted);">Тёмное стекло</p>
<div class="tag">glass-dark</div>
</div>
<div class="demo-card glass-gradient">
<h3 style="font-family: 'Syne', sans-serif;">Gradient</h3>
<p style="font-size: 0.8rem; color: var(--muted);">Градиентное стекло</p>
<div class="tag">glass-gradient</div>
</div>
<div class="demo-card glass-chrome">
<h3 style="font-family: 'Syne', sans-serif;">Chrome</h3>
<p style="font-size: 0.8rem; color: var(--muted);">Хромированное</p>
<div class="tag">glass-chrome</div>
</div>
<div class="demo-card glass-neon">
<h3 style="font-family: 'Syne', sans-serif;">Neon Glass</h3>
<p style="font-size: 0.8rem; color: var(--muted);">Неоновое свечение</p>
<div class="tag">glass-neon</div>
</div>
</div>
</div>
</section>
<!-- ========== MAGNETIC ========== -->
<section class="section">
<span class="section-label">16-20 — Magnetic Attraction</span>
<h2 class="section-title">Магнитное притяжение</h2>
<p class="section-desc">Элементы тянутся к курсору с разной силой и физикой.</p>
<div class="divider"></div>
<div class="btn-row" style="justify-content: center; gap: 40px; padding: 60px 0;">
<button class="btn btn-primary magnetic" data-strength="0.3">Soft</button>
<button class="btn btn-glow magnetic-strong" data-strength="0.6">Strong</button>
<button class="btn btn-gold magnetic-elastic" data-strength="0.4">Elastic</button>
<button class="btn btn-cyber magnetic-smooth" data-strength="0.5">Smooth</button>
<button class="btn btn-dark magnetic-snap" data-strength="0.8">Snap</button>
</div>
<div style="text-align: center; color: var(--muted); font-size: 0.8rem;">Наведи на кнопки — они притянутся к мыши</div>
</section>
<!-- ========== RIPPLE EFFECTS ========== -->
<section class="section">
<span class="section-label">21-25 — Ripple & Waves</span>
<h2 class="section-title">Волны и рябь</h2>
<p class="section-desc">Кликай — создавай волны разных цветов и интенсивности.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card ripple" onclick="createRipple(event, this, '#fff')" style="background: var(--surface);">
<h3>White Ripple</h3>
<div class="tag">ripple</div>
</div>
<div class="demo-card ripple-dark" onclick="createRipple(event, this, '#000')" style="background: #fff; color: #000;">
<h3>Dark Ripple</h3>
<div class="tag">ripple-dark</div>
</div>
<div class="demo-card ripple-color" onclick="createRipple(event, this, 'var(--accent)')" style="background: var(--surface);">
<h3>Color Ripple</h3>
<div class="tag">ripple-color</div>
</div>
<div class="demo-card ripple-glow" onclick="createRipple(event, this, 'var(--accent3)')" style="background: var(--surface);">
<h3>Glow Ripple</h3>
<div class="tag">ripple-glow</div>
</div>
<div class="demo-card ripple-multi" onclick="createMultiRipple(event, this)" style="background: var(--surface);">
<h3>Multi Ripple</h3>
<div class="tag">ripple-multi</div>
</div>
</div>
</section>
<!-- ========== 3D TILT ========== -->
<section class="section">
<span class="section-label">26-30 — 3D Tilt</span>
<h2 class="section-title">3D наклон и блики</h2>
<p class="section-desc">Карточки реагируют на мышь с параллаксом и бликами.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card tilt-3d" style="background: linear-gradient(135deg, rgba(124,109,255,0.2), rgba(0,212,255,0.1));">
<h3>Basic 3D</h3>
<div class="tag">tilt-3d</div>
</div>
<div class="demo-card tilt-glare" style="background: linear-gradient(135deg, rgba(255,95,160,0.2), rgba(124,109,255,0.1));">
<h3>With Glare</h3>
<div class="tag">tilt-glare</div>
</div>
<div class="demo-card tilt-parallax" style="background: linear-gradient(135deg, rgba(0,255,136,0.2), rgba(0,136,255,0.1));">
<div class="parallax-layer">
<h3>Parallax</h3>
<p style="font-size: 0.8rem;">Layer 1</p>
</div>
<div class="tag">tilt-parallax</div>
</div>
<div class="demo-card tilt-mirror" style="background: linear-gradient(135deg, rgba(255,170,0,0.2), rgba(255,0,68,0.1));">
<h3>Mirror</h3>
<div class="tag">tilt-mirror</div>
</div>
</div>
</section>
<!-- ========== PARTICLES ========== -->
<section class="section">
<span class="section-label">31-35 — Particle Effects</span>
<h2 class="section-title">Частицы и свечение</h2>
<p class="section-desc">Микро-анимации: всплывающие частицы, орбиты, искры.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card particle-float" style="overflow: hidden;">
<h3>Float Up</h3>
<div class="tag">particle-float</div>
</div>
<div class="demo-card particle-orbit">
<h3>Orbit</h3>
<div class="tag">particle-orbit</div>
</div>
<div class="demo-card particle-trail" id="trailCard">
<h3>Mouse Trail</h3>
<div class="tag">particle-trail (hover)</div>
</div>
<div class="demo-card particle-sparkle">
<h3>Sparkle</h3>
<div class="tag">particle-sparkle</div>
</div>
<div class="demo-card particle-dust" id="dustCard">
<h3>Dust Field</h3>
<div class="tag">particle-dust</div>
</div>
</div>
</section>
<!-- ========== GLITCH & RETRO ========== -->
<section class="section">
<span class="section-label">36-40 — Glitch & Retro</span>
<h2 class="section-title">Глитч и ретро-эффекты</h2>
<p class="section-desc">Цифровые артефакты: глитч, сканлайны, CRT, голограммы.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card glitch-hover" style="background: #1a1a2e;">
<h3>Glitch</h3>
<div class="tag">glitch-hover</div>
</div>
<div class="demo-card scanline" style="background: linear-gradient(135deg, #0a0a1a, #1a0a2e);">
<h3>Scanlines</h3>
<div class="tag">scanline</div>
</div>
<div class="demo-card crt" style="background: #0a0a0a; color: #0f0;">
<h3>CRT Monitor</h3>
<div class="tag">crt</div>
</div>
<div class="demo-card hologram">
<h3>Hologram</h3>
<div class="tag">hologram</div>
</div>
<div class="demo-card neon-pulse" style="background: var(--surface); border: 2px solid var(--accent);">
<h3>Neon Pulse</h3>
<div class="tag">neon-pulse</div>
</div>
</div>
</section>
<!-- ========== TEXT EFFECTS ========== -->
<section class="section">
<span class="section-label">41-45 — Text Effects</span>
<h2 class="section-title">Анимированный текст</h2>
<p class="section-desc">Буквы танцуют: волны, градиенты, тени, свечение.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card">
<h2 class="text-wave" style="font-family: 'Syne', sans-serif; font-size: 2rem;">
<span style="animation-delay: 0s;">W</span><span style="animation-delay: 0.1s;">A</span><span style="animation-delay: 0.2s;">V</span><span style="animation-delay: 0.3s;">E</span>
</h2>
<div class="tag">text-wave</div>
</div>
<div class="demo-card">
<h2 class="text-shake" style="font-family: 'Syne', sans-serif; font-size: 2rem; cursor: pointer;">SHAKE</h2>
<div class="tag">text-shake (hover)</div>
</div>
<div class="demo-card">
<h2 class="text-gradient" style="font-family: 'Syne', sans-serif; font-size: 2rem;">GRADIENT</h2>
<div class="tag">text-gradient</div>
</div>
<div class="demo-card" style="background: #000;">
<h2 class="text-glow" style="font-family: 'Syne', sans-serif; font-size: 2rem; color: var(--accent2);">GLOW</h2>
<div class="tag">text-glow</div>
</div>
<div class="demo-card">
<h2 class="text-3d" style="font-family: 'Syne', sans-serif; font-size: 2rem; color: var(--text);">3D TEXT</h2>
<div class="tag">text-3d</div>
</div>
</div>
</section>
<!-- ========== INTERACTIVE ========== -->
<section class="section">
<span class="section-label">46-50 — Interactive</span>
<h2 class="section-title">Интерактивные жесты</h2>
<p class="section-desc">Перетаскивание, перевороты, сжатие, вращение.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card drag-elastic" id="dragCard" style="cursor: grab; background: var(--surface);">
<h3>Drag Me</h3>
<p style="font-size: 0.8rem; color: var(--muted);">Elastic drag</p>
<div class="tag">drag-elastic</div>
</div>
<div class="demo-card squeeze-hover" style="background: var(--surface);">
<h3>Squeeze</h3>
<div class="tag">squeeze-hover</div>
</div>
<div class="demo-card flip-hover" style="background: linear-gradient(135deg, var(--accent), var(--accent2)); transform-style: preserve-3d;">
<div style="backface-visibility: hidden;">
<h3>Flip</h3>
<p>Hover to flip</p>
</div>
<div style="position: absolute; inset: 0; background: var(--accent3); border-radius: 20px; transform: rotateY(180deg); backface-visibility: hidden; display: flex; align-items: center; justify-content: center;">
<h3>Back!</h3>
</div>
<div class="tag">flip-hover</div>
</div>
<div class="demo-card rotate-hover" style="background: var(--surface);">
<h3>Rotate</h3>
<div class="tag">rotate-hover</div>
</div>
<div class="demo-card skew-hover" style="background: var(--surface);">
<h3>Skew</h3>
<div class="tag">skew-hover</div>
</div>
</div>
</section>
<!-- ========== BONUS: COMBOS ========== -->
<section class="section">
<span class="section-label">BONUS — Combos</span>
<h2 class="section-title">Комбинированные эффекты</h2>
<p class="section-desc">Сочетай классы для уникальных результатов.</p>
<div class="divider"></div>
<div class="effects-grid">
<div class="demo-card glass-frost morph-blob" style="background: linear-gradient(135deg, rgba(124,109,255,0.3), rgba(0,212,255,0.2));">
<h3>Glass + Morph</h3>
<div class="tag">glass-frost morph-blob</div>
</div>
<div class="demo-card magnetic tilt-3d neon-pulse" style="background: var(--surface); border: 2px solid var(--accent3);">
<h3>Magnetic + Tilt + Neon</h3>
<div class="tag">magnetic tilt-3d neon-pulse</div>
</div>
<div class="demo-card ripple particle-sparkle hologram" onclick="createRipple(event, this, '#0ff')">
<h3>Ripple + Sparkle + Hologram</h3>
<div class="tag">ripple particle-sparkle hologram</div>
</div>
<div class="demo-card elastic-btn text-gradient bounce-btn">
<h3>Elastic + Gradient + Bounce</h3>
<div class="tag">elastic-btn text-gradient bounce-btn</div>
</div>
</div>
</section>
<!-- ========== API ========== -->
<section class="section">
<span class="section-label">API</span>
<h2 class="section-title">Как использовать</h2>
<div class="divider"></div>
<div class="code-block">
&lt;!-- Подключи CSS (или скопируй в свой) --&gt;
&lt;link rel="stylesheet" href="liquidx.css"&gt;
&lt;!-- Добавь классы к элементам --&gt;
&lt;button class="elastic-btn"&gt;Кнопка&lt;/button&gt;
&lt;div class="morph-blob glass-frost"&gt;...&lt;/div&gt;
&lt;div class="magnetic tilt-3d"&gt;...&lt;/div&gt;
&lt;!-- Доступные эффекты (40+) --&gt;
ELASTIC: elastic-btn, bounce-btn, pulse-btn, wobble-btn, jelly-intense
MORPH: morph-blob, morph-square, liquid-shape, amoeba
GLASS: glass-frost, glass-dark, glass-gradient, glass-chrome, glass-neon
MAGNETIC: magnetic, magnetic-strong, magnetic-elastic, magnetic-smooth, magnetic-snap
RIPPLE: ripple, ripple-dark, ripple-color, ripple-glow, ripple-multi
TILT: tilt-3d, tilt-glare, tilt-parallax, tilt-mirror
PARTICLES: particle-float, particle-orbit, particle-trail, particle-sparkle, particle-dust
GLITCH: glitch-hover, scanline, crt, hologram, neon-pulse
TEXT: text-wave, text-shake, text-gradient, text-glow, text-3d
INTERACTIVE: drag-elastic, squeeze-hover, flip-hover, rotate-hover, skew-hover
</div>
</section>
</main>
<footer>
<strong>LiquidUI v2.0</strong> · 40+ эффектов · Масштабируемо до 500+ · MIT License<br>
<span style="margin-top:8px;display:block;">Просто добавь класс · Комбинируй · Создавай жидкие интерфейсы</span>
</footer>
<script>
// ========== MAGNETIC EFFECTS ==========
document.querySelectorAll('.magnetic, .magnetic-strong, .magnetic-elastic, .magnetic-smooth, .magnetic-snap').forEach(el => {
const strength = parseFloat(el.dataset.strength) || 0.3;
el.addEventListener('mousemove', e => {
const r = el.getBoundingClientRect();
const cx = r.left + r.width/2, cy = r.top + r.height/2;
el.style.transform = `translate(${(e.clientX-cx)*strength}px,${(e.clientY-cy)*strength}px)`;
});
el.addEventListener('mouseleave', () => el.style.transform = 'translate(0,0)');
});
// ========== TILT EFFECTS ==========
document.querySelectorAll('.tilt-3d, .tilt-glare, .tilt-parallax, .tilt-mirror').forEach(card => {
card.addEventListener('mousemove', e => {
const r = card.getBoundingClientRect();
const x = (e.clientX - r.left) / r.width - 0.5;
const y = (e.clientY - r.top) / r.height - 0.5;
const intensity = 20;
card.style.transform = `perspective(600px) rotateX(${-y * intensity}deg) rotateY(${x * intensity}deg) scale(1.02)`;
});
card.addEventListener('mouseleave', () => {
card.style.transform = 'perspective(600px) rotateX(0) rotateY(0) scale(1)';
});
});
// ========== RIPPLE EFFECTS ==========
function createRipple(e, el, color = '#fff') {
const r = el.getBoundingClientRect();
const x = e.clientX - r.left, y = e.clientY - r.top;
const ripple = document.createElement('span');
const size = Math.max(r.width, r.height) * 2;
ripple.style.cssText = `position:absolute;border-radius:50%;pointer-events:none;width:${size}px;height:${size}px;left:${x-size/2}px;top:${y-size/2}px;background:radial-gradient(circle,${color} 0%,transparent 70%);transform:scale(0);opacity:0.6;animation:ripple-anim 0.8s ease-out forwards;`;
el.appendChild(ripple);
setTimeout(() => ripple.remove(), 800);
}
function createMultiRipple(e, el) {
const colors = ['#7c6dff', '#00d4ff', '#ff5fa0', '#ffaa00'];
colors.forEach((color, i) => {
setTimeout(() => createRipple(e, el, color), i * 100);
});
}
// ========== PARTICLE TRAIL ==========
const trailCard = document.getElementById('trailCard');
if (trailCard) {
trailCard.addEventListener('mousemove', e => {
const r = trailCard.getBoundingClientRect();
const dot = document.createElement('div');
dot.style.cssText = `position:absolute;width:6px;height:6px;background:var(--accent2);border-radius:50%;pointer-events:none;left:${e.clientX-r.left}px;top:${e.clientY-r.top}px;animation:fade-out 1s forwards;`;
trailCard.appendChild(dot);
setTimeout(() => dot.remove(), 1000);
});
}
// ========== DUST FIELD ==========
const dustCard = document.getElementById('dustCard');
if (dustCard && !window.matchMedia('(pointer: coarse)').matches) {
for (let i = 0; i < 20; i++) {
const dust = document.createElement('div');
dust.style.cssText = `position:absolute;width:2px;height:2px;background:rgba(255,255,255,0.3);border-radius:50%;left:${Math.random()*100}%;top:${Math.random()*100}%;animation:float ${3+Math.random()*4}s ease-in-out infinite;animation-delay:${-Math.random()*5}s;pointer-events:none;`;
dustCard.appendChild(dust);
}
}
// ========== DRAG ELASTIC ==========
const dragCard = document.getElementById('dragCard');
if (dragCard) {
let isDragging = false, startX, startY, currentX = 0, currentY = 0;
dragCard.addEventListener('mousedown', e => {
isDragging = true;
startX = e.clientX - currentX;
startY = e.clientY - currentY;
dragCard.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', e => {
if (!isDragging) return;
currentX = e.clientX - startX;
currentY = e.clientY - startY;
dragCard.style.transform = `translate(${currentX}px, ${currentY}px)`;
});
document.addEventListener('mouseup', () => {
if (!isDragging) return;
isDragging = false;
dragCard.style.cursor = 'grab';
// Elastic return
dragCard.style.transition = 'transform 0.5s cubic-bezier(0.68,-0.55,0.265,1.55)';
dragCard.style.transform = 'translate(0,0)';
currentX = currentY = 0;
setTimeout(() => dragCard.style.transition = '', 500);
});
}
// Add fade-out animation
const style = document.createElement('style');
style.textContent = `
@keyframes fade-out { 0% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(0); } }
@keyframes float { 0%, 100% { transform: translateY(0) translateX(0); } 25% { transform: translateY(-20px) translateX(10px); } 50% { transform: translateY(-10px) translateX(-10px); } 75% { transform: translateY(-30px) translateX(5px); } }
`;
document.head.appendChild(style);
</script>
</body>
</html>"""
@app.get("/", response_class=HTMLResponse)
async def root():
return HTML_PAGE
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)