Skip to content

Commit 112e7e6

Browse files
committed
feat: enhance mobile navigation and update progress dot functionality in V2 announcement modal
1 parent 8dc08ef commit 112e7e6

5 files changed

Lines changed: 290 additions & 60 deletions

File tree

static/css/v2-announcement.css

Lines changed: 149 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@
246246
}
247247

248248
.v2-nav-arrow:hover {
249-
background: linear-gradient(135deg, rgba(124, 58, 237, 0.95), rgba(37, 99, 235, 0.95));
250-
border-color: rgba(255, 255, 255, 0.2);
249+
background: rgba(124, 58, 237, 0.35);
250+
border-color: rgba(124, 58, 237, 0.5);
251251
color: #fff;
252252
}
253253

@@ -428,9 +428,7 @@
428428
font-size: clamp(32px, 4vw, 44px);
429429
font-weight: 700;
430430
margin: 0;
431-
background: linear-gradient(120deg, var(--v2-accent), var(--v2-accent-2), var(--v2-accent-3));
432-
-webkit-background-clip: text;
433-
-webkit-text-fill-color: transparent;
431+
color: var(--v2-text);
434432
}
435433

436434
.v2-subtitle {
@@ -500,18 +498,19 @@
500498
font-size: 15px;
501499
font-weight: 600;
502500
cursor: pointer;
503-
transition: transform 0.18s ease, box-shadow 0.18s ease;
501+
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.2s ease;
504502
display: inline-flex;
505503
align-items: center;
506504
justify-content: center;
507505
gap: 8px;
508506
color: #fff;
509-
background: linear-gradient(135deg, var(--v2-accent-2), var(--v2-accent));
510-
box-shadow: var(--v2-button-shadow);
507+
background: var(--v2-accent);
508+
box-shadow: 0 4px 14px rgba(124, 58, 237, 0.25);
511509
}
512510

513511
.v2-button:hover {
514-
transform: translateY(-3px);
512+
background: #6d31d4;
513+
transform: translateY(-2px);
515514
}
516515

517516
.v2-button-ghost {
@@ -565,8 +564,8 @@
565564
height: 100%;
566565
width: 0%;
567566
border-radius: inherit;
568-
background: linear-gradient(90deg, rgba(124,58,237,1), rgba(99,102,241,1));
569-
box-shadow: 0 6px 14px rgba(124,58,237,0.18);
567+
background: rgba(124, 58, 237, 0.85);
568+
box-shadow: none;
570569
pointer-events: none;
571570
z-index: 0;
572571
transition: width 15000ms linear;
@@ -583,55 +582,176 @@
583582
opacity: 0.85;
584583
}
585584

