moonlantern1 commited on
Commit
d6ac878
·
verified ·
1 Parent(s): 1ceabda

Animate full hero headline

Browse files
Files changed (1) hide show
  1. app.py +32 -8
app.py CHANGED
@@ -554,8 +554,8 @@ INDEX_HTML = r"""<!DOCTYPE html>
554
  .eyebrow { font-size: 0.75rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold); font-weight: 500; margin-bottom: 16px; }
555
  .hero-title { font-family: 'Cormorant Garamond', serif; font-size: clamp(2rem, 5vw, 3.6rem); font-weight: 500; line-height: 1.15; color: var(--ink); max-width: 620px; margin-bottom: 12px; }
556
  .hero-title em { font-style: italic; color: var(--gold); }
557
- .typing-text { display: inline-block; min-width: 4.4em; white-space: nowrap; border-right: 0.05em solid var(--gold); }
558
- @media (prefers-reduced-motion: reduce) { .typing-text { border-right: 0; } }
559
  .hero-sub { font-size: 0.95rem; color: var(--ink-muted); margin-bottom: 48px; font-weight: 300; }
560
  .input-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 36px; width: 100%; max-width: 520px; box-shadow: 0 8px 32px rgba(42,31,14,0.07); }
561
  .mode-tabs { display: flex; background: var(--surface); border-radius: 10px; padding: 4px; margin-bottom: 28px; gap: 4px; }
@@ -670,7 +670,7 @@ INDEX_HTML = r"""<!DOCTYPE html>
670
  </nav>
671
  <div class="screen active" id="screen-input">
672
  <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:calc(100vh - 65px);padding:40px 20px;text-align:center;">
673
- <h1 class="hero-title">Convert your long video to <em class="typing-text" id="hero-typed">short clips</em> for social media</h1>
674
  <p class="hero-sub">Upload a file - we handle the rest</p>
675
  <div class="input-card">
676
  <div class="input-section" id="mode-yt" style="display:none">
@@ -760,18 +760,41 @@ INDEX_HTML = r"""<!DOCTYPE html>
760
  function startHeroTyping() {
761
  const el = document.getElementById('hero-typed');
762
  if (!el || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
763
- const text = 'short clips';
 
 
 
764
  const typeMs = 3000;
765
  const eraseMs = 3000;
766
  const holdMs = 650;
767
- let index = text.length;
768
- let deleting = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
 
770
  function step() {
771
  const delay = (deleting ? eraseMs : typeMs) / text.length;
772
  if (deleting) {
773
  index = Math.max(0, index - 1);
774
- el.textContent = text.slice(0, index) || '\u00a0';
775
  if (index === 0) {
776
  deleting = false;
777
  setTimeout(step, holdMs);
@@ -779,7 +802,7 @@ INDEX_HTML = r"""<!DOCTYPE html>
779
  }
780
  } else {
781
  index = Math.min(text.length, index + 1);
782
- el.textContent = text.slice(0, index);
783
  if (index === text.length) {
784
  deleting = true;
785
  setTimeout(step, holdMs);
@@ -789,6 +812,7 @@ INDEX_HTML = r"""<!DOCTYPE html>
789
  setTimeout(step, delay);
790
  }
791
 
 
792
  setTimeout(step, holdMs);
793
  }
794
 
 
554
  .eyebrow { font-size: 0.75rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold); font-weight: 500; margin-bottom: 16px; }
555
  .hero-title { font-family: 'Cormorant Garamond', serif; font-size: clamp(2rem, 5vw, 3.6rem); font-weight: 500; line-height: 1.15; color: var(--ink); max-width: 620px; margin-bottom: 12px; }
556
  .hero-title em { font-style: italic; color: var(--gold); }
557
+ .typing-line { display: inline; border-right: 0.045em solid var(--gold); padding-right: 0.04em; }
558
+ @media (prefers-reduced-motion: reduce) { .typing-line { border-right: 0; } }
559
  .hero-sub { font-size: 0.95rem; color: var(--ink-muted); margin-bottom: 48px; font-weight: 300; }
560
  .input-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 36px; width: 100%; max-width: 520px; box-shadow: 0 8px 32px rgba(42,31,14,0.07); }
561
  .mode-tabs { display: flex; background: var(--surface); border-radius: 10px; padding: 4px; margin-bottom: 28px; gap: 4px; }
 
670
  </nav>
671
  <div class="screen active" id="screen-input">
672
  <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:calc(100vh - 65px);padding:40px 20px;text-align:center;">
673
+ <h1 class="hero-title" aria-label="Convert your long video to short clips for social media"><span class="typing-line" id="hero-typed">Convert your long video to <em>short clips</em> for social media</span></h1>
674
  <p class="hero-sub">Upload a file - we handle the rest</p>
675
  <div class="input-card">
676
  <div class="input-section" id="mode-yt" style="display:none">
 
760
  function startHeroTyping() {
761
  const el = document.getElementById('hero-typed');
762
  if (!el || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
763
+ const text = 'Convert your long video to short clips for social media';
764
+ const highlight = 'short clips';
765
+ const highlightStart = text.indexOf(highlight);
766
+ const highlightEnd = highlightStart + highlight.length;
767
  const typeMs = 3000;
768
  const eraseMs = 3000;
769
  const holdMs = 650;
770
+ let index = 0;
771
+ let deleting = false;
772
+
773
+ function render(count) {
774
+ el.replaceChildren();
775
+ if (count <= 0) {
776
+ el.textContent = '\u00a0';
777
+ return;
778
+ }
779
+ const beforeEnd = Math.min(count, highlightStart);
780
+ if (beforeEnd > 0) {
781
+ el.append(document.createTextNode(text.slice(0, beforeEnd)));
782
+ }
783
+ if (count > highlightStart) {
784
+ const em = document.createElement('em');
785
+ em.textContent = text.slice(highlightStart, Math.min(count, highlightEnd));
786
+ el.append(em);
787
+ }
788
+ if (count > highlightEnd) {
789
+ el.append(document.createTextNode(text.slice(highlightEnd, count)));
790
+ }
791
+ }
792
 
793
  function step() {
794
  const delay = (deleting ? eraseMs : typeMs) / text.length;
795
  if (deleting) {
796
  index = Math.max(0, index - 1);
797
+ render(index);
798
  if (index === 0) {
799
  deleting = false;
800
  setTimeout(step, holdMs);
 
802
  }
803
  } else {
804
  index = Math.min(text.length, index + 1);
805
+ render(index);
806
  if (index === text.length) {
807
  deleting = true;
808
  setTimeout(step, holdMs);
 
812
  setTimeout(step, delay);
813
  }
814
 
815
+ render(index);
816
  setTimeout(step, holdMs);
817
  }
818