Files
site-portfolio/public/cases/mini-projects-showcase/index.html
T
root daa623fe73 feat(mesh-bg): fade-in шейдера фона + modulepreload графа
- canvas стартует с opacity:0 и плавно проявляется (0.9s) при .is-ready
- initPageMeshShader ставит is-ready после первого кадра
- modulepreload page-mesh-shader.js для параллельной загрузки с main.js
- пересборка страниц (build:pages)
2026-09-24 21:37:40 +02:00

1420 lines
67 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru" data-theme="dark" data-radius="medium" data-font="merriweather">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta
name="description"
content="Каталог карточек секции «Активность»: типы, ширина в клетках и живая masonry-сетка."
/>
<title>Активность — каталог карточек</title>
<script>
(function () {
var FONT_CSS = {
merriweather: "/assets/fonts/merriweather.css",
geologica: "/assets/fonts/geologica.css",
mono: "/assets/fonts/mono.css",
};
try {
var t = JSON.parse(localStorage.getItem("siteThemeV1") || "{}");
var d = document.documentElement;
var m = t.mode;
if (m !== "light" && m !== "dark" && m !== "system") m = "system";
var eff =
m === "system"
? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light"
: m;
d.dataset.theme = eff;
var h = typeof t.h === "number" ? t.h : typeof t.hue === "number" ? t.hue : null;
var s = typeof t.s === "number" ? t.s : typeof t.sat === "number" ? t.sat : null;
var l = typeof t.l === "number" ? t.l : typeof t.light === "number" ? t.light : null;
if (h !== null) d.style.setProperty("--theme-h", String(h));
if (s !== null) d.style.setProperty("--theme-s", String(s));
if (l !== null) {
var L = Number(l);
if (Number.isFinite(L)) d.style.setProperty("--theme-l", String(Math.min(70, Math.max(20, L))));
}
var hVal = h !== null ? h : 210;
var sVal = s !== null ? s : 90;
var lVal = l !== null ? Math.min(70, Math.max(20, Number(l))) : 40;
var H = (((hVal % 360) + 360) % 360) / 360;
var S = Math.min(100, Math.max(0, sVal)) / 100;
var Lv = Math.min(100, Math.max(0, lVal)) / 100;
var r, g, b;
if (S === 0) { r = g = b = Lv; }
else {
var q = Lv < 0.5 ? Lv * (1 + S) : Lv + S - Lv * S;
var p = 2 * Lv - q;
var hue2rgb = function(p, q, t) {
if (t < 0) t += 1; if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
r = hue2rgb(p, q, H + 1/3);
g = hue2rgb(p, q, H);
b = hue2rgb(p, q, H - 1/3);
}
var lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
d.style.setProperty("--on-accent", lum > 0.58 ? "hsl(220 14% 12%)" : "hsl(0 0% 98%)");
var rad = t.radius;
if (rad === "soft" || rad === "medium" || rad === "strong") {
d.dataset.radius = rad;
} else {
d.dataset.radius = "medium";
}
var f = t.font;
if (f === "onest") f = "geologica";
if (f === "anonymous") f = "mono";
if (f !== "merriweather" && f !== "mono" && f !== "geologica") f = "merriweather";
d.dataset.font = f;
var mb = t.meshBg;
if (mb === "off") {
d.dataset.meshBg = "off";
} else {
d.dataset.meshBg = "on";
}
var href = FONT_CSS[f];
if (href && !document.querySelector('link[data-theme-font="' + f + '"]')) {
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = href;
link.setAttribute("data-theme-font", f);
document.head.appendChild(link);
}
} catch (e) {}
})();
</script>
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/assets/favicon/apple-touch-icon.png" />
<link rel="manifest" href="/assets/favicon/site.webmanifest" />
<link rel="preload" href="/assets/fonts/merriweather.css" as="style" />
<link rel="preload" href="/css/main.css" as="style" />
<link rel="preload" href="/js/skill-connectors.js" as="script" />
<link rel="modulepreload" href="/js/main.js" />
<link rel="modulepreload" href="/js/modules/ui/page-mesh-shader.js" />
<link rel="stylesheet" href="/css/main.css" />
<link rel="canonical" href="https://mamadaevv.site/cases/mini-projects-showcase/" />
<link rel="alternate" hreflang="ru" href="https://mamadaevv.site/cases/mini-projects-showcase/" />
<link rel="alternate" hreflang="en" href="https://mamadaevv.site/en/cases/mini-projects-showcase/" />
<link rel="alternate" hreflang="x-default" href="https://mamadaevv.site/cases/mini-projects-showcase/" />
<meta name="author" content="Николай Мамадаев" />
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#0f1115" />
<!-- Open Graph -->
<meta property="og:type" content="article" />
<meta property="og:site_name" content="Николай Мамадаев — продуктовый дизайнер" />
<meta property="og:locale" content="ru_RU" />
<meta property="og:locale:alternate" content="en_US" />
<meta property="og:title" content="Активность — каталог карточек" />
<meta property="og:description" content="Каталог карточек секции «Активность»: типы, ширина в клетках и живая masonry-сетка." />
<meta property="og:url" content="https://mamadaevv.site/cases/mini-projects-showcase/" />
<meta property="og:image" content="https://mamadaevv.site/assets/og/og-default.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Активность — каталог карточек" />
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Активность — каталог карточек" />
<meta name="twitter:description" content="Каталог карточек секции «Активность»: типы, ширина в клетках и живая masonry-сетка." />
<meta name="twitter:image" content="https://mamadaevv.site/assets/og/og-default.png" />
<meta name="twitter:image:alt" content="Активность — каталог карточек" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Person",
"@id": "https://mamadaevv.site/#person",
"name": "Николай Мамадаев",
"url": "https://mamadaevv.site",
"jobTitle": "Продуктовый дизайнер",
"description": "Продуктовый дизайнер с опытом более 10 лет. Проектирую цифровые продукты, интерфейсы и дизайн-системы.",
"knowsAbout": [
"UX/UI-дизайн",
"продуктовый дизайн",
"дизайн-системы",
"прототипирование",
"Figma",
"no-code"
],
"sameAs": [
"https://t.me/mamadaevv",
"https://vk.com/mamadaevv",
"https://github.com/mamadaevv"
]
},
{
"@type": "WebSite",
"@id": "https://mamadaevv.site/#website",
"url": "https://mamadaevv.site/",
"name": "Николай Мамадаев — продуктовый дизайнер",
"inLanguage": "ru",
"author": {
"@id": "https://mamadaevv.site/#person"
},
"publisher": {
"@id": "https://mamadaevv.site/#person"
}
},
{
"@type": "CreativeWork",
"@id": "https://mamadaevv.site/cases/mini-projects-showcase/#page",
"url": "https://mamadaevv.site/cases/mini-projects-showcase/",
"name": "Активность — каталог карточек",
"description": "Каталог карточек секции «Активность»: типы, ширина в клетках и живая masonry-сетка.",
"isPartOf": {
"@id": "https://mamadaevv.site/#website"
},
"author": {
"@id": "https://mamadaevv.site/#person"
},
"inLanguage": "ru"
},
{
"@type": "BreadcrumbList",
"@id": "https://mamadaevv.site/cases/mini-projects-showcase/#breadcrumbs",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Николай Мамадаев — продуктовый дизайнер",
"item": "https://mamadaevv.site/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Кейсы",
"item": "https://mamadaevv.site/cases/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Активность — каталог карточек",
"item": "https://mamadaevv.site/cases/mini-projects-showcase/"
}
]
}
]
}
</script>
<script src="/js/skill-connectors.js" defer></script>
<script type="module" src="/js/main.js"></script>
<style>
#main {
padding-bottom: clamp(3.5rem, 8vw, 5.5rem);
}
.case-article.mp-showcase-page {
max-width: 56rem;
margin: 0 auto;
}
.case-article.mp-showcase-page .case-page__sub {
margin-bottom: clamp(1.5rem, 4vw, 2.25rem);
}
.case-article.mp-showcase-page .section__prose {
margin-bottom: 1.1rem;
line-height: 1.65;
color: var(--text);
}
.case-article.mp-showcase-page > h2 {
margin: 0 0 1rem;
padding-top: clamp(2.5rem, 5vw, 3.5rem);
font-family: var(--font);
font-size: clamp(1.15rem, 2vw, 1.35rem);
font-weight: 700;
color: var(--text);
scroll-margin-top: 1.5rem;
border-top: 1px solid color-mix(in srgb, var(--line) 90%, transparent);
}
.case-article.mp-showcase-page > h2#intro {
padding-top: clamp(1.25rem, 3vw, 2rem);
border-top: none;
}
.mp-showcase-toc {
margin: 0 0 1.75rem;
padding: 1rem 1.15rem;
border: 1px solid var(--line);
border-radius: var(--radius-md);
background: var(--surface);
font-size: 0.92rem;
line-height: 1.55;
}
.mp-showcase-toc ol {
margin: 0.35rem 0 0;
padding-left: 1.2rem;
}
.mp-showcase-toc a {
color: var(--accent-text);
}
.mp-showcase-specimens {
display: flex;
flex-direction: column;
gap: 0;
margin: 1.25rem 0 0.5rem;
}
.mp-showcase-specimen-row {
display: grid;
grid-template-columns: minmax(14rem, 1fr) minmax(12rem, 1.15fr);
gap: 1rem 1.75rem;
align-items: start;
padding-bottom: 2.25rem;
margin-bottom: 2.25rem;
border-bottom: 1px solid color-mix(in srgb, var(--line) 88%, transparent);
}
.mp-showcase-specimen-row:last-child {
padding-bottom: 0;
margin-bottom: 0;
border-bottom: none;
}
@media (max-width: 719px) {
.mp-showcase-specimen-row {
grid-template-columns: 1fr;
}
}
.mp-showcase-specimen__card {
display: flex;
flex-direction: column;
gap: 0.45rem;
min-width: 0;
}
.mp-showcase-specimen__desc .section__prose {
margin-bottom: 0;
}
.mp-showcase-specimen__desc .section__prose strong {
font-weight: 600;
color: var(--text);
}
.mp-showcase-specimen__label {
margin: 0;
font-size: 0.78rem;
line-height: 1.4;
color: var(--text-muted);
}
.mp-showcase-specimen__card .mp-card {
flex: 1 1 auto;
}
/* На главной тоже, но в узкой колонке демо center давал пустоту над заголовком */
.mp-showcase-specimen__card .mp-card--horizontal .mp-card__body {
justify-content: flex-start;
}
.mp-showcase-grid-section {
padding-top: clamp(2.75rem, 6vw, 4.5rem);
}
.mp-showcase-grid-section .section__title {
margin-bottom: 0.85rem;
}
.mp-showcase-grid-section .section__prose {
margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}
.mp-showcase-cells-table {
width: 100%;
margin: 1rem 0 0.5rem;
border-collapse: collapse;
font-size: 0.88rem;
}
.mp-showcase-cells-table th,
.mp-showcase-cells-table td {
padding: 0.55rem 0.65rem;
border: 1px solid var(--line);
text-align: left;
vertical-align: top;
}
.mp-showcase-cells-table th {
font-weight: 600;
color: var(--text);
background: color-mix(in srgb, var(--surface) 88%, var(--accent));
}
.mp-showcase-cells-demo {
margin: 1.35rem 0 1.65rem;
padding: 1.1rem 1.15rem 1.2rem;
border: 1px solid var(--line);
border-radius: var(--radius-md);
background: color-mix(in srgb, var(--surface) 90%, var(--accent));
}
.mp-showcase-cells-demo__variant {
margin-bottom: 1.2rem;
}
.mp-showcase-cells-demo__variant:last-of-type {
margin-bottom: 0;
}
.mp-showcase-cells-demo__legend {
display: grid;
gap: 0.5rem;
margin-bottom: 0.85rem;
font-size: 0.72rem;
color: var(--text-muted);
text-align: center;
}
.mp-showcase-cells-demo__legend--6 {
grid-template-columns: repeat(6, 1fr);
}
.mp-showcase-cells-demo__legend--4 {
grid-template-columns: repeat(4, 1fr);
}
.mp-showcase-cells-demo__legend--2 {
grid-template-columns: repeat(2, 1fr);
}
.mp-showcase-cells-demo__legend span {
padding: 0.2rem 0;
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--line) 35%, transparent);
}
.mp-showcase-cells-demo__row-label {
font-size: 0.82rem;
font-weight: 600;
color: var(--text);
margin: 0 0 0.55rem;
}
.mp-showcase-cells-demo__grid {
display: grid;
gap: 0.5rem;
margin-bottom: 0.55rem;
}
.mp-showcase-cells-demo__grid--6 {
grid-template-columns: repeat(6, 1fr);
}
.mp-showcase-cells-demo__grid--4 {
grid-template-columns: repeat(4, 1fr);
}
.mp-showcase-cells-demo__grid--2 {
grid-template-columns: repeat(2, 1fr);
}
.mp-showcase-cells-demo__block {
min-height: 2.35rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-sm);
border: 1px solid color-mix(in srgb, var(--accent) 42%, var(--line));
background: color-mix(in srgb, var(--accent) 14%, var(--surface));
font-size: 0.78rem;
font-weight: 600;
color: var(--accent-text);
}
.mp-showcase-cells-demo__block--1 {
grid-column: span 1;
}
.mp-showcase-cells-demo__block--2 {
grid-column: span 2;
}
.mp-showcase-cells-demo__block--3 {
grid-column: span 3;
}
.mp-showcase-cells-demo__block--4 {
grid-column: span 4;
}
.mp-showcase-cells-demo__caption {
margin: 0.85rem 0 0;
font-size: 0.82rem;
line-height: 1.45;
color: var(--text-muted);
}
.mp-showcase-cells-demo__row-label {
margin: 0.65rem 0 0.35rem;
font-size: 0.75rem;
color: var(--text-muted);
}
.mp-showcase-grid-section .mini-projects-grid {
margin-top: 0;
}
</style>
<script>
(function () {
try {
var raw = localStorage.getItem("siteThemeV1");
if (!raw) return;
var t = JSON.parse(raw);
var d = document.documentElement;
if (t.mode === "light" || t.mode === "dark") d.dataset.theme = t.mode;
if (t.radius === "soft" || t.radius === "medium" || t.radius === "strong") d.dataset.radius = t.radius;
var f = t.font;
if (f === "onest") f = "geologica";
if (f === "anonymous") f = "mono";
if (f === "merriweather" || f === "mono" || f === "geologica") d.dataset.font = f;
if (t.meshBg === "off") d.dataset.meshBg = "off";
} catch (e) {}
})();
</script>
</head>
<body>
<svg class="liquid-glass-defs" aria-hidden="true" width="0" height="0" focusable="false" style="position:absolute;width:0;height:0;overflow:hidden;pointer-events:none">
<filter id="liquid-glass-displacement" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<feImage href="../../assets/liquid-glass/displacement.png" xlink:href="../../assets/liquid-glass/displacement.png" preserveAspectRatio="none" result="lgMap"/>
<feDisplacementMap in="SourceGraphic" in2="lgMap" scale="14" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<filter id="liquid-glass-displacement-btn" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<feImage href="../../assets/liquid-glass/displacement.png" xlink:href="../../assets/liquid-glass/displacement.png" preserveAspectRatio="none" result="lgMapBtn"/>
<feDisplacementMap in="SourceGraphic" in2="lgMapBtn" scale="6" xChannelSelector="R" yChannelSelector="G"/>
</filter>
</svg>
<a class="skip-link" href="#main">К основному содержимому</a>
<header class="header" data-theme-picker>
<div class="header__inner">
<a class="logo" href="../../" aria-label="На главную">НМ</a>
<div class="header__cluster">
<nav class="nav" aria-label="Разделы страницы">
<a href="../../">На главную</a>
</nav>
</div>
</div>
</header>
<div class="fab-stack" aria-label="Быстрые действия">
<div class="theme-panel-fab-wrap">
<button type="button" class="fab fab--secondary glass-neutral" data-theme-panel-btn aria-expanded="false" aria-controls="theme-panel" aria-label="Настройки стиля">
<span class="theme-panel-fab__icon theme-panel-fab__icon--open-hide" aria-hidden="true"></span>
<svg class="theme-panel-fab__icon theme-panel-fab__icon--close" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M6 6L18 18M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
</button>
<div id="theme-panel" class="theme-picker__panel glass-neutral" role="dialog" aria-label="Тема и оформление" hidden data-theme-panel>
<div class="theme-picker__stack">
<section class="theme-picker__section" aria-labelledby="theme-accent-title">
<h3 id="theme-accent-title" class="theme-picker__section-title">Акцентный цвет</h3>
<div class="colorpicker" data-colorpicker>
<div class="colorpicker__plane-box">
<canvas class="colorpicker__plane" width="320" height="180" data-sl-canvas aria-label="Насыщенность и светлость акцента"></canvas>
<div class="colorpicker__knob" data-sl-knob></div>
</div>
<div class="colorpicker__hue-wrap">
<label class="colorpicker__hue-sr" for="theme-hue-slider">Оттенок акцента</label>
<input id="theme-hue-slider" class="colorpicker__hue" type="range" min="0" max="360" value="210" data-hue-slider aria-valuemin="0" aria-valuemax="360" />
</div>
</div>
</section>
<section class="theme-picker__section" aria-labelledby="theme-mode-title">
<h3 id="theme-mode-title" class="theme-picker__section-title">Тема</h3>
<div class="radius-seg theme-mode-seg" role="radiogroup" aria-labelledby="theme-mode-title">
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-theme-mode="system" aria-label="Как в системе"><span class="radius-seg__viz" aria-hidden="true"><span class="theme-mode-seg__ico theme-mode-seg__ico--system"></span></span></button>
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-theme-mode="light" aria-label="Светлая тема"><span class="radius-seg__viz" aria-hidden="true"><span class="theme-mode-seg__ico theme-mode-seg__ico--light"></span></span></button>
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-theme-mode="dark" aria-label="Тёмная тема"><span class="radius-seg__viz" aria-hidden="true"><span class="theme-mode-seg__ico theme-mode-seg__ico--dark"></span></span></button>
</div>
</section>
<section class="theme-picker__section" aria-labelledby="theme-radius-title">
<h3 id="theme-radius-title" class="theme-picker__section-title">Скругление углов</h3>
<div class="radius-seg" role="radiogroup" aria-labelledby="theme-radius-title">
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-radius-option="soft" aria-label="Слабое скругление"><span class="radius-seg__viz" aria-hidden="true"><span class="radius-seg__arc radius-seg__arc--soft"></span></span></button>
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-radius-option="medium" aria-label="Среднее скругление"><span class="radius-seg__viz" aria-hidden="true"><span class="radius-seg__arc radius-seg__arc--medium"></span></span></button>
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-radius-option="strong" aria-label="Сильное скругление"><span class="radius-seg__viz" aria-hidden="true"><span class="radius-seg__arc radius-seg__arc--strong"></span></span></button>
</div>
</section>
<section class="theme-picker__section" aria-labelledby="theme-typography-title">
<h3 id="theme-typography-title" class="theme-picker__section-title">Типографика</h3>
<div class="radius-seg" role="radiogroup" aria-labelledby="theme-typography-title">
<button type="button" class="radius-seg__btn theme-font-seg__btn" role="radio" aria-checked="false" data-font-option="geologica" aria-label="Sans"><span class="theme-font-seg__label">Sans</span></button>
<button type="button" class="radius-seg__btn theme-font-seg__btn" role="radio" aria-checked="false" data-font-option="merriweather" aria-label="Sans Serif"><span class="theme-font-seg__label">Sans Serif</span></button>
<button type="button" class="radius-seg__btn theme-font-seg__btn" role="radio" aria-checked="false" data-font-option="mono" aria-label="Mono"><span class="theme-font-seg__label">Mono</span></button>
</div>
</section>
<section class="theme-picker__section" aria-labelledby="theme-mesh-bg-title">
<h3 id="theme-mesh-bg-title" class="theme-picker__section-title">Фон страницы</h3>
<div class="radius-seg radius-seg--two" role="radiogroup" aria-labelledby="theme-mesh-bg-title">
<button type="button" class="radius-seg__btn theme-mesh-bg-seg__btn" role="radio" aria-checked="false" data-mesh-bg-option="on" aria-label="Показать фоновый градиент"><span class="theme-mesh-bg-seg__label">Вкл</span></button>
<button type="button" class="radius-seg__btn theme-mesh-bg-seg__btn" role="radio" aria-checked="false" data-mesh-bg-option="off" aria-label="Скрыть фоновый градиент"><span class="theme-mesh-bg-seg__label">Выкл</span></button>
</div>
</section>
</div>
</div>
</div>
<a class="fab fab--primary" href="../../#contact" aria-label="Связаться">
<span class="fab-primary__ico" aria-hidden="true"></span>
</a>
</div>
<div class="content-edit-fab-stack" aria-label="Правки контента" data-content-edit-ui>
<button
type="button"
class="fab fab--secondary glass-neutral content-edit-fab"
data-content-edit-mode-toggle
hidden
aria-pressed="false"
aria-label="Режим правок контента"
title="Режим правок"
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path
d="M4 20h4l10.5-10.5a2.12 2.12 0 0 0 0-3L16.5 4.5a2.12 2.12 0 0 0-3 0L3 15v5z"
stroke="currentColor"
stroke-width="1.75"
stroke-linejoin="round"
/>
</svg>
<span class="content-edit-fab__badge" hidden aria-hidden="true">0</span>
</button>
</div>
<main id="main">
<section class="section section--tight">
<div class="section__inner">
<div class="case-article mp-showcase-page">
<p class="hero__eyebrow">Design System · Активность</p>
<h1 class="case-page__h1">Карточки активности</h1>
<p class="case-page__sub">Все типы карточек и живая сетка для подбора раскладки на главной</p>
<p class="section__prose">
Страница для верстки и контента: здесь те же классы и скрипты, что в секции на главной. Меняйте шрифт и тему в панели
справа, сужайте окно — сетка пересчитается. Подписи в заголовках карточек — подсказки, в проде их убрать.
</p>
<nav class="mp-showcase-toc" aria-label="Оглавление каталога">
<p><strong>Разделы</strong></p>
<ol>
<li><a href="#types">Типы карточек</a></li>
<li><a href="#cells">Ширина в клетках</a></li>
<li><a href="#grid">Живая сетка masonry</a></li>
<li><a href="#fixed-height">Фиксированная высота</a></li>
</ol>
</nav>
<h2 id="intro">Как пользоваться</h2>
<p class="section__prose">
Сначала выберите <strong>тип</strong> (stack, horizontal, compact, decor), затем <strong>data-mp-cells</strong> (1–4) и
порядок <code>li</code> в списке. Карточки с <code>data-mp-extra</code> не влияют на высоту списка до кнопки «Показать все».
Подробности — в <code>docs/mini-projects-cards.md</code>.
</p>
<h2 id="types">Типы карточек</h2>
<p class="section__prose">
Слева пример карточки с текстом, справа название типа и пояснение по классам.
</p>
<div class="mp-showcase-specimens" aria-label="Типы карточек">
<div class="mp-showcase-specimen-row">
<div class="mp-showcase-specimen__card">
<p class="mp-showcase-specimen__label"><code>mp-card--stack</code> + <code>mp-card--link</code></p>
<a
class="mp-card mp-card--link mp-card--stack"
href="#"
aria-label="Stack со ссылкой — пример карточки"
>
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
Stack со ссылкой
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">веб-сайт</li>
</ul>
<p class="mp-card__desc">
Небольшой скрипт: черновики текстов из ключевых слов, без внешних API.
</p>
</div>
</a>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Stack со ссылкой.</strong> Превью на всю ширину, текст снизу. Для внешней ссылки:
<code>mp-card--link</code> и <code>mp-card__ext</code> в заголовке (как в блоке опыта).
</p>
</div>
</div>
<div class="mp-showcase-specimen-row">
<div class="mp-showcase-specimen__card">
<p class="mp-showcase-specimen__label"><code>mp-card--stack</code> без ссылки</p>
<div class="mp-card mp-card--stack">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Stack без ссылки</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">заметка</li>
</ul>
<p class="mp-card__desc">
Связка баз и rollup для недельного обзора, только для личного пользования.
</p>
</div>
</div>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Stack без ссылки.</strong> Тот же вид. Обёртка <code>div</code> или <code>article</code>, без
<code>mp-card--link</code>.
</p>
</div>
</div>
<div class="mp-showcase-specimen-row">
<div class="mp-showcase-specimen__card">
<p class="mp-showcase-specimen__label"><code>mp-card--horizontal</code> + <code>mp-card--link</code></p>
<a
class="mp-card mp-card--link mp-card--horizontal"
href="#"
aria-label="Horizontal со ссылкой — пример карточки"
>
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="360"
height="203"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
Horizontal со ссылкой
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">приложение</li>
</ul>
<p class="mp-card__desc">
Локальный таймер с короткими звуками и блокировкой лишних вкладок.
</p>
</div>
</a>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Horizontal со ссылкой.</strong> Миниатюра слева, текст справа. На узком экране карточка
складывается в столбик.
</p>
</div>
</div>
<div class="mp-showcase-specimen-row">
<div class="mp-showcase-specimen__card">
<p class="mp-showcase-specimen__label"><code>mp-card--horizontal</code> без ссылки</p>
<div class="mp-card mp-card--horizontal">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="360"
height="203"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Horizontal без ссылки</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">шаблон</li>
</ul>
<p class="mp-card__desc">
Статический сайт с расписанием и формой заявки на отдельном сервисе.
</p>
</div>
</div>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Horizontal без ссылки.</strong> Тот же расклад, карточка не ведёт на внешний URL.
</p>
</div>
</div>
<div class="mp-showcase-specimen-row">
<div class="mp-showcase-specimen__card">
<p class="mp-showcase-specimen__label"><code>mp-card--compact</code> + <code>mp-card--link</code></p>
<a
class="mp-card mp-card--link mp-card--compact"
href="#"
aria-label="Compact со ссылкой — пример карточки"
>
<div class="mp-card__body">
<h3 class="mp-card__title">
Compact со ссылкой
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">скрипт</li>
</ul>
<p class="mp-card__desc">
Префиксы, нумерация и dry-run перед записью файлов на диск.
</p>
</div>
</a>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Compact со ссылкой.</strong> Только текст, без <code>mp-card__media</code>. В сетке часто с
<code>data-mp-cells="1"</code>.
</p>
</div>
</div>
<div class="mp-showcase-specimen-row">
<div class="mp-showcase-specimen__card">
<p class="mp-showcase-specimen__label"><code>mp-card--compact</code> без ссылки</p>
<article class="mp-card mp-card--compact">
<div class="mp-card__body">
<h3 class="mp-card__title">Compact без ссылки</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">документ</li>
</ul>
<p class="mp-card__desc">
Markdown-список проверок перед релизом мелких правок в интерфейсе.
</p>
</div>
</article>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Compact без ссылки.</strong> Короткая заметка или чеклист без внешней ссылки.
</p>
</div>
</div>
<div class="mp-showcase-specimen-row">
<div class="mp-showcase-specimen__card">
<p class="mp-showcase-specimen__label"><code>mp-card--decor</code></p>
<div class="mp-card mp-card--decor" aria-hidden="true">
<span class="mp-card__decor-pattern" aria-hidden="true"></span>
</div>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Декоративная ячейка.</strong> Заглушка в сетке, без проекта и ссылки. Внутри
<code>span.mp-card__decor-pattern</code>.
</p>
</div>
</div>
</div>
<h2 id="cells">Ширина в клетках</h2>
<p class="section__prose">
Атрибут <code>data-mp-cells</code> на <code>li.mini-projects-grid__item</code> задаёт желаемую ширину от 1 до 4 клеток.
Скрипт не даст карточке быть шире текущего ряда.
</p>
<p class="section__prose">
Для адаптивной ширины используйте <code>data-mp-cells-sm</code> (при 4 клетках в ряду, ≥ 880 px) и
<code>data-mp-cells-xs</code> (при 2 клетках, ≥ 520 px). Скрипт выбирает значение по текущему числу клеток в ряду.
</p>
<div class="mp-showcase-attrs-table">
<table class="mp-showcase-cells-table">
<thead>
<tr>
<th scope="col">Атрибут</th>
<th scope="col">Когда применяется</th>
<th scope="col">Пример</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>data-mp-cells</code></td>
<td>Широкий экран (6 клеток, ≥ 1180 px)</td>
<td><code>data-mp-cells="3"</code></td>
</tr>
<tr>
<td><code>data-mp-cells-sm</code></td>
<td>Средний экран (4 клетки, ≥ 880 px)</td>
<td><code>data-mp-cells-sm="2"</code></td>
</tr>
<tr>
<td><code>data-mp-cells-xs</code></td>
<td>Узкий экран (2 клетки, ≥ 520 px)</td>
<td><code>data-mp-cells-xs="1"</code></td>
</tr>
</tbody>
</table>
</div>
<p class="section__prose" style="margin-top:0.55rem">
<strong>Пример:</strong> <code>data-mp-cells="3" data-mp-cells-sm="2"</code> — карточка занимает 3 клетки на широком экране, 2 на среднем, 1 на узком.
</p>
<figure class="mp-showcase-cells-demo" aria-label="Схема ширины карточек в клетках">
<div class="mp-showcase-cells-demo__variant">
<div class="mp-showcase-cells-demo__legend mp-showcase-cells-demo__legend--6" aria-hidden="true">
<span>1</span><span>2</span><span>3</span><span>4</span><span>5</span><span>6</span>
</div>
<p class="mp-showcase-cells-demo__row-label">≥ 1180 px — 6 клеток</p>
<div class="mp-showcase-cells-demo__grid mp-showcase-cells-demo__grid--6">
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--3">3</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--3">3</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--1">1</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--3">3</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--4">4</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
</div>
</div>
<div class="mp-showcase-cells-demo__variant">
<div class="mp-showcase-cells-demo__legend mp-showcase-cells-demo__legend--4" aria-hidden="true">
<span>1</span><span>2</span><span>3</span><span>4</span>
</div>
<p class="mp-showcase-cells-demo__row-label">≥ 880 px — 4 клетки</p>
<div class="mp-showcase-cells-demo__grid mp-showcase-cells-demo__grid--4">
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--3">3</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--1">1</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--1">1</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--4">4</div>
</div>
</div>
<div class="mp-showcase-cells-demo__variant">
<div class="mp-showcase-cells-demo__legend mp-showcase-cells-demo__legend--2" aria-hidden="true">
<span>1</span><span>2</span>
</div>
<p class="mp-showcase-cells-demo__row-label">≥ 520 px — 2 клетки</p>
<div class="mp-showcase-cells-demo__grid mp-showcase-cells-demo__grid--2">
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--1">1</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--1">1</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--2">2</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--1">1</div>
<div class="mp-showcase-cells-demo__block mp-showcase-cells-demo__block--1">1</div>
</div>
</div>
<figcaption class="mp-showcase-cells-demo__caption">
Цифра в блоке = значение <code>data-mp-cells</code>. Карточка занимает столько клеток подряд. При узком экране клеток меньше — карточки с шириной &gt; число клеток переносятся на следующий ряд. С <code>data-mp-cells-sm</code> / <code>data-mp-cells-xs</code> карточка меняет ширину при сужении.
</figcaption>
</figure>
<table class="mp-showcase-cells-table">
<thead>
<tr>
<th scope="col">Ширина списка</th>
<th scope="col">Клеток в ряду</th>
</tr>
</thead>
<tbody>
<tr>
<td>≥ 1180px</td>
<td>6</td>
</tr>
<tr>
<td>≥ 880px</td>
<td>4</td>
</tr>
<tr>
<td>≥ 520px</td>
<td>2</td>
</tr>
<tr>
<td>уже</td>
<td>1 (вся строка)</td>
</tr>
</tbody>
</table>
<p class="section__prose">
На шести клетках пара <code>3 + 3</code> или тройка <code>2 + 2 + 2</code> обычно смотрится ровнее случайного набора.
Декор (<code>mp-card--decor</code>) заполняет пустоты в сетке.
</p>
</div>
</div>
</section>
<section id="grid" class="section section--surface mp-showcase-grid-section" aria-labelledby="grid-title">
<div class="section__inner">
<h2 id="grid-title" class="section__title">Живая сетка masonry</h2>
<p class="section__prose">
Демо блока «Активность» с главной: здесь все типы карточек в одной живой сетке. В подписях у каждой — тип
раскладки и <code>data-mp-cells</code>. Меняйте ширину окна и шрифт в панели темы, смотрите, как сетка
перестраивается, и переносите удачный порядок на главную.
</p>
<ol
id="mini-projects-showcase-grid"
class="mini-projects-grid mp-showcase-grid"
data-mini-projects-list
>
<li class="mini-projects-grid__item" data-mp-cells="2">
<a class="mp-card mp-card--link mp-card--stack" href="#" aria-label="[2] stack · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[2] stack · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">веб-сайт</li>
</ul>
<p class="mp-card__desc">Базовая пара в ряду из шести клеток.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2">
<div class="mp-card mp-card--stack">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">[2] stack</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">заметка</li>
</ul>
<p class="mp-card__desc">Stack без внешней ссылки.</p>
</div>
</div>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2">
<div class="mp-card mp-card--horizontal">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="360"
height="203"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">[2] horizontal</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">утилита</li>
</ul>
<p class="mp-card__desc">Horizontal на две клетки, без ссылки.</p>
</div>
</div>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2">
<article class="mp-card mp-card--compact">
<div class="mp-card__body">
<h3 class="mp-card__title">[2] compact</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">шаблон</li>
</ul>
<p class="mp-card__desc">Текстовая карточка на две клетки.</p>
</div>
</article>
</li>
<li class="mini-projects-grid__item" data-mp-cells="1">
<a class="mp-card mp-card--link mp-card--compact" href="#" aria-label="[1] compact · ссылка">
<div class="mp-card__body">
<h3 class="mp-card__title">
[1] compact · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">cli</li>
</ul>
<p class="mp-card__desc">Узкая колонка со ссылкой.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="1">
<article class="mp-card mp-card--compact">
<div class="mp-card__body">
<h3 class="mp-card__title">[1] compact</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">документ</li>
</ul>
<p class="mp-card__desc">Compact на одну клетку.</p>
</div>
</article>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2">
<div class="mp-card mp-card--decor" aria-hidden="true">
<span class="mp-card__decor-pattern" aria-hidden="true"></span>
</div>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3">
<a class="mp-card mp-card--link mp-card--stack" href="#" aria-label="[3] stack · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[3] stack · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">приложение</li>
</ul>
<p class="mp-card__desc">
Половина шестиклеточной строки. Длиннее описание, чтобы проверить перенос и высоту после смены шрифта.
</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3">
<a class="mp-card mp-card--link mp-card--horizontal" href="#" aria-label="[3] horizontal · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="360"
height="203"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[3] horizontal · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">плагин</li>
</ul>
<p class="mp-card__desc">Широкий horizontal: превью и текст в одной полосе.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3">
<div class="mp-card mp-card--horizontal">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../assets/shared/placeholder.webp"
alt=""
width="360"
height="203"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">[3] horizontal</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">виджет</li>
</ul>
<p class="mp-card__desc">Horizontal на три клетки, без ссылки.</p>
</div>
</div>
</li>
</ol>
</div>
</section>
<section id="fixed-height" class="section section--surface mp-showcase-grid-section" aria-labelledby="fixed-height-title">
<div class="section__inner">
<h2 id="fixed-height-title" class="section__title">Фиксированная высота</h2>
<p class="section__prose">
Атрибут <code>data-mp-height="…"</code> на <code>li.mini-projects-grid__item</code> задаёт фиксированную высоту карточки в пикселях. Текст обрезается с многоточием, если не влезает.
</p>
<ol
id="mini-projects-fixed-height-grid"
class="mini-projects-grid mp-showcase-grid"
data-mini-projects-list
>
<li class="mini-projects-grid__item" data-mp-cells="4" data-mp-cells-sm="4" data-mp-cells-xs="2" data-mp-height="600">
<a class="mp-card mp-card--link mp-card--stack" href="#" aria-label="[4→4→2] height=600 · stack · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[4→4→2] height=600 · stack · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">веб-сайт</li>
</ul>
<p class="mp-card__desc">4 → 4 → 2. data-mp-cells-sm="4" data-mp-cells-xs="2". Высота 600px.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-mp-height="600" data-mp-height-sm="400">
<a class="mp-card mp-card--link mp-card--stack" href="#" aria-label="[2] height=600 · stack · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[2] height=600 · stack · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">заметка</li>
</ul>
<p class="mp-card__desc">Две клетки, высота 600px.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-mp-height="400">
<a class="mp-card mp-card--link mp-card--stack" href="#" aria-label="[2] height=400 · stack · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[2] height=400 · stack · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">утилита</li>
</ul>
<p class="mp-card__desc">Две клетки, высота 400px.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-mp-height="400">
<a class="mp-card mp-card--link mp-card--stack" href="#" aria-label="[2] height=400 · stack · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[2] height=400 · stack · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">шаблон</li>
</ul>
<p class="mp-card__desc">Две клетки, высота 400px.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-mp-height="400">
<a class="mp-card mp-card--link mp-card--stack" href="#" aria-label="[2] height=400 · stack · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[2] height=400 · stack · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">плагин</li>
</ul>
<p class="mp-card__desc">Две клетки, высота 400px.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-mp-cells-xs="1">
<a class="mp-card mp-card--link mp-card--compact" href="#" aria-label="[3→2→1] · compact · ссылка">
<div class="mp-card__body">
<h3 class="mp-card__title">
[3→2→1] · compact · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">плагин</li>
</ul>
<p class="mp-card__desc">3 → 2 → 1. data-mp-cells-sm="2" data-mp-cells-xs="1". Авто-высота.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-mp-cells-xs="1">
<a class="mp-card mp-card--link mp-card--compact" href="#" aria-label="[3→2→1] · compact · ссылка">
<div class="mp-card__body">
<h3 class="mp-card__title">
[3→2→1] · compact · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">документ</li>
</ul>
<p class="mp-card__desc">3 → 2 → 1. data-mp-cells-sm="2" data-mp-cells-xs="1".</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-mp-height="400">
<a class="mp-card mp-card--link mp-card--stack-bottom" href="#" aria-label="[2] height=400 · stack-bottom · ссылка">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/shared/placeholder.webp"
alt=""
width="560"
height="315"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">
[2] height=400 · stack-bottom · ссылка
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">плагин</li>
</ul>
<p class="mp-card__desc">Текст сверху, изображение снизу. Высота 400px.</p>
</div>
</a>
</li>
</ol>
</div>
</section>
</main>
<footer class="footer">
<div class="footer__inner">
<span class="footer__chunk footer__chunk--date">© 2015–2026<span class="footer__sep" aria-hidden="true"> · </span></span>
<span class="footer__chunk footer__chunk--main">Дизайн и разработка сайта — Николай Мамадаев</span>
<span class="footer__chunk footer__chunk--meta footer__attr"><span class="footer__sep" aria-hidden="true"> · </span><a href="../../cases/design-system/">Design System</a><span class="footer__sep" aria-hidden="true"> · </span><a href="../../cookie-policy/">Политика cookie</a></span>
<span class="footer__chunk footer__chunk--lang"><span class="footer__sep" aria-hidden="true"> · </span><nav class="lang-switch" aria-label="Выбор языка" data-lang-switch>
<a class="lang-switch__btn" data-lang="en" href="/en/">English</a>
</nav>
<script>
(function () {
var p = location.pathname.replace(/^\/+/, "");
var isEn = p.indexOf("en/") === 0;
var base = isEn ? p.slice(3) : p;
var links = document.querySelectorAll("[data-lang-switch] [data-lang]");
for (var i = 0; i < links.length; i++) {
var l = links[i].getAttribute("data-lang");
links[i].setAttribute("href", (l === "en" ? "/en/" : "/") + base);
}
})();
</script>
</span>
</div>
</footer>
<aside class="cookie-notice glass-neutral" hidden data-cookie-notice>
<p class="cookie-notice__text">
Сайт использует cookie для комфортной работы. Подробнее — в
<a href="../../cookie-policy/" data-cookie-policy-link>cookie policy</a>.
</p>
<button type="button" class="btn btn--secondary btn--flat cookie-notice__accept" data-cookie-accept>
<span>Принять</span>
<span class="cookie-notice__accept-icon" aria-hidden="true">
<img src="../../assets/shared/icons/сookie.svg" alt="" width="20" height="20" loading="eager" decoding="async" />
</span>
</button>
</aside>
<div id="nav-more-menu-floating" class="nav-more__floating" hidden data-nav-more-menu-floating aria-hidden="true"></div>
</body>
</html>