585+
/* ── Mobile navigation bar (prev / dots / next) ── */
586+
.v2-mobile-nav {
587+
display: none;
588+
}
589+
586590
/* Responsive tweaks */
591+
592+
/* Tablet: hide absolute-positioned arrows, show inline mobile nav */
587593
@media (max-width: 900px) {
588-
.v2-prev-btn {
594+
.v2-modal > .v2-prev-btn,
595+
.v2-modal > .v2-next-btn {
596+
/* hide the absolutely-positioned side arrows */
589597
display: none;
590598
}
591599

592-
.v2-next-btn.v2-nav-arrow {
600+
/* show the inline mobile nav bar */
601+
.v2-mobile-nav {
602+
display: flex;
603+
align-items: center;
604+
justify-content: center;
605+
gap: 16px;
606+
margin-top: 12px;
607+
}
608+
609+
.v2-mobile-nav .v2-nav-arrow {
610+
display: flex;
611+
position: static;
612+
transform: none;
613+
width: 42px;
614+
height: 42px;
615+
flex-shrink: 0;
616+
}
617+
618+
/* move the progress dots into the mobile nav bar */
619+
.v2-modal-footer {
593620
display: none;
594621
}
622+
623+
.v2-mobile-nav .v2-progress-dots {
624+
padding: 8px 14px;
625+
gap: 8px;
626+
}
595627
}
596628

597629
@media (max-width: 720px) {
598630
.v2-modal {
599-
padding: 56px 28px 72px;
600-
display: grid;
601-
grid-template-columns: repeat(2, minmax(0, 1fr));
602-
gap: 16px;
603-
align-items: center;
631+
padding: 36px 20px 28px;
632+
border-radius: 20px;
633+
width: min(520px, calc(100% - 16px));
604634
}
605635

606-
.v2-modal-content-wrapper,
607-
.v2-modal-footer {
608-
grid-column: 1 / -1;
636+
.v2-modal-content-wrapper {
637+
min-height: auto;
609638
}
610639

611-
.v2-nav-arrow {
612-
position: relative;
613-
transform: none;
614-
margin: 0 auto;
615-
width: 48px;
616-
height: 48px;
640+
.v2-modal-content {
641+
min-height: auto;
642+
}
643+
644+
.v2-modal-content.active {
645+
justify-content: flex-start;
646+
gap: 12px;
647+
padding-bottom: 12px;
648+
}
649+
650+
.v2-title {
651+
font-size: clamp(26px, 6vw, 34px);
652+
}
653+
654+
.v2-subtitle {
655+
font-size: 15px;
656+
}
657+
658+
.v2-feature-title {
659+
font-size: 24px;
660+
margin-bottom: 4px;
661+
}
662+
663+
.v2-feature-description {
664+
font-size: 14px;
665+
margin-bottom: 8px;
666+
}
667+
668+
.v2-emoji-plain img {
669+
width: 110px !important;
670+
}
671+
672+
.v2-emoji-feature img {
673+
width: 80px;
674+
}
675+
676+
.v2-feature-highlight-item {
677+
padding: 10px 14px;
678+
font-size: 14px;
679+
border-radius: 12px;
680+
}
681+
682+
.v2-feature-highlights {
683+
gap: 8px;
684+
}
685+
686+
.v2-button {
687+
padding: 14px 28px;
688+
font-size: 14px;
617689
}
618690
}
619691

620692
@media (max-width: 480px) {
621693
.v2-badge {
622694
bottom: 16px;
623695
right: 16px;
696+
padding: 12px 22px;
697+
font-size: 15px;
624698
}
625699

626700
.v2-modal {
627-
border-radius: 26px;
701+
padding: 28px 16px 22px;
702+
border-radius: 18px;
703+
}
704+
705+
.v2-title {
706+
font-size: clamp(22px, 6vw, 28px);
707+
}
708+
709+
.v2-subtitle {
710+
font-size: 14px;
711+
}
712+
713+
.v2-feature-title {
714+
font-size: 20px;
715+
}
716+
717+
.v2-emoji-plain img {
718+
width: 90px !important;
719+
}
720+
721+
.v2-emoji-feature img {
722+
width: 64px;
628723
}
629724

630725
.v2-button {
631726
width: 100%;
632727
}
633728

634729
.v2-feature-highlight-item {
635-
font-size: 14px;
730+
font-size: 13px;
731+
padding: 8px 12px;
732+
gap: 8px;
733+
}
734+
735+
.v2-mobile-nav {
736+
gap: 10px;
737+
}
738+
739+
.v2-mobile-nav .v2-nav-arrow {
740+
width: 36px;
741+
height: 36px;
742+
}
743+
744+
.v2-mobile-nav .v2-progress-dots {
745+
padding: 6px 10px;
746+
gap: 6px;
747+
}
748+
749+
.v2-mobile-nav .v2-progress-dot {
750+
width: 8px;
751+
height: 8px;
752+
}
753+
754+
.v2-mobile-nav .v2-progress-dot.active {
755+
width: 28px;
636756
}
637757
}

static/js/v2-announcement.js

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,18 @@ class V2Announcement {
110110
btn.addEventListener('click', () => this.prevStep());
111111
});
112112

113-
// Progress dots
114-
const dots = document.querySelectorAll('.v2-progress-dot');
115-
dots.forEach((dot, index) => {
116-
dot.addEventListener('click', () => this.showStep(index + 1));
113+
// Progress dots (both desktop and mobile sets)
114+
const dotSets = document.querySelectorAll('.v2-progress-dots');
115+
dotSets.forEach(container => {
116+
const dots = container.querySelectorAll('.v2-progress-dot');
117+
dots.forEach((dot, index) => {
118+
dot.addEventListener('click', () => this.showStep(index + 1));
119+
});
117120
});
118121

122+
// Swipe navigation for touch devices
123+
this.setupSwipeNavigation();
124+
119125
// Continue as guest
120126
const guestBtn = document.getElementById('v2-guest-btn');
121127
if (guestBtn) {
@@ -143,6 +149,32 @@ class V2Announcement {
143149
}
144150
}
145151

152+
setupSwipeNavigation() {
153+
const wrapper = document.querySelector('.v2-modal-content-wrapper');
154+
if (!wrapper) return;
155+
156+
let startX = 0;
157+
let startY = 0;
158+
159+
wrapper.addEventListener('touchstart', (e) => {
160+
startX = e.touches[0].clientX;
161+
startY = e.touches[0].clientY;
162+
}, { passive: true });
163+
164+
wrapper.addEventListener('touchend', (e) => {
165+
if (!this.modalShown) return;
166+
const dx = e.changedTouches[0].clientX - startX;
167+
const dy = e.changedTouches[0].clientY - startY;
168+
// only count horizontal swipes (ignore vertical scrolls)
169+
if (Math.abs(dx) < 50 || Math.abs(dy) > Math.abs(dx)) return;
170+
if (dx < 0) {
171+
this.nextStep();
172+
} else {
173+
this.prevStep();
174+
}
175+
}, { passive: true });
176+
}
177+
146178
openModal() {
147179
if (this.modalShown) return;
148180

@@ -285,34 +317,27 @@ class V2Announcement {
285317

286318
animateProgressForDot(stepNumber) {
287319
if (this.prefersReducedMotion) return;
288-
const dots = document.querySelectorAll('.v2-progress-dot');
289-
dots.forEach((dot, idx) => {
290-
// ensure relative positioning for fill
320+
// Animate across all dot containers (desktop + mobile)
321+
const allDots = document.querySelectorAll('.v2-progress-dot');
322+
allDots.forEach((dot) => {
291323
dot.style.position = 'relative';
292-
// remove existing fill
293324
const existing = dot.querySelector('.v2-progress-fill');
294325
if (existing) existing.remove();
295-
// only add fill to active dot
296-
if (idx + 1 === stepNumber) {
326+
327+
if (dot.classList.contains('active')) {
297328
const fill = document.createElement('span');
298329
fill.className = 'v2-progress-fill';
299-
// initial styles
300330
fill.style.position = 'absolute';
301331
fill.style.left = '0';
302332
fill.style.top = '0';
303333
fill.style.height = '100%';
304334
fill.style.width = '0%';
305335
fill.style.borderRadius = '999px';
306-
fill.style.background = 'linear-gradient(90deg, rgba(124,58,237,0.9), rgba(99,102,241,0.9))';
336+
fill.style.background = 'rgba(124, 58, 237, 0.85)';
307337
fill.style.zIndex = '0';
308338
fill.style.transition = `width ${this.autoAdvanceInterval}ms linear`;
309339
dot.appendChild(fill);
310-
// force layout then animate to full width
311-
// expand to match the dot's computed width
312340
requestAnimationFrame(() => {
313-
// ensure the dot has been sized (active dot may be wider)
314-
const targetW = dot.clientWidth + 'px';
315-
// use percentage to fill entire dot
316341
fill.style.width = '100%';
317342
});
318343
}
@@ -362,16 +387,21 @@ class V2Announcement {
362387
}
363388

364389
updateProgressDots(activeStep) {
365-
const dots = document.querySelectorAll('.v2-progress-dot');
366-
dots.forEach((dot, index) => {
367-
if (index + 1 === activeStep) {
368-
dot.classList.add('active');
369-
// ensure active dot has progress animation started
370-
this.animateProgressForDot(activeStep);
371-
} else {
372-
dot.classList.remove('active');
373-
}
390+
// There are two sets of dots (desktop footer + mobile nav), both use
391+
// .v2-progress-dots containers. Update every dot across all containers.
392+
const dotSets = document.querySelectorAll('.v2-progress-dots');
393+
dotSets.forEach(container => {
394+
const dots = container.querySelectorAll('.v2-progress-dot');
395+
dots.forEach((dot, index) => {
396+
if (index + 1 === activeStep) {
397+
dot.classList.add('active');
398+
} else {
399+
dot.classList.remove('active');
400+
}
401+
});
374402
});
403+
// animate progress fill across all active dots
404+
this.animateProgressForDot(activeStep);
375405
}
376406

377407
nextStep() {

templates/base.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<link rel="stylesheet" href="{{ url_for('static', path='css/contacts-modal.css') }}?v=9">
1717
<link rel="stylesheet" href="{{ url_for('static', path='css/auth.css') }}?v=1">
1818
<link rel="stylesheet" href="{{ url_for('static', path='css/self-promo.css') }}?v=2">
19-
<link rel="stylesheet" href="{{ url_for('static', path='css/v2-announcement.css') }}?v=2">
19+
<link rel="stylesheet" href="{{ url_for('static', path='css/v2-announcement.css') }}?v=3">
2020
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons@latest/iconfont/tabler-icons.min.css">
2121

2222
{% if sentry_client_key or clarity_id %}
@@ -64,7 +64,7 @@
6464
<script src="{{ url_for('static', path='js/header.js') }}?v=6" defer></script>
6565
<script src="{{ url_for('static', path='js/self-promo.js') }}?v=2" defer></script>
6666
<script src="{{ url_for('static', path='js/auth.js') }}?v=3" defer></script>
67-
<script src="{{ url_for('static', path='js/v2-announcement.js') }}?v=3" defer></script>
67+
<script src="{{ url_for('static', path='js/v2-announcement.js') }}?v=4" defer></script>
6868

6969
{% block scripts %}{% endblock %}
7070
</body>

0 commit comments

Comments
 (0)