Files
cima-progetti/README.md
T
stefanomanca e3098d3004 refactor[ frontend / components ]: aggiornamento UI globale, ottimizzazione header e gestione errori
Header: Rifatto il componente con menu mobile ed effetti di background.

Cleanup: Rimossi i componenti Hero e Services non più necessari.

Layout: Migliorata la struttura e lo stile della pagina Contacts.

Core: Implementata la gestione globale degli errori in main.js.

Styles: Aggiornati gli stili globali e le media queries per una migliore responsività.
2026-05-06 23:05:17 +02:00

17 KiB
Raw Blame History

Cima Progetti - Sito Aziendale

Sito web moderno, performante e accessibile sviluppato con Svelte 5 + Vite per presentare servizi, metodologia e contatti aziendali. Completamente responsivo con supporto completo per telefoni e tablet.

Funzionalità principali

  • 🌓 Tema light/dark con persistenza dual-storage (localStorage + cookie backup)
  • 📱 Design completamente responsivo con breakpoint intelligenti
  • ⬆️ Header smart — trasparente all'inizio, sfondo blur opaco dopo scroll
  • 🍔 Menu hamburger con animazione fluida di apertura/chiusura
  • 🎨 Sistema di componenti riusabili e theme-aware
  • 🚀 Routing client-side con History API e fallback 404 per pagine non create
  • 🔐 Cookie consent con personalizzazione categorie
  • Accessibilità — focus-visible preservato, tap highlight rimosso su touch
  • Svelte 5 Runes — reattività state-driven con $state(), $derived()
  • 🐛 Global error handling — trap di SyntaxError visibile su dev server

🛠️ Requisiti

  • Node.js 18+ (consigliato LTS)
  • npm 9+

🚀 Avvio rapido

npm install
npm run dev

Applicazione disponibile su http://localhost:5173 (accesso locale) e http://192.168.XX.XXX:5173 (rete locale).

📝 Script disponibili

Script Descrizione
npm run dev Avvia il server di sviluppo con hot reload (esposto su 0.0.0.0:5173)
npm run build Genera la build di produzione ottimizzata in dist/
npm run preview Avvia preview della build (simula produzione in locale)

📁 Struttura del progetto

cimaprogetti/
├── index.html                    # Template HTML entry point
├── package.json                  # Dependencies e scripts
├── vite.config.js               # Configurazione Vite (SPA + host 0.0.0.0)
├── svelte.config.js             # Configurazione Svelte
│
├── src/
│   ├── main.js                  # Entry point con error trap globale
│   ├── App.svelte               # Root component con routing e layout
│   │
│   ├── components/
│   │   ├── Header/
│   │   │   ├── Header.svelte    # Navbar sticky con tema + scroll detection
│   │   │   └── Header.css       # Stili header (blur backdrop, absolute menu)
│   │   │
│   │   ├── Button/
│   │   │   ├── Button.svelte    # Button/link riusabile (16 prop)
│   │   │   └── Button.css       # Stili + focus-visible + no tap highlight
│   │   │
│   │   ├── CookiePopUp/
│   │   │   ├── CookiePopUp.svelte    # Cookie consent con toggle personalizzato
│   │   │   ├── CookiePopUp.css       # Dialog mobile + desktop
│   │   │   └── assets/               # SVG cookie icon
│   │   │
│   │   ├── Toggle/
│   │   │   ├── Toggle.svelte    # Switch accessibile
│   │   │   └── Toggle.css       # Stili toggle (animated knob)
│   │   │
│   │   ├── ThemeToggle/
│   │   │   ├── ThemeToggle.svelte
│   │   │   └── ThemeToggle.css
│   │   │
│   │   ├── Footer/
│   │   │   ├── Footer.svelte
│   │   │   └── Footer.css
│   │   │
│   │   └── Hero/, Services/     # Componenti home
│   │
│   ├── pages/
│   │   ├── index.svelte         # Home page
│   │   ├── Contacts.svelte      # Pagina contatti con team profiles
│   │   ├── 403.svelte, 404.svelte, 500.svelte, errore.svelte  # Error pages
│   │
│   ├── lib/
│   │   ├── theme.js             # Theme persistence (localStorage + cookie)
│   │   ├── navigation.js        # Client-side routing utilities
│   │   ├── cookieConsent.js     # Cookie storage & consent management
│   │   └── errorRouting.js      # Path normalization & aliases
│   │
│   └── styles/
│       ├── global.css           # CSS variables, typography, resets
│       ├── App.css              # Layout principale
│       ├── contacts.css         # Pagina contatti (flex + absolute positioning)
│       └── error-pages.css      # Pagine errore (clamp typography)
│
└── public/
    └── images/
        ├── background/          # Background patterns
        ├── icons/               # Logo, UI icons
        └── contacts/            # Hero images (light/dark theme)

