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à.
This commit is contained in:
@@ -1,20 +1,24 @@
|
||||
# Cima Progetti - Sito Aziendale
|
||||
|
||||
Sito web moderno e responsivo sviluppato con **Svelte + Vite** per presentare servizi, metodologia e contatti aziendali.
|
||||
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 locale
|
||||
- 📱 **Design completamente responsivo**
|
||||
- ⬆️ **Navbar collapsibile** con hide/show al scroll
|
||||
- 🎨 **Sistema di componenti riusabili**
|
||||
- 🚀 **Routing client-side** con pagine dedicate e fallback 404
|
||||
- ♿ **Accessibilità** e buone pratiche web
|
||||
- 🌓 **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+
|
||||
- **Node.js** 18+ (consigliato LTS)
|
||||
- **npm** 9+
|
||||
|
||||
## 🚀 Avvio rapido
|
||||
|
||||
@@ -23,76 +27,201 @@ npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Applicazione disponibile su `http://localhost:5173`.
|
||||
Applicazione disponibile su `http://localhost:5173` (accesso locale) e `http://192.168.XX.XXX:5173` (rete locale).
|
||||
|
||||
## 📝 Script disponibili
|
||||
|
||||
- `npm run dev` - avvia il server di sviluppo con hot reload
|
||||
- `npm run build` - genera la build di produzione
|
||||
- `npm run preview` - avvia un'anteprima della build
|
||||
| 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
|
||||
|
||||
```
|
||||
src/
|
||||
├── App.svelte # Componente root con routing
|
||||
├── main.js # Punto di ingresso
|
||||
├── styles/
|
||||
│ ├── global.css # Stili globali e variabili tema
|
||||
│ ├── App.css # Layout principale app
|
||||
│ └── error-pages.css # Stili pagine errore
|
||||
├── components/
|
||||
│ ├── Header/ # Navbar con scroll hide/show
|
||||
│ ├── Hero/ # Sezione hero principale
|
||||
│ ├── Services/ # Griglia servizi
|
||||
│ ├── Footer/ # Footer con info aziendali
|
||||
│ ├── Button/ # Componente button riusabile
|
||||
│ └── ThemeToggle/ # Toggle tema light/dark
|
||||
└── pages/
|
||||
├── Contacts.svelte # Pagina contatti
|
||||
├── 403.svelte # Accesso negato
|
||||
├── 404.svelte # Pagina non trovata
|
||||
├── 500.svelte # Errore server
|
||||
└── ErrorGeneric.svelte # Errore generico
|
||||
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**:
|
||||
|
||||
```js
|
||||
// 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 è gestito in `src/App.svelte` leggendo `window.location.pathname` e reagendo ai cambi di History API.
|
||||
Il routing è **client-side only** — nessuna pagina server. Gestito in `App.svelte` leggendo `window.location.pathname` e reagendo ai cambi di History API.
|
||||
|
||||
| Route | Componente | Descrizione |
|
||||
|-------|-----------|-------------|
|
||||
| `/` | `Hero` + `Services` | Home page |
|
||||
| `/contatti`, `/contacts` | `Contacts` | Pagina contatti |
|
||||
| `/403` | `Forbidden` | Accesso negato |
|
||||
| `/404` | `NotFound` | Pagina non trovata |
|
||||
| `/500` | `ServerError` | Errore server |
|
||||
| `/errore` | `ErrorGeneric` | Errore generico |
|
||||
| qualsiasi altra route | `NotFound` | Fallback 404 per pagine non ancora create |
|
||||
### Tabella rotte
|
||||
|
||||
Le voci della navbar puntano a route reali come `/servizi`, `/metodo`, `/progetti` e `/chi-siamo`: se una pagina non esiste ancora, il contenuto mostrato è il 404 ma l'URL resta quello richiesto.
|
||||
| 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 |
|
||||
|
||||
## 🎨 Tema e Stili
|
||||
### Meccanismo di routing
|
||||
|
||||
### Variabili CSS globali
|
||||
```js
|
||||
// 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**:
|
||||
```svelte
|
||||
import MyPage from "./pages/MyPage.svelte"
|
||||
```
|
||||
3. **Aggiungi la route** in `getRoute()`:
|
||||
```js
|
||||
if (normalized === '/my-page') return 'mypage'
|
||||
```
|
||||
4. **Renderizza nel template**:
|
||||
```svelte
|
||||
{: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.
|
||||
|
||||
```js
|
||||
// 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`:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--primary-color: #1343F0; /* Colore primario */
|
||||
--text-color: #111827; /* Testo light mode */
|
||||
--surface: #ffffff; /* Sfondo light mode */
|
||||
--navbar-height: 140px;
|
||||
--primary-color: #1343F0; /* Blu Cima */
|
||||
--text-color: #111827; /* Grigio scuro */
|
||||
--surface: #ffffff; /* Background carta */
|
||||
--background: rgba(246, 249, 255, 0.5);
|
||||
/* ... altre variabili */
|
||||
--background-opaque: #00000033;
|
||||
--muted-color: #6b7280;
|
||||
--lateral-margin: 12rem;
|
||||
}
|
||||
|
||||
:root[data-theme='dark'] {
|
||||
--primary-color: #4983F2;
|
||||
--text-color: #e6eef8;
|
||||
--primary-color: #4983F2; /* Blu chiaro */
|
||||
--text-color: #e6eef8; /* Grigio molto chiaro */
|
||||
--surface: #000;
|
||||
--background: rgba(0, 0, 0, 0.8);
|
||||
/* ... altre variabili */
|
||||
--background-opaque: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Mobile breakpoint */
|
||||
@media (max-width: 900px) {
|
||||
:root {
|
||||
--navbar-height: 110px;
|
||||
--lateral-margin: 1.5rem;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -100,126 +229,362 @@ Tutte le variabili tema sono in `src/styles/global.css`:
|
||||
|
||||
```svelte
|
||||
<div style="color: var(--text-color); background: var(--surface);">
|
||||
Contenuto che segue il tema
|
||||
Contenuto automaticamente theme-aware
|
||||
</div>
|
||||
```
|
||||
|
||||
## 🧩 Componenti principali
|
||||
## 🧩 Componenti Principali
|
||||
|
||||
### Button
|
||||
### Header (Navbar Intelligente)
|
||||
|
||||
Componente button/link riusabile con tema integrato.
|
||||
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)
|
||||
|
||||
```svelte
|
||||
<!-- Button base -->
|
||||
<Button text="Click me" />
|
||||
<Header />
|
||||
```
|
||||
|
||||
<!-- Button con link -->
|
||||
<Button text="HOME" href="/" />
|
||||
**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
|
||||
|
||||
<!-- Button con bordo interno -->
|
||||
**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>`.
|
||||
|
||||
```svelte
|
||||
<Button
|
||||
text="Custom Button"
|
||||
href="/"
|
||||
borderWidth="2px"
|
||||
/>
|
||||
|
||||
<!-- Stili avanzati -->
|
||||
<Button
|
||||
text="Premium"
|
||||
color="var(--primary-color)"
|
||||
textColor="var(--surface)"
|
||||
text="PRENOTA UNA CALL"
|
||||
href="/contatti"
|
||||
borderWidth="1.5px"
|
||||
borderColor="var(--text-color)"
|
||||
color="transparent"
|
||||
textColor="var(--text-color)"
|
||||
padding="12px 32px"
|
||||
round="12px"
|
||||
borderWidth="1px"
|
||||
round="5px"
|
||||
margin="1rem 0 0"
|
||||
bold
|
||||
/>
|
||||
```
|
||||
|
||||
**Prop disponibili:**
|
||||
- `text` - testo del button (default: `'Button'`)
|
||||
- `color` - colore sfondo (default: `var(--primary-color)`)
|
||||
- `textColor` - colore testo (default: `var(--surface)`)
|
||||
- `padding` - padding interno (default: `'8px 24px'`)
|
||||
- `round` - border-radius (default: `'8px'`)
|
||||
- `href` - se presente, renderizza come `<a>` tag
|
||||
- `borderWidth` - spessore bordo interno (default: `'0px'`)
|
||||
**Tutte le prop:**
|
||||
|
||||
Se `href` punta a una route interna, il click usa la navigazione client-side e aggiorna il contenuto senza ricaricare la pagina.
|
||||
| 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 |
|
||||
|
||||
### Header
|
||||
**Accessibilità:**
|
||||
- Outline su `:focus-visible` (tastiera)
|
||||
- Tap highlight rimosso su touch (`:focus:not(:focus-visible)`)
|
||||
|
||||
Navbar sticky che si nasconde al scroll verso il basso e riappare al scroll verso l'alto.
|
||||
### CookiePopUp (Cookie Consent Dialog)
|
||||
|
||||
Le voci della navbar usano route interne. Le pagine non ancora create devono restituire 404.
|
||||
Dialog modale con due view:
|
||||
|
||||
Modifica il contenuto in `src/components/Header/Header.svelte`.
|
||||
|
||||
### ThemeToggle
|
||||
|
||||
Pulsante per switchare tra tema light e dark. Il tema viene salvato in `localStorage` e come backup in cookie, così resta disponibile anche dopo operazioni che puliscono parte dello storage del browser.
|
||||
|
||||
## 📱 Modificare i contenuti
|
||||
|
||||
- **Header/Navigazione:** `src/components/Header/Header.svelte`
|
||||
- **Routing e redirect:** `src/App.svelte`, `src/lib/navigation.js`, `src/lib/errorRouting.js`
|
||||
- **Sezione hero:** `src/components/Hero/Hero.svelte`
|
||||
- **Servizi:** `src/components/Services/Services.svelte`
|
||||
- **Footer:** `src/components/Footer/Footer.svelte`
|
||||
- **Pagine errore:** `src/pages/*.svelte`
|
||||
|
||||
## ➕ Aggiungere nuove pagine
|
||||
|
||||
1. Crea un nuovo file `.svelte` in `src/pages/`
|
||||
2. Aggiungi una route in `src/App.svelte` nello script:
|
||||
1. **Main** — Descrizione generica + bottoni accetta/rifiuta/personalizza
|
||||
2. **Prefs** — Toggle per tre categorie: Tecnici (always on), Analitici, Profilazione
|
||||
|
||||
```svelte
|
||||
import MyPage from "./pages/MyPage.svelte"
|
||||
<CookiePopUp />
|
||||
```
|
||||
|
||||
const getRoute = (path) => {
|
||||
// ...
|
||||
if (normalized === '/my-page') return 'mypage'
|
||||
// ...
|
||||
**Varianti layout:**
|
||||
- **Desktop** (> 900px): Fixed al bottom, full-width
|
||||
- **Tablet** (768–900px): 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.
|
||||
|
||||
```svelte
|
||||
<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.
|
||||
|
||||
```svelte
|
||||
<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: `12rem` → `1.5rem`
|
||||
- Navbar height ridotta: `140px` → `110px`
|
||||
- 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:
|
||||
|
||||
```css
|
||||
/* 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);
|
||||
}
|
||||
```
|
||||
|
||||
3. Aggiungi il rendering nel template:
|
||||
## 🔐 Cookie Consent & Privacy
|
||||
|
||||
```svelte
|
||||
{:else if currentRoute === 'mypage'}
|
||||
<MyPage />
|
||||
### Gestione Categoria Cookie
|
||||
|
||||
```js
|
||||
// src/lib/cookieConsent.js
|
||||
|
||||
const consent = {
|
||||
value: 'custom', // 'accepted_all', 'rejected_all', 'custom'
|
||||
preferences: {
|
||||
tecnici: true, // Obbligatorio
|
||||
analitici: false, // Personalizzabile
|
||||
profilazione: false // Personalizzabile
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Se non aggiungi la route in `getRoute`, il path continuerà a mostrare il fallback 404.
|
||||
### Persistenza
|
||||
|
||||
## 🚀 Build e Deploy
|
||||
- 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
|
||||
|
||||
### Build di produzione
|
||||
## 🐛 Error Handling & Debugging
|
||||
|
||||
### Global Error Trap (main.js)
|
||||
|
||||
```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**:
|
||||
```bash
|
||||
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:
|
||||
|
||||
```svelte
|
||||
<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
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Genera i file ottimizzati in `dist/`.
|
||||
Genera file ottimizzati in `dist/`:
|
||||
- `.html` minificato
|
||||
- `.js` bundle (tree-shaken)
|
||||
- `.css` minificato
|
||||
- Source map opzionali
|
||||
|
||||
### Preview locale
|
||||
### Preview Locale
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
- **Svelte** - Framework UI
|
||||
- **Vite** - Build tool e dev server
|
||||
- **@sveltejs/vite-plugin-svelte** - Plugin Svelte per Vite
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||
"svelte": "^5.55.5",
|
||||
"vite": "^8.0.10"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎓 Tecnologie utilizzate
|
||||
**Zero runtime dependencies** — tutta la logica è vanilla JavaScript.
|
||||
|
||||
- **Svelte 5** - UI framework reattivo
|
||||
- **Vite** - Build tool moderno e veloce
|
||||
- **CSS3** - Styling con variabili CSS
|
||||
- **Vanilla JS** - JavaScript puro
|
||||
## 🎓 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
|
||||
|
||||
## 🔗 Link Utili
|
||||
|
||||
- [Svelte 5 Docs](https://svelte.dev)
|
||||
- [Vite Docs](https://vitejs.dev)
|
||||
- [Svelte REPL](https://svelte.dev/repl)
|
||||
- [Can I Use](https://caniuse.com) — Compatibilità browser
|
||||
|
||||
## 📄 Licenza
|
||||
|
||||
Progetto proprietario © CIMA PROGETTI
|
||||
Progetto proprietario © **CIMA PROGETTI**
|
||||
Reference in New Issue
Block a user