Files
site-portfolio/public/en/cases/template-elements-demo/index.html
T

1306 lines
75 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="A demo page for the case study featuring all the developed elements: link cards and media blocks." />
<title>Case Study Elements Demo — template</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 DEF = { h: 210, s: 90, l: 40 };
function pickAccent(o) {
if (!o) return null;
var h = typeof o.h === "number" ? o.h : typeof o.hue === "number" ? o.hue : null;
var s = typeof o.s === "number" ? o.s : typeof o.sat === "number" ? o.sat : null;
var l = typeof o.l === "number" ? o.l : typeof o.light === "number" ? o.light : null;
if (h === null && s === null && l === null) return null;
return {
h: h === null ? DEF.h : h,
s: s === null ? DEF.s : Math.min(100, Math.max(30, s)),
l: l === null ? DEF.l : Math.min(70, Math.max(20, l)),
};
}
// Текущий формат: отдельный акцент для светлой и тёмной темы ({light, dark}).
// Старый плоский формат ({h, s, l}) остаётся совместимым.
var acc =
pickAccent(eff === "dark" ? t.dark : t.light) ||
pickAccent(eff === "dark" ? t.light : t.dark) ||
pickAccent(t) ||
DEF;
d.style.setProperty("--theme-h", String(acc.h));
d.style.setProperty("--theme-s", String(acc.s));
d.style.setProperty("--theme-l", String(acc.l));
var H = (((acc.h % 360) + 360) % 360) / 360;
var S = Math.min(100, Math.max(0, acc.s)) / 100;
var Lv = Math.min(100, Math.max(0, acc.l)) / 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 = "geologica";
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>
<!-- Аналитика грузится только на прод-домене: dev/preview (localhost:4175, dev.mamadaevv.site) в статистику не попадают. -->
<script>
(function () {
var PROD_HOSTS = ["mamadaevv.site", "www.mamadaevv.site"];
if (PROD_HOSTS.indexOf(location.hostname) === -1) return;
var s = document.createElement("script");
s.defer = true;
s.src = "https://cloud.umami.is/script.js";
s.setAttribute("data-website-id", "59e8e66e-2dab-4a86-acc6-b97985ad0473");
document.head.appendChild(s);
})();
</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/en/cases/template-elements-demo/" />
<link rel="alternate" hreflang="ru" href="https://mamadaevv.site/cases/template-elements-demo/" />
<link rel="alternate" hreflang="en" href="https://mamadaevv.site/en/cases/template-elements-demo/" />
<link rel="alternate" hreflang="x-default" href="https://mamadaevv.site/cases/template-elements-demo/" />
<link rel="describedby" href="https://mamadaevv.site/llms.txt" title="llms.txt" />
<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="Case Study Elements Demo — template" />
<meta property="og:description" content="A demo page for the case study featuring all the developed elements: link cards and media blocks." />
<meta property="og:url" content="https://mamadaevv.site/en/cases/template-elements-demo/" />
<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="Case Study Elements Demo — template" />
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Case Study Elements Demo — template" />
<meta name="twitter:description" content="A demo page for the case study featuring all the developed elements: link cards and media blocks." />
<meta name="twitter:image" content="https://mamadaevv.site/assets/og/og-default.png" />
<meta name="twitter:image:alt" content="Case Study Elements Demo — template" />
<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/template-elements-demo/#page",
"url": "https://mamadaevv.site/en/cases/template-elements-demo/",
"name": "Case Study Elements Demo — template",
"description": "A demo page for the case study featuring all the developed elements: link cards and media blocks.",
"isPartOf": {
"@id": "https://mamadaevv.site/#website"
},
"author": {
"@id": "https://mamadaevv.site/#person"
},
"inLanguage": "en"
},
{
"@type": "BreadcrumbList",
"@id": "https://mamadaevv.site/en/cases/template-elements-demo/#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": "Case Study Elements Demo — template",
"item": "https://mamadaevv.site/cases/template-elements-demo/"
}
]
}
]
}
</script>
<script src="/js/skill-connectors.js" defer></script>
<script type="module" src="/js/main.js"></script>
<link rel="stylesheet" href="/vendor/plyr/plyr.css" />
<style>
.case-article {
max-width: 48rem;
margin: 0 auto;
}
.case-article h2 {
margin: 2rem 0 0.75rem;
font-size: clamp(1.15rem, 2vw, 1.35rem);
font-weight: 700;
color: var(--text);
}
.case-article h3 {
font-size: 1.05rem;
font-weight: 600;
color: var(--text);
}
.case-article .section__prose {
margin-bottom: 0.75rem;
color: var(--text);
}
.case-article a:not(.btn) {
color: var(--accent);
}
</style>
<script>
(function () {
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 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") {
d.dataset.font = f;
} else {
d.dataset.font = "merriweather";
}
var mb = t.meshBg;
if (mb === "off") {
d.dataset.meshBg = "off";
} else {
d.dataset.meshBg = "on";
}
} catch (e) {}
})();
</script>
</head>
<body>
<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" class="section section--tight">
<div class="section__inner case-article">
<p class="hero__eyebrow">Demo · template</p>
<h1 class="case-page__h1">Case Study Example: Demo Elements</h1>
<p class="case-page__sub">Text blocks and elements for quickly building a longread</p>
<p class="section__prose case-page__code">The media for the demo is located in <code>public/assets/</code> (the page builder does not copy them). See <code>public/assets/README.md</code>.</p>
<h2>Context</h2>
<p class="section__prose">
This section shows an example of a text block for the introduction. Here, you can briefly describe the product, the audience, and the project’s limitations.
The text is neutral: the goal is to quickly sketch out the structure and check the page’s visual rhythm.
</p>
<h2>Task</h2>
<p class="section__prose">
We need to put together a demo longread with the main UI elements: link cards, media blocks, and examples of content layout.
It’s also important to check how the blocks behave on narrow and wide screens.
</p>
<h2>Solution</h2>
<p class="section__prose">Below are some ready-made examples that you can copy into real-world projects.</p>
<h3>Link Cards</h3>
<div class="case-links-grid">
<a class="case-link-card" href="https://www.examples.com/" target="_blank" rel="noopener noreferrer"><span class="case-link-card__media" aria-hidden="true"><img src="../../../assets/template-elements-demo/wallpaper-1.jpg" alt="" loading="lazy" decoding="async" /></span></a>
<a class="case-link-card" href="https://www.examples.com/" target="_blank" rel="noopener noreferrer"><span class="case-link-card__body"><span class="case-link-card__title">Source with preview</span>
<span class="case-link-card__meta">Short caption: what the user will see when they click the link</span>
www.examples.com
</span></a><a class="case-link-card case-link-card--favicon" href="https://www.examples.com/docs" target="_blank" rel="noopener noreferrer"><span class="case-link-card__media" aria-hidden="true"><img src="https://www.google.com/s2/favicons?domain=www.examples.com&sz=128" alt="" decoding="async" /></span></a>
<a class="case-link-card case-link-card--favicon" href="https://www.examples.com/docs" target="_blank" rel="noopener noreferrer"><span class="case-link-card__body"><span class="case-link-card__title">Source with favicon</span>
<span class="case-link-card__meta">Option for simpler links without a screenshot</span>
<span class="case-link-card__url">www.examples.com/docs</span>
</span></a></div>
<h3>Image: 50% of the original size (no preview)</h3>
<figure class="case-media case-media--natural">
<img src="../../../assets/template-elements-demo/hello.gif" alt="Full-size demo GIF" loading="lazy" decoding="async" />
<figcaption>Image at reduced actual size, centered.</figcaption>
</figure>
<h3>Image: width of the content (with preview)</h3>
<figure class="case-media case-media--content">
<img src="../../../assets/template-elements-demo/wallpaper-1.jpg" alt="Demo images based on content width" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Image matches the width of the content. Clicking opens the viewer.</figcaption>
</figure>
<h3>Image: slightly wider than the content (with preview)</h3>
<figure class="case-media case-media--wide">
<img src="../../../assets/template-elements-demo/wallpaper-1.jpg" alt="The demo image is wider than the content" loading="lazy" decoding="async" data-image-viewer />
<figcaption>The image extends slightly beyond the content column.</figcaption>
</figure>
<h3>Long screenshot: collapsed height and button inside the preview</h3>
<p class="section__prose">
Block <code>data-case-tall-media</code>: width is specified by the <code>data-case-tall-width="wide"</code> (like a “wide” image) or <code>content</code> (per column). When collapsed, the top part of the frame is visible; at the bottom of the preview, there’s a solid “fog.” An “Expand” / “Collapse” button with a chevron icon appears above the bottom of the frame, inside the border; when expanded, the fog disappears; when collapsed, the page scrolls back to the preview. The preview height is set <code>.case-media-tall__clip</code> via <code>--case-tall-max</code> (default <code>min(70vh, 32rem)</code>).
</p>
<figure class="case-media" data-case-tall-media data-case-tall-width="wide">
<div class="case-media-tall" data-case-tall-root>
<div class="case-media-tall__clip" id="template-demo-tall-clip" data-case-tall-clip>
<img
src="../../../assets/template-elements-demo/img-long.webp"
alt="Demo: Long vertical screenshot of the interface"
width="1200"
loading="lazy"
decoding="async"
data-image-viewer
/>
<div class="case-media-tall__fog" aria-hidden="true"></div>
</div>
<div class="case-media-tall__actions">
<button
type="button"
class="case-media-tall__btn btn btn--ghost btn--flat"
data-case-tall-toggle
aria-expanded="false"
aria-controls="template-demo-tall-clip"
><span class="case-media-tall__btn-chev" aria-hidden="true"></span>
<span data-case-tall-label-more>Expand</span>
<span data-case-tall-label-less hidden>Collapse</span>
</button>
</div>
</div>
<figcaption>Long image <code>img-long.webp</code> (source PNG next to it in <code>public/assets/template-elements-demo/</code>).</figcaption>
</figure>
<h3>Gallery: grid based on content width</h3>
<p class="section__prose">
The number of columns on a wide screen is set via <code>figure</code>: the <code>style="--case-gallery-cols: 3"</code>. Below these thresholds, the grid collapses to two or one column. Clicking on a thumbnail opens the general case viewer.
</p>
<figure class="case-media case-media-gallery" style="--case-gallery-cols: 3">
<div class="case-media-gallery__grid">
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-1.webp" alt="Demo Thumbnail 1" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-2.webp" alt="Demo Thumbnail 2" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-3.webp" alt="Demo Thumbnail 3" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-4.webp" alt="Demo Thumbnail 4" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-5.webp" alt="Demo Thumbnail 5" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-6.webp" alt="Demo Thumbnail 6" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-7.webp" alt="Demo Thumbnail 7" loading="lazy" decoding="async" data-image-viewer />
</div>
</div>
<figcaption>Grid in an article column; in this example, three columns on a desktop.</figcaption>
</figure>
<h3>Gallery: Full-page grid with side margins</h3>
<p class="section__prose">
The <code>case-media-gallery--wide</code> — same logic for extending beyond a column as in the single-column <code>case-media--wide</code>. Here, there are four columns on a wide screen (<code>--case-gallery-cols: 4</code>).
</p>
<figure class="case-media case-media-gallery case-media-gallery--wide" style="--case-gallery-cols: 4">
<div class="case-media-gallery__grid">
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-1.webp" alt="Wide shot, frame 1" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-2.webp" alt="Wide shot, take 2" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-3.webp" alt="Wide shot, take 3" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-4.webp" alt="Wide shot, take 4" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-5.webp" alt="Wide shot, take 5" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-6.webp" alt="Wide shot, shot 6" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-gallery__item">
<img src="../../../assets/template-elements-demo/small-image-7.webp" alt="Wide shot, shot 7" loading="lazy" decoding="async" data-image-viewer />
</div>
</div>
<figcaption>Width is the same as for a `wide` image; on a narrow screen, the `wide` wrapper collapses to 100% of the column width (see media queries).</figcaption>
</figure>
<h3>Slider: one image, arrows, and dots</h3>
<p class="section__prose">
Block <code>.case-media-slider</code> displays slides one at a time: navigation using “prev/next” arrows,
paging using dots (the active one is highlighted in accent color). Keyboard support (←/→, Home/End on dots),
touch/mouse swipe, and lazy loading of adjacent frames via <code>data-src</code>. Clicking on the active
slide opens the general case viewer.
</p>
<figure class="case-media case-media-slider" data-case-media-slider>
<div class="case-media-slider__viewport" data-slider-viewport>
<div class="case-media-slider__track" data-slider-track>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-demo-1.webp" alt="Slider, Frame 1" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-demo-2.webp" alt="Slider, Frame 2" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-demo-3.webp" alt="Slider, Frame 3" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-demo-4.webp" alt="Slider, Frame 4" loading="lazy" decoding="async" data-image-viewer />
</div>
</div>
<div class="case-media-slider__nav-group">
<button type="button" class="case-media-slider__nav case-media-slider__nav--prev" data-slider-prev aria-label="Previous image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--left icon-press" aria-hidden="true"></span>
</button>
<button type="button" class="case-media-slider__nav case-media-slider__nav--next" data-slider-next aria-label="Next image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--right icon-press" aria-hidden="true"></span>
</button>
</div>
</div>
<div class="case-media-slider__dots" data-slider-dots role="tablist" aria-label="Selecting an Image">
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 1"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 2"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 3"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Figure 4"></button>
</div>
</figure>
<p class="section__prose">
The same slider, but with the modifier <code>case-media--wide</code> — extends beyond the content column,
like single wide images. The previous/next buttons and dots behave the same way.
</p>
<figure class="case-media case-media-slider case-media--wide" data-case-media-slider>
<div class="case-media-slider__viewport" data-slider-viewport>
<div class="case-media-slider__track" data-slider-track>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-1.webp" alt="Wide slider, frame 1" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-2.webp" alt="Wide slider, frame 2" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-3.webp" alt="Wide slider, frame 3" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-4.webp" alt="Wide slider, frame 4" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-5.webp" alt="Wide Slider, Frame 5" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-6.webp" alt="Wide slider, frame 6" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-7.webp" alt="Wide slider, frame 7" loading="lazy" decoding="async" data-image-viewer />
</div>
</div>
<div class="case-media-slider__nav-group">
<button type="button" class="case-media-slider__nav case-media-slider__nav--prev" data-slider-prev aria-label="Previous image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--left icon-press" aria-hidden="true"></span>
</button>
<button type="button" class="case-media-slider__nav case-media-slider__nav--next" data-slider-next aria-label="Next image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--right icon-press" aria-hidden="true"></span>
</button>
</div>
</div>
<div class="case-media-slider__dots" data-slider-dots role="tablist" aria-label="Selecting an Image">
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 1"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 2"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 3"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Figure 4"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 5"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 6"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Figure 7"></button>
</div>
</figure>
<p class="section__prose">
Slider with horizontal (wide) frames of the same size—all images
<code>1024×800</code>.
</p>
<figure class="case-media case-media-slider case-media--natural" data-case-media-slider style="--slider-natural-w: 1024px">
<div class="case-media-slider__viewport" data-slider-viewport>
<div class="case-media-slider__track" data-slider-track>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-1.webp" alt="Wide Slider, Frame 1" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-2.webp" alt="Wide Slider, Frame 2" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-3.webp" alt="Wide Slider, Frame 3" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-4.webp" alt="Wide Slider, Frame 4" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-5.webp" alt="Wide slider, frame 5" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-6.webp" alt="Wide Slider, Frame 6" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-wide-7.webp" alt="Wide Slider, Frame 7" loading="lazy" decoding="async" data-image-viewer />
</div>
</div>
<div class="case-media-slider__nav-group">
<button type="button" class="case-media-slider__nav case-media-slider__nav--prev" data-slider-prev aria-label="Previous image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--left icon-press" aria-hidden="true"></span>
</button>
<button type="button" class="case-media-slider__nav case-media-slider__nav--next" data-slider-next aria-label="Next image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--right icon-press" aria-hidden="true"></span>
</button>
</div>
</div>
<div class="case-media-slider__dots" data-slider-dots role="tablist" aria-label="Selecting an Image">
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 1"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 2"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 3"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Figure 4"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 5"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 6"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Figure 7"></button>
</div>
</figure>
<p class="section__prose">
The same slider, but with vertical frames of the same size—all images
<code>600×800</code>.
</p>
<figure class="case-media case-media-slider case-media--natural" data-case-media-slider style="--slider-natural-w: 600px">
<div class="case-media-slider__viewport" data-slider-viewport>
<div class="case-media-slider__track" data-slider-track>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-portrait-1.webp" alt="Vertical slider, frame 1" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-portrait-2.webp" alt="Vertical slider, frame 2" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-portrait-3.webp" alt="Vertical slider, frame 3" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-portrait-4.webp" alt="Vertical slider, frame 4" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-portrait-5.webp" alt="Vertical slider, frame 5" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-portrait-6.webp" alt="Vertical slider, frame 6" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-portrait-7.webp" alt="Vertical slider, frame 7" loading="lazy" decoding="async" data-image-viewer />
</div>
</div>
<div class="case-media-slider__nav-group">
<button type="button" class="case-media-slider__nav case-media-slider__nav--prev" data-slider-prev aria-label="Previous image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--left icon-press" aria-hidden="true"></span>
</button>
<button type="button" class="case-media-slider__nav case-media-slider__nav--next" data-slider-next aria-label="Next image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--right icon-press" aria-hidden="true"></span>
</button>
</div>
</div>
<div class="case-media-slider__dots" data-slider-dots role="tablist" aria-label="Selecting an Image">
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 1"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 2"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 3"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Figure 4"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 5"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 6"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Figure 7"></button>
</div>
</figure>
<p class="section__prose">
A separate example: a toggle for the frame-switching animation mode—
<code>slide</code> (slide), <code>fade</code> (crossfade), and <code>none</code> (no transition).
The mode is set by the attribute <code>data-slider-anim</code> on the shape.
</p>
<figure class="case-media case-media-slider" data-case-media-slider data-slider-anim="fade">
<div class="case-media-slider__anim-switch" data-slider-anim-switch role="group" aria-label="Slider Animation Mode">
<button type="button" data-mode="slide" aria-pressed="false">Slide</button>
<button type="button" data-mode="fade" aria-pressed="true">Fade</button>
<button type="button" data-mode="none" aria-pressed="false">None</button>
</div>
<div class="case-media-slider__viewport" data-slider-viewport>
<div class="case-media-slider__track" data-slider-track>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-demo-1.webp" alt="Animation example, frame 1" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-demo-2.webp" alt="Animation example, frame 2" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-demo-3.webp" alt="Animation example, frame 3" loading="lazy" decoding="async" data-image-viewer />
</div>
<div class="case-media-slider__slide">
<img src="../../../assets/template-elements-demo/slider-demo-4.webp" alt="Animation example, frame 4" loading="lazy" decoding="async" data-image-viewer />
</div>
</div>
<div class="case-media-slider__nav-group">
<button type="button" class="case-media-slider__nav case-media-slider__nav--prev" data-slider-prev aria-label="Previous image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--left icon-press" aria-hidden="true"></span>
</button>
<button type="button" class="case-media-slider__nav case-media-slider__nav--next" data-slider-next aria-label="Next image">
<span class="case-media-slider__nav-ico case-media-slider__nav-ico--right icon-press" aria-hidden="true"></span>
</button>
</div>
</div>
<div class="case-media-slider__dots" data-slider-dots role="tablist" aria-label="Selecting an Image">
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 1"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 2"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Image 3"></button>
<button type="button" class="case-media-slider__dot" data-slider-dot aria-label="Go to Figure 4"></button>
</div>
</figure>
<h3>Callout: Important paragraph with an emoji</h3>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>This is an example of a highlighted callout block. The emoji is aligned to the left and pulses to visually emphasize an important point.</p>
</div>
</aside>
<h3>Video: Card with a shadow and fixed small corners</h3>
<figure class="case-media" style="margin: 0.85rem 0 1.2rem">
<div class="case-video-shell">
<video controls preload="metadata" playsinline poster="../../../assets/template-elements-demo/video-1.webp" data-case-video-player style="display: block; width: 100%; height: auto">
<source src="../../../assets/template-elements-demo/video-1.webm" type="video/webm" />
<source src="../../../assets/template-elements-demo/video-1.mp4" type="video/mp4" />
Your browser does not support video.
</video>
</div>
<figcaption>An example of a case-style video card.</figcaption>
</figure>
<h2>Result</h2>
<p class="section__prose">
The template is ready to be copied into a new case study: all that’s left is to replace the fish with the actual text and insert the project’s real links and media.
</p>
</div>
</main>
<section id="portfolio" class="section section--surface" data-reveal>
<div class="section__inner">
<h2 class="section__title">Portfolio</h2>
<ol class="mini-projects-grid" data-mini-projects-list>
<li class="mini-projects-grid__item" data-mp-cells="2" data-related-slug="ai-platform-primo-rpa">
<a class="mp-card mp-card--link mp-card--stack-bottom" data-glass="false" href="ai-platform-primo-rpa/" aria-label="AI platform — open case">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../../assets/ai-platform-primo-rpa/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Primo AI Server</h3>
<ul class="mp-card__tags" aria-label="Project type">
<li class="mp-card__tag">UX/UI</li>
<li class="mp-card__tag">Web App</li>
</ul>
<p class="mp-card__desc">Interface for training and using AI models in business processes: data work, automation scenarios, and Smart OCR.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-related-slug="lunacy">
<a class="mp-card mp-card--link mp-card--stack-bottom" data-glass="false" href="lunacy/" aria-label="Graphics editor Lunacy — open case">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../../assets/lunacy/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Lunacy</h3>
<ul class="mp-card__tags" aria-label="Project type">
<li class="mp-card__tag">UX/UI</li>
<li class="mp-card__tag">Desktop App</li>
</ul>
<p class="mp-card__desc">Work on the Lunacy interface at Icons8: user flows, complex prototypes, improved functionality and interface logic.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-related-slug="manu-game-maker" hidden>
<a class="mp-card mp-card--link mp-card--stack-bottom" data-glass="false" href="manu-game-maker/" aria-label="MANU game engine — open case">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../../assets/manu-game-maker/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">MANU game engine</h3>
<ul class="mp-card__tags" aria-label="Project type">
<li class="mp-card__tag">Product Design</li>
<li class="mp-card__tag">Desktop App</li>
</ul>
<p class="mp-card__desc">Redesign and evolution of the game engine interface: a new working concept, refreshed visual system, and hundreds of complex user scenarios worked through.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-related-slug="mobile-clinic-app" hidden>
<a class="mp-card mp-card--link mp-card--stack-bottom" data-glass="false" href="mobile-clinic-app/" aria-label="Mobile app for a clinic network — open case">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../../assets/mobile-clinic-app/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Mobile app for a clinic network</h3>
<ul class="mp-card__tags" aria-label="Project type">
<li class="mp-card__tag">UX/UI</li>
<li class="mp-card__tag">Mobile App</li>
</ul>
<p class="mp-card__desc">Design of a mobile app for booking doctors and managing medical services. Focus on a simple user path, clear navigation, and quick access to the right actions.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-related-slug="orc-automation-panel">
<a class="mp-card mp-card--link mp-card--stack-bottom" data-glass="false" href="orc-automation-panel/" aria-label="Web panel for automation orchestration — open case">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../../assets/orc-primo-rpa/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Primo Orc</h3>
<ul class="mp-card__tags" aria-label="Project type">
<li class="mp-card__tag">Mentoring &amp; coaching</li>
</ul>
<p class="mp-card__desc">Primo RPA orchestrator web console: projects, launch and control of RPA bots. Redesign of key screens for production without halting releases.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-related-slug="task-distribution-concept" hidden>
<a class="mp-card mp-card--link mp-card--stack-bottom" data-glass="false" href="task-distribution-concept/" aria-label="Task distribution service concept — open case">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../../assets/task-distribution-concept/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Task distribution service concept</h3>
<ul class="mp-card__tags" aria-label="Project type">
<li class="mp-card__tag">UX/UI</li>
<li class="mp-card__tag">Analytics</li>
</ul>
<p class="mp-card__desc">A product concept for a service to set, distribute and track tasks, with emphasis on process transparency and comfortable teamwork.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="2" data-related-slug="task-tracker" hidden>
<a class="mp-card mp-card--link mp-card--stack-bottom" data-glass="false" href="task-tracker/" aria-label="Task tracker for a back-office system — open case">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../../assets/task-tracker/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Task tracker for a back-office system</h3>
<ul class="mp-card__tags" aria-label="Project type">
<li class="mp-card__tag">Product Design</li>
<li class="mp-card__tag">Web App</li>
</ul>
<p class="mp-card__desc">Interface for planning and distributing tasks in the operations department: shared pool, personal ToDo, per-assignee overview; tabs, filters, board / list / table views, drag &amp; drop.</p>
</div>
</a>
</li>
</ol>
</div>
</section>
<script>
(function () {
// Пастельная («постельная») палитра тегов портфолио.
// Цвет детерминирован по тексту тега — один и тот же текст всегда один цвет.
function hashStr(s) {
var h = 0;
for (var i = 0; i < s.length; i++) { h = (h * 31 + s.charCodeAt(i)) >>> 0; }
return h;
}
function colorTags(root) {
var tags = root.querySelectorAll('.mp-card--stack-bottom .mp-card__tag');
for (var i = 0; i < tags.length; i++) {
var t = tags[i];
var key = (t.textContent || '').trim().toLowerCase();
if (!key) continue;
var hue = hashStr(key) % 360;
t.style.setProperty('--tag-hue', String(hue));
}
}
colorTags(document);
})();
</script>
<section hidden class="section section--surface" aria-hidden="true">
<div class="section__inner">
<div class="projects__heading">
<h2 class="section__title projects__title">Portfolio</h2>
<div class="projects__controls" aria-label="Portfolio card navigation">
<button type="button" class="projects__nav-btn" data-projects-prev aria-label="Scroll left" aria-controls="projects-viewport">
<span class="projects__nav-ico projects__nav-ico--left" aria-hidden="true"></span>
</button>
<button type="button" class="projects__nav-btn" data-projects-next aria-label="Scroll right" aria-controls="projects-viewport">
<span class="projects__nav-ico projects__nav-ico--right" aria-hidden="true"></span>
</button>
</div>
</div>
<div class="projects-carousel">
<div id="projects-viewport" class="projects-carousel__viewport" data-projects-viewport>
<div class="projects-carousel__track">
<div class="projects-carousel__spacer" aria-hidden="true"></div>
<article class="projects-carousel__item">
<a class="portfolio-card" data-glass="false" href="../orc-automation-panel/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Web panel for automation orchestration</h3>
<p class="portfolio-card__summary">
Primo RPA orchestrator web console: projects, launch and control of RPA bots. Redesign of key screens for production without halting releases.
</p>
<ul class="portfolio-card__tags" aria-label="Key tags">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Web App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../../assets/orc-primo-rpa/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card" data-glass="false" href="../ai-platform-primo-rpa/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Platform for working with AI</h3>
<p class="portfolio-card__summary">
Interface for training and using AI models in business processes: data work, automation scenarios, and Smart OCR.
</p>
<ul class="portfolio-card__tags" aria-label="Key tags">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Web App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../../assets/ai-platform-primo-rpa/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card" data-glass="false" href="../task-tracker/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Task tracker for a back-office system</h3>
<p class="portfolio-card__summary">
Interface for planning and distributing tasks in the operations department: shared pool, personal ToDo, per-assignee overview; tabs, filters, board / list / table views, drag &amp; drop.
</p>
<ul class="portfolio-card__tags" aria-label="Key tags">
<li class="portfolio-card__tag">Product Design</li>
<li class="portfolio-card__tag">Web App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../../assets/task-tracker/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card" data-glass="false" href="../manu-game-maker/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">MANU game engine</h3>
<p class="portfolio-card__summary">
Redesign and evolution of the game engine interface: a new working concept, refreshed visual system, and hundreds of complex user scenarios worked through.
</p>
<ul class="portfolio-card__tags" aria-label="Key tags">
<li class="portfolio-card__tag">Product Design</li>
<li class="portfolio-card__tag">Desktop App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../../assets/manu-game-maker/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card" data-glass="false" href="../lunacy/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Graphics editor Lunacy</h3>
<p class="portfolio-card__summary">
Work on the Lunacy interface at Icons8: user flows, complex prototypes, improved functionality and interface logic.
</p>
<ul class="portfolio-card__tags" aria-label="Key tags">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Desktop App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../../assets/lunacy/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card" data-glass="false" href="../mobile-clinic-app/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Mobile app for a clinic network</h3>
<p class="portfolio-card__summary">
Design of a mobile app for booking doctors and managing medical services. Focus on a simple user path, clear navigation, and quick access to the right actions.
</p>
<ul class="portfolio-card__tags" aria-label="Key tags">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Mobile App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../../assets/mobile-clinic-app/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card" data-glass="false" href="../task-distribution-concept/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Task distribution service concept</h3>
<p class="portfolio-card__summary">
A product concept for a service to set, distribute and track tasks, with emphasis on process transparency and comfortable teamwork.
</p>
<ul class="portfolio-card__tags" aria-label="Key tags">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Analytics</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../../assets/task-distribution-concept/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card" data-glass="false" href="../obsidian-plugin-columns-view/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Columns view — plugin for Obsidian</h3>
<p class="portfolio-card__summary">
Interface for multi-column note work: convenient navigation, filtering and column-view settings right inside the Obsidian editor.
</p>
<ul class="portfolio-card__tags" aria-label="Key tags">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Plugin</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../../assets/obsidian-plugin-columns-view/filter use.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<div class="projects-carousel__spacer" aria-hidden="true"></div>
</div>
</div>
<button
type="button"
class="projects-carousel__edge-hit projects-carousel__edge-hit--left"
data-projects-edge-prev
aria-label="Scroll portfolio left"
></button>
<button
type="button"
class="projects-carousel__edge-hit projects-carousel__edge-hit--right"
data-projects-edge-next
aria-label="Scroll portfolio right"
></button>
</div>
</div>
</section>
<section id="contact" class="section section--surface section--footerish" data-reveal>
<div class="section__inner">
<h2 class="section__title">Contact</h2>
<ul class="contact-grid">
<li><a class="contact-card contact-card--link contact-card--brand-tg" href="https://t.me/mamadaevv" rel="noopener noreferrer"><span class="contact-card__corner" aria-hidden="true"></span><span class="contact-card__icon" aria-hidden="true"><img src="../../../assets/shared/icons/tg.svg" alt="" width="24" height="24" loading="eager" decoding="async" /></span><span class="contact-card__body"><span class="contact-card__name">Telegram</span><span class="contact-card__hint">I'll reply today</span></span></a></li>
<li><a class="contact-card contact-card--link contact-card--brand-whatsapp" href="https://wa.me/qr/E2AWW5ZHE3ANJ1" rel="noopener noreferrer"><span class="contact-card__corner" aria-hidden="true"></span><span class="contact-card__icon" aria-hidden="true"><img src="../../../assets/shared/icons/whatsapp.svg" alt="" width="24" height="24" loading="eager" decoding="async" /></span><span class="contact-card__body"><span class="contact-card__name">WhatsApp</span><span class="contact-card__hint">Stable connection</span></span></a></li>
<li><button type="button" class="contact-card contact-card--action contact-card--brand-email" data-contact-copy data-email-b64="bmlrQG1hbWFkYWV2di5zaXRl" data-hint-default="Copy address" aria-label="Copy email address to clipboard"><span class="contact-card__corner contact-card__corner--copy" aria-hidden="true"></span><span class="contact-card__icon contact-card__icon--accent" aria-hidden="true"></span><span class="contact-card__body"><span class="contact-card__name">Email</span><span class="contact-card__hint" data-contact-copy-hint>Copy address</span></span></button></li>
</ul>
</div>
</section>
<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>