🌐 Configurazione Network

Il server Vite è configurato per essere accessibile dalla rete locale:

// vite.config.js
server: {
    host: '0.0.0.0',  // Ascolta su tutte le interfacce
    port: 5173,
}

Accesso dal telefono sulla stessa Wi-Fi

  1. Avvia il dev server: npm run dev
  2. Nel terminale vedrai l'indirizzo IP locale (es. 192.168.68.119)
  3. Dal telefono apri: http://192.168.68.119:5173

Nota: Per produzione, usa npm run build && npm run preview per servire la versione compilata su http://192.168.68.119:4173 (compatibile con browser vecchi).

🗺️ Routing

Il routing è client-side only — nessuna pagina server. Gestito in App.svelte leggendo window.location.pathname e reagendo ai cambi di History API.

Tabella rotte

Route Pagina Comportamento
/ index.svelte Home page (Hero + Services)
/contatti o /contacts Contacts.svelte Info contatti + team profiles
/servizi, /metodo, /progetti, /chi-siamo 404.svelte Non create — mostra 404 ma mantiene URL
/403, /404, /500, /errore Error pages Pagine errore dedicate
Qualsiasi altra rotta 404.svelte Fallback automatico

Meccanismo di routing

// src/App.svelte
const getRoute = (path) => {
    const normalized = resolvePathname(path)  // Normalizza trailing slash
    if (normalized === '/') return 'home'
    if (normalized === '/contatti' || normalized === '/contacts') return 'contacts'
    // ...
    return '404'  // Fallback
}

let currentRoute = $derived(getRoute(pathname))

Come aggiungere una nuova pagina

  1. Crea il componente in src/pages/MyPage.svelte
  2. Importa in App.svelte:
    import MyPage from "./pages/MyPage.svelte"
    
  3. Aggiungi la route in getRoute():
    if (normalized === '/my-page') return 'mypage'
    
  4. Renderizza nel template:
    {:else if currentRoute === 'mypage'}
        <MyPage />
    

🎨 Sistema di Tema

Tema Persistente Dual-Storage

Il tema viene salvato su localStorage (primario) e cookie (backup) simultaneamente. Questo assicura che il tema sopravviva anche se l'utente cancella parte dello storage del browser.

// src/lib/theme.js

export const getSavedTheme = () => {
    // Prova localStorage prima, poi cookie come fallback
    return localStorage.getItem('theme') || getCookie('theme')
}

export const saveTheme = (theme) => {
    localStorage.setItem('theme', theme)
    setCookie('theme', theme)  // Backup redundante
}

export const applyTheme = (theme) => {
    document.documentElement.setAttribute('data-theme', theme)
}

Variabili CSS Globali

Tutte le variabili tema sono in src/styles/global.css:

:root {
    --navbar-height: 140px;
    --primary-color: #1343F0;      /* Blu Cima */
    --text-color: #111827;          /* Grigio scuro */
    --surface: #ffffff;             /* Background carta */
    --background: rgba(246, 249, 255, 0.5);
    --background-opaque: #00000033;
    --muted-color: #6b7280;
    --lateral-margin: 12rem;
}

:root[data-theme='dark'] {
    --primary-color: #4983F2;       /* Blu chiaro */
    --text-color: #e6eef8;           /* Grigio molto chiaro */
    --surface: #000;
    --background: rgba(0, 0, 0, 0.8);
    --background-opaque: rgba(255, 255, 255, 0.2);
}

/* Mobile breakpoint */
@media (max-width: 900px) {
    :root {
        --navbar-height: 110px;
        --lateral-margin: 1.5rem;
    }
}

Come usare i colori tema

<div style="color: var(--text-color); background: var(--surface);">
    Contenuto automaticamente theme-aware
</div>

🧩 Componenti Principali

Header (Navbar Intelligente)

La navbar è sticky e reagisce allo scroll con una transizione elegante:

  • Trasparente all'inizio (background-color: transparent)
  • Sfondo blur dopo 100px di scroll (backdrop-filter: blur(8px))
  • Scompare al scroll verso il basso (nascosto con translateY(-100%))
  • Riappare al scroll verso l'alto (velocità rilevata)
