Загрузка...

Адаптивная инфо-панель с геолокацией, часами GMT и живым временем. Идеально для хедера, профиля или дашборда.
<div class="flex gap-4 flex-wrap mb-8 items-center justify-between w-full max-w-3xl">
<div class="flex items-center gap-3 text-sm">
<div class="flex items-center gap-2 text-neutral-700">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="map-pin" class="lucide lucide-map-pin h-4 w-4 text-neutral-400"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" class=""></path><circle cx="12" cy="10" r="3" class=""></circle></svg>
<span class="font-geist">Located in <span class="font-medium text-neutral-800 font-geist">San Francisco</span> • Available Worldwide</span>
</div>
</div>
<div class="hidden sm:flex items-center gap-2 text-sm">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="globe" class="lucide lucide-globe h-4 w-4 text-neutral-400"><circle cx="12" cy="12" r="10" class=""></circle><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" class=""></path><path d="M2 12h20" class=""></path></svg>
<span class="text-neutral-700 font-geist">Local Time <span id="tz-offset" class="text-neutral-500" data-locked="true">(PST)</span></span>
</div>
<div class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="clock" class="lucide lucide-clock h-4 w-4 text-neutral-400 hidden sm:block"><path d="M12 6v6l4 2" class=""></path><circle cx="12" cy="12" r="10" class=""></circle></svg>
<span id="tz-time" class="font-geist-mono text-xl sm:text-2xl tracking-tight text-neutral-900">20:14:53</span>
</div>
<script>
(function () {
const tz = 'America/Los_Angeles';
const timeEl = document.getElementById('tz-time');
const offsetEl = document.getElementById('tz-offset');
function getGMTOffsetLabel(date) {
try {
const fmt = new Intl.DateTimeFormat('en', { timeZone: tz, timeZoneName: 'shortOffset', hour: '2-digit' });
const parts = fmt.formatToParts(date);
const tzPart = parts.find(p => p.type === 'timeZoneName');
if (tzPart && /GMT[+-]\d+/.test(tzPart.value)) return '(' + tzPart.value + ')';
} catch (e) {}
try {
const fmt = new Intl.DateTimeFormat('en', { timeZone: tz, timeZoneName: 'short', hour: '2-digit' });
const parts = fmt.formatToParts(date);
const tzPart = parts.find(p => p.type === 'timeZoneName');
if (tzPart) {
const m = tzPart.value.match(/GMT[+-]\d+/);
if (m) return '(' + m[0] + ')';
}
} catch (e) {}
// Fallback: approximate via offset minutes
const dtf = new Intl.DateTimeFormat('en-US', { timeZone: tz, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });
const parts = dtf.formatToParts(date);
const y = parts.find(p => p.type === 'year').value;
const M = parts.find(p => p.type === 'month').value;
const d = parts.find(p => p.type === 'day').value;
const h = parts.find(p => p.type === 'hour').value;
const m = parts.find(p => p.type === 'minute').value;
const s = parts.find(p => p.type === 'second').value;
const localAsUTC = new Date(`${y}-${M}-${d}T${h}:${m}:${s}Z`);
const minutes = Math.round((localAsUTC - date) / 60000);
const sign = minutes >= 0 ? '+' : '-';
const hrs = String(Math.floor(Math.abs(minutes) / 60));
return `(GMT${sign}${hrs})`;
}
function tick() {
const now = new Date();
const fmt = new Intl.DateTimeFormat('en-GB', { timeZone: tz, hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
timeEl.textContent = fmt.format(now);
if (offsetEl && !offsetEl.dataset.locked) {
offsetEl.textContent = getGMTOffsetLabel(now);
offsetEl.dataset.locked = 'true';
}
setTimeout(tick, 1000);
}
tick();
try { window.lucide && window.lucide.createIcons && window.lucide.createIcons(); } catch (e) {}
})();
</script>
</div>