1st commit

This commit is contained in:
2025-08-06 11:16:18 +00:00
commit b9e3467c01
49 changed files with 2345 additions and 0 deletions

45
src/public/js/lazy.js Normal file
View File

@ -0,0 +1,45 @@
// js for Lumeex
// https://git.djeex.fr/Djeex/lumeex
window.addEventListener("DOMContentLoaded", () => {
// Lazy loading
const lazyImages = document.querySelectorAll('img.lazyload');
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
console.log("Lazy-loading image:", img.dataset.src);
img.src = img.dataset.src;
img.onload = () => {
img.classList.add("loaded");
};
obs.unobserve(img);
}
});
}, {
rootMargin: "0px 0px 300px 0px",
threshold: 0.01
});
lazyImages.forEach(img => observer.observe(img));
// Fade-in effect for loaded images (even outside lazy ones)
const fadeImages = document.querySelectorAll("img.fade-in-img");
fadeImages.forEach(img => {
const onLoad = () => {
console.log("Image loaded (fade-in):", img.src);
img.classList.add("loaded");
};
if (img.complete && img.naturalHeight !== 0) {
onLoad(); // already loaded
} else {
img.addEventListener("load", onLoad, { once: true });
img.addEventListener("error", () => {
console.warn("Image failed to load:", img.dataset.src || img.src);
});
}
});
});

152
src/public/js/lumeex.js Normal file
View File

@ -0,0 +1,152 @@
// js for Lumeex
// https://git.djeex.fr/Djeex/lumeex
// Fade in effect for elements with class 'appear'
const setupIntersectionObserver = () => {
const items = document.querySelectorAll('.appear');
const io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
entry.target.classList.toggle('inview', entry.isIntersecting);
});
});
items.forEach((item) => io.observe(item));
};
// Loader fade out after page load
const setupLoader = () => {
window.addEventListener('load', () => {
setTimeout(() => {
const loader = document.querySelector('.page-loader');
if (loader) {
loader.classList.add('hidden');
}
}, 50);
});
};
// Gallery randomizer to shuffle gallery sections on page load
const shuffleGallery = () => {
const gallery = document.querySelector('.gallery');
if (!gallery) return;
const sections = Array.from(gallery.querySelectorAll('.section'));
while (sections.length) {
const randomIndex = Math.floor(Math.random() * sections.length);
gallery.appendChild(sections.splice(randomIndex, 1)[0]);
}
};
// Hero background randomizer
const randomizeHeroBackground = () => {
const heroBg = document.querySelector(".hero-background");
if (!heroBg) return;
fetch("/data/gallery.json")
.then((res) => res.json())
.then((images) => {
if (images.length === 0) return;
let currentIndex = Math.floor(Math.random() * images.length);
heroBg.style.backgroundImage = `url(/img/${images[currentIndex]})`;
setInterval(() => {
let nextIndex;
do {
nextIndex = Math.floor(Math.random() * images.length);
} while (nextIndex === currentIndex);
const nextImage = images[nextIndex];
heroBg.style.setProperty("--next-image", `url(/img/${nextImage})`);
heroBg.classList.add("fade-in");
const onTransitionEnd = () => {
heroBg.style.backgroundImage = `url(/img/${nextImage})`;
heroBg.classList.remove("fade-in");
heroBg.removeEventListener("transitionend", onTransitionEnd);
};
heroBg.addEventListener("transitionend", onTransitionEnd);
currentIndex = nextIndex;
}, 7000);
})
.catch(console.error);
};
// Tags filter functionality
const setupTagFilter = () => {
const allSections = document.querySelectorAll('.section[data-tags]');
const allTags = document.querySelectorAll('.tag');
let activeTags = [];
const applyFilter = () => {
allSections.forEach((section) => {
const sectionTags = section.dataset.tags.toLowerCase().split(/\s+/);
const hasAllTags = activeTags.every((tag) => sectionTags.includes(tag));
section.style.display = hasAllTags ? '' : 'none';
});
allTags.forEach((tagEl) => {
const tagText = tagEl.textContent.replace('#', '').toLowerCase();
tagEl.classList.toggle('active', activeTags.includes(tagText));
});
const base = window.location.pathname;
const query = activeTags.length > 0 ? `?tag=${activeTags.join(',')}` : '';
window.history.pushState({}, '', base + query);
};
allTags.forEach((tagEl) => {
tagEl.addEventListener('click', () => {
const tagText = tagEl.textContent.replace('#', '').toLowerCase();
activeTags = activeTags.includes(tagText)
? activeTags.filter((t) => t !== tagText)
: [...activeTags, tagText];
applyFilter();
});
});
window.addEventListener('DOMContentLoaded', () => {
const params = new URLSearchParams(window.location.search);
const urlTags = params.get('tag');
if (urlTags) {
activeTags = urlTags.split(',').map((t) => t.toLowerCase());
applyFilter();
}
});
};
// Disable right-click context menu and image dragging
const disableRightClickAndDrag = () => {
document.addEventListener("contextmenu", (e) => e.preventDefault());
document.addEventListener("dragstart", (e) => {
if (e.target.tagName === "IMG") {
e.preventDefault();
}
});
};
// Scroll-to-top button functionality
const setupScrollToTopButton = () => {
const scrollBtn = document.getElementById("scrollToTop");
window.addEventListener("scroll", () => {
scrollBtn.style.display = window.scrollY > 300 ? "block" : "none";
});
scrollBtn.addEventListener("click", () => {
window.scrollTo({ top: 0, behavior: "smooth" });
});
};
// Adjust navigation list items
const fixNavSeparators = () => {
const items = document.querySelectorAll('.nav-list li');
let prevTop = null;
items.forEach((item) => {
const top = item.getBoundingClientRect().top;
item.classList.toggle('first-on-line', prevTop !== null && top !== prevTop);
prevTop = top;
});
};
// Initialize all functions
document.addEventListener("DOMContentLoaded", () => {
setupIntersectionObserver();
setupLoader();
shuffleGallery();
randomizeHeroBackground();
setupTagFilter();
disableRightClickAndDrag();
setupScrollToTopButton();
fixNavSeparators();
});
window.addEventListener('resize', fixNavSeparators);

