Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/_data/navLinks.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@
{
"link":"https://play.mlut.style/",
"text":"Sandbox"
},
{
"link":"/showcase",
"text":"Showcase"
}
]
58 changes: 58 additions & 0 deletions src/_data/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
[
{
"id": 1,
"name": "John Smith",
"age": 31,
"avatar": ["/assets/avatars/Emily_Blunt-new.jpg", "/assets/avatars/Koshak_Koteevich.jpg"],
"quote": "Все, что человеческий разум способен понять и во что он способен поверить, достижимо."
},
{
"id": 2,
"name": "Emily Blunt",
"age": 27,
"avatar": ["/assets/avatars/John_Smith-new.jpg", "/assets/avatars/Emily_Blunt-new.jpg", "/assets/avatars/Koshak_Koteevich.jpg"],
"quote": "Сложнее всего начать действовать, все остальное зависит только от упорства."
},
{
"id": 3,
"name": "Koshak Koteevich",
"age": 18,
"avatar": ["/assets/avatars/Tigra_Leopardovich.jpg", "/assets/avatars/Emily_Blunt-new.jpg", "/assets/avatars/Koshak_Koteevich.jpg"],
"quote": "Неудача — это возможность начать заново, но уже более мудро."
},
{
"id": 4,
"name": "Luna Lovegood",
"age": 21,
"avatar": ["/assets/avatars/orange-wood-table.jpg", "/assets/avatars/Koshak_Koteevich.jpg"],
"quote": "Если проблему можно решить, не стоит о ней беспокоиться. Сложнее всего начать действовать, все остальное зависит только от упорства."
},
{
"id": 5,
"name": "Milana Clark",
"age": 39,
"avatar": ["/assets/avatars/Panda_Padlovna.jpg", "/assets/avatars/orange-wood-table.jpg", "/assets/avatars/Emily_Blunt-new.jpg", "/assets/avatars/Koshak_Koteevich.jpg"],
"quote": "Обстоятельства часто можно изменить, изменив свое отношение к ним."
},
{
"id": 6,
"name": "Leopardovskiy Orange Apelsinovich",
"age": 26,
"avatar": ["/assets/avatars/Koshak_Koteevich.jpg", "/assets/avatars/Emily_Blunt-new.jpg"],
"quote": "Обстоятельства часто можно изменить, изменив свое отношение к ним."
},
{
"id": 7,
"name": "Panda Padlovna",
"age": 45,
"avatar": ["/assets/avatars/Milana_Clark-new.jpg", "/assets/avatars/Koshak_Koteevich.jpg"],
"quote": "Вдохновение приходит только во время работы. Обстоятельства часто можно изменить, изменив свое отношение к ним."
},
{
"id": 8,
"name": "Tigra Leopardovich",
"age": 35,
"avatar": ["/assets/avatars/Luna_Lovegood-new.jpg", "/assets/avatars/Emily_Blunt-new.jpg", "/assets/avatars/Koshak_Koteevich.jpg", "/assets/avatars/Panda_Padlovna.jpg"],
"quote": "Если проблему можно решить, не стоит о ней беспокоиться."
}
]
43 changes: 43 additions & 0 deletions src/_includes/components/show-card.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<%
const tag = it.optTag ?? 'li';
const avatars = it.avatar && Array.isArray(it.avatar) ? it.avatar : [];
const defaultAvatar = avatars[0] || '';
%>

<<%= tag %> class="">
<a class="Txd-n C-ih W100p H100p D-f Fld-c Bdt1;s;$accent750 Bdb1;s;$accent750 Bdt1;s;$brand_h Bdb1;s;$brand_h Ts-Bd;0.3s;ease Cs" id="<%= it.id %>" href="#0">
<div class="W100p H263px P10px Ps container">
<% if (defaultAvatar) { %>
<img
src="<%= defaultAvatar %>"
alt="<%= it.name %>"
class="W100p H100p Ojf-cv Ojp-c Bdrd8px hover-switch-image"
data-images='<%= JSON.stringify(avatars) %>'
/>
<% } %>
<!-- Контейнер для индикатора -->
<div class="image-indicator D-n Ps-a B14px L0 R0 D-f Jc-c Gap8px Z1">
<% for (let i = 0; i < avatars.length; i++) { %>
<span class="indicator-dot W24px H2px Bg#fff O0.5 Ts-all;0.3s;ease" data-index="<%= i %>"></span>
<% } %>
</div>
<button class="back D-n W40px H40px Bdrd8px Bd-n Ps-a T110px L20px Bgc#fff O0.5 O0.3_a" type="button">
<img src="../../assets/img/arrow.svg" alt="arrow back" class="Tf -Rt180deg"/>
</button>
<button class="next D-n W40px H40px Bdrd8px Bd-n Ps-a T110px R20px Bgc#fff O0.5 O0.3_a" type="button">
<img src="../../assets/img/arrow.svg" alt="arrow next"/>
</button>
</div>
<div class="D-f Fld-c P10px Bdt1;s;$accent750">
<span class="Txwm-n Ov-h Txo-e Mb12px"><%= it.name ?? '' %></span>
<span class="Whs-pw Ov-h webkit Fns16px C-$gray350"><%= it.quote ?? '' %></span>
</div>
</a>
</<%= tag %>>

