Spaces:
Sleeping
Sleeping
| 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"> | |
| <!-- Подключи CSS (или скопируй в свой) --> | |
| <link rel="stylesheet" href="liquidx.css"> | |
| <!-- Добавь классы к элементам --> | |
| <button class="elastic-btn">Кнопка</button> | |
| <div class="morph-blob glass-frost">...</div> | |
| <div class="magnetic tilt-3d">...</div> | |
| <!-- Доступные эффекты (40+) --> | |
| 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>""" | |
| async def root(): | |
| return HTML_PAGE | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |