Refine landing layout: calendar actions and editorial gift section
This commit is contained in:
82
index.html
82
index.html
@ -17,23 +17,8 @@
|
||||
<div class="hero__overlay"></div>
|
||||
|
||||
<div class="hero__content container">
|
||||
<!-- <p class="eyebrow">Save the date</p> -->
|
||||
<h1 class="hero__title">Hey, ci sono i 40 anni del Gianca!</h1>
|
||||
<div class="hero__underline" aria-hidden="true"></div>
|
||||
<br />
|
||||
<div class="event-tag__date">Save the date</div>
|
||||
|
||||
<!-- <div class="event-tag event-tag--icon" aria-label="Informazioni evento">
|
||||
<div class="event-tag__icon">
|
||||
<span class="party-popper" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="event-tag__content">
|
||||
<div class="event-tag__date">25 aprile 2026</div>
|
||||
<div class="event-tag__place">Lost Paradise Beach Club</div>
|
||||
<div class="event-tag__divider"></div>
|
||||
<div class="event-tag__meta">Bacoli • ore 12.00</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -52,9 +37,26 @@
|
||||
</div>
|
||||
|
||||
<div class="details-card">
|
||||
<div class="details-card__item">
|
||||
<div class="details-card__item details-card__item--calendar">
|
||||
<span class="details-card__label">Quando</span>
|
||||
<strong>Sabato 25 aprile 2026</strong>
|
||||
<div class="calendar-tools">
|
||||
<a
|
||||
class="calendar-tools__action"
|
||||
id="google-calendar-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Google Calendar
|
||||
</a>
|
||||
<button
|
||||
class="calendar-tools__action calendar-tools__action--button"
|
||||
type="button"
|
||||
id="ical-download"
|
||||
>
|
||||
iCal / Apple Calendar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="details-card__item">
|
||||
<span class="details-card__label">Dove</span>
|
||||
@ -69,6 +71,52 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section gift" id="regalo">
|
||||
<div class="container gift__shell">
|
||||
<div class="gift__layout" aria-labelledby="gift-title">
|
||||
<h3 class="gift__title" id="gift-title">Dettagli regalo</h3>
|
||||
<div class="gift__col gift__col--left">
|
||||
<p class="section-kicker gift__kicker">Regalo / quota viaggio</p>
|
||||
<h2 class="gift__heading">Un piccolo gesto, con il sorriso.</h2>
|
||||
<p class="gift__lead">
|
||||
Se ti fa piacere contribuire al regalo o alla quota viaggio, qui trovi i riferimenti.
|
||||
</p>
|
||||
<p class="gift__note" id="gift-note">Nota facoltativa: un pensiero semplice è più che sufficiente.</p>
|
||||
</div>
|
||||
|
||||
<div class="gift__col gift__col--right">
|
||||
<span class="gift__accent" aria-hidden="true"></span>
|
||||
<!-- Dati contributo: modifica rapidamente intestatario, IBAN, causale e nota in script.js (costante GIFT_DETAILS). -->
|
||||
<dl class="gift__details">
|
||||
<div class="gift__row">
|
||||
<dt>Intestatario</dt>
|
||||
<dd id="gift-holder">Giancarlo ...</dd>
|
||||
</div>
|
||||
<div class="gift__row gift__row--iban">
|
||||
<dt>IBAN</dt>
|
||||
<dd id="gift-iban">IT00X0000000000000000000000</dd>
|
||||
<div class="gift__iban-actions">
|
||||
<button
|
||||
class="button button--secondary button--compact gift__copy"
|
||||
type="button"
|
||||
id="copy-iban"
|
||||
data-copy-target="gift-iban"
|
||||
>
|
||||
Copia IBAN
|
||||
</button>
|
||||
<p class="gift__feedback" id="iban-feedback" aria-live="polite"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gift__row">
|
||||
<dt>Causale consigliata</dt>
|
||||
<dd id="gift-causal">Regalo 40 anni Gianca</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section rsvp" id="rsvp">
|
||||
<div class="container rsvp__grid">
|
||||
<div class="rsvp__copy">
|
||||
@ -175,7 +223,7 @@
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container footer__inner">
|
||||
<p>Ci vediamo a Bacoli.</p>
|
||||
<p>Sviluppato con ♥ da bisco con Codex</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
138
script.js
138
script.js
@ -5,6 +5,33 @@ const attendanceInputs = Array.from(document.querySelectorAll('input[name="atten
|
||||
const successModal = document.getElementById('success-modal');
|
||||
const successOkButton = document.getElementById('success-ok');
|
||||
const closeModalNodes = Array.from(document.querySelectorAll('[data-close-modal]'));
|
||||
const googleCalendarLink = document.getElementById('google-calendar-link');
|
||||
const iCalDownloadButton = document.getElementById('ical-download');
|
||||
const copyIbanButton = document.getElementById('copy-iban');
|
||||
const ibanFeedback = document.getElementById('iban-feedback');
|
||||
const giftHolder = document.getElementById('gift-holder');
|
||||
const giftIban = document.getElementById('gift-iban');
|
||||
const giftCausal = document.getElementById('gift-causal');
|
||||
const giftNote = document.getElementById('gift-note');
|
||||
|
||||
// Modifica qui i dati del calendario evento.
|
||||
const EVENT_CALENDAR = {
|
||||
title: 'I 40 anni del Gianca',
|
||||
location: 'Lost Paradise Beach Club, Bacoli',
|
||||
description: 'Conferma la tua presenza per i 40 anni del Gianca.',
|
||||
date: '2026-04-25',
|
||||
startTime: '12:00',
|
||||
durationHours: 6,
|
||||
timezone: 'Europe/Rome'
|
||||
};
|
||||
|
||||
// Modifica qui i dati regalo / quota viaggio.
|
||||
const GIFT_DETAILS = {
|
||||
holder: 'Giancarlo ...',
|
||||
iban: 'IT00X0000000000000000000000',
|
||||
causal: 'Regalo 40 anni Gianca',
|
||||
note: 'Nota facoltativa: un pensiero semplice e sempre gradito.'
|
||||
};
|
||||
|
||||
function updateAttendanceFields() {
|
||||
const selected = document.querySelector('input[name="attendance"]:checked');
|
||||
@ -34,15 +61,126 @@ function closeSuccessModal() {
|
||||
document.body.classList.remove('modal-open');
|
||||
}
|
||||
|
||||
function pad(value) {
|
||||
return String(value).padStart(2, '0');
|
||||
}
|
||||
|
||||
function createFloatingDateTime(date, time, plusHours = 0) {
|
||||
const [year, month, day] = date.split('-').map(Number);
|
||||
const [hours, minutes] = time.split(':').map(Number);
|
||||
const normalizedDate = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0));
|
||||
normalizedDate.setUTCHours(normalizedDate.getUTCHours() + plusHours);
|
||||
|
||||
return `${normalizedDate.getUTCFullYear()}${pad(normalizedDate.getUTCMonth() + 1)}${pad(normalizedDate.getUTCDate())}T${pad(normalizedDate.getUTCHours())}${pad(normalizedDate.getUTCMinutes())}00`;
|
||||
}
|
||||
|
||||
function buildGoogleCalendarUrl(eventData) {
|
||||
const start = createFloatingDateTime(eventData.date, eventData.startTime, 0);
|
||||
const end = createFloatingDateTime(eventData.date, eventData.startTime, eventData.durationHours);
|
||||
const params = new URLSearchParams({
|
||||
action: 'TEMPLATE',
|
||||
text: eventData.title,
|
||||
details: eventData.description,
|
||||
location: eventData.location,
|
||||
ctz: eventData.timezone,
|
||||
dates: `${start}/${end}`
|
||||
});
|
||||
return `https://calendar.google.com/calendar/render?${params.toString()}`;
|
||||
}
|
||||
|
||||
function escapeIcsText(value) {
|
||||
return value
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/,/g, '\\,')
|
||||
.replace(/;/g, '\\;');
|
||||
}
|
||||
|
||||
function buildIcsContent(eventData) {
|
||||
const start = createFloatingDateTime(eventData.date, eventData.startTime, 0);
|
||||
const end = createFloatingDateTime(eventData.date, eventData.startTime, eventData.durationHours);
|
||||
const stamp = `${createFloatingDateTime(eventData.date, eventData.startTime, 0)}Z`;
|
||||
const uid = `gianca-40-${start}@gianca-event`;
|
||||
|
||||
return [
|
||||
'BEGIN:VCALENDAR',
|
||||
'VERSION:2.0',
|
||||
'PRODID:-//Gianca40//Event Landing//IT',
|
||||
'CALSCALE:GREGORIAN',
|
||||
'BEGIN:VEVENT',
|
||||
`UID:${uid}`,
|
||||
`DTSTAMP:${stamp}`,
|
||||
`DTSTART;TZID=${eventData.timezone}:${start}`,
|
||||
`DTEND;TZID=${eventData.timezone}:${end}`,
|
||||
`SUMMARY:${escapeIcsText(eventData.title)}`,
|
||||
`LOCATION:${escapeIcsText(eventData.location)}`,
|
||||
`DESCRIPTION:${escapeIcsText(eventData.description)}`,
|
||||
'END:VEVENT',
|
||||
'END:VCALENDAR'
|
||||
].join('\r\n');
|
||||
}
|
||||
|
||||
function triggerIcsDownload() {
|
||||
const content = buildIcsContent(EVENT_CALENDAR);
|
||||
const blob = new Blob([content], { type: 'text/calendar;charset=utf-8' });
|
||||
const downloadUrl = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadUrl;
|
||||
link.download = 'i-40-anni-del-gianca.ics';
|
||||
document.body.append(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(downloadUrl);
|
||||
}
|
||||
|
||||
function setIbanFeedback(message) {
|
||||
if (!ibanFeedback) return;
|
||||
ibanFeedback.textContent = message;
|
||||
}
|
||||
|
||||
async function copyIbanToClipboard() {
|
||||
if (!copyIbanButton) return;
|
||||
const ibanNodeId = copyIbanButton.dataset.copyTarget;
|
||||
const ibanText = document.getElementById(ibanNodeId || '')?.textContent?.trim();
|
||||
if (!ibanText) return;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(ibanText);
|
||||
setIbanFeedback('IBAN copiato negli appunti.');
|
||||
} catch (error) {
|
||||
const fallbackField = document.createElement('input');
|
||||
fallbackField.value = ibanText;
|
||||
document.body.append(fallbackField);
|
||||
fallbackField.select();
|
||||
const copied = document.execCommand('copy');
|
||||
fallbackField.remove();
|
||||
setIbanFeedback(copied ? 'IBAN copiato negli appunti.' : 'Copia non riuscita. Riprova.');
|
||||
}
|
||||
}
|
||||
|
||||
attendanceInputs.forEach((input) => input.addEventListener('change', updateAttendanceFields));
|
||||
closeModalNodes.forEach((node) => node.addEventListener('click', closeSuccessModal));
|
||||
successOkButton?.addEventListener('click', closeSuccessModal);
|
||||
iCalDownloadButton?.addEventListener('click', triggerIcsDownload);
|
||||
copyIbanButton?.addEventListener('click', async () => {
|
||||
await copyIbanToClipboard();
|
||||
window.clearTimeout(copyIbanButton.dataset.feedbackTimeoutId);
|
||||
const timeoutId = window.setTimeout(() => setIbanFeedback(''), 2500);
|
||||
copyIbanButton.dataset.feedbackTimeoutId = String(timeoutId);
|
||||
});
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape' && successModal?.classList.contains('success-modal--open')) {
|
||||
closeSuccessModal();
|
||||
}
|
||||
});
|
||||
updateAttendanceFields();
|
||||
if (googleCalendarLink) {
|
||||
googleCalendarLink.href = buildGoogleCalendarUrl(EVENT_CALENDAR);
|
||||
}
|
||||
if (giftHolder) giftHolder.textContent = GIFT_DETAILS.holder;
|
||||
if (giftIban) giftIban.textContent = GIFT_DETAILS.iban;
|
||||
if (giftCausal) giftCausal.textContent = GIFT_DETAILS.causal;
|
||||
if (giftNote) giftNote.textContent = GIFT_DETAILS.note;
|
||||
|
||||
function encode(data) {
|
||||
return new URLSearchParams(data).toString();
|
||||
|
||||
282
style.css
282
style.css
@ -286,6 +286,11 @@ img {
|
||||
border: 1px solid rgba(40, 89, 138, 0.08);
|
||||
}
|
||||
|
||||
.details-card__item--calendar {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
.details-card__item strong,
|
||||
.details-card__item span {
|
||||
display: block;
|
||||
@ -300,6 +305,42 @@ img {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.calendar-tools {
|
||||
margin-top: 0.3rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.calendar-tools__action {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 40px;
|
||||
width: auto;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(30, 87, 138, 0.28);
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
color: var(--sea-dark);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 700;
|
||||
padding: 0.42rem 0.82rem;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: background .18s ease, border-color .18s ease, transform .18s ease, box-shadow .18s ease;
|
||||
}
|
||||
|
||||
.calendar-tools__action:hover {
|
||||
background: rgba(30, 87, 138, 0.1);
|
||||
border-color: rgba(30, 87, 138, 0.5);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 16px rgba(21, 60, 96, 0.1);
|
||||
}
|
||||
|
||||
.calendar-tools__action--button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
padding: 1.8rem;
|
||||
}
|
||||
@ -419,6 +460,205 @@ textarea:focus {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.button--secondary {
|
||||
color: var(--sea-dark);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0%, rgba(241, 246, 252, 0.92) 100%);
|
||||
border: 1px solid rgba(30, 87, 138, 0.3);
|
||||
box-shadow: 0 10px 20px rgba(21, 60, 96, 0.12);
|
||||
}
|
||||
|
||||
.button--compact {
|
||||
min-height: 44px;
|
||||
padding: 0.55rem 1rem;
|
||||
font-size: 0.93rem;
|
||||
}
|
||||
|
||||
.gift {
|
||||
padding-top: 1.3rem;
|
||||
}
|
||||
|
||||
.gift__shell {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.gift__title {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.gift__layout {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
|
||||
column-gap: clamp(1.4rem, 3.2vw, 2.7rem);
|
||||
row-gap: 0;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.gift__layout::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -0.68rem;
|
||||
width: min(360px, 44%);
|
||||
height: 1px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, rgba(30, 87, 138, 0.38), rgba(30, 87, 138, 0.05));
|
||||
}
|
||||
|
||||
.gift__layout::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -0.4rem;
|
||||
width: min(190px, 26%);
|
||||
height: 1px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, rgba(30, 87, 138, 0.04), rgba(30, 87, 138, 0.22));
|
||||
}
|
||||
|
||||
.gift__col {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.gift__col--left {
|
||||
display: grid;
|
||||
gap: 0.55rem;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.gift__kicker {
|
||||
margin: 0 0 0.2rem;
|
||||
}
|
||||
|
||||
.gift__heading {
|
||||
max-width: 17ch;
|
||||
margin: 0;
|
||||
font-size: clamp(1.72rem, 3.25vw, 2.55rem);
|
||||
line-height: 1.06;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.gift__lead {
|
||||
margin: 0;
|
||||
color: var(--ink);
|
||||
max-width: 56ch;
|
||||
font-size: 0.99rem;
|
||||
line-height: 1.68;
|
||||
}
|
||||
|
||||
.gift__note {
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
line-height: 1.58;
|
||||
font-size: 0.92rem;
|
||||
max-width: 44ch;
|
||||
}
|
||||
|
||||
.gift__col--right {
|
||||
position: relative;
|
||||
align-self: start;
|
||||
padding-left: clamp(1rem, 1.9vw, 1.3rem);
|
||||
}
|
||||
|
||||
.gift__accent {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(180deg, rgba(30, 87, 138, 0.62), rgba(30, 87, 138, 0.16));
|
||||
}
|
||||
|
||||
.gift__accent::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: -3px;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(30, 87, 138, 0.24);
|
||||
}
|
||||
|
||||
.gift__details {
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 0.74rem;
|
||||
}
|
||||
|
||||
.gift__row {
|
||||
margin: 0;
|
||||
padding: 0 0 0.64rem;
|
||||
border-bottom: 1px solid rgba(30, 87, 138, 0.18);
|
||||
}
|
||||
|
||||
.gift__row:last-child {
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
.gift__row dt {
|
||||
color: var(--sea);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0.22rem;
|
||||
}
|
||||
|
||||
.gift__row dd {
|
||||
margin: 0;
|
||||
color: var(--ink);
|
||||
font-weight: 700;
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.gift__row--iban dd {
|
||||
font-size: clamp(1.15rem, 2.3vw, 1.42rem);
|
||||
letter-spacing: 0.038em;
|
||||
color: var(--sea-dark);
|
||||
margin-bottom: 0.38rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.gift__row--iban {
|
||||
padding-top: 0.04rem;
|
||||
padding-bottom: 0.82rem;
|
||||
border-bottom-color: rgba(30, 87, 138, 0.22);
|
||||
}
|
||||
|
||||
.gift__copy {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.gift__iban-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.55rem 0.75rem;
|
||||
}
|
||||
|
||||
.gift__feedback {
|
||||
min-height: 1.15rem;
|
||||
margin: 0;
|
||||
color: var(--sea-dark);
|
||||
font-size: 0.84rem;
|
||||
font-weight: 700;
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.form-status {
|
||||
min-height: 1.4rem;
|
||||
margin: 0;
|
||||
@ -482,6 +722,24 @@ textarea:focus {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.gift__layout {
|
||||
grid-template-columns: 1fr;
|
||||
row-gap: 0.88rem;
|
||||
}
|
||||
|
||||
.gift__layout::before,
|
||||
.gift__layout::after {
|
||||
width: min(320px, 100%);
|
||||
}
|
||||
|
||||
.gift__col--right {
|
||||
padding-left: 1.15rem;
|
||||
}
|
||||
|
||||
.gift__heading {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 4rem 0;
|
||||
}
|
||||
@ -538,7 +796,8 @@ textarea:focus {
|
||||
}
|
||||
|
||||
.form-card,
|
||||
.details-card {
|
||||
.details-card,
|
||||
.gift__layout {
|
||||
padding: 1.2rem;
|
||||
}
|
||||
|
||||
@ -550,6 +809,27 @@ textarea:focus {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.calendar-tools__action {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gift__layout {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gift__copy {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gift__iban-actions {
|
||||
align-items: stretch;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.gift__feedback {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user