<Header />

Modifiche in Header.svelte:

  • Stato hasBackground che attiva a scrollY > 100
  • Classe with-bg per applicare blur e shadow
  • Mobile menu hamburger con animazione da basso

CSS Features:

  • backdrop-filter: blur(8px) per effetto frosted glass
  • position: fixed con transition: transform 0.28s
  • Menu mobile transform: translateY(100%)translateY(0) con cubic-bezier smoothing

Button (Componente Riusabile)

Componente ultra-flessibile che renderizza come <a> se ha href, altrimenti <button>.

<Button 
    text="PRENOTA UNA CALL"
    href="/contatti"
    borderWidth="1.5px"
    borderColor="var(--text-color)"
    color="transparent"
    textColor="var(--text-color)"
    padding="12px 32px"
    round="5px"
    margin="1rem 0 0"
    bold
/>

Tutte le prop:

Prop Default Descrizione
text 'Button' Testo del pulsante
color var(--primary-color) Background
textColor var(--surface) Colore testo
padding '10px 20px' Padding interno
round '5px' border-radius
href Se presente, renderizza come <a> con navigazione client-side
borderWidth '0px' Spessore border interno (inset)
borderColor var(--primary-color) Colore border
bold true Font-weight bold
margin '0' NEW — margini esterni

Accessibilità:

  • Outline su :focus-visible (tastiera)
  • Tap highlight rimosso su touch (:focus:not(:focus-visible))

Dialog modale con due view:

  1. Main — Descrizione generica + bottoni accetta/rifiuta/personalizza
  2. Prefs — Toggle per tre categorie: Tecnici (always on), Analitici, Profilazione
<CookiePopUp />

Varianti layout:

  • Desktop (> 900px): Fixed al bottom, full-width
  • Tablet (768900px): Drawer da destra (clip-path animation)
  • Mobile (< 768px): Full-page dal basso (translateY animation)

Accessibilità:

  • role="dialog" + aria-modal="true"
  • Focus trap — primo elemento focusabile automaticamente
  • Escape key per chiudere (con reject)
  • Tap highlight disabilitato

Contacts Page (Pagina Contatti)

Layout two-column con immagine decorativa e team profiles.

<div class="contact-us">
    <div class="contact-us-container">
        <!-- Titolo, testo, email/WhatsApp, button -->
    </div>
    <div class="contact-image">
        <!-- SVG tema-aware (light/dark) -->
    </div>
</div>

<div class="behind-cima">
    <h2>PEOPLE BEHIND CIMA</h2>
    <div class="profiles-container">
        <!-- Due profile cards con flex: 1 1 0 -->
    </div>
</div>

Responsive:

  • Desktop (> 900px): Due colonne con gap 40px
  • Mobile (< 900px):
    • contact-image diventa position: absolute centrata
    • contact-us-container full width
    • Profili stacked verticalmente

Toggle (Accessibile Switch)

Switch accessibile con label opzionale e stato animato.

<Toggle 
    bind:checked={analitici}
    label="Analitici"
    disabled={false}
/>

CSS Variables (customizzabile):

  • --toggle-on-bg — colore quando ON
  • --toggle-off-bg — colore quando OFF
  • --toggle-knob — colore pallina
  • --toggle-width / --toggle-height — dimensioni

📱 Responsive Design

Breakpoint Principale

L'unico breakpoint è 900px (tablet). Sotto questo valore attivano:

  • Margini orizzontali ridotti: 12rem1.5rem
  • Navbar height ridotta: 140px110px
  • Menu hamburger (al posto di nav desktop)
  • Header flexibile trasparente con blur
  • Contatti layout single-column
  • Profiles stacked
  • Error pages con tipografia clamp()

Clamp() per Tipografia Fluida

Errori, heading e font-size critici usano clamp() per scalare fluidamente:

/* Error code number */
.error-code {
    font-size: clamp(2rem, 5vw, 4rem);
}

/* Error page title background */
.error-text {
    font-size: clamp(10rem, 20vw, 15rem) !important;
}

/* Links in contacts */
.contact-info-item a {
    font-size: clamp(1.25rem, 2.5vw, 2rem);
}
// src/lib/cookieConsent.js

