Structure before decoration.
Use rails, numbered labels and strong headings to make the page easy to scan. Reserve ink rules for section boundaries; hair rules divide content.
Eugene Perepetschko / Portfolio
A design system for turning complex product work into clear, credible stories.
An editorial system for explaining product work, with the evidence given as much care as the story.
Use rails, numbered labels and strong headings to make the page easy to scan. Reserve ink rules for section boundaries; hair rules divide content.
Accent is purposeful: links, primary actions, indices and the variant in a comparison. Use neutral text for results; avoid making every positive number green.
Respect image resolution, preserve proportions and distinguish mobile frames from desktop captures. Show uplifts under the variant only.
A cool neutral palette, one primary blue, and a small feedback palette. Click a swatch to copy its value.
Contrast is computed from the selected sRGB values. AA requires 4.5:1 for normal text, 3:1 for large text.
Hairline colors are decorative separators. Do not use --hair as body text or as the only boundary of an interactive control.
Archivo delivers the editorial voice. Instrument Sans carries the explanation. Geist Mono identifies structure and data.
Make the work
make sense.
Connect customer needs, commercial outcomes and the evidence behind each decision.
Geist Mono / labels & data03 / EXPERIMENTATION +7.0%
| Role | Range | Weight / line-height | Treatment |
|---|---|---|---|
| Hero / display-xl | 44–88px | 800 / .92 | Archivo, uppercase; width axis 118%; −.03em |
| Section / display-l | 28–40px | 700 / 1 | Archivo, uppercase; width axis 112%; −.02em |
| Subsection / display-m | 26–32px | 700 / 1.05 | Archivo, uppercase; width axis 108% |
| Supporting / display-s | 20–22px | 700 / 1.15 | Archivo |
| Card / card-title | 20–24px | 700 / 1.08 | Sentence case; maximum 24px |
| Lead / .lead + .body-copy | 18–21px | 400 / 1.5 | Instrument Sans; same lead sizing |
| Body | 18px / 16px mobile | 400 / 1.6 | Instrument Sans |
| Label / .label | 12px / 11px mobile | 500 / 1.5 | Geist Mono, uppercase; .08em |
| Small label / .label-sm | 11px / 10px mobile | 500 / 1.4 | Geist Mono |
| Metric / .stat-value | 30px / 22px mobile | 500 / 1 | Geist Mono; −.04em |
Fonts use the same Google Fonts request as the portfolio. An internet connection is needed for exact font rendering; system fallbacks remain available. Header-only embedded font subsets are preserved in the source snapshot, not promoted as full text fonts.
Use a 4px base rhythm, fluid section spacing and a 1200px content shell.
space-14pxspace-28pxspace-312pxspace-416pxspace-520pxspace-624pxspace-832pxspace-1248pxspace-1664pxspace-2496px| Token / rule | Value | Use |
|---|---|---|
--gutter | clamp(20px, 4vw, 32px) | Page edges |
--space-section | clamp(64px, 8vw, 96px) | Section padding |
--space-block | clamp(24px, 4vw, 48px) | Content groups |
--space-grid | clamp(16px, 2.4vw, 24px) | General grids |
--space-split | clamp(32px, 5vw, 64px) | Two-column compositions |
.tile-grid | 5px | Deliberately tighter tile spacing |
| Section rule | 1px solid --ink | Between sections |
| Content rule | 1px solid --hair | Between articles and metric cells |
Keep one border owner at each boundary to avoid doubled lines. Use minmax(0, 1fr) for flexible grid tracks and min-width: 0 for children with long copy.
A quiet identity band above the headline makes Home, About and Projects part of one family.
<div class="home-hero__rail label">
<span>Product · UX · Growth</span>
<span>Munich, Germany</span>
</div><h1 class="display-xl">Product thinking.<br>Measurable impact.</h1>Primary actions are blue and square; secondary actions are outlined. In-page exploration uses a text link with a downward arrow.
<div class="hero-actions"><a class="link-button" href="#demo">Featured work ↓</a><a class="link-button link-button--plain" href="#demo">Personal projects →</a></div><p><a class="text-link" href="#demo">Explore the case ↓</a></p>Use links for navigation and buttons for actions. Mobile content buttons fill their container at ≤720px, with a 48px minimum height. Navigation links and text links stay compact. No disabled-button visual is standardized in the current portfolio; define a semantic disabled state if a new workflow needs it.
Flat white surfaces, square edges, a thin outline and compact metadata. Skill rows remain editorial rather than becoming a collection of boxes.
<div class="tile-grid"><a class="tile tile--link" href="work/puma/app-optimization.html">
<span class="label text-faint">
Product management
</span>
<h3 class="card-title">
Optimizing the shopping app
</h3>
<p class="small">
Implemented and refined the feature design of the shopping app to improve its performance.
</p>
<span class="tile__arrow">
→
</span>
</a><a class="tile tile--link" href="work/puma/loyalty-program.html">
<span class="label text-faint">
Product design
</span>
<h3 class="card-title">
Scalable loyalty program
</h3>
<p class="small">
Led the design of a tiered loyalty experience aimed at increasing repeat purchases.
</p>
<span class="tile__arrow">
→
</span>
</a></div><div class="about-skill">
<div class="about-skill__heading">
<span class="about-skill__index">
01
</span>
<h3 class="about-skill__title">
Product Management
</h3>
</div>
<p>
Product Strategy · Product Discovery · Roadmapping · OKRs · Prioritization
</p>
</div>Card titles cap at 24px. Tags use white backgrounds, --hair-strong borders and 5px × 8px padding. Avoid shadows and hover lifts. Skill items use the exact separators and wrapping found in the supplied archive; they are content, not filters.
A value has one label, compact spacing, and a clear scope. Numbers must not imply results beyond the evidence.
<div class="metric-row metric-row--divided">
<div class="stat">
<strong class="stat-value">
~67%
</strong>
<span class="label">
Cost reduction
</span>
</div>
<div class="stat">
<strong class="stat-value">
+117%
</strong>
<span class="label">
Testing velocity
</span>
</div>
</div>Homepage metrics: 16px top padding, 6px value-to-label gap and internal hair rules. Stat icons use half the stat-value font size. Optimisation-page values are black. Color supports meaning but does not replace labels or context.
The reusable experiment card connects the hypothesis, evidence and outcome without mixing the control and variant results.
<article class="experiment experiment-ds">
<header class="experiment-ds__header"><span class="experiment-ds__id">Test 03 · Product</span>
<h3 class="experiment-ds__title">
Product gallery
</h3>
<span class="experiment-ds__result">−16% Bounce rate</span></header><div class="experiment-ds__hypothesis"><h4 class="label label--strong">
Hypothesis
</h4>
<p>
Showing products as an image grid above the fold when the PDP is viewed on desktop will reduce the bounce rate by helping users better evaluate the appearance of the products.
</p>
</div><div class="compare compare--single experiment-ds__comparison--landscape experiment-ds__comparison">
<figure>
<figcaption class="experiment-ds__badge experiment-ds__badge--control">A · Control</figcaption>
<img class="maxw-500 media-desktop" loading="lazy" decoding="async" width="800" height="485" src="../../assets/images/work/puma/website-optimization/gallery-control.webp" alt="Product gallery control">
</figure>
<figure>
<figcaption class="experiment-ds__badge experiment-ds__badge--variant">B · Variant</figcaption>
<img class="maxw-500 media-desktop" loading="lazy" decoding="async" width="800" height="485" src="../../assets/images/work/puma/website-optimization/gallery-variant.webp" alt="Expanded product gallery variant">
</figure>
</div>
<div class="experiment-ds__footer"><div class="experiment-ds__kpi-label">Primary KPIs</div><div class="metric-row experiment-ds__metrics" aria-label="Variant uplifts">
<div class="stat">
<strong class="stat-value">
−16%
</strong>
<span class="label">Bounce rate</span>
</div>
<div class="stat">
<strong class="stat-value">
+5%
</strong>
<span class="label">E-commerce CR</span>
</div>
</div>
</div></article>Keep the image sizing tied to the capture. PUMA product-gallery images cap at 400px and sit side by side above 720px, with a vertical hair divider. SIXT experiment images have 12px corners. Preserve existing asset-specific clipping that removes baked-in corners.
Media frames protect the evidence rather than becoming the visual focus.
<div class="frame media-plate media-plate--left mobile-hero-frame">
<img class="media-phone" width="1170" height="2532" src="../../assets/images/work/puma/shop-the-look/shop-the-look-outfit.webp" alt="PUMA product page showing a model wearing the complete outfit">
</div><a class="lab-project lab-project--last" href="#gallery-little-moments" data-project-gallery="little-moments" aria-haspopup="dialog">
<div>
<h3 class="display-m">
Little Moments
</h3>
<span class="lab-project__former label-sm text-faint">
Family tracking
</span>
</div>
<div class="lab-project__copy">
<p>
A calm feed-and-sleep tracker for parents built around one-tap tracking, resilient timers, a continuous timeline and useful patterns.
</p>
<div class="tags">
<span class="tag label-sm">
PWA
</span>
<span class="tag label-sm">
iOS
</span>
<span class="tag label-sm">
Timers
</span>
<span class="tag label-sm">
Data viz
</span>
</div>
<div class="lab-project__cta label">
View gallery
<span aria-hidden="true">
→
</span>
</div>
</div>
<div class="lab-thumb lab-thumb--contain">
<!-- <div class="lab-placeholder">
<b class="display-m mb-0">
LM
</b>
<span class="label-sm">
Track what counts
</span>
</div>
-->
<div class="lab-thumb__screens lab-thumb__screens--mobile lab-thumb__screens--little-moments" aria-hidden="true">
<img src="../assets/images/projects/little-moments/thumb-evening.webp" alt="" width="822" height="698" loading="lazy" decoding="async">
</div>
</div>
</a>
<!-- Matching template and shared dialog are in projects/start.html. -->| Pattern | Geometry | Behavior |
|---|---|---|
| Mobile hero | 300px maximum; 2px ink border; 16px / 12px corners | Auto height; object-fit: contain |
| Desktop evidence | Original ratio; size cap by context | No extra border when a device frame is baked in |
| Experiment / SIXT | 12px corners | Source-specific masks retained |
| Project thumbnail | 4:3 crop; one main screen | WebP derivative; independent from gallery image |
| Product gallery | Up to four centered columns on wide desktop | Wrapping rows; incomplete rows centered |
| Mobile gallery | Single horizontal row; scroll snap | ~78vw screen width, capped at 320px |
| EV gallery | One desktop capture | Natural ratio, centered; no phone frame |
Mobile galleries currently contain four screens per product; the pattern supports 3–10. EV Chargers is the explicit one-desktop-screen exception. Use descriptive alt text in galleries and empty alt text for decorative thumbnails that repeat a card’s content.
Treat responsive rules as a change in composition, not a scaled-down desktop screenshot.
| Breakpoint | Current behavior |
|---|---|
| ≤720px | Mobile navigation; full-width content buttons; stacked split sections; horizontally scrolling product gallery |
| 721–980px | Intermediate layouts; project gallery uses two centered columns |
| >980px | Primary desktop grids and splits |
| >1500px | Follow the current four-column gallery cap, not the older three-column recipe |
Use the source snapshot for exact existing behavior, and the normalized recipes for new components.
| Existing class | Normalized recipe | Status |
|---|---|---|
| .link-button | .ds-button | Opt-in starter |
| .link-button--plain | .ds-button--secondary | Opt-in starter |
| .text-link | .ds-link | Opt-in starter |
| .tile / .card | .ds-card | Common surface recipe; preserve existing layouts |
| .tag | .ds-tag | Opt-in starter |
| .metric-row / .stat | .ds-metrics / .ds-metric | Auto-fitting starter metrics |
| .home-hero__rail / .lab-intro__band | .ds-rail | Shared rail recipe |
| .mobile-hero-frame img | .ds-media-plate / .ds-phone | Mobile evidence recipe |
<link rel="stylesheet" href="tokens.css">
<link rel="stylesheet" href="components.css">
<div class="ds-card">
<span class="ds-tag">Product design</span>
<h3 class="ds-card__title">A clear case-study title</h3>
<p>Describe the problem and the decision.</p>
<a class="ds-link" href="case.html">Explore the case →</a>
</div>Not a drop-in stylesheet replacement. The current production cascade remains in reference/css. New ds-* recipes are deliberately opt-in. Load existing pages in their original order: styles.css → page stylesheet → design-system.css. Do not load both component families on the same element without a migration.
Known technical debt: warm-color aliases in styles.css are superseded by the cool palette; blog selectors remain even though blog pages are excluded; device masks are asset-specific; a few existing visual roles are context-sensitive. These are documented, not silently removed.