BIN
src/public/style/.DS_Store vendored Normal file

Binary file not shown.

496
src/public/style/style.css Normal file
View File

@ -0,0 +1,496 @@
/*-----------------------------------*/
/* CSS style for Lumeex */
/* https://git.djeex.fr/Djeex/lumeex */
/*-----------------------------------*/
:root {
--color-primary: #0065a1;
--color-primary-dark: #005384;
--color-secondary: #00b0f0;
--color-accent: #ffc700;
--color-text-dark: #333333;
--color-background: #ffffff;
}
/* Custom scroll bar */
/* width */
::-webkit-scrollbar {
width: 3px;
}
/* Track */
::-webkit-scrollbar-track {
border-radius: 10px;
background-color:var(--color-background)
}
/* Handle */
::-webkit-scrollbar-thumb {
background-color: var(--color-primary);
border-radius: 10px;
}
/* Scroll to top */
.scroll-up {
position: fixed;
bottom: 20px;
right: 20px;
background: none;
color: var(--color-primary-dark);
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
font-size: 20px;
cursor: pointer;
z-index: 1000;
display: none;
transition: opacity 0.3s ease;
}
.scroll-up:hover, .back-button:hover {
color:var(--color-secondary);
}
/* back button */
.back-button {
padding: 20px;
text-decoration: none;
color: var(--color-primary-dark);
font-size: 1.1rem;
font-family: arial;
border-radius: 8px;
transition: background 0.2s ease;
}
/* Body structure */
html,body {
height: 100%;
margin: 0px;
padding: 0px;
font-family: var(--font-secondary), Helvetica, sans-serif;
min-width:320px;
font-weight: 400;
line-height:1.5;
color:var(--color-primary-dark);
}
html {
scroll-behavior: smooth;
}
@media screen and (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
body a, body a:hover {
transition: all 0.25s ease-out;
text-decoration:none;
}
.inner {
max-width:1200px;
margin-left: auto;
margin-right: auto;
}
h2 {
font-family: var(--font-primary), Arial, sans-serif;
font-size: 18px;
text-align: left;
}
/* Loader */
.page-loader {
width: 100%;
height: 100vh;
position: fixed;
background: var(--color-background);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
opacity: 1;
transition: opacity 1s ease-out;
}
/* Hide the loader with a fade-out effect */
.page-loader.hidden {
opacity: 0;
pointer-events: none;
}
/* Spinner */
.spinner {
width: 80px;
height: 80px;
background-color: var(--color-primary);
border-radius: 100%;
animation: sk-scaleout 1.0s infinite ease-in-out;
}
@keyframes sk-scaleout {
0% {
transform: scale(0);
}
100% {
transform: scale(1.0);
opacity: 0;
}
}
/* navigation */
.nav-item {
display: inline;
}
.nav-list {
list-style-type: disc;
margin: 0px;
padding: 0px;
}
.nav-list li {
white-space: nowrap;
}
.nav-list > li + li::before {
content: " • ";
color: var(--color-accent);
margin: -5px;
}
.nav-list li.first-on-line::before {
visibility: hidden;
}
/* animation */
.appear {
-webkit-transition: all 0.3s;
transition: all 0.3s;
opacity: 0;
-webkit-transform: translateY(20px);
transform: translateY(20px);
}
.appear.inview {
opacity: 1;
-webkit-transform: none;
transform: none;
}
.appear.inview:nth-child(1) {
-webkit-transition-delay: 0s;
transition-delay: 0s;
}
.appear.inview:nth-child(2) {
-webkit-transition-delay: 0.2s;
transition-delay: 0.2s;
}
.appear.inview:nth-child(3) {
-webkit-transition-delay: 0.4s;
transition-delay: 0.4s;
}
.appear.inview:nth-child(4) {
-webkit-transition-delay: 0.6s;
transition-delay: 0.6s;
}
.appear.inview:nth-child(5) {
-webkit-transition-delay: 0.8s;
transition-delay: 0.8s;
}
.appear.inview:nth-child(6) {
-webkit-transition-delay: 1s;
transition-delay: 1s;
}
/* img fade in */
.fade-in-img {
opacity: 0;
transform: scale(1.02);
transition: opacity 1.2s ease-out, transform 1.2s ease-out;
will-change: opacity, transform;
}
.fade-in-img.loaded {
opacity: 1;
transform: scale(1);
}
/* tag */
.tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 10px;
}
.tag {
padding: 5px 5px;
cursor: pointer;
font-size: 18px;
transition: background 0.2s ease;
}
.tag:hover,
.tag.active {
color: var(--color-secondary);
}
/* Content */
/* wrapper */
.content-wrapper {
margin: 0 100px;
}
/* Hero */
#hero {
height: 100%;
width: 100%;
}
#hero .content-wrapper, #hero .section {
height:100%;
}
.hero-background {
height: 66%;
width: 100%;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
position: relative;
display: flex;
align-items: center;
flex-direction: column-reverse;
z-index: 0;
overflow: hidden;
}
.hero-background::after {
content: "";
position: absolute;
inset: 0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 1.5s ease-in-out;
z-index: 1; /* derrière le contenu */
pointer-events: none;
background-image: var(--next-image);
}
.hero-background.fade-in::after {
opacity: 1;
background-image: var(--next-image);
}
.hero-menu {
font-size: 18px;
}
.hero-title {
text-align: center;
margin-bottom: 40px;
color: var(--color-background);
margin-right: auto;
margin-left: auto;
display: flex;
position: relative;
z-index: 2;
}
.hero-title h1 {
font-size: 38px;
margin: 0;
}
.hero-title p {
margin: 0;
font-style: italic;
font-size: 22px;
}
/* Sections */
.section {
max-width: 1140px;
margin:auto;
}
.section img {
width:100%;
margin: 0 0 60px 0;
}
.text-block {
padding:10px;
margin:10px;
}
.text-block p {
font-size: 18px;
font-family: var(--font-secondary), Helvetica, sans-serif;
font-weight: 200;
}
/* Buttons */
/* Text */
.text-inner {
margin-left: 10%;
margin-right:10%;
}
/* Footer */
.navigation {
text-align: center;
padding-top: 40px;
}
.navigation-title {
font-family: var(--font-primary), Arial, sans-serif;
font-weight: bold;
font-size: 32px;
color:var(--color-text-dark);
margin-top: 0px;
margin-bottom: 20px;
}
.navigation a {
color:var(--color-primary);
}
.navigation a:hover {
color:var(--color-secondary);
}
.navigation-subtitle {
color:var(--color-text-dark);
font-size: 12px;
}
.navigation-bottom-link {
color:var(--color-accent);
font-size: 12px;
}
.social {
display: inline;
text-align: center;
padding: 0;
}
.social-item {
display: inline;
text-align: center;
font-size: 25px;
padding: 10px;
}
.navigation .nav-links {
margin: 20px 0;
font-family: var(--font-secondary), Helvetica, sans-serif;
font-weight: 200;
}
.nav-list > li + li::before {
margin: 3px;
}
.bottom-link {
padding-bottom: 40px;
}
.bottom-link p {
margin-left:10px;
margin-right:10px;
}
/* legals */
#legals.content-wrapper {
max-width: 1140px;
margin-top: 100px;
margin-bottom: 100px;
margin-left: auto;
margin-right: auto;
}
.legals-content h2 {
font-size: 22px;
}
.legals-content {
margin: 0 100px;
}
/* responsive */
@media (max-width: 1000px) {
.button {
font-size: 14px;
}
}
@media (max-width: 768px) {
.content-wrapper {
margin: 0;
}
.content-wrapper.gallery {
margin: 0 5%;
}
.navigation .nav-links {
margin: 20px;
}
.nav-links > ul {
padding: 0;
list-style-type: none;
font-size: 20px;
top: 15%;
position: relative;
}
h2 {
font-size:18px;
}
#legals.content-wrapper {
max-width: 90%;
margin: auto;
margin-top: 50px;
}
.legals-content {
margin: 0;
}
.back-button {
padding-left: 0;
margin-top: 60px;
}
}