Files
remotion_service/docs/superpowers/specs/2026-04-06-preset-grid-demo.html
T
2026-04-06 01:44:58 +03:00

626 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Subtitle Preset Grid Redesign Demo</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Lobster&display=swap" rel="stylesheet">
<style>
/* Catppuccin Mocha - Matching the actual project colors */
:root {
/* Backgrounds */
--bg-canvas: #11111b;
--bg-default: #1e1e2e;
--bg-surface: #313244;
--bg-hover: #45475a;
--bg-default-invert: #eff1f5;
/* Text */
--text-primary: #cdd6f4;
--text-secondary: #bac2de;
--text-tertiary: #9399b2;
/* Borders */
--border-default: #45475a;
--border-subtle: #313244;
/* Purples (accent) */
--purple-400: #cba6f7;
--purple-500: #d9bcfa;
--purple-600: #e4cffc;
--purple-700: #eddfff;
--purple-300: #6a5a93;
--purple-200: #4b4168;
--purple-100: #362f4c;
--purple-50: #2b253b;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(17, 17, 27, 0.5);
--shadow-md: 0 4px 6px -1px rgba(17, 17, 27, 0.58), 0 24px 48px -12px rgba(17, 17, 27, 0.52);
--shadow-lg: 0 10px 15px -3px rgba(17, 17, 27, 0.6), 0 40px 80px -20px rgba(17, 17, 27, 0.7);
/* Accent glow */
--accent-shadow: rgba(203, 166, 247, 0.22);
--accent-shadow-hover: rgba(203, 166, 247, 0.3);
/* Preview background */
--preview-bg: #0c0a1a;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg-canvas);
color: var(--text-primary);
padding: 40px 20px;
min-height: 100vh;
}
body::before {
content: "";
position: fixed;
inset: 0;
background: radial-gradient(circle at 50% 0%, rgba(203, 166, 247, 0.08) 0%, transparent 55%);
pointer-events: none;
z-index: -1;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
h1 {
font-size: 24px;
font-weight: 600;
margin-bottom: 32px;
color: var(--text-primary);
}
/* Controls */
.controls {
display: flex;
gap: 16px;
margin-bottom: 32px;
flex-wrap: wrap;
}
.control-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.control-group label {
font-size: 11px;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.8px;
font-weight: 500;
}
.aspect-buttons {
display: flex;
gap: 8px;
}
.aspect-btn {
padding: 8px 14px;
background: var(--bg-surface);
border: 1px solid var(--border-default);
color: var(--text-secondary);
border-radius: 8px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.15s ease;
}
.aspect-btn:hover {
background: var(--bg-hover);
border-color: var(--purple-400);
color: var(--text-primary);
}
.aspect-btn.active {
background: var(--purple-100);
border-color: var(--purple-400);
color: var(--purple-400);
}
/* Grid */
.preset-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 48px;
}
/* Preset Card */
.preset-card {
position: relative;
border-radius: 12px;
overflow: hidden;
background: var(--bg-default);
border: 1px solid var(--border-subtle);
cursor: pointer;
transition: all 0.2s cubic-bezier(0.2, 0.8, 0.2, 1);
display: flex;
flex-direction: column;
}
.preset-card:hover {
border-color: var(--purple-400);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.preset-card.selected {
border-color: var(--purple-400);
box-shadow:
0 0 0 1px var(--purple-400),
0 0 20px rgba(203, 166, 247, 0.25),
var(--shadow-lg);
}
.preset-card.selected::before {
content: "";
position: absolute;
inset: -1px;
border-radius: 12px;
padding: 1px;
background: linear-gradient(135deg, var(--purple-400), var(--purple-600));
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
/* Preview Area */
.preview-area {
position: relative;
aspect-ratio: 16 / 9;
background: var(--preview-bg);
overflow: hidden;
transition: aspect-ratio 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.preview-area.vertical {
aspect-ratio: 9 / 16;
}
.preview-area.square {
aspect-ratio: 1 / 1;
}
.preview-area.instagram {
aspect-ratio: 4 / 5;
}
.preview-text {
text-align: center;
padding: 16px;
font-size: 28px;
line-height: 1.4;
z-index: 1;
}
.preview-text .highlight {
font-weight: 700;
}
/* Card Footer */
.card-footer {
padding: 14px 16px;
background: linear-gradient(to top, var(--bg-surface), var(--bg-default));
border-top: 1px solid var(--border-subtle);
}
.preset-name {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 6px;
display: flex;
align-items: center;
gap: 8px;
}
.system-badge {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 2px 8px;
background: var(--purple-100);
color: var(--purple-400);
border-radius: 4px;
}
.style-chars {
font-size: 12px;
color: var(--text-tertiary);
display: flex;
align-items: center;
gap: 8px;
}
.color-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1);
}
/* Create New Card */
.create-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
aspect-ratio: 16 / 9;
background: transparent;
border: 2px dashed var(--border-default);
border-radius: 12px;
cursor: pointer;
transition: all 0.2s ease;
color: var(--text-tertiary);
min-height: 100%;
}
.create-card:hover {
border-color: var(--purple-400);
background: rgba(203, 166, 247, 0.05);
color: var(--text-secondary);
}
.create-card svg {
width: 32px;
height: 32px;
opacity: 0.6;
}
/* Skeleton Loading - Improved */
.skeleton-card {
border-radius: 12px;
overflow: hidden;
background: var(--bg-default);
border: 1px solid var(--border-subtle);
display: flex;
flex-direction: column;
}
.skeleton-preview {
aspect-ratio: 16 / 9;
background: var(--bg-surface);
position: relative;
overflow: hidden;
}
.skeleton-preview::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
90deg,
transparent 0%,
rgba(203, 166, 247, 0.08) 50%,
transparent 100%
);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.skeleton-footer {
padding: 14px 16px;
background: linear-gradient(to top, var(--bg-surface), var(--bg-default));
border-top: 1px solid var(--border-subtle);
display: flex;
flex-direction: column;
gap: 10px;
}
.skeleton-line {
height: 14px;
background: var(--bg-hover);
border-radius: 4px;
width: 60%;
position: relative;
overflow: hidden;
}
.skeleton-line.short {
width: 40%;
height: 10px;
}
.skeleton-line::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
90deg,
transparent 0%,
rgba(203, 166, 247, 0.06) 50%,
transparent 100%
);
animation: shimmer 1.5s infinite;
}
/* Section Label */
.section-label {
font-size: 11px;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.8px;
margin-bottom: 12px;
font-weight: 500;
}
/* Selected indicator */
.selected-indicator {
position: absolute;
top: 8px;
right: 8px;
width: 20px;
height: 20px;
background: var(--purple-400);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(203, 166, 247, 0.4);
}
.selected-indicator svg {
width: 12px;
height: 12px;
color: var(--bg-canvas);
}
/* Responsive */
@media (max-width: 768px) {
.preset-grid {
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Выбор пресета субтитров</h1>
<!-- Controls -->
<div class="controls">
<div class="control-group">
<label>Аспектное соотношение видео</label>
<div class="aspect-buttons">
<button class="aspect-btn active" data-ratio="16:9">16:9 (Широкое)</button>
<button class="aspect-btn" data-ratio="9:16">9:16 (Вертикальное)</button>
<button class="aspect-btn" data-ratio="1:1">1:1 (Квадрат)</button>
<button class="aspect-btn" data-ratio="4:5">4:5 (Instagram)</button>
</div>
</div>
</div>
<!-- Section: Ready Presets -->
<div class="section-label">Пример: готовые пресеты</div>
<div class="preset-grid" id="presetGrid">
<!-- Card 1: Классические -->
<div class="preset-card selected" data-preset="classic">
<div class="preview-area">
<div class="preview-text" style="font-family: 'Lobster', cursive; color: #ffffff;">
Пример <span class="highlight" style="color: #FFD700;">субтитров</span>
</div>
<div class="selected-indicator">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
</div>
</div>
<div class="card-footer">
<div class="preset-name">
Классические
<span class="system-badge">Системный</span>
</div>
<div class="style-chars">
Lobster
<span style="color: var(--border-default);">·</span>
<span class="color-dot" style="background: #FFD700;"></span>
<span style="color: #FFD700;">Золотой</span>
</div>
</div>
</div>
<!-- Card 2: Неон -->
<div class="preset-card" data-preset="neon">
<div class="preview-area">
<div class="preview-text" style="font-family: 'Inter', sans-serif; font-weight: 700; color: #ffffff; text-shadow: 0 0 10px #00ffff, 0 0 20px #00ffff;">
Пример <span class="highlight" style="color: #00ffff;">субтитров</span>
</div>
</div>
<div class="card-footer">
<div class="preset-name">
Неон
<span class="system-badge">Системный</span>
</div>
<div class="style-chars">
Inter Bold
<span style="color: var(--border-default);">·</span>
<span class="color-dot" style="background: #00ffff; box-shadow: 0 0 6px #00ffff;"></span>
<span style="color: #00ffff;">Неоновый</span>
</div>
</div>
</div>
<!-- Card 3: Минимализм -->
<div class="preset-card" data-preset="minimal">
<div class="preview-area">
<div class="preview-text" style="font-family: 'Inter', sans-serif; font-weight: 400; color: #e0e0e0; font-size: 24px;">
Пример <span class="highlight" style="color: #ffffff; font-weight: 500;">субтитров</span>
</div>
</div>
<div class="card-footer">
<div class="preset-name">
Минимализм
<span class="system-badge">Системный</span>
</div>
<div class="style-chars">
Inter Regular
<span style="color: var(--border-default);">·</span>
<span class="color-dot" style="background: #ffffff;"></span>
<span style="color: #ffffff;">Белый</span>
</div>
</div>
</div>
<!-- Card 4: Жирный -->
<div class="preset-card" data-preset="bold">
<div class="preview-area">
<div class="preview-text" style="font-family: 'Inter', sans-serif; font-weight: 900; color: #ffffff; -webkit-text-stroke: 2px #000000; font-size: 32px;">
Пример <span class="highlight" style="color: #ff006e;">субтитров</span>
</div>
</div>
<div class="card-footer">
<div class="preset-name">
Жирный
<span class="system-badge">Системный</span>
</div>
<div class="style-chars">
Inter Black
<span style="color: var(--border-default);">·</span>
<span class="color-dot" style="background: #ff006e;"></span>
<span style="color: #ff006e;">Розовый</span>
</div>
</div>
</div>
<!-- Create New Card -->
<div class="create-card">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
<span style="font-size: 14px; font-weight: 500;">Создать пресет</span>
</div>
</div>
<!-- Section: Loading State -->
<div class="section-label">Пример: состояние загрузки</div>
<div class="preset-grid">
<div class="skeleton-card">
<div class="skeleton-preview"></div>
<div class="skeleton-footer">
<div class="skeleton-line"></div>
<div class="skeleton-line short"></div>
</div>
</div>
<div class="skeleton-card">
<div class="skeleton-preview"></div>
<div class="skeleton-footer">
<div class="skeleton-line"></div>
<div class="skeleton-line short"></div>
</div>
</div>
<div class="skeleton-card">
<div class="skeleton-preview"></div>
<div class="skeleton-footer">
<div class="skeleton-line"></div>
<div class="skeleton-line short"></div>
</div>
</div>
<div class="skeleton-card">
<div class="skeleton-preview"></div>
<div class="skeleton-footer">
<div class="skeleton-line"></div>
<div class="skeleton-line short"></div>
</div>
</div>
</div>
</div>
<script>
// Aspect ratio switching
const aspectButtons = document.querySelectorAll('.aspect-btn');
const previewAreas = document.querySelectorAll('.preview-area');
const skeletonPreviews = document.querySelectorAll('.skeleton-preview');
const createCard = document.querySelector('.create-card');
aspectButtons.forEach(btn => {
btn.addEventListener('click', () => {
// Update active button
aspectButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const ratio = btn.dataset.ratio;
// Update preview areas
previewAreas.forEach(preview => {
preview.classList.remove('vertical', 'square', 'instagram');
if (ratio === '9:16') preview.classList.add('vertical');
if (ratio === '1:1') preview.classList.add('square');
if (ratio === '4:5') preview.classList.add('instagram');
});
// Update skeleton previews
skeletonPreviews.forEach(preview => {
if (ratio === '9:16') preview.style.aspectRatio = '9 / 16';
else if (ratio === '1:1') preview.style.aspectRatio = '1 / 1';
else if (ratio === '4:5') preview.style.aspectRatio = '4 / 5';
else preview.style.aspectRatio = '16 / 9';
});
// Update create card
if (ratio === '9:16') createCard.style.aspectRatio = '9 / 16';
else if (ratio === '1:1') createCard.style.aspectRatio = '1 / 1';
else if (ratio === '4:5') createCard.style.aspectRatio = '4 / 5';
else createCard.style.aspectRatio = '16 / 9';
});
});
// Card selection
const presetCards = document.querySelectorAll('.preset-card');
presetCards.forEach(card => {
card.addEventListener('click', () => {
presetCards.forEach(c => {
c.classList.remove('selected');
const indicator = c.querySelector('.selected-indicator');
if (indicator) indicator.remove();
});
card.classList.add('selected');
// Add checkmark indicator
const preview = card.querySelector('.preview-area');
if (!preview.querySelector('.selected-indicator')) {
const indicator = document.createElement('div');
indicator.className = 'selected-indicator';
indicator.innerHTML = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
`;
preview.appendChild(indicator);
}
});
});
</script>
</body>
</html>