Galériák és képek
Modern galéria
Élő előnézet – a komponens teljes méretben.
Élő előnézet
HTML · CSS · JS
<section class="architecture-gallery" id="architecture-gallery">
<div class="ag-heading">
<div>
<span class="ag-eyebrow">VÁLOGATOTT ALKOTÁSOK / 001</span>
<h2>Vizuális <em>történetek</em></h2>
</div>
<p class="ag-intro">
Minden épület egy gondolat. Minden részlet egy új nézőpont.
</p>
</div>
<div class="ag-toolbar">
<div class="ag-filters" role="group" aria-label="Galéria szűrése">
<button class="ag-filter active" data-filter="all">Összes kép</button>
<button class="ag-filter" data-filter="modern">Modern</button>
<button class="ag-filter" data-filter="details">Részletek</button>
<button class="ag-filter" data-filter="spaces">Terek</button>
</div>
<div class="ag-total">
<span class="ag-count">04</span>
<span>ALKOTÁS</span>
</div>
</div>
<div class="ag-grid">
<article class="ag-card ag-large" data-category="modern"
data-title="A jövő vonalai"
data-description="Modern építészet és letisztult geometria.">
<button class="ag-image-button" aria-label="A jövő vonalai – kép nagyítása">
<img src="https://images.unsplash.com/photo-1487958449943-2429e8be8625?auto=format&fit=crop&w=1400&q=85"
alt="Modern épület letisztult geometriai formákkal" loading="lazy">
<span class="ag-overlay"></span>
<span class="ag-open">↗</span>
<span class="ag-index">01 / 04</span>
</button>
<div class="ag-caption">
<div>
<span class="ag-category">MODERN ÉPÍTÉSZET</span>
<h3>A jövő vonalai</h3>
</div>
<span class="ag-arrow">↗</span>
</div>
</article>
<article class="ag-card ag-tall" data-category="details"
data-title="A részletek nyelve"
data-description="Formák, arányok és apró építészeti felfedezések.">
<button class="ag-image-button" aria-label="A részletek nyelve – kép nagyítása">
<img src="https://images.unsplash.com/photo-1511818966892-d7d671e672a2?auto=format&fit=crop&w=1000&q=85"
alt="Különleges építészeti forma és homlokzat" loading="lazy">
<span class="ag-overlay"></span>
<span class="ag-open">↗</span>
<span class="ag-index">02 / 04</span>
</button>
<div class="ag-caption">
<div>
<span class="ag-category">ÉPÍTÉSZETI RÉSZLETEK</span>
<h3>A részletek nyelve</h3>
</div>
<span class="ag-arrow">↗</span>
</div>
</article>
<article class="ag-card ag-wide" data-category="spaces"
data-title="Tér és arány"
data-description="Az épített környezet és a geometria találkozása.">
<button class="ag-image-button" aria-label="Tér és arány – kép nagyítása">
<img src="https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?auto=format&fit=crop&w=1400&q=85"
alt="Világos, modern belső tér" loading="lazy">
<span class="ag-overlay"></span>
<span class="ag-open">↗</span>
<span class="ag-index">03 / 04</span>
</button>
<div class="ag-caption">
<div>
<span class="ag-category">TÉR ÉS GEOMETRIA</span>
<h3>Tér és arány</h3>
</div>
<span class="ag-arrow">↗</span>
</div>
</article>
<article class="ag-card ag-medium" data-category="modern"
data-title="Időtlen formák"
data-description="Az építészet karaktere a formák találkozásában.">
<button class="ag-image-button" aria-label="Időtlen formák – kép nagyítása">
<img src="https://images.unsplash.com/photo-1518005020951-eccb494ad742?auto=format&fit=crop&w=1400&q=85"
alt="Modern építészeti kompozíció" loading="lazy">
<span class="ag-overlay"></span>
<span class="ag-open">↗</span>
<span class="ag-index">04 / 04</span>
</button>
<div class="ag-caption">
<div>
<span class="ag-category">FORMA ÉS KARAKTER</span>
<h3>Időtlen formák</h3>
</div>
<span class="ag-arrow">↗</span>
</div>
</article>
</div>
<div class="ag-end" aria-hidden="true">
<span></span><b>A.</b><span></span>
</div>
<div class="ag-lightbox" role="dialog" aria-modal="true"
aria-label="Képnézegető" aria-hidden="true">
<button class="ag-close" aria-label="Bezárás">×</button>
<button class="ag-nav ag-prev" aria-label="Előző kép">←</button>
<div class="ag-lightbox-content">
<div class="ag-lightbox-image">
<img src="" alt="">
</div>
<div class="ag-lightbox-info">
<span class="ag-lightbox-number">01 / 04</span>
<div>
<h3></h3>
<p></p>
</div>
</div>
</div>
<button class="ag-nav ag-next" aria-label="Következő kép">→</button>
</div>
</section>
/* ==========================================
ARCHITECTURE GALLERY
Önálló, névterezett galéria
========================================== */
.architecture-gallery {
--ag-bg: #171815;
--ag-gold: #c4a46a;
--ag-gold-light: #e3c995;
--ag-text: #f1eee7;
--ag-muted: #929087;
--ag-line: rgba(196, 164, 106, .25);
--ag-serif: "Cormorant Garamond", Georgia, serif;
--ag-sans: "Manrope", Arial, sans-serif;
position: relative;
padding: 100px 7.1%;
background: var(--ag-bg);
color: var(--ag-text);
font-family: var(--ag-sans);
font-size: 14px;
line-height: 1.7;
overflow: hidden;
}
.architecture-gallery *,
.architecture-gallery *::before,
.architecture-gallery *::after {
box-sizing: border-box;
}
.architecture-gallery button {
color: inherit;
font: inherit;
}
.architecture-gallery button:focus-visible {
outline: 2px solid var(--ag-gold-light);
outline-offset: 4px;
}
.architecture-gallery .ag-heading {
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 35px;
margin-bottom: 48px;
}
.architecture-gallery .ag-eyebrow {
color: var(--ag-gold);
font-size: 9px;
font-weight: 600;
letter-spacing: 2.6px;
}
.architecture-gallery .ag-heading h2 {
margin: 13px 0 0;
font: 400 clamp(42px, 5.3vw, 70px)/1.05 var(--ag-serif);
letter-spacing: -1px;
}
.architecture-gallery .ag-heading h2 em {
color: var(--ag-gold);
font-weight: 400;
}
.architecture-gallery .ag-intro {
max-width: 235px;
margin: 0;
padding-bottom: 6px;
color: var(--ag-muted);
font-size: 11px;
line-height: 2;
}
.architecture-gallery .ag-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 20px;
padding: 15px 0;
margin-bottom: 28px;
border-top: 1px solid var(--ag-line);
border-bottom: 1px solid var(--ag-line);
}
.architecture-gallery .ag-filters {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.architecture-gallery .ag-filter {
padding: 9px 15px;
border: 1px solid transparent;
background: transparent;
color: #96948a;
cursor: pointer;
font-size: 10px;
transition: color .25s, border-color .25s, background .25s;
}
.architecture-gallery .ag-filter:hover,
.architecture-gallery .ag-filter.active {
border-color: var(--ag-line);
background: rgba(196, 164, 106, .07);
color: var(--ag-gold-light);
}
.architecture-gallery .ag-total {
display: flex;
align-items: baseline;
gap: 9px;
white-space: nowrap;
color: var(--ag-muted);
font-size: 8px;
letter-spacing: 1.5px;
}
.architecture-gallery .ag-count {
color: var(--ag-gold);
font: 400 27px/1 var(--ag-serif);
letter-spacing: 0;
}
/* Aszimmetrikus képrács */
.architecture-gallery .ag-grid {
display: grid;
grid-template-columns: 1.15fr .85fr;
grid-template-rows: 290px 290px;
gap: 48px 28px;
align-items: start;
}
.architecture-gallery .ag-card {
min-width: 0;
animation: agCardIn .65s cubic-bezier(.2, .75, .25, 1) both;
}
.architecture-gallery .ag-card.ag-hidden {
display: none;
}
.architecture-gallery .ag-large {
grid-column: 1;
grid-row: 1;
}
.architecture-gallery .ag-tall {
grid-column: 2;
grid-row: 1 / span 2;
margin-top: 65px;
}
.architecture-gallery .ag-wide {
grid-column: 1;
grid-row: 2;
}
.architecture-gallery .ag-medium {
grid-column: 1 / -1;
width: 64%;
margin: 35px auto 0;
}
.architecture-gallery .ag-image-button {
position: relative;
display: block;
width: 100%;
aspect-ratio: 1.65 / 1;
padding: 0;
overflow: hidden;
border: 0;
background: #252620;
cursor: zoom-in;
text-align: left;
}
.architecture-gallery .ag-tall .ag-image-button {
aspect-ratio: 3 / 4.2;
}
.architecture-gallery .ag-medium .ag-image-button {
aspect-ratio: 1.9 / 1;
}
.architecture-gallery .ag-image-button img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
filter: saturate(.72) brightness(.85);
transition: transform .9s cubic-bezier(.2, .75, .25, 1),
filter .6s;
}
.architecture-gallery .ag-card:hover .ag-image-button img {
transform: scale(1.055);
filter: saturate(.95) brightness(.97);
}
.architecture-gallery .ag-overlay {
position: absolute;
inset: 0;
background: linear-gradient(
180deg,
transparent 45%,
rgba(12, 13, 11, .55) 100%
);
pointer-events: none;
transition: background .4s;
}
.architecture-gallery .ag-card:hover .ag-overlay {
background: linear-gradient(
180deg,
rgba(12, 13, 11, .03),
rgba(12, 13, 11, .65)
);
}
.architecture-gallery .ag-open {
position: absolute;
top: 18px;
right: 18px;
display: grid;
place-items: center;
width: 43px;
height: 43px;
border: 1px solid rgba(255, 255, 255, .5);
border-radius: 50%;
color: #fff;
font-size: 18px;
opacity: 0;
transform: translate(-5px, 5px);
transition: opacity .35s, transform .35s, background .35s;
}
.architecture-gallery .ag-card:hover .ag-open,
.architecture-gallery .ag-image-button:focus-visible .ag-open {
opacity: 1;
transform: translate(0, 0);
background: rgba(196, 164, 106, .25);
}
.architecture-gallery .ag-index {
position: absolute;
bottom: 17px;
left: 20px;
color: rgba(255, 255, 255, .9);
font-size: 9px;
letter-spacing: 2px;
}
.architecture-gallery .ag-caption {
display: flex;
align-items: center;
justify-content: space-between;
gap: 15px;
padding-top: 19px;
}
.architecture-gallery .ag-category {
display: block;
margin-bottom: 5px;
color: var(--ag-gold);
font-size: 8px;
font-weight: 600;
letter-spacing: 2px;
}
.architecture-gallery .ag-caption h3 {
margin: 0;
color: var(--ag-text);
font: 400 28px/1.2 var(--ag-serif);
transition: color .3s;
}
.architecture-gallery .ag-card:hover .ag-caption h3 {
color: var(--ag-gold-light);
}
.architecture-gallery .ag-arrow {
display: grid;
place-items: center;
flex-shrink: 0;
width: 34px;
height: 34px;
border: 1px solid var(--ag-line);
border-radius: 50%;
color: var(--ag-gold);
transition: transform .3s, background .3s;
}
.architecture-gallery .ag-card:hover .ag-arrow {
transform: translate(3px, -3px);
background: rgba(196, 164, 106, .1);
}
.architecture-gallery .ag-end {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
margin-top: 90px;
}
.architecture-gallery .ag-end span {
width: 80px;
height: 1px;
background: var(--ag-line);
}
.architecture-gallery .ag-end b {
color: var(--ag-gold);
font: 500 25px/1 var(--ag-serif);
}
/* Teljes képernyős képnézegető */
.architecture-gallery .ag-lightbox {
position: fixed;
z-index: 99999;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
gap: 30px;
padding: 75px 7%;
background: rgba(9, 10, 8, .97);
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity .35s, visibility .35s;
}
.architecture-gallery .ag-lightbox.ag-opened {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.architecture-gallery .ag-close {
position: absolute;
top: 25px;
right: 35px;
z-index: 3;
width: 46px;
height: 46px;
border: 1px solid var(--ag-line);
background: transparent;
color: var(--ag-gold-light);
font-size: 30px;
line-height: 1;
cursor: pointer;
transition: background .3s, transform .3s;
}
.architecture-gallery .ag-close:hover {
background: rgba(196, 164, 106, .15);
transform: rotate(90deg);
}
.architecture-gallery .ag-lightbox-content {
width: min(100%, 1050px);
min-width: 0;
}
.architecture-gallery .ag-lightbox-image {
height: min(68vh, 720px);
display: flex;
align-items: center;
justify-content: center;
}
.architecture-gallery .ag-lightbox-image img {
display: block;
max-width: 100%;
max-height: 100%;
object-fit: contain;
box-shadow: 0 20px 80px rgba(0, 0, 0, .4);
}
.architecture-gallery .ag-lightbox-info {
display: flex;
align-items: center;
gap: 28px;
padding-top: 24px;
margin-top: 22px;
border-top: 1px solid var(--ag-line);
}
.architecture-gallery .ag-lightbox-number {
flex-shrink: 0;
color: var(--ag-gold);
font: 400 27px/1 var(--ag-serif);
}
.architecture-gallery .ag-lightbox-info h3 {
margin: 0;
font: 400 29px/1.2 var(--ag-serif);
}
.architecture-gallery .ag-lightbox-info p {
margin: 4px 0 0;
color: var(--ag-muted);
font-size: 11px;
}
.architecture-gallery .ag-nav {
display: grid;
place-items: center;
flex-shrink: 0;
width: 47px;
height: 47px;
border: 1px solid var(--ag-line);
border-radius: 50%;
background: transparent;
color: var(--ag-gold-light);
font-size: 20px;
cursor: pointer;
transition: background .3s, color .3s;
}
.architecture-gallery .ag-nav:hover {
background: var(--ag-gold);
color: #111210;
}
@keyframes agCardIn {
from {
opacity: 0;
transform: translateY(18px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Tablet */
@media (max-width: 1050px) {
.architecture-gallery {
padding-inline: 5%;
}
.architecture-gallery .ag-grid {
grid-template-rows: 245px 245px;
}
.architecture-gallery .ag-caption h3 {
font-size: 25px;
}
}
/* Mobil */
@media (max-width: 720px) {
.architecture-gallery {
padding: 70px 6%;
}
.architecture-gallery .ag-heading {
display: block;
margin-bottom: 32px;
}
.architecture-gallery .ag-heading h2 {
font-size: clamp(43px, 10vw, 60px);
}
.architecture-gallery .ag-intro {
margin-top: 15px;
max-width: 300px;
}
.architecture-gallery .ag-toolbar {
align-items: flex-start;
flex-direction: column;
gap: 12px;
padding-block: 14px;
}
.architecture-gallery .ag-filters {
gap: 5px;
}
.architecture-gallery .ag-filter {
padding: 8px 10px;
font-size: 9px;
}
.architecture-gallery .ag-grid {
grid-template-columns: 1fr;
grid-template-rows: auto;
gap: 36px;
}
.architecture-gallery .ag-large,
.architecture-gallery .ag-tall,
.architecture-gallery .ag-wide,
.architecture-gallery .ag-medium {
grid-column: 1;
grid-row: auto;
width: 100%;
margin: 0;
}
.architecture-gallery .ag-image-button,
.architecture-gallery .ag-tall .ag-image-button,
.architecture-gallery .ag-medium .ag-image-button {
aspect-ratio: 1.28 / 1;
}
.architecture-gallery .ag-tall .ag-image-button {
aspect-ratio: 1 / 1.13;
}
.architecture-gallery .ag-caption h3 {
font-size: 27px;
}
.architecture-gallery .ag-open {
top: 12px;
right: 12px;
width: 36px;
height: 36px;
opacity: 1;
transform: none;
font-size: 15px;
}
.architecture-gallery .ag-end {
margin-top: 60px;
}
.architecture-gallery .ag-lightbox {
gap: 10px;
padding: 65px 5% 35px;
}
.architecture-gallery .ag-close {
top: 13px;
right: 15px;
width: 38px;
height: 38px;
}
.architecture-gallery .ag-lightbox-content {
width: calc(100% - 70px);
}
.architecture-gallery .ag-lightbox-image {
height: 55vh;
}
.architecture-gallery .ag-nav {
width: 34px;
height: 34px;
font-size: 16px;
}
.architecture-gallery .ag-lightbox-info {
align-items: flex-start;
gap: 12px;
}
.architecture-gallery .ag-lightbox-number {
font-size: 21px;
}
.architecture-gallery .ag-lightbox-info h3 {
font-size: 24px;
}
.architecture-gallery .ag-lightbox-info p {
font-size: 10px;
}
}
@media (prefers-reduced-motion: reduce) {
.architecture-gallery *,
.architecture-gallery *::before,
.architecture-gallery *::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
transition-duration: .01ms !important;
}
}
(() => {
"use strict";
const galleries = document.querySelectorAll(".architecture-gallery");
galleries.forEach(gallery => {
if (gallery.dataset.agInitialized === "true") return;
gallery.dataset.agInitialized = "true";
const cards = Array.from(gallery.querySelectorAll(".ag-card"));
const filters = Array.from(gallery.querySelectorAll(".ag-filter"));
const count = gallery.querySelector(".ag-count");
const lightbox = gallery.querySelector(".ag-lightbox");
const lightboxImage = lightbox.querySelector(".ag-lightbox-image img");
const lightboxNumber = lightbox.querySelector(".ag-lightbox-number");
const lightboxTitle = lightbox.querySelector(".ag-lightbox-info h3");
const lightboxDescription = lightbox.querySelector(".ag-lightbox-info p");
const closeButton = gallery.querySelector(".ag-close");
const prevButton = gallery.querySelector(".ag-prev");
const nextButton = gallery.querySelector(".ag-next");
let visibleCards = [];
let currentIndex = 0;
let previousFocus = null;
let previousBodyOverflow = "";
const pad = number => String(number).padStart(2, "0");
function updateCards(category = "all") {
visibleCards = cards.filter(card => {
const matches =
category === "all" ||
card.dataset.category === category;
card.classList.toggle("ag-hidden", !matches);
return matches;
});
count.textContent = pad(visibleCards.length);
visibleCards.forEach((card, index) => {
card.style.animationDelay = `${index * 90}ms`;
const indexLabel = card.querySelector(".ag-index");
if (indexLabel) {
indexLabel.textContent =
`${pad(index + 1)} / ${pad(visibleCards.length)}`;
}
});
}
// Kategóriaszűrés
filters.forEach(button => {
button.addEventListener("click", () => {
filters.forEach(filter => {
filter.classList.remove("active");
});
button.classList.add("active");
updateCards(button.dataset.filter);
});
});
// A kártya saját képét használjuk, így a nagyított
// változat útvonala is ugyanaz marad.
function getCardData(card) {
const image = card.querySelector(".ag-image-button img");
return {
src: image.currentSrc || image.src,
alt: image.alt || card.dataset.title || "Építészeti kép",
title: card.dataset.title || "Építészeti alkotás",
description: card.dataset.description || ""
};
}
function renderImage() {
const card = visibleCards[currentIndex];
if (!card) return;
const data = getCardData(card);
lightboxImage.src = data.src;
lightboxImage.alt = data.alt;
lightboxTitle.textContent = data.title;
lightboxDescription.textContent = data.description;
lightboxNumber.textContent =
`${pad(currentIndex + 1)} / ${pad(visibleCards.length)}`;
}
function openLightbox(index) {
if (!visibleCards.length) return;
currentIndex =
(index + visibleCards.length) % visibleCards.length;
previousFocus = document.activeElement;
previousBodyOverflow = document.body.style.overflow;
renderImage();
lightbox.classList.add("ag-opened");
lightbox.setAttribute("aria-hidden", "false");
document.body.style.overflow = "hidden";
closeButton.focus();
}
function closeLightbox() {
if (!lightbox.classList.contains("ag-opened")) return;
lightbox.classList.remove("ag-opened");
lightbox.setAttribute("aria-hidden", "true");
document.body.style.overflow = previousBodyOverflow;
lightboxImage.removeAttribute("src");
lightboxImage.alt = "";
if (previousFocus && typeof previousFocus.focus === "function") {
previousFocus.focus();
}
}
function moveLightbox(direction) {
if (!visibleCards.length) return;
currentIndex =
(currentIndex + direction + visibleCards.length) %
visibleCards.length;
renderImage();
}
// Kép megnyitása
cards.forEach(card => {
const button = card.querySelector(".ag-image-button");
button.addEventListener("click", () => {
const index = visibleCards.indexOf(card);
if (index !== -1) {
openLightbox(index);
}
});
});
closeButton.addEventListener("click", closeLightbox);
prevButton.addEventListener("click", () => moveLightbox(-1));
nextButton.addEventListener("click", () => moveLightbox(1));
// Kattintás a sötét háttérre: bezárás
lightbox.addEventListener("click", event => {
if (event.target === lightbox) {
closeLightbox();
}
});
// Billentyűzetes kezelés
document.addEventListener("keydown", event => {
if (!lightbox.classList.contains("ag-opened")) return;
if (event.key === "Escape") {
closeLightbox();
} else if (event.key === "ArrowLeft") {
event.preventDefault();
moveLightbox(-1);
} else if (event.key === "ArrowRight") {
event.preventDefault();
moveLightbox(1);
} else if (event.key === "Tab") {
const focusable = [
closeButton,
prevButton,
nextButton
];
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (
event.shiftKey &&
document.activeElement === first
) {
event.preventDefault();
last.focus();
} else if (
!event.shiftKey &&
document.activeElement === last
) {
event.preventDefault();
first.focus();
}
}
});
// Ha valamelyik távoli kép nem érhető el,
// jelezzük a kártyán és a nagyított képnél is.
gallery.querySelectorAll(".ag-image-button img").forEach(image => {
image.addEventListener("error", () => {
image.alt = "A kép nem tölthető be.";
});
});
updateCards();
});
})();