The Evolution

Case Study

UI, UX, & IA

The Evolution (Before & After)

Itinerary Stop Information

Before (4/18/2026) - Issues

  • Too many images (having 3 images per stop). Heavy page weight causing slower load times.
  • Details were scattered. Forcing the user’s eyes to go back and forth to find the address, time, and stop number caused unnecessary frustration.
  • The  CTA ticket button is not in the center of the card (content area).

After (8/19/2026) - Fixes

  • Use just one image thats enough to understand the stop they’ll be going to. Also good for load times.
  • Have just the name of the place and not the neighborhood (will be in the beginning of the route heading).
  • Have the details of the stop (address, stop number, time) closer together to the name of the stop.
  • Have the CTA in the middle of the content area (card).
  • Vital: The intro text for the stop and the must do text will be shown when the plus icon at the bottom is clicked and activated. I feel this information is not important for users to see right away. ( see code below ↓).

Using Code Process

Goal: I wanted the intro text and must do text hidden and to only be activated when the users requested it. I used Google Ai web browser to achieve this.

First prompt (initial short version): “So the item to appear has css class hover-description and its inside the container (the trigger) which has css class trigger-container. so the item (container) when it appears it will be out of the viewers view. i need when the user interacts with the trigger for it to have an anchor roll effect meaning to atomically scroll up to the appearing item (overall to be in center of the viewport screen with all devices…pc , mobile, ipad)”.

Another prompt: “I do want to add this scenario. If the user clicks/taps on the peeking content on the right for pc/tablet (the next trigger) don’t have the appearing container try to appear. So basically the browser needs to ignore this command”.

Another prompt: “Also add in if user is scrolling down and stops and the trigger container (content area) is not in center view (they can’t read anything) and they tap don’t let the appearing container appear until the trigger container is 100% in viewport (auto anchor to center the trigger container). After this tapping again in the content area (or plus icon) will have the appearing container appear”.

Final prompt (after 5 hours of conversations – shortened): “Also when they leave it (click/tap out) instead of leaving where they are on the screen we need to force them to view the content and slide into center view of the screen (as this can be a mistake tap). If they click or tap above the appearing container (or anywhere else above) let it disappear as this may indicate they are no longer interested”.

Google Ai code given after final prompt:

				
					<script>
