Overview
Guigolo includes several utility UI components in ~/components/ui/ that handle common patterns like contact tracking, scroll restoration, and visual effects.
A wrapper component that tracks where contact CTAs are clicked from.
Props
Unique identifier for tracking this CTA’s origin (e.g., “hero-contact”, “projects-contact”)
CSS classes to apply to the link element
Usage
import ContactLink from '@/components/ui/ContactLink';
<ContactLink
ctaId="hero-contact"
className="rounded-md bg-accent-lime px-6 py-3 text-black font-medium"
>
Contactar
</ContactLink>
How It Works
-
When clicked, stores the
ctaId in sessionStorage:
sessionStorage.setItem('contact_origin', ctaId);
-
Navigates to the
#contacto section using smooth scroll
-
The Contact component reads this origin and includes it in form submissions
-
Unlocks the
almost_talked achievement when the form becomes visible
The origin tracking helps you understand which CTAs drive the most contact form submissions.
Real Implementation
From ContactLink.tsx:15-25:
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault();
if (typeof window !== 'undefined') {
sessionStorage.setItem('contact_origin', ctaId);
const contactSection = document.getElementById('contacto');
contactSection?.scrollIntoView({ behavior: 'smooth' });
}
};
Automatically restores scroll position after navigation or page reload.
Usage
import RestoreScroll from '@/components/ui/RestoreScroll';
// In your root layout
<RestoreScroll />
How It Works
- On mount, checks sessionStorage for saved scroll position
- If found, scrolls to that position
- On unmount, saves current scroll position
- Handles hash-based navigation (e.g.,
#contacto)
useEffect(() => {
const savedPosition = sessionStorage.getItem('scroll_position');
if (savedPosition) {
window.scrollTo(0, parseInt(savedPosition, 10));
sessionStorage.removeItem('scroll_position');
}
// Handle hash navigation
if (window.location.hash) {
const target = document.querySelector(window.location.hash);
target?.scrollIntoView({ behavior: 'smooth' });
}
return () => {
sessionStorage.setItem('scroll_position', String(window.scrollY));
};
}, []);
This component improves UX by maintaining scroll context across client-side navigations.
BlueprintCard
A styled card component with blueprint-inspired design elements.
Props
Usage
import BlueprintCard from '@/components/ui/BlueprintCard';
<BlueprintCard className="p-6">
<h3>Card Title</h3>
<p>Card content goes here</p>
</BlueprintCard>
Styling
The component includes:
- Dashed border with low opacity (
border-dashed border-[#ededed1a])
- Dark background with transparency (
bg-neutral-black-900/35)
- Consistent spacing and corner treatment
<div className="border border-dashed border-[#ededed1a] bg-neutral-black-900/35 p-6">
{children}
</div>
Utility functions for tracking contact form origins.
Functions
Retrieves the stored contact origin from sessionStorage:
import { getContactOrigin } from '@/components/ui/contactOrigin';
const origin = getContactOrigin();
console.log(origin); // "hero-contact" or null
Stores a contact origin:
import { setContactOrigin } from '@/components/ui/contactOrigin';
setContactOrigin('footer-cta');
Clears the stored origin (typically after form submission):
import { clearContactOrigin } from '@/components/ui/contactOrigin';
clearContactOrigin();
Implementation
const ORIGIN_KEY = 'contact_origin';
export function getContactOrigin(): string | null {
if (typeof window === 'undefined') return null;
return sessionStorage.getItem(ORIGIN_KEY);
}
export function setContactOrigin(origin: string): void {
if (typeof window === 'undefined') return;
sessionStorage.setItem(ORIGIN_KEY, origin);
}
export function clearContactOrigin(): void {
if (typeof window === 'undefined') return;
sessionStorage.removeItem(ORIGIN_KEY);
}
Best Practices
Use ContactLink instead of regular anchor tags for all CTAs that lead to the contact form. This ensures consistent origin tracking.
SessionStorage is cleared when the tab closes. For persistent tracking across sessions, consider using localStorage or a database.
All UI components handle SSR gracefully by checking typeof window !== 'undefined' before accessing browser APIs.
Component Patterns
Client-Side Only Logic
'use client';
import { useEffect } from 'react';
export default function ClientComponent() {
useEffect(() => {
// Browser API access is safe here
const data = sessionStorage.getItem('key');
}, []);
return <div>Content</div>;
}
SSR-Safe Conditionals
const handleClick = () => {
if (typeof window !== 'undefined') {
sessionStorage.setItem('key', 'value');
}
};