<!--
text-wrap: nowrap; Txwm-n
overflow: hidden; Ov-h
text-overflow: ellipsis; Txo-e
white-space: wrap; Whs-pw
-->
Binary file added src/assets/avatars/Emily_Blunt-new.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avatars/John_Smith-new.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avatars/Koshak_Koteevich.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avatars/Luna_Lovegood-new.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avatars/Milana_Clark-new.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avatars/Orange_Apelsinovich.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avatars/Panda_Padlovna.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avatars/Tigra_Leopardovich.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avatars/orange-wood-table.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/img/arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
162 changes: 162 additions & 0 deletions src/assets/script/hover-switch-image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
document.addEventListener("DOMContentLoaded", () => {
// Медиа-запрос
const mediaQuery = window.matchMedia("(max-width: 769px)");

// Функция для получения текущего режима
const isMobile = () => mediaQuery.matches;

// Находим все изображения
const images = document.querySelectorAll(".hover-switch-image");

images.forEach((img) => {
let imagesList = [];
try {
imagesList = JSON.parse(img.dataset.images);
} catch (e) {}
if (!imagesList.length) return;

const originalSrc = imagesList[0];
let currentIndex = 0;

// DOM-элементы
const container = img.closest(".container");
if (!container) return;

const indicator = container.querySelector(".image-indicator");
if (!indicator) return;
const dots = indicator.querySelectorAll(".indicator-dot");
const totalImages = imagesList.length;

const backBtn = container.querySelector(".back");
const nextBtn = container.querySelector(".next");

// Функция обновления активной точки
const setActiveDot = (index) => {
dots.forEach((dot, i) => {
if (i === index) dot.classList.add("active");
else dot.classList.remove("active");
});
};

// Функция обновления видимости кнопок в мобильном режиме
const updateMobileButtons = () => {
if (!backBtn || !nextBtn) return;
if (!isMobile()) return; // на десктопе кнопки скрыты
if (currentIndex === 0) {
backBtn.classList.add('D-n');
nextBtn.classList.remove('D-n');
} else if (currentIndex === totalImages - 1) {
backBtn.classList.remove('D-n');
nextBtn.classList.add('D-n');
} else {
backBtn.classList.remove('D-n');
nextBtn.classList.remove('D-n');
}
};

// Функция смены изображения
const setImageByIndex = (newIndex) => {
if (newIndex < 0) newIndex = 0;
if (newIndex >= totalImages) newIndex = totalImages - 1;
if (newIndex === currentIndex) return;
currentIndex = newIndex;
img.src = imagesList[currentIndex];
setActiveDot(currentIndex);
if (isMobile()) {
updateMobileButtons();
};
};

// Сброс к первому изображению (при уходе мыши или смене режима)
const resetToFirst = () => {
setImageByIndex(0);
setActiveDot(0)
};

// --- Обработчики событий, общие для обоих режимов ---

// Движение мыши – работает только на десктопе
img.addEventListener("mousemove", (e) => {
if (isMobile()) return; // игнорируем на мобильных
const rect = img.getBoundingClientRect();
const x = e.clientX - rect.left;
const width = rect.width;
const ratio = x / width;
let newIndex = Math.floor(ratio * totalImages);
if (newIndex >= totalImages) newIndex = totalImages - 1;
if (newIndex !== currentIndex) {
setImageByIndex(newIndex);
}
});

// Показ индикатора (только на десктопе)
img.addEventListener("mouseenter", () => {
if (isMobile()) return;
indicator.classList.remove("D-n");
setActiveDot(0);
if (img.src !== originalSrc) {
img.src = originalSrc;
currentIndex = 0;
}
});

// Скрытие индикатора и сброс (только на десктопе)
img.addEventListener("mouseleave", () => {
if (isMobile()) return;
indicator.classList.add("D-n");
if (img.src !== originalSrc) {
img.src = originalSrc;
currentIndex = 0;
setActiveDot(0);
}
});

// Клик на кнопку "назад"
if (backBtn) {
backBtn.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
if (!isMobile()) return;
setImageByIndex(currentIndex - 1);
});
}

// Клик на кнопку "вперёд"
if (nextBtn) {
nextBtn.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
if (!isMobile()) return;
setImageByIndex(currentIndex + 1);
});
}

