Files
site-portfolio/public/en/cases/mini-projects-showcase/index.html
T
root 5fd8324b47 feat(glass): Liquid Glass эффект через SVG displacement-фильтр (по статье Хабра)
- PNG карта мелкого равномерного шума (public/assets/liquid-glass/displacement.png)
- инлайн SVG feDisplacementMap инъецируется во все страницы сборщиком (build-pages.mjs)
- glass-neutral + шапка/панели/FAB/overflow получают url(#filter) blur в Chromium
- Safari/Firefox: детект через @supports not (hanging-punctuation:first), остаётся чистый blur
- токены --v12-glass-neutral-filter-lg / -elevated-lg для обеих тем
2026-09-24 15:14:55 +02:00

1415 lines
61 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="en" 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=""Activity" Section Card Catalog: Types, Cell Widths, and a Live Masonry Grid."
/>
<title>Activity — Card Catalog</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="stylesheet" href="/css/main.css" />
<link rel="canonical" href="https://mamadaevv.site/en/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="Nikolai Mamadaev" />
<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="Nikolai Mamadaev — Product Designer" />
<meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="ru_RU" />
<meta property="og:title" content="Activity — Card Catalog" />
<meta property="og:description" content=""Activity" Section Card Catalog: Types, Cell Widths, and a Live Masonry Grid." />
<meta property="og:url" content="https://mamadaevv.site/en/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="Activity — Card Catalog" />
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Activity — Card Catalog" />
<meta name="twitter:description" content=""Activity" Section Card Catalog: Types, Cell Widths, and a Live Masonry Grid." />
<meta name="twitter:image" content="https://mamadaevv.site/assets/og/og-default.png" />
<meta name="twitter:image:alt" content="Activity — Card Catalog" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Person",
"@id": "https://mamadaevv.site/#person",
"name": "Nikolai Mamadaev",
"url": "https://mamadaevv.site",
"jobTitle": "Product Designer",
"description": "Product designer with 10+ years of experience. Designing digital products, interfaces and design systems.",
"knowsAbout": [
"UX/UI design",
"product design",
"design systems",
"prototyping",
"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": "Nikolai Mamadaev — Product Designer",
"inLanguage": "en",
"author": {
"@id": "https://mamadaevv.site/#person"
},
"publisher": {
"@id": "https://mamadaevv.site/#person"
}
},
{
"@type": "CreativeWork",
"@id": "https://mamadaevv.site/en/cases/mini-projects-showcase/#page",
"url": "https://mamadaevv.site/en/cases/mini-projects-showcase/",
"name": "Activity — Card Catalog",
"description": "\"Activity\" Section Card Catalog: Types, Cell Widths, and a Live Masonry Grid.",
"isPartOf": {
"@id": "https://mamadaevv.site/#website"
},
"author": {
"@id": "https://mamadaevv.site/#person"
},
"inLanguage": "en"
},
{
"@type": "BreadcrumbList",
"@id": "https://mamadaevv.site/en/cases/mini-projects-showcase/#breadcrumbs",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Nikolai Mamadaev — Product Designer",
"item": "https://mamadaevv.site/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Cases",
"item": "https://mamadaevv.site/cases/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Activity — Card Catalog",
"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="28" xChannelSelector="R" yChannelSelector="G"/>
</filter>
</svg>
<a class="skip-link" href="#main">Skip to main content</a>
<header class="header" data-theme-picker>
<div class="header__inner">
<a class="logo" href="../../" aria-label="Home">NM</a>
<div class="header__cluster">
<nav class="nav" aria-label="Page sections">
<a href="../../">Home</a>
</nav>
</div>
</div>
</header>
<div class="fab-stack" aria-label="Quick Actions">
<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="Style Settings">
<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="Theme and Design" 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">Accent Color</h3>
<div class="colorpicker" data-colorpicker>
<div class="colorpicker__plane-box">
<canvas class="colorpicker__plane" width="320" height="180" data-sl-canvas aria-label="Saturation and brightness of the accent"></canvas>
<div class="colorpicker__knob" data-sl-knob></div>
</div>
<div class="colorpicker__hue-wrap">
<label class="colorpicker__hue-sr" for="theme-hue-slider">Accent shade</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">Theme</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="As in the system"><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="Light-colored theme"><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="Dark Theme"><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">Corner Rounding</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="Slight rounding"><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="Average rounding"><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="Significant rounding"><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">Typography</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">Page Background</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="Show background gradient"><span class="theme-mesh-bg-seg__label">On</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="Hide the background gradient"><span class="theme-mesh-bg-seg__label">Off</span></button>
</div>
</section>
</div>
</div>
</div>
<a class="fab fab--primary" href="../../#contact" aria-label="Contact Us">
<span class="fab-primary__ico" aria-hidden="true"></span>
</a>
</div>
<div class="content-edit-fab-stack" aria-label="Content Edits" 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="Content Editing Mode"
title="Edit Mode"
>
<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 · Activity</p>
<h1 class="case-page__h1">Activity Cards</h1>
<p class="case-page__sub">All card types and a live grid for selecting layouts on the homepage</p>
<p class="section__prose">
Layout and content page: the same classes and scripts are used here as in the section on the homepage. Change the font and theme in the panel on the right;
narrow the window, and the grid will recalculate. The text in the card headers is just placeholder text; you can remove it when previewing.
</p>
<nav class="mp-showcase-toc" aria-label="Catalog Table of Contents">
<p><strong>Sections</strong></p>
<ol>
<li><a href="#types">Card Types</a></li>
<li><a href="#cells">Width in Cells</a></li>
<li><a href="#grid">Live Masonry Grid</a></li>
<li><a href="#fixed-height">Fixed height</a></li>
</ol>
</nav>
<h2 id="intro">How to Use</h2>
<p class="section__prose">
First, select <strong>a type</strong> (stack, horizontal, compact, decor), then <strong>data-mp-cells</strong> (1–4) and
the order <code>li</code> in the list. Cards with <code>data-mp-extra</code> do not affect the height of the list until the “Show All” button is clicked.
For more details, see <code>docs/mini-projects-cards.md</code>.
</p>
<h2 id="types">Card Types</h2>
<p class="section__prose">
On the left is an example of a text card; on the right is the type name and an explanation of the classes.
</p>
<div class="mp-showcase-specimens" aria-label="Card Types">
<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 with a link — example card"
>
<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 with a link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">website</li>
</ul>
<p class="mp-card__desc">
A small script: drafts generated from keywords, without external APIs.
</p>
</div>
</a>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Stack with a link.</strong> Full-width preview, text at the bottom. For an external link:
<code>mp-card--link</code> and <code>mp-card__ext</code> in the title (as in the &quot;Experience&quot; block).
</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> without a link</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 without a link</h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">note</li>
</ul>
<p class="mp-card__desc">
A collection of databases and a rollup for a weekly overview, for personal use only.
</p>
</div>
</div>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Stack without a link.</strong> Same view. Wrapper <code>div</code> or <code>article</code>, without
<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 with a link — example card"
>
<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 with a link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">app</li>
</ul>
<p class="mp-card__desc">
Local timer with short sounds and a feature to block unnecessary tabs.
</p>
</div>
</a>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Horizontal with a link.</strong> Thumbnail on the left, text on the right. On a narrow screen, the card
stacks vertically.
</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> No link</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 without a link</h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">Template</li>
</ul>
<p class="mp-card__desc">
Static website with a schedule and an application form on a separate service.
</p>
</div>
</div>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Horizontal without a link.</strong> Same layout; the card does not link to an external 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 with a link — example card"
>
<div class="mp-card__body">
<h3 class="mp-card__title">
Compact with a link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">script</li>
</ul>
<p class="mp-card__desc">
Prefixes, numbering, and a dry run before writing files to disk.
</p>
</div>
</a>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Compact with a link.</strong> Text only, no <code>mp-card__media</code>. In the grid, often with
<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> no link</p>
<article class="mp-card mp-card--compact">
<div class="mp-card__body">
<h3 class="mp-card__title">Compact without a link</h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">document</li>
</ul>
<p class="mp-card__desc">
Markdown checklist before releasing minor UI changes.
</p>
</div>
</article>
</div>
<div class="mp-showcase-specimen__desc">
<p class="section__prose">
<strong>Compact without a link.</strong> A short note or checklist without an external link.
</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>Decorative cell.</strong> A placeholder in the grid, with no project or link. Inside
<code>span.mp-card__decor-pattern</code>.
</p>
</div>
</div>
</div>
<h2 id="cells">Width in Cells</h2>
<p class="section__prose">
Attribute <code>data-mp-cells</code> sets <code>li.mini-projects-grid__item</code> specifies the desired width from 1 to 4 cells.
The script will not allow the card to be wider than the current row.
</p>
<p class="section__prose">
For responsive width, use <code>data-mp-cells-sm</code> (for 4 cells per row, ≥ 880 px) and
<code>data-mp-cells-xs</code> (for 2 cells, ≥ 520 px). The script selects the value based on the current number of cells in a row.
</p>
<div class="mp-showcase-attrs-table">
<table class="mp-showcase-cells-table">
<thead>
<tr>
<th scope="col">The</th>
<th scope="col">When to use</th>
<th scope="col">Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>data-mp-cells</code></td>
<td>Wide screen (6 cells, ≥ 1180 px)</td>
<td><code>data-mp-cells="3"</code></td>
</tr>
<tr>
<td><code>data-mp-cells-sm</code></td>
<td>Medium screen (4 cells, ≥ 880 px)</td>
<td><code>data-mp-cells-sm="2"</code></td>
</tr>
<tr>
<td><code>data-mp-cells-xs</code></td>
<td>Narrow screen (2 cells, ≥ 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>Example:</strong> <code>data-mp-cells="3" data-mp-cells-sm="2"</code> — A card takes up 3 cells on a wide screen, 2 on a medium screen, and 1 on a narrow screen.
</p>
<figure class="mp-showcase-cells-demo" aria-label="Chart showing the width of the cards in the cells">
<div class="mp-showcase-cells-demo__variant">
<div class="mp-showcase-cells-demo__legend mp-showcase-cells-demo__legend--6" aria-hidden="true">
<span>123456</span>
</div>
<p class="mp-showcase-cells-demo__row-label">≥ 1180 px — 6 cells</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>1234</span>
</div>
<p class="mp-showcase-cells-demo__row-label">≥ 880 px — 4 cells</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>12</span>
</div>
<p class="mp-showcase-cells-demo__row-label">≥ 520 px — 2 cells</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">
The number in the block = the value <code>data-mp-cells</code>. The card occupies that many cells in a row. On a narrow screen, there are fewer cells—cards with a width greater than the number of cells are carried over to the next row. As <code>data-mp-cells-sm</code> / <code>data-mp-cells-xs</code> the card changes its width when the view is narrowed.
</figcaption>
</figure>
<table class="mp-showcase-cells-table">
<thead>
<tr>
<th scope="col">List width</th>
<th scope="col">Cells per row</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>already</td>
<td>1 (entire row)</td>
</tr>
</tbody>
</table>
<p class="section__prose">
A pair on six cells <code>3 + 3</code> or a trio <code>2 + 2 + 2</code> usually looks neater than a random set.
Decor (<code>mp-card--decor</code>) fills in the gaps in the grid.
</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">Live masonry grid</h2>
<p class="section__prose">
A demo of the “Activity” block from the homepage: here, all card types are in a single live grid. Each card’s caption shows the type of
layout and <code>data-mp-cells</code>. Change the window width and font in the theme panel, watch how the grid
rearranges itself, and apply the best layout to the homepage.
</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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">website</li>
</ul>
<p class="mp-card__desc">A base pair in a row of six cells.</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="Project Type">
<li class="mp-card__tag">note</li>
</ul>
<p class="mp-card__desc">Stack without an external link.</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="Project Type">
<li class="mp-card__tag">utility</li>
</ul>
<p class="mp-card__desc">Horizontal, two cells wide, without a link.</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="Project Type">
<li class="mp-card__tag">template</li>
</ul>
<p class="mp-card__desc">A two-cell text card.</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 · link">
<div class="mp-card__body">
<h3 class="mp-card__title">
[1] compact · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">cli</li>
</ul>
<p class="mp-card__desc">A narrow column with a link.</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="Project Type">
<li class="mp-card__tag">document</li>
</ul>
<p class="mp-card__desc">Compact, one cell wide.</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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">Appendix</li>
</ul>
<p class="mp-card__desc">
Half of a six-cell row. A longer description to check for line breaks and height after changing the font.
</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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">plugin</li>
</ul>
<p class="mp-card__desc">Wide horizontal: preview and text in a single column.</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="Project Type">
<li class="mp-card__tag">widget</li>
</ul>
<p class="mp-card__desc">Three-cell Horizontal, without a link.</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">Fixed Height</h2>
<p class="section__prose">
Attribute <code>data-mp-height="…"</code> sets <code>li.mini-projects-grid__item</code> sets a fixed card height in pixels. Text is truncated with an ellipsis if it doesn’t fit.
</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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">website</li>
</ul>
<p class="mp-card__desc">4 → 4 → 2. data-mp-cells-sm=&quot;4&quot; data-mp-cells-xs=&quot;2&quot;. Height 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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">note</li>
</ul>
<p class="mp-card__desc">Two cells, height 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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">Utility</li>
</ul>
<p class="mp-card__desc">Two cells, 400px high.</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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">template</li>
</ul>
<p class="mp-card__desc">Two cells, 400px tall.</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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">plugin</li>
</ul>
<p class="mp-card__desc">Two cells, 400px tall.</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 · link">
<div class="mp-card__body">
<h3 class="mp-card__title">
[3→2→1] · compact · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">plugin</li>
</ul>
<p class="mp-card__desc">3 → 2 → 1. data-mp-cells-sm=&quot;2&quot; data-mp-cells-xs=&quot;1&quot;. Auto-height.</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 · link">
<div class="mp-card__body">
<h3 class="mp-card__title">
[3→2→1] · compact · link
<span class="mp-card__ext" aria-hidden="true"></span></h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">document</li>
</ul>
<p class="mp-card__desc">3 → 2 → 1. data-mp-cells-sm=&quot;2&quot; data-mp-cells-xs=&quot;1&quot;.</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 · link">
<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 · link
<span class="mp-card__ext" aria-hidden="true"></span>
</h3>
<ul class="mp-card__tags" aria-label="Project Type">
<li class="mp-card__tag">plugin</li>
</ul>
<p class="mp-card__desc">Text on top, image at the bottom. Height 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">Website design and development — Nikolay Mamadaev</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 policy</a></span> ·<span class="footer__chunk footer__chunk--lang"><span class="footer__sep" aria-hidden="true">
</span><nav class="lang-switch" aria-label="Choose language" data-lang-switch>
<a class="lang-switch__btn" data-lang="ru" href="/">Русский</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">
This site uses cookies to ensure a smooth user experience. For more information, see the
<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>Accept</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>