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:
2026-05-06 23:05:17 +02:00
parent 575e2b4779
commit e3098d3004
25 changed files with 1426 additions and 565 deletions
+6 -5
View File
@@ -1,11 +1,10 @@
<script>
import { onMount } from 'svelte'
import Hero from "./components/Hero/Hero.svelte";
import Services from "./components/Services/Services.svelte";
import Footer from "./components/Footer/Footer.svelte";
import Header from "./components/Header/Header.svelte";
import CookiePopUp from "./components/CookiePopUp/CookiePopUp.svelte";
import Contacts from "./pages/Contacts.svelte";
import IndexPage from "./pages/index.svelte";
import Contacts from "./pages/contacts.svelte";
import Forbidden from "./pages/403.svelte";
import ServerError from "./pages/500.svelte";
import ErrorGeneric from "./pages/errore.svelte";
@@ -41,6 +40,9 @@
}
onMount(() => {
console.log("Svelte App.svelte mounted!");
console.log("Current pathname:", pathname);
console.log("Current route:", currentRoute);
initializeStoredConsent()
try {
@@ -80,8 +82,7 @@
<Header />
<main class:center-content={isCenteredMain}>
{#if currentRoute === 'home'}
<Hero />
<Services />
<IndexPage />
{:else if currentRoute === 'contacts'}
<Contacts />
{:else if currentRoute === '403'}
+28 -2
View File
@@ -2,21 +2,34 @@
display: inline-flex;
align-items: center;
justify-content: center;
width: fit-content;
inline-size: fit-content;
background-color: var(--button-color);
color: var(--button-text-color);
padding: var(--button-padding);
margin: var(--button-margin, 0);
border-radius: var(--button-radius);
font: inherit;
cursor: pointer;
font-weight: bold;
font-weight: normal;
transition: transform 0.2s, opacity 0.2s, background-color 0.2s, box-shadow 0.2s;
text-decoration: none;
border: none;
box-shadow: inset 0 0 0 var(--button-border-width) var(--primary-color);
box-shadow: inset 0 0 0 var(--button-border-width) var(--button-border-color);
font-family: 'IBM Plex Mono', monospace;
white-space: nowrap;
}
/* Occupa tutto lo spazio disponibile nel contenitore padre */
.button.full-width {
width: 100%;
inline-size: 100%;
}
.button.bold {
font-weight: bold;
}
.button:hover {
opacity: 0.92;
transform: translateY(-1px);
@@ -29,4 +42,17 @@
.button:focus-visible {
outline: 2px solid var(--primary-color);
outline-offset: 3px;
}
/* Remove touch highlight on buttons and hide focus for touch/pointer events
while keeping keyboard focus-visible for accessibility */
.button,
.button * {
-webkit-tap-highlight-color: transparent;
tap-highlight-color: transparent;
}
.button:focus:not(:focus-visible) {
outline: none !important;
box-shadow: none !important;
}
+8 -3
View File
@@ -8,8 +8,11 @@
textColor = '#fff',
round = '5px',
padding = '10px 20px',
margin = '0',
href = null,
borderWidth = '0px',
borderColor = 'var(--primary-color)',
fullWidth = false,
...restProps
} = $props()
@@ -25,9 +28,10 @@
{#if href}
<a
class="button"
style={`--button-color: ${color}; --button-text-color: ${textColor}; --button-radius: ${round}; --button-padding: ${padding}; --button-border-width: ${borderWidth};`}
class:full-width={fullWidth}
style={`--button-color: ${color}; --button-text-color: ${textColor}; --button-radius: ${round}; --button-padding: ${padding}; --button-border-width: ${borderWidth}; --button-border-color: ${borderColor}; --button-margin: ${margin};`}
{href}
on:click={handleClick}
onclick={handleClick}
{...restProps}
>
{text}
@@ -35,7 +39,8 @@
{:else}
<button
class="button"
style={`--button-color: ${color}; --button-text-color: ${textColor}; --button-radius: ${round}; --button-padding: ${padding}; --button-border-width: ${borderWidth};`}
class:full-width={fullWidth}
style={`--button-color: ${color}; --button-text-color: ${textColor}; --button-radius: ${round}; --button-padding: ${padding}; --button-border-width: ${borderWidth}; --button-border-color: ${borderColor}; --button-margin: ${margin};`}
{...restProps}
>
{text}
+134 -75
View File
@@ -1,9 +1,9 @@
.cookie-backdrop {
position: fixed;
inset: 0;
z-index: 199;
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(2px);
position: fixed;
inset: 0;
z-index: 199;
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(2px);
}
.cookie-popup {
@@ -11,29 +11,42 @@
justify-content: space-between;
align-items: center;
gap: 50px;
z-index: 200;
position: fixed;
left: 50%;
bottom: 25px;
transform: translateX(-50%);
width: min(100%, 1100px);
max-width: calc(100% - 25px * 2);
padding: 50px;
border-radius: 5px;
background-color: var(--surface);
color: var(--text-color);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
border: 2px solid rgba(127, 127, 127, 0.3);
z-index: 200;
position: fixed;
left: 50%;
bottom: 25px;
transform: translateX(-50%);
width: min(100%, 1100px);
max-width: calc(100% - 25px * 2);
max-height: calc(100vh - 50px);
overflow-y: auto;
padding: 50px;
border-radius: 5px;
background-color: var(--surface);
color: var(--text-color);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
border: 2px solid rgba(127, 127, 127, 0.3);
}
/* Remove mobile tap highlight and browser default focus ring inside cookie popup */
.cookie-popup,
.cookie-popup * {
-webkit-tap-highlight-color: transparent;
}
.cookie-popup :focus {
outline: none;
box-shadow: none;
}
.cookie-popup-content p {
margin: 15px 0 15px;
margin: 15px 0 15px;
}
.cookie-popup-title {
display: flex;
align-items: center;
gap: 20px;
display: flex;
align-items: center;
gap: 20px;
}
.cookie-popup-title h2 {
@@ -42,44 +55,44 @@
}
.cookie-icon {
width: 30px;
height: 30px;
display: inline-block;
background-color: var(--text-color);
-webkit-mask-image: url('./assets/cookie.svg');
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
-webkit-mask-size: contain;
mask-image: url('./assets/cookie.svg');
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
transition: background-color 0.2s ease;
width: 30px;
height: 30px;
display: inline-block;
background-color: var(--text-color);
-webkit-mask-image: url('./assets/cookie.svg');
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
-webkit-mask-size: contain;
mask-image: url('./assets/cookie.svg');
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
transition: background-color 0.2s ease;
}
.cookie-popup-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 50px;
flex-wrap: nowrap;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 50px;
flex-wrap: nowrap;
}
.cookie-link {
color: var(--primary-color);
text-decoration: none;
color: var(--primary-color);
text-decoration: none;
cursor: pointer;
}
.cookie-link-button {
background: transparent;
border: 0;
padding: 0;
font: inherit;
background: transparent;
border: 0;
padding: 0;
font: inherit;
}
.cookie-link:hover {
text-decoration: underline;
text-decoration: underline;
}
.cookie-popup-buttons {
@@ -90,14 +103,12 @@
.cookie-popup-buttons .button {
white-space: nowrap;
flex-shrink: 0;
min-width: fit-content;
}
.cookie-toggle-item {
cursor: pointer;
display: flex;
align-items: center;
align-items: center;
padding: 8px 0;
margin: 0;
}
@@ -115,36 +126,84 @@
}
.cookie-toggle-container {
display: flex;
flex-wrap: wrap;
gap: 50px;
display: flex;
flex-wrap: wrap;
gap: 0 50px;
margin-bottom: 12px;
}
/* toggle item styling moved to Toggle.css */
/* ============================================================
MEDIA QUERIES
============================================================ */
@media (max-width: 640px) {
.cookie-popup {
bottom: 12px;
padding: 16px;
flex-direction: column;
align-items: stretch;
gap: 16px;
}
@media (max-width: 1280px) {}
.cookie-popup-actions {
justify-content: stretch;
gap: 12px;
flex-direction: column;
}
@media (max-width: 1024px) {
.cookie-popup-buttons {
flex-wrap: wrap;
flex-direction: column;
}
.cookie-popup-actions .button,
.cookie-link {
flex: 1 1 100%;
text-align: center;
}
.cookie-popup-buttons .button {
width: 100%;
flex: none;
}
.cookie-link-button {
padding: 10px 0;
}
.cookie-popup-buttons .button {
flex: 1 1 auto;
text-align: center;
}
.cookie-link-button {
padding: 8px 0;
text-align: center;
}
}
@media (max-width: 900px) {
.cookie-popup {
left: 0;
right: 0;
bottom: 0;
transform: none;
width: 100%;
max-width: 100%;
max-height: 85vh;
padding: 20px 16px;
border-radius: 12px 12px 0 0;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: 16px;
overflow-y: auto;
}
.cookie-popup-actions {
flex-direction: column;
align-items: stretch;
gap: 10px;
}
.cookie-popup-buttons {
flex-direction: row;
}
.cookie-popup-buttons .button {
flex: 1;
text-align: center;
}
}
@media (max-width: 480px) {}
@media (max-width: 360px) {
.cookie-popup-buttons {
flex-direction: column;
}
.cookie-popup-buttons .button {
width: 100%;
flex: none;
}
}
+4 -3
View File
@@ -1,5 +1,5 @@
.footer p, .footer a, .footer h4 {
font-size: 0.7rem;
font-size: 0.9rem;
margin: 0;
}
@@ -176,7 +176,7 @@
color: var(--muted-color);
}
@media (max-width: 768px) {
@media (max-width: 900px) {
.footer {
padding: 40px 20px 20px;
}
@@ -198,5 +198,6 @@
.footer-section-info-container {
flex-direction: column;
gap: 24px;
margin-right: 0;
}
}
}
+4 -4
View File
@@ -19,28 +19,28 @@
<p>Scoprirci anche sui nostri canali social</p>
<div class="footer-links">
<a
href="https://www.facebook.com/cimaprogetti"
href="https://www.facebook.com/profile.php?id=61587060986122"
target="_blank"
rel="noopener noreferrer">
<span class="social-icon icon-facebook" aria-hidden="true"></span>
<span class="sr-only">Facebook</span>
</a>
<a
href="https://www.instagram.com/cimaprogetti"
href="https://www.instagram.com/cimaprogetti/"
target="_blank"
rel="noopener noreferrer">
<span class="social-icon icon-instagram" aria-hidden="true"></span>
<span class="sr-only">Instagram</span>
</a>
<a
href="https://www.linkedin.com/company/cimaprogetti"
href="https://www.linkedin.com/company/cima-progetti-srls/"
target="_blank"
rel="noopener noreferrer">
<span class="social-icon icon-linkedin" aria-hidden="true"></span>
<span class="sr-only">LinkedIn</span>
</a>
<a
href="https://www.tiktok.com/@cimaprogetti"
href="https://www.tiktok.com/@cima.progetti"
target="_blank"
rel="noopener noreferrer">
<span class="social-icon icon-tiktok" aria-hidden="true"></span>
+222 -25
View File
@@ -1,3 +1,7 @@
/* ============================================================
HEADER — struttura base
============================================================ */
.header {
position: fixed;
top: 0;
@@ -5,7 +9,18 @@
width: 100%;
z-index: 100;
transform: translateY(0);
transition: transform 0.28s ease;
transition: transform 0.3s ease, background-color 1s ease, backdrop-filter 0.3s ease, -webkit-backdrop-filter 0.3s ease, box-shadow 0.3s ease;
background-color: transparent;
backdrop-filter: blur(0px);
-webkit-backdrop-filter: blur(0px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0);
}
.header.with-bg {
background-color: var(--background-header);
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.header.hidden {
@@ -13,15 +28,18 @@
}
.header-container {
max-width: 1000px;
margin: 0 auto;
padding: 0 20px;
margin: 0 var(--lateral-margin);
display: flex;
align-items: center;
height: var(--navbar-height);
justify-content: space-between;
}
.logo-img{
/* ============================================================
LOGO
============================================================ */
.logo-img {
width: 98px;
height: 70px;
margin-right: auto;
@@ -30,7 +48,7 @@
}
.logo-mask {
background-color: var(--text-color);
background-color: var(--primary-color);
-webkit-mask-image: url('/images/icons/logo-nero.svg');
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
@@ -45,18 +63,16 @@
transform: translateY(-1px);
}
/* ============================================================
NAV DESKTOP
============================================================ */
.nav {
display: flex;
gap: 30px;
margin: 0 auto;
}
.header-actions {
display: flex;
gap: 12px;
align-items: center;
}
.nav a {
text-decoration: none;
color: var(--text-color);
@@ -74,22 +90,203 @@
color: var(--primary-color);
}
/*Temporaneo*/
@media (max-width: 768px) {
.header-container {
flex-direction: column;
height: auto;
padding: 15px 20px;
/* ============================================================
HEADER ACTIONS (desktop)
============================================================ */
.header-actions {
display: flex;
gap: 12px;
align-items: center;
}
/* ============================================================
HAMBURGER BUTTON — icona SVG, niente span
============================================================ */
.hamburger {
display: none;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background: transparent;
border: none;
cursor: pointer;
padding: 0;
color: var(--text-color);
flex-shrink: 0;
transition: color 0.2s;
}
.hamburger:hover {
color: var(--primary-color);
}
.hamburger svg {
display: block;
}
/* ============================================================
MENU MOBILE FULL-PAGE (sale dal basso)
============================================================ */
.mobile-menu {
position: fixed;
inset: 0;
z-index: 99;
background-color: var(--surface);
display: flex;
flex-direction: column;
justify-content: flex-end;
/* Nascosto: traslato completamente fuori dal basso */
transform: translateY(100%);
/* Transizione sempre attiva per animare sia l'apertura che la chiusura */
transition: transform 0.38s cubic-bezier(0.4, 0, 0.2, 1);
/* Solo su mobile */
display: none;
}
.mobile-menu.open {
transform: translateY(0);
}
/* ============================================================
NAV INTERNA AL MENU MOBILE
============================================================ */
.mobile-menu-nav {
display: flex;
flex-direction: column;
padding: 0 1.5rem 32px;
}
.mobile-menu-nav a {
text-decoration: none;
color: var(--text-color);
font-family: 'IBM Plex Mono', monospace;
font-weight: 600;
font-size: clamp(2rem, 8vw, 3.5rem);
line-height: 1;
padding: 20px 0;
border-bottom: 2px solid var(--background-opaque);
transition: color 0.2s, padding-left 0.2s;
display: block;
}
.mobile-menu-nav a:first-child {
border-top: 2px solid var(--background-opaque);
}
.mobile-menu-nav a:hover,
.mobile-menu-nav a.active {
color: var(--primary-color);
padding-left: 12px;
}
/* ============================================================
FOOTER DEL MENU MOBILE (bottone contattaci)
============================================================ */
.mobile-menu-footer {
padding: 24px 1.5rem 40px;
border-top: 2px solid var(--background-opaque);
}
.mobile-menu-footer .button {
display: block;
width: 100%;
text-align: center;
}
/* ============================================================
MEDIA QUERIES
============================================================ */
/* —— Desktop grande (> 1280px) ————————————————————————————— */
@media (min-width: 1280px) {}
/* —— Desktop medio (1024px 1279px) ————————————————————— */
@media (min-width: 1024px) and (max-width: 1279px) {}
/* —— Tablet (768px 1023px): pannello laterale 50% ————————— */
@media (min-width: 768px) and (max-width: 1023px) {
.mobile-menu {
left: auto;
right: 0;
top: 0;
bottom: 0;
width: 50%;
box-shadow: -4px 0 24px rgba(0, 0, 0, 0.12);
border-left: 2px solid var(--background-opaque);
clip-path: inset(0 0 0 100%);
transition: clip-path 0.35s cubic-bezier(0.4, 0, 0.2, 1);
transform: none;
}
.nav {
gap: 15px;
width: 100%;
justify-content: center;
flex-wrap: wrap;
.mobile-menu.open {
clip-path: inset(0 0 0 0%);
transition: clip-path 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
.nav a {
font-size: 14px;
.mobile-menu-nav a {
font-size: clamp(1.2rem, 3vw, 1.6rem);
padding: 16px 0;
}
}
/* —— Mobile (< 900px): attiva hamburger + menu full-page ——— */
@media (max-width: 900px) {
.header-container {
margin: 0 1.5rem;
}
/* Nasconde nav e bottone desktop */
.nav-desktop,
.header-actions {
display: none;
}
/* Mostra hamburger */
.hamburger {
display: flex;
}
/* Abilita il menu mobile (di default display:none) */
.mobile-menu {
display: flex;
}
}
/* —— Mobile piccolo (< 480px) ————————————————————————————— */
@media (max-width: 480px) {}
/* —— Mobile minimo (< 360px) —————————————————————————————— */
@media (max-width: 360px) {}
/* Touch / tap highlight & focus handling for mobile interaction
- disable native tap highlight which creates colored rectangles
- keep keyboard focus-visible outlines intact for accessibility
*/
.hamburger,
.mobile-menu a,
.mobile-menu .button,
.mobile-menu .button * {
-webkit-tap-highlight-color: transparent;
-webkit-user-select: none;
user-select: none;
-webkit-touch-callout: none;
}
/* Hide focus ring for pointer/touch interactions but preserve for keyboard
users via :focus-visible (only remove when element is focused but not
focus-visible). */
.hamburger:focus:not(:focus-visible),
.mobile-menu a:focus:not(:focus-visible),
.mobile-menu .button:focus:not(:focus-visible) {
outline: none !important;
box-shadow: none !important;
}
+96 -20
View File
@@ -6,27 +6,41 @@
let activeNav = $state(typeof window !== 'undefined' ? window.location.pathname : '/')
let isHidden = $state(false)
let isMenuOpen = $state(false)
let hasBackground = $state(false)
let lastScrollY = 0
const SCROLL_DELTA = 8
const TOP_OFFSET = 24
const BG_THRESHOLD = 100
function handleNavClick(event, path) {
if (!isInternalPath(path)) return
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) return
event.preventDefault()
isMenuOpen = false
navigateTo(path)
}
function toggleMenu() {
isMenuOpen = !isMenuOpen
}
function handleScroll() {
const currentScrollY = window.scrollY || 0
const diff = currentScrollY - lastScrollY
// Aggiungi sfondo quando scrolli oltre il threshold
hasBackground = currentScrollY > BG_THRESHOLD
if (currentScrollY <= TOP_OFFSET) {
isHidden = false
hasBackground = false
} else if (diff > SCROLL_DELTA) {
isHidden = true
hasBackground = false
isMenuOpen = false
} else if (diff < -SCROLL_DELTA) {
isHidden = false
}
@@ -50,49 +64,111 @@
})
</script>
<header class="header" class:hidden={isHidden} id="home">
<header class="header" class:hidden={isHidden} class:with-bg={hasBackground} id="home">
<div class="header-container">
<div class="logo">
<a href="/" aria-label="CIMA PROGETTI home" on:click={(e) => handleNavClick(e, '/')}>
<a href="/" aria-label="CIMA PROGETTI home" onclick={(e) => handleNavClick(e, '/')}>
<span class="logo-img logo-mask" aria-hidden="true"></span>
</a>
</div>
<nav class="nav">
<!-- Nav desktop -->
<nav class="nav nav-desktop" aria-label="Navigazione principale">
<a
href="/servizi"
class:active={activeNav === '/servizi'}
on:click={(e) => handleNavClick(e, '/servizi')}
onclick={(e) => handleNavClick(e, '/servizi')}
aria-current={activeNav === '/servizi' ? 'page' : undefined}
>
Servizi
</a>
>Servizi</a>
<a
href="/metodo"
class:active={activeNav === '/metodo'}
on:click={(e) => handleNavClick(e, '/metodo')}
onclick={(e) => handleNavClick(e, '/metodo')}
aria-current={activeNav === '/metodo' ? 'page' : undefined}
>
Il metodo
</a>
>Il metodo</a>
<a
href="/progetti"
class:active={activeNav === '/progetti'}
on:click={(e) => handleNavClick(e, '/progetti')}
onclick={(e) => handleNavClick(e, '/progetti')}
aria-current={activeNav === '/progetti' ? 'page' : undefined}
>
Progetti
</a>
>Progetti</a>
<a
href="/chi-siamo"
class:active={activeNav === '/chi-siamo'}
on:click={(e) => handleNavClick(e, '/chi-siamo')}
onclick={(e) => handleNavClick(e, '/chi-siamo')}
aria-current={activeNav === '/chi-siamo' ? 'page' : undefined}
>
Chi Siamo
</a>
>Chi Siamo</a>
</nav>
<div class="header-actions">
<Button text="Contattaci" href="/contatti" />
</div>
<!-- Pulsante hamburger / close (solo mobile) — SVG inline, niente span -->
<button
class="hamburger"
onclick={toggleMenu}
aria-label={isMenuOpen ? 'Chiudi menu' : 'Apri menu'}
aria-expanded={isMenuOpen}
aria-controls="mobile-menu"
type="button"
>
{#if isMenuOpen}
<!-- Icona X -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<line x1="4" y1="4" x2="20" y2="20" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
<line x1="20" y1="4" x2="4" y2="20" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
</svg>
{:else}
<!-- Icona hamburger -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
<line x1="3" y1="12" x2="21" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
<line x1="3" y1="18" x2="21" y2="18" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
</svg>
{/if}
</button>
</div>
</header>
</header>
<!-- Menu mobile full-page (si apre dal basso) -->
<div
class="mobile-menu"
class:open={isMenuOpen}
id="mobile-menu"
role="dialog"
aria-modal="true"
aria-label="Menu di navigazione"
aria-hidden={!isMenuOpen}
>
<nav class="mobile-menu-nav" aria-label="Navigazione mobile">
<a
href="/servizi"
class:active={activeNav === '/servizi'}
onclick={(e) => handleNavClick(e, '/servizi')}
aria-current={activeNav === '/servizi' ? 'page' : undefined}
>Servizi</a>
<a
href="/metodo"
class:active={activeNav === '/metodo'}
onclick={(e) => handleNavClick(e, '/metodo')}
aria-current={activeNav === '/metodo' ? 'page' : undefined}
>Il metodo</a>
<a
href="/progetti"
class:active={activeNav === '/progetti'}
onclick={(e) => handleNavClick(e, '/progetti')}
aria-current={activeNav === '/progetti' ? 'page' : undefined}
>Progetti</a>
<a
href="/chi-siamo"
class:active={activeNav === '/chi-siamo'}
onclick={(e) => handleNavClick(e, '/chi-siamo')}
aria-current={activeNav === '/chi-siamo' ? 'page' : undefined}
>Chi Siamo</a>
</nav>
<div class="mobile-menu-footer">
<Button text="Contattaci" href="/contatti" onclick={() => { isMenuOpen = false }} padding="14px 32px" />
</div>
</div>
-111
View File
@@ -1,111 +0,0 @@
.hero {
background: linear-gradient(135deg, #0066cc 0%, #0052a3 100%);
color: white;
padding: 120px 20px;
text-align: center;
}
.hero-container {
max-width: 1200px;
margin: 0 auto;
}
.hero-content h1 {
font-size: 48px;
margin: 0 0 20px 0;
}
.hero-content p {
font-size: 24px;
margin: 0 0 40px 0;
opacity: 0.9;
}
.cta-button {
background-color: #ff6b35;
color: white;
border: none;
padding: 14px 40px;
font-size: 18px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
.cta-button:hover {
background-color: #e55a24;
}
@media (max-width: 768px) {
.hero {
padding: 80px 20px;
}
.hero-content h1 {
font-size: 32px;
}
.hero-content p {
font-size: 18px;
}
.cta-button {
padding: 12px 30px;
font-size: 16px;
}
}.hero {
background: linear-gradient(135deg, #0066cc 0%, #0052a3 100%);
color: white;
padding: 120px 20px;
text-align: center;
}
.hero-container {
max-width: 1200px;
margin: 0 auto;
}
.hero-content h1 {
font-size: 48px;
margin: 0 0 20px 0;
}
.hero-content p {
font-size: 24px;
margin: 0 0 40px 0;
opacity: 0.9;
}
.cta-button {
background-color: #ff6b35;
color: white;
border: none;
padding: 14px 40px;
font-size: 18px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
.cta-button:hover {
background-color: #e55a24;
}
@media (max-width: 768px) {
.hero {
padding: 80px 20px;
}
.hero-content h1 {
font-size: 32px;
}
.hero-content p {
font-size: 18px;
}
.cta-button {
padding: 12px 30px;
font-size: 16px;
}
}
-11
View File
@@ -1,11 +0,0 @@
<script>
import './Hero.css'
</script>
<section class="hero">
<div class="hero-container">
<div class="hero-content">
</div>
</div>
</section>
-68
View File
@@ -1,68 +0,0 @@
.services {
padding: 80px 20px;
background-color: #ffffff;
}
.services-container {
max-width: 1200px;
margin: 0 auto;
}
.services h2 {
text-align: center;
font-size: 36px;
margin-bottom: 50px;
color: #333;
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
}
.service-card {
background: #f9f9f9;
padding: 30px;
border-radius: 8px;
text-align: center;
transition: transform 0.3s, box-shadow 0.3s;
}
.service-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
.icon {
font-size: 48px;
margin-bottom: 15px;
}
.service-card h3 {
font-size: 20px;
margin: 15px 0;
color: #0066cc;
}
.service-card p {
color: #666;
line-height: 1.6;
margin: 0;
}
@media (max-width: 768px) {
.services {
padding: 60px 20px;
}
.services h2 {
font-size: 28px;
margin-bottom: 30px;
}
.services-grid {
grid-template-columns: 1fr;
gap: 20px;
}
}
-45
View File
@@ -1,45 +0,0 @@
<script>
import './Services.css'
const services = [
{
id: 1,
title: 'Consulenza',
description: 'Supporto strategico per la crescita della tua azienda',
icon: '💼'
},
{
id: 2,
title: 'Sviluppo Web',
description: 'Siti e applicazioni web moderne e performanti',
icon: '🚀'
},
{
id: 3,
title: 'Analisi Dati',
description: 'Insights approfonditi dai tuoi dati aziendali',
icon: '📊'
},
{
id: 4,
title: 'Cloud Solutions',
description: 'Migrazione e gestione dell\'infrastruttura cloud',
icon: '☁️'
}
]
</script>
<section class="services">
<div class="services-container">
<h2>I Nostri Servizi</h2>
<div class="services-grid">
{#each services as service (service.id)}
<div class="service-card">
<div class="icon">{service.icon}</div>
<h3>{service.title}</h3>
<p>{service.description}</p>
</div>
{/each}
</div>
</div>
</section>
+19 -3
View File
@@ -2,8 +2,24 @@ import './styles/global.css'
import { mount } from 'svelte'
import App from './App.svelte'
const app = mount(App, {
target: document.getElementById('app'),
})
// Global error trap to catch syntax errors before app mount
window.addEventListener('error', (event) => {
const msg = `ERROR: ${event.error?.message || event.message}`;
console.error(msg, event.error);
alert(msg);
}, true);
console.log("main.js is starting!");
try {
const app = mount(App, {
target: document.getElementById('app'),
})
console.log("App mounted successfully!");
} catch (e) {
const msg = `Mount Error: ${e.message}`;
alert(msg);
console.error("Error mounting App:", e);
}
export default app
+105 -21
View File
@@ -1,26 +1,110 @@
<script>
import "../styles/contacts.css";
import Button from "../components/Button/Button.svelte";
</script>
<div class="contacts-page">
<h1>Contatti</h1>
<p>Pagina contatti - contenuto da aggiungere</p>
<div class="contact-us">
<div class="contact-us-container">
<div class="contact-title">
<h1>Contatta<span>ci</span></h1>
<span class="divider"></span>
</div>
<p>
Siamo qui per <span style="font-weight: bold;">ascoltarti</span>.
</p>
<div class="contact-info-container">
<div class="contact-info-item">
<h4 class="contact-info-type">EMAIL</h4>
<a href="mailto:info@cimaprogetti.it"
>info@cimaprogetti.it</a
>
</div>
<div class="contact-info-item">
<h4 class="contact-info-type">WHATSAPP</h4>
<a href="https://wa.me/393382451171" target="_blank"
>+39 338 245 1171</a
>
</div>
</div>
<Button
text="PRENOTA UNA CALL"
borderWidth="1.5px"
borderColor="var(--text-color)"
color="transparent"
textColor="var(--text-color)"
href="https://google.com"
margin="1rem 0 0"
/>
</div>
<div class="contact-image"></div>
</div>
<div class="behind-cima">
<h2>PEOPLE BEHIND CIMA</h2>
<div class="profiles-container">
<div class="behind-cima-profile">
<h1>Nicola Leone CIARDI</h1>
<div class="profile-role">
<span class="divider"></span>
<h4>CO-FOUNDER & CEO</h4>
</div>
<p>Management and computer science</p>
<div class="contact-info-container-personal">
<div class="contact-info-item">
<h4 class="contact-info-type">EMAIL</h4>
<a
href="mailto:nicolaleone.ciardi@cimaprogetti.it"
class="personal"
>nicolaleone.ciardi@cimaprogetti.it</a
>
</div>
<div class="contact-info-item">
<h4 class="contact-info-type">WHATSAPP</h4>
<a
href="https://wa.me/393382451178"
target="_blank"
class="personal">+39 338 245 1178</a
>
</div>
</div>
<Button
text="Parliamone!"
href="https://google.com"
margin="2rem 0 0"
/>
</div>
<div class="behind-cima-profile">
<h1>Valentina MADAUDO</h1>
<div class="profile-role">
<span class="divider"></span>
<h4>CO-FOUNDER & CFO</h4>
</div>
<p>Jr Engineer & economist for sustainable development</p>
<div class="contact-info-container-personal">
<div class="contact-info-item">
<h4 class="contact-info-type">EMAIL</h4>
<a
href="mailto:valentina.madaudo@cimaprogetti.it"
class="personal"
>valentina.madaudo@cimaprogetti.it</a
>
</div>
<div class="contact-info-item">
<h4 class="contact-info-type">WHATSAPP</h4>
<a
href="https://wa.me/393393580805"
target="_blank"
class="personal">+39 339 358 0805</a
>
</div>
</div>
<Button
text="Confrontiamoci!"
href="https://google.com"
margin="2rem 0 0"
/>
</div>
</div>
</div>
</div>
<style>
.contacts-page {
max-width: 1200px;
margin: 0 auto;
padding: 40px 20px;
min-height: 60vh;
}
h1 {
font-size: 2.5rem;
margin-bottom: 20px;
}
p {
font-size: 1.1rem;
color: #666;
}
</style>
+9
View File
@@ -0,0 +1,9 @@
<script>
import "../styles/global.css";
import "../styles/index.css";
import Button from "../components/Button/Button.svelte";
</script>
<div class="index-page">
<h1>Home</h1>
</div>
View File
+169
View File
@@ -0,0 +1,169 @@
.contacts-page {
margin: 0 var(--lateral-margin) ;
padding-bottom: var(--lateral-margin);
}
.contact-us {
display: flex;
gap: 40px;
}
.contact-us-container {
display: flex;
flex-direction: row;
justify-content: center;
flex-direction: column;
flex: 1 1 30%;
min-height: 100vh;
min-height: 100dvh;
gap: 20px;
}
.contact-us-container p {
margin: 0 0 20px 0;
}
.contact-title {
width: fit-content;
}
.contact-title span {
color: var(--primary-color);
}
h4.contact-info-type {
color: var(--primary-color);
margin: 0;
}
.contact-info-container {
display: flex;
flex-direction: column;
gap: 50px;
}
.contact-info-item a {
color: var(--text-color);
text-decoration: none;
margin-top: 5px;
display: inline-block;
font-size: clamp(1.25rem, 2.5vw, 2rem);
font-family: 'IBM Plex Mono', monospace;
font-weight: bold;
}
.contact-image {
flex: 0 0 clamp(240px, 24vw, 400px);
width: clamp(240px, 24vw, 400px);
aspect-ratio: 500 / 657;
background-image: url("/images/contacts/hero-lighttheme.svg");
background-position: center;
background-repeat: no-repeat;
background-size: contain;
align-self: center;
}
:root[data-theme="dark"] .contact-image {
background-image: url("/images/contacts/hero-blacktheme.svg");
}
.contact-button {
margin-top: 50px;
}
.contact-button:hover {
background-color: var(--background-opaque);
color: var(--surface);
}
.behind-cima {
display: flex;
flex-direction: column;
gap: 50px;
}
.profiles-container {
display: flex;
gap: clamp(20px, 3vw, 40px);
width: 100%;
}
.behind-cima-profile {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1 1 0;
}
.profile-role {
display: flex;
align-items: center;
}
.profile-role span {
margin: 0 1rem 0 0;
width: 5rem;;
}
a.personal {
font-size: clamp(0.9rem, 2vw, 1.25rem);
}
.behind-cima-profile p {
margin: 0 0 50px;
}
.contact-info-container-personal {
display: flex;
flex-direction: column;
gap: 30px;
}
@media (max-width: 1280px) {
}
@media (max-width: 1024px) {
}
@media (max-width: 900px) {
.contact-us {
position: relative;
flex-direction: column;
}
.contact-us-container {
flex: 1 1 auto;
}
.contact-image {
position: absolute;
top: 50%;
left: 80%;
transform: translate(-50%, -50%);
flex: none;
width: clamp(120px, 30vw, 400px);
z-index: 1;
}
.profiles-container {
flex-direction: column;
gap: 8rem;
}
.behind-cima-profile {
flex: 1 1 auto;
}
}
@media (max-width: 600px) {
.contact-image {
top: 80%;
left: 80%;
transform: translate(-50%, -50%);
width: clamp(120px, 15vw, 180px);
}
}
+25 -17
View File
@@ -1,16 +1,12 @@
.error-page {
min-height: 100vh;
min-height: 100dvh;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.error-page h1 {
margin: 0;
font-size: 3rem;
}
.error-content {
display: flex;
flex-direction: column;
@@ -41,6 +37,7 @@
flex-direction: column;
align-items: flex-start;
line-height: 0.9;
transform: translateX(1rem);
}
.error-page .title-top {
@@ -52,15 +49,6 @@
position: relative;
}
.divider {
display: block;
position: relative;
width: 50%;
height: 5px;
background-color: var(--primary-color);
margin-top: 8px;
}
.error-message,
.error-solution {
text-align: center;
@@ -79,12 +67,32 @@
}
.error-code {
font-size: 4rem;
font-size: clamp(1rem, 5vw, 4rem);
line-height: 0.4;
}
.error-text {
font-size: 15rem !important;
font-size: clamp(5rem, 20vw, 15rem) !important;
opacity: 0.2;
line-height: 0.8;
}
}
/* ============================================================
MEDIA QUERIES
============================================================ */
@media (max-width: 1280px) {}
@media (max-width: 1024px) {
}
@media (max-width: 900px) {
.error-content {
margin: 0 var(--lateral-margin);
}
}
@media (max-width: 480px) {}
@media (max-width: 360px) {}
+64 -15
View File
@@ -5,7 +5,14 @@
--muted-color: #6b7280;
--surface: #ffffff;
--background: rgba(246, 249, 255, 0.5);
--background-opaque: rgba(0, 0, 0, 0.2);
--background-opaque: #00000033;
--background-header: rgba(249, 249, 249, 0.5);
--lateral-margin: 12rem;
}
/* Fix mobile overscroll area color (rubber-band / bounce effect) */
html {
background-color: #f6f9ff;
}
/* Dark theme overrides (toggle by setting attribute on <html>) */
@@ -16,6 +23,15 @@
--surface: #000;
--background: rgba(0, 0, 0, 0.8);
--background-opaque: rgba(255, 255, 255, 0.2);
--background-header: rgba(51, 51, 51, 0.5);
background-color: #000;
}
@media (max-width: 900px) {
:root {
--navbar-height: 110px;
--lateral-margin: 1.5rem;
}
}
* {
@@ -29,7 +45,7 @@ body {
font-family: Helvetica, 'Segoe UI';
color: var(--text-color);
background-color: var(--surface);
background-image: url('/images/background/paper.png');
background-image: linear-gradient(var(--background), var(--background)), url('/images/background/paper.png');
background-repeat: repeat;
position: relative;
}
@@ -37,19 +53,15 @@ body {
html,
body,
#app {
height: 100%;
position: relative;
z-index: 1;
}
/* Background overlay layer that follows theme color */
body::before {
content: "";
position: fixed;
inset: 0;
background-color: var(--background);
pointer-events: none;
z-index: 0;
main {
min-height: 100vh;
min-height: 100dvh;
display: flow-root;
}
/* Reusable theme asset helpers:
@@ -84,6 +96,8 @@ html[data-theme='dark'] {
}
h1 {
margin: 0;
font-size: 3rem;
font-family: 'IBM Plex Mono', monospace;
}
@@ -107,7 +121,42 @@ p, a {
transition: background-color 0.2s ease;
}
/* Usage: add data attribute with SVG path
Example: <div class="svg-mask" style="--mask-url: url('/path/to/icon.svg'); width: 40px; height: 40px;"></div>
Then use inline style: -webkit-mask-image: var(--mask-url); mask-image: var(--mask-url);
OR use a specific class that sets the mask-image */
.divider {
display: block;
position: relative;
width: 50%;
height: 5px;
background-color: var(--primary-color);
margin-top: 8px;
}
/* ============================================================
MEDIA QUERIES
============================================================ */
@media (max-width: 1280px) {
:root {
--lateral-margin: 8rem;
}
}
@media (max-width: 1024px) {
:root {
--lateral-margin: 5rem;
}
}
@media (max-width: 900px) {
:root {
--lateral-margin: 1.5rem;
}
}
@media (max-width: 480px) {
}
@media (max-width: 360px) {
}
+7
View File
@@ -0,0 +1,7 @@
.index-page {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
text-decoration: underline;
}