// --- Функция переключения режима при изменении ширины ---
const switchMode = () => {
if (isMobile()) {
// Мобильный режим: показываем индикатор и кнопки
indicator.classList.remove("D-n");
if (backBtn) backBtn.classList.remove("D-n");
if (nextBtn) nextBtn.classList.remove("D-n");
resetToFirst();
updateMobileButtons();
} else {
// Десктопный режим: скрываем индикатор и кнопки
indicator.classList.add("D-n");
if (backBtn) {
backBtn.classList.add("D-n");
}
if (nextBtn) {
nextBtn.classList.add("D-n");
}
resetToFirst();
}
};

// Слушаем изменение ширины
mediaQuery.addEventListener("change", switchMode);

// Первоначальная настройка
switchMode();
});
});
19 changes: 19 additions & 0 deletions src/assets/style/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ html{
--ml-blueGray:rgb(236, 240, 246,0.5);
--ml-gray200:#d1d5db;
--ml-gray350:#cacacaa0;
//
--ml-castomWidth: calc(100% + 280px);
--ml-imageGradient:
repeating-linear-gradient(315deg, var(--ml-accent750) 0 1px, transparent 1px 6px );
}


Expand Down Expand Up @@ -159,3 +163,18 @@ html{

}

// .gtc-auto-fit {
// grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
// }

.indicator-dot.active {
opacity: 1;
transform: scale(1.2);
}

.webkit {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
}
3 changes: 2 additions & 1 deletion src/layouts/base.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if (it.area === "main"){
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/style/style.min.css">
<link rel="icon" href="/assets/img/favicon.png" type="image/x-icon">
<link rel="icon" type="image/x-icon" href="/assets/img/favicon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css">
<title>mlut - make CSS exciting again!</title>
</head>
Expand All @@ -35,5 +35,6 @@ if (it.area === "main"){
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<script src="../assets/script/toggleLogo.js" type="module"></script>
<script src="../assets/script/hover-switch-image.js" type="module"></script>
</body>
</html>
29 changes: 29 additions & 0 deletions src/showcase.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: Showcase
layout: base.ejs
---

<div class="P-$headerH;100px;0 D-f C-$accent900 @:w<769px_P60px;5px;0">
<div class="D-f W40px Bdl1px;s;$accent750 Bdr1px;s;$accent750 @:w<769px_D-n"></div>
<div class="Pb40px W100p">
<div class="M40px;-140px;80px P0;160px W-$castomWidth Bdt1px;s;$accent750 Bdb1px;s;$accent750 @:w<769px_W100p @:w<769px_M40px;0;80px @:w<769px_P0;20px">
<h1><span class="C-$brand">Mlut</span> is more than just CSS</h1>
</div>
<div class="H40px W-$castomWidth Ml-140px Bdt1px;s;$accent750 Bdb1px;s;$accent750 @:w<769px_W100p @:w<769px_Ml0"></div>
<ul class="Lss-n P0 M40px;20px D-g Gtc-t4 Gap40px @:w<1441px_Gtc-t3 @:w<1025px_Gtc-t2 @:w<425px_Gtc-t1">
<% for (let user of it.users) { %>
<%- include('_includes/components/show-card.ejs', {
optTag: 'li',
avatar: user.avatar,
name: user.name,
quote: user.quote,
id: user.id
}) %>
<% } %>
</ul>
<div class="H40px W-$castomWidth Ml-140px Bdt1px;s;$accent750 Bdb1px;s;$accent750 @:w<769px_W100p @:w<769px_Ml0"></div>
</div>
<div class="D-f W40px Bdl1px;s;$accent750 Bdr1px;s;$accent750 @:w<769px_D-n"></div>
</div>

<!-- Bgi-$imageGradient -->