const consent = {
    value: 'custom',  // 'accepted_all', 'rejected_all', 'custom'
    preferences: {
        tecnici: true,           // Obbligatorio
        analitici: false,        // Personalizzabile
        profilazione: false      // Personalizzabile
    }
}

Persistenza

  • Cookie di consenso salvato in cookie-consent (via js-cookie)
  • Configurazione letta al mount di App.svelte
  • Toggle popup non riappare se esiste consenso valido

🐛 Error Handling & Debugging

Global Error Trap (main.js)

// Cattura SyntaxError e runtime errors prima del mount
window.addEventListener('error', (event) => {
    const msg = `ERROR: ${event.error?.message || event.message}`;
    console.error(msg, event.error);
    alert(msg);  // Visibile su telefono per debug
}, true);

try {
    const app = mount(App, { target: document.getElementById('app') })
    console.log("App mounted successfully!")
} catch (e) {
    alert(`Mount Error: ${e.message}`)
}

Diagnostica Schermo Bianco

Se il telefono mostra schermo bianco:

  1. Apri DevTools → Console → controlla errori
  2. Prova build di produzione:
    npm run build
    npm run preview
    # Accedi da http://192.168.XX.XXX:4173
    
  3. Se l'alert appare, c'è un SyntaxError nei moduli ES
  4. Prova altro browser (Chrome vs Safari su iOS)

🔄 Svelte 5 & Modern JavaScript

Svelte 5 Runes

Il progetto usa Svelte 5 Runes per reattività state-driven:

<script>
    let activeNav = $state(window.location.pathname)
    let isMenuOpen = $state(false)
    let hasBackground = $state(false)
    
    let currentRoute = $derived(getRoute(pathname))
</script>

Evitare:

  • on:click → Usare onclick={...}
  • Stores writable() → Usare $state()
  • reactive → Usare $derived()

Rune Disponibili

  • $state(value) — Mutabile, reattivo
  • $derived(expr) — Calcolato e cached
  • $effect(fn) — Side effect reattivo
  • $props() — Default props type-safe

🚀 Build & Deploy

Build di Produzione

npm run build

Genera file ottimizzati in dist/:

  • .html minificato
  • .js bundle (tree-shaken)
  • .css minificato
  • Source map opzionali

Preview Locale

npm run preview

Simula il server di produzione su http://localhost:4173 — ottimo per testare la build prima di deployare.

Deployment

Il progetto è una SPA statica — deployabile su:

  • Vercel, Netlify (drag & drop dist/)
  • GitHub Pages (aggiungere base: "/repo-name/" in vite.config.js)
  • AWS S3 + CloudFront
  • Qualsiasi server web (serve dist/ con fallback 404 → /index.html)

📦 Dipendenze

{
  "devDependencies": {
    "@sveltejs/vite-plugin-svelte": "^7.0.0",
    "svelte": "^5.55.5",
    "vite": "^8.0.10"
  }
}

Zero runtime dependencies — tutta la logica è vanilla JavaScript.

🎓 Tecnologie & Approcci

Aspetto Soluzione
Framework UI Svelte 5 (Runes + reactivity)
Build Vite 8 (zero-config, super veloce)
CSS CSS3 nativo (custom properties + clamp)
Routing History API (client-side only)
Tema Data attribute + CSS variables
Cookies Vanilla JS (no library)
Accessibilità ARIA roles + focus-visible + keyboard nav
Mobile Responsive design + touch optimized

🎯 Convenzioni Codice

Naming

  • Components: PascalCase (Header.svelte, Button.svelte)
  • Pages: PascalCase (Contacts.svelte, index.svelte)
  • Styles: kebab-case (.contact-us-container, .mobile-menu)
  • JS utils: camelCase (getSavedTheme, navigateTo)

Organizzazione File

Component/
├── Component.svelte      # Logica + template
├── Component.css         # Stili isolati
└── assets/               # SVG, immagini specifiche

CSS Classi

  • .component-name per root
  • .component-name-child per nested (BEM-like)
  • Aggiungere media query alla fine del file
  • Commentare sezioni con /* ===== SECTION NAME ===== */

🤝 Contribuire

Workflow

  1. Crea branch per feature: git checkout -b feature/nome
  2. Test locale: npm run dev + mobile preview
  3. Build test: npm run build && npm run preview
  4. Commit: Messaggi chiari (feat:, fix:, docs:)
  5. Push e open PR

📄 Licenza

Progetto proprietario © CIMA PROGETTI