/** 2. SLIDER INTERCEPT ENGINE & CONTENT CARD VISIBILITY GUARDS **/
document.addEventListener('DOMContentLoaded', function() {
    const containers = document.querySelectorAll('.trigger-container');
    if (containers.length === 0) return;

    containers.forEach(container => {
        const description = container.querySelector('.hover-description');
        const parentSlider = container.closest('.horizontal-slider');
        if (!description) return;

        description.style.opacity = '0';
        description.style.transition = 'opacity 0.3s ease-in-out';
        description.style.pointerEvents = 'none';
        container.setAttribute('data-state', 'none');

        let touchStartX = 0, touchStartY = 0, isDraggingMode = false;
        let lastSliderScrollLeft = parentSlider ? parentSlider.scrollLeft : 0;

        function forceFocusOnCard() {
            const mainCard = container.parentElement.querySelector('.main-content-card');
            if (mainCard) {
                const cardRect = mainCard.getBoundingClientRect();
                const cardTopOnPage = cardRect.top + window.scrollY;
                const targetVerticalScroll = cardTopOnPage - (window.innerHeight / 2) + (mainCard.offsetHeight / 2);
                
                window.globalSliderState.isAutoScrolling = true;
                window.scrollTo({ top: targetVerticalScroll, behavior: 'smooth' });
                setTimeout(() => { window.globalSliderState.isAutoScrolling = false; }, 600);
            }
        }

        const dragStart = function(clientX, clientY) {
            touchStartX = clientX; touchStartY = clientY; isDraggingMode = false;
            if (parentSlider) lastSliderScrollLeft = parentSlider.scrollLeft;
        };
        container.addEventListener('touchstart', function(e) { dragStart(e.touches.clientX, e.touches.clientY); }, { passive: true });
        container.addEventListener('mousedown', function(e) { dragStart(e.clientX, e.clientY); }, { passive: true });

        const dragMove = function(clientX, clientY) {
            if (Math.abs(clientX - touchStartX) > 10 || Math.abs(clientY - touchStartY) > 10) {
                isDraggingMode = true;
                if (description.style.opacity === '1') {
                    container.setAttribute('data-state', 'none');
                    description.style.opacity = '0';
                    description.style.pointerEvents = 'none';
                }
            }
        };
        container.addEventListener('touchmove', function(e) { dragMove(e.touches.clientX, e.touches.clientY); }, { passive: true });
        container.addEventListener('mousemove', function(e) { if (e.buttons === 1) dragMove(e.clientX, e.clientY); }, { passive: true });

        if (parentSlider) {
            parentSlider.addEventListener('scroll', function() {
                if (description.style.opacity === '1' && Math.abs(parentSlider.scrollLeft - lastSliderScrollLeft) > 15) {
                    container.setAttribute('data-state', 'none');
                    description.style.opacity = '0';
                    description.style.pointerEvents = 'none';
                }
            }, { passive: true });
        }

        // 2. UNIFIED INTENTIONAL CLICK / TAP EVENT
        container.addEventListener('click', function(e) {
            if (e.target.closest('a, button, .elementor-button')) {
                e.stopPropagation();
                return; 
            }

            if (isDraggingMode) return;

            const mainCard = container.parentElement.querySelector('.main-content-card');
            if (mainCard) {
                const cardRect = mainCard.getBoundingClientRect();
                const isCardFullyOnScreen = (cardRect.top >= 0) && (cardRect.bottom <= window.innerHeight);
                
                if (!isCardFullyOnScreen) {
                    e.stopPropagation();
                    forceFocusOnCard();
                    return;
                }
            }

            if (parentSlider) {
                const sRect = parentSlider.getBoundingClientRect();
                const cRect = container.getBoundingClientRect();
                if (cRect.left < sRect.left - 15 || cRect.right > sRect.right + 15) {
                    return;
                }
            }

            e.stopPropagation();
            
            containers.forEach(other => {
                if (other !== container) {
                    other.setAttribute('data-state', 'none');
                    const otherDesc = other.querySelector('.hover-description');
                    if (otherDesc) { otherDesc.style.opacity = '0'; otherDesc.style.pointerEvents = 'none'; }
                }
            });

            if (description.style.opacity === '1') {
                container.setAttribute('data-state', window.matchMedia('(pointer: coarse)').matches ? 'none' : 'forced-closed');
                description.style.opacity = '0';
                description.style.pointerEvents = 'none';
                forceFocusOnCard(); 
            } else {
                container.setAttribute('data-state', 'locked-open');
                description.style.opacity = '1';
                description.style.pointerEvents = 'auto';
                
                window.globalSliderState.originalScrollPos = window.scrollY;
                window.globalSliderState.isAutoScrolling = true;
                
                description.style.willChange = 'transform';
                
                // TARGETED DESCRIPTION CENTERING OVERRIDE
                setTimeout(() => {
                    const descRect = description.getBoundingClientRect();
                    const descTopOnPage = descRect.top + window.scrollY;
                    
                    // Center ONLY the .hover-description container directly on the screen height
                    const targetVerticalScroll = descTopOnPage - (window.innerHeight / 2) + (description.offsetHeight / 2);
                    
                    window.scrollTo({
                        top: targetVerticalScroll,
                        behavior: 'smooth'
                    });
                    
                    setTimeout(() => { 
                        window.globalSliderState.isAutoScrolling = false; 
                        description.style.willChange = 'auto';
                    }, 600);
                }, 100);
            }
        });
    });

    document.addEventListener('click', function(e) {
        if (e.target.closest('.itinerary-btn')) {
            containers.forEach(container => {
                const description = container.querySelector('.hover-description');
                if (description && description.style.opacity === '1') {
                    container.setAttribute('data-state', 'none');
                    description.style.opacity = '0';
                    description.style.pointerEvents = 'none';
                }
            });
            window.globalSliderState.originalScrollPos = 0;
        }
    }, { passive: true });
});
</script>

				
			

How i solved this / got to this point?: I gave the Ai multiple scenarios and did multiple tests. When something  didn’t work i had to be very detailed in my prompt of the issue.

» Overall this fix is not as overwhelming as the old layout. The user can understand the stop (even without clicking the plus icon) in just a quick glance. The information is packed together efficiently rather than being spread out. See it live ↗

Top Attractions Basics

Before (1/23/2026) - Issues

  • Too many images (having 8 to 12 images per tab!!).
  • The images were too wide and unstructured.
  • The native Elementor tabs widget injected heavy, unnecessary scripts that bogged down the page.
  • The tabs had excessive padding, making the UI feel clunky.
  • The text in the tabs is not tourist friendly. Users that are new to NYC will not know what Hudson Yards or Rockefeller Center is!
  • The CTA needs to be obvious to the user! Don’t have them search or they will leave!

After (9/14/2026) - Fixes

  • Reduced to 2-3 images per tab to prevent choice paralysis and drastically improve page load times.
  • Have the image more square like. Have it at 33% for pc and tablet. Less than 100% for mobile. Also have the image speak first and then the text (meta data) say the rest.
  • Removed the heavy widget entirely. I used an AI-assisted workflow to write custom code for a lightweight horizontal slider and triggering content changes via clean header links (shaped like tabs).
  • The text in the tabs are universal language topics (Most Known and Parks). Easier for users to process and they have also found their answer (what should we/i see first?)
  • The CTA is present in white background. Very easy to see.

» Tourists arriving in NYC are often overwhelmed and stressed about what to see. I believe if you give someone too many options they won’t even look. But if you give a couple options (3 or 2) they will look because their mind is at ease (Hick’s Law). The fix gives the tourists a more narrow idea (while also being more specific) and helps the page with loading times while also feeling professional in design.   See it live ↗