17 / 20
All 20 in 20 CSS Minimalist UI
- 01Modular Type Scale with a Ratio Variable
- 02Type Hierarchy Without Any Color Change
- 03System Font Stack That Needs No Webfont
- 04Optical Alignment vs Mathematical Centering
- 05Spacing Scale from a Single Base Unit
- 06Whitespace That Groups Instead of Separates
- 07Vertical Rhythm with a Consistent Baseline
- 08Minimal Layout with CSS Grid and No Divs
- 09The One Accent Color Restraint Budget
- 10Grayscale Palette with Enough Contrast
- 11Borders and Dividers You Can Actually Remove
- 12Elevation Without Shadows or Gradients
- 13Why Minimal Interfaces Fail Accessibility
- 14Minimalist and Brutalist Side by Side
- 15Minimalist Dashboard Card and Metric
- 16Minimalist Product Card for Ecommerce
- 17Minimalist Portfolio Project Listing
- 18Minimalist Article Layout and Typography
- 19Minimalist Mobile App Screen in a Frame
- 20Minimalist Landing Page Section
Minimalist Portfolio Project Listing
A project list as rows, where the hover state is the only decorative moment in the whole page. It also argues the case: when items are text-led, a list beats a card grid, because a grid spends most of its space on containers and thumbnails you did not need — and the whole row is one link, one tab stop, at a 44px minimum height.
Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time
Untitled UI · Affiliate link
The code
<section class="mu-17" aria-labelledby="mu-17-title">
<h2 class="mu-17__sr" id="mu-17-title">Selected work, 2022–2026</h2>
<div class="mu-17__theme">
<input class="mu-17__theme-input" type="checkbox" id="mu-17-theme">
<label class="mu-17__theme-label" for="mu-17-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
<span class="mu-17__sr">Toggle dark theme</span>
</label>
</div>
<div class="mu-17__stage">
<nav class="mu-17__nav" aria-label="Project index">
<ul class="mu-17__list">
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Meridian platform rebuild</span>
<span class="mu-17__meta">Product design · 8 months</span>
<span class="mu-17__year">2026</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Basalt identity and type system</span>
<span class="mu-17__meta">Brand · with Priya Sharma</span>
<span class="mu-17__year">2025</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Orbit developer documentation</span>
<span class="mu-17__meta">Information architecture</span>
<span class="mu-17__year">2025</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Fern onboarding, mobile first</span>
<span class="mu-17__meta">Product design · iOS and Android</span>
<span class="mu-17__year">2024</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Quill editorial commissioning tool</span>
<span class="mu-17__meta">Interface design · with Jordan Lee</span>
<span class="mu-17__year">2023</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Vale annual report</span>
<span class="mu-17__meta">Editorial · print and web</span>
<span class="mu-17__year">2022</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
</ul>
</nav>
</div>
</section><section class="mu-17" aria-labelledby="mu-17-title">
<h2 class="mu-17__sr" id="mu-17-title">Selected work, 2022–2026</h2>
<div class="mu-17__theme">
<input class="mu-17__theme-input" type="checkbox" id="mu-17-theme">
<label class="mu-17__theme-label" for="mu-17-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
<span class="mu-17__sr">Toggle dark theme</span>
</label>
</div>
<div class="mu-17__stage">
<nav class="mu-17__nav" aria-label="Project index">
<ul class="mu-17__list">
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Meridian platform rebuild</span>
<span class="mu-17__meta">Product design · 8 months</span>
<span class="mu-17__year">2026</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Basalt identity and type system</span>
<span class="mu-17__meta">Brand · with Priya Sharma</span>
<span class="mu-17__year">2025</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Orbit developer documentation</span>
<span class="mu-17__meta">Information architecture</span>
<span class="mu-17__year">2025</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Fern onboarding, mobile first</span>
<span class="mu-17__meta">Product design · iOS and Android</span>
<span class="mu-17__year">2024</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Quill editorial commissioning tool</span>
<span class="mu-17__meta">Interface design · with Jordan Lee</span>
<span class="mu-17__year">2023</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Vale annual report</span>
<span class="mu-17__meta">Editorial · print and web</span>
<span class="mu-17__year">2022</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
</ul>
</nav>
</div>
</section>.mu-17 {
--mu-17-row-pad: 1.125rem;
--mu-17-travel: 4px;
--mu-17-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--mu-17-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--mu-17-bg: #eef0f1;
--mu-17-bg: oklch(94.5% 0.003 220);
--mu-17-ink: #15181a;
--mu-17-ink: oklch(19.5% 0.005 240);
--mu-17-ink-2: #4e565b;
--mu-17-ink-2: oklch(45% 0.008 240);
--mu-17-line: #c9ced1;
--mu-17-line: oklch(84.5% 0.005 240);
--mu-17-acc: #15181a;
--mu-17-acc: oklch(19.5% 0.005 240);
--mu-17-ring: #1f5fd0;
--mu-17-ring: oklch(48% 0.17 262);
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--mu-17-bg);
color: var(--mu-17-ink);
font-family: var(--mu-17-ui);
line-height: 1.5;
-webkit-text-size-adjust: 100%;
}
.mu-17 *,
.mu-17 *::before,
.mu-17 *::after {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.mu-17 {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
}
.mu-17[data-theme="dark"],
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
@media (prefers-color-scheme: light) {
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
}
@media (prefers-color-scheme: dark) {
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #eef0f1;
--mu-17-bg: oklch(94.5% 0.003 220);
--mu-17-ink: #15181a;
--mu-17-ink: oklch(19.5% 0.005 240);
--mu-17-ink-2: #4e565b;
--mu-17-ink-2: oklch(45% 0.008 240);
--mu-17-line: #c9ced1;
--mu-17-line: oklch(84.5% 0.005 240);
--mu-17-acc: #15181a;
--mu-17-acc: oklch(19.5% 0.005 240);
--mu-17-ring: #1f5fd0;
--mu-17-ring: oklch(48% 0.17 262);
}
}
.mu-17__sr {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.mu-17__theme {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
z-index: 5;
}
.mu-17__theme-input {
position: absolute;
inset: 0;
inline-size: 44px;
block-size: 44px;
margin: 0;
opacity: 0;
cursor: pointer;
}
.mu-17__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--mu-17-line);
border-radius: 2px;
background: transparent;
color: var(--mu-17-ink-2);
cursor: pointer;
}
.mu-17__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.mu-17__theme-input:focus-visible + .mu-17__theme-label {
outline: 2px solid var(--mu-17-ring);
outline-offset: 2px;
}
.mu-17__stage {
max-inline-size: 62rem;
margin-inline: auto;
padding: 4.5rem 1.25rem 5rem;
display: grid;
gap: 2.5rem;
grid-template-columns: minmax(0, 1fr);
}
.mu-17__nav {
min-inline-size: 0;
}
.mu-17__list {
margin: 0;
padding: 0;
list-style: none;
border-block-start: 1px solid var(--mu-17-line);
}
.mu-17__row {
min-inline-size: 0;
border-block-end: 1px solid var(--mu-17-line);
}
.mu-17__link {
min-inline-size: 0;
min-block-size: 44px;
display: grid;
align-items: baseline;
gap: 0.125rem 1rem;
grid-template-columns: minmax(0, 1fr) auto 1.25rem;
padding-block: var(--mu-17-row-pad);
color: var(--mu-17-ink);
text-decoration: none;
transition: color 160ms ease;
}
.mu-17__name {
grid-column: 1;
min-inline-size: 0;
font-size: clamp(1.0625rem, 1rem + 0.5vw, 1.375rem);
line-height: 1.2;
letter-spacing: -0.018em;
font-weight: 500;
text-wrap: pretty;
transition: translate 180ms ease;
}
.mu-17__meta {
grid-column: 1;
grid-row: 2;
min-inline-size: 0;
font-size: 0.8125rem;
color: var(--mu-17-ink-2);
}
.mu-17__year {
grid-column: 2;
grid-row: 1;
font-family: var(--mu-17-mono);
font-size: 0.8125rem;
color: var(--mu-17-ink-2);
font-variant-numeric: tabular-nums;
}
.mu-17__arrow {
grid-column: 3;
grid-row: 1;
inline-size: 20px;
block-size: 20px;
color: var(--mu-17-ink-2);
transition: translate 180ms ease, color 160ms ease;
}
.mu-17__row:hover {
border-block-end-color: var(--mu-17-acc);
}
.mu-17__link:hover .mu-17__arrow,
.mu-17__link:focus-visible .mu-17__arrow {
translate: var(--mu-17-travel) 0;
color: var(--mu-17-acc);
}
.mu-17__link:hover .mu-17__name,
.mu-17__link:focus-visible .mu-17__name {
translate: calc(var(--mu-17-travel) / 2) 0;
}
.mu-17__link:focus-visible {
outline: 2px solid var(--mu-17-ring);
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.mu-17__link,
.mu-17__name,
.mu-17__arrow {
transition: color 160ms ease;
}
.mu-17__link:hover .mu-17__arrow,
.mu-17__link:focus-visible .mu-17__arrow,
.mu-17__link:hover .mu-17__name,
.mu-17__link:focus-visible .mu-17__name {
translate: none;
}
}
@media (min-width: 44rem) {
.mu-17__link {
grid-template-columns: minmax(0, 1fr) minmax(0, 14rem) 4rem 1.5rem;
align-items: center;
}
.mu-17__meta {
grid-column: 2;
grid-row: 1;
}
.mu-17__year {
grid-column: 3;
justify-self: end;
}
.mu-17__arrow {
grid-column: 4;
justify-self: end;
}
}
@media (min-width: 56rem) {
.mu-17__stage {
padding-inline: 3rem;
}
}.mu-17 {
--mu-17-row-pad: 1.125rem;
--mu-17-travel: 4px;
--mu-17-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--mu-17-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--mu-17-bg: #eef0f1;
--mu-17-bg: oklch(94.5% 0.003 220);
--mu-17-ink: #15181a;
--mu-17-ink: oklch(19.5% 0.005 240);
--mu-17-ink-2: #4e565b;
--mu-17-ink-2: oklch(45% 0.008 240);
--mu-17-line: #c9ced1;
--mu-17-line: oklch(84.5% 0.005 240);
--mu-17-acc: #15181a;
--mu-17-acc: oklch(19.5% 0.005 240);
--mu-17-ring: #1f5fd0;
--mu-17-ring: oklch(48% 0.17 262);
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--mu-17-bg);
color: var(--mu-17-ink);
font-family: var(--mu-17-ui);
line-height: 1.5;
-webkit-text-size-adjust: 100%;
}
.mu-17 *,
.mu-17 *::before,
.mu-17 *::after {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.mu-17 {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
}
.mu-17[data-theme="dark"],
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
@media (prefers-color-scheme: light) {
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
}
@media (prefers-color-scheme: dark) {
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #eef0f1;
--mu-17-bg: oklch(94.5% 0.003 220);
--mu-17-ink: #15181a;
--mu-17-ink: oklch(19.5% 0.005 240);
--mu-17-ink-2: #4e565b;
--mu-17-ink-2: oklch(45% 0.008 240);
--mu-17-line: #c9ced1;
--mu-17-line: oklch(84.5% 0.005 240);
--mu-17-acc: #15181a;
--mu-17-acc: oklch(19.5% 0.005 240);
--mu-17-ring: #1f5fd0;
--mu-17-ring: oklch(48% 0.17 262);
}
}
.mu-17__sr {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.mu-17__theme {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
z-index: 5;
}
.mu-17__theme-input {
position: absolute;
inset: 0;
inline-size: 44px;
block-size: 44px;
margin: 0;
opacity: 0;
cursor: pointer;
}
.mu-17__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--mu-17-line);
border-radius: 2px;
background: transparent;
color: var(--mu-17-ink-2);
cursor: pointer;
}
.mu-17__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.mu-17__theme-input:focus-visible + .mu-17__theme-label {
outline: 2px solid var(--mu-17-ring);
outline-offset: 2px;
}
.mu-17__stage {
max-inline-size: 62rem;
margin-inline: auto;
padding: 4.5rem 1.25rem 5rem;
display: grid;
gap: 2.5rem;
grid-template-columns: minmax(0, 1fr);
}
.mu-17__nav {
min-inline-size: 0;
}
.mu-17__list {
margin: 0;
padding: 0;
list-style: none;
border-block-start: 1px solid var(--mu-17-line);
}
.mu-17__row {
min-inline-size: 0;
border-block-end: 1px solid var(--mu-17-line);
}
.mu-17__link {
min-inline-size: 0;
min-block-size: 44px;
display: grid;
align-items: baseline;
gap: 0.125rem 1rem;
grid-template-columns: minmax(0, 1fr) auto 1.25rem;
padding-block: var(--mu-17-row-pad);
color: var(--mu-17-ink);
text-decoration: none;
transition: color 160ms ease;
}
.mu-17__name {
grid-column: 1;
min-inline-size: 0;
font-size: clamp(1.0625rem, 1rem + 0.5vw, 1.375rem);
line-height: 1.2;
letter-spacing: -0.018em;
font-weight: 500;
text-wrap: pretty;
transition: translate 180ms ease;
}
.mu-17__meta {
grid-column: 1;
grid-row: 2;
min-inline-size: 0;
font-size: 0.8125rem;
color: var(--mu-17-ink-2);
}
.mu-17__year {
grid-column: 2;
grid-row: 1;
font-family: var(--mu-17-mono);
font-size: 0.8125rem;
color: var(--mu-17-ink-2);
font-variant-numeric: tabular-nums;
}
.mu-17__arrow {
grid-column: 3;
grid-row: 1;
inline-size: 20px;
block-size: 20px;
color: var(--mu-17-ink-2);
transition: translate 180ms ease, color 160ms ease;
}
.mu-17__row:hover {
border-block-end-color: var(--mu-17-acc);
}
.mu-17__link:hover .mu-17__arrow,
.mu-17__link:focus-visible .mu-17__arrow {
translate: var(--mu-17-travel) 0;
color: var(--mu-17-acc);
}
.mu-17__link:hover .mu-17__name,
.mu-17__link:focus-visible .mu-17__name {
translate: calc(var(--mu-17-travel) / 2) 0;
}
.mu-17__link:focus-visible {
outline: 2px solid var(--mu-17-ring);
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.mu-17__link,
.mu-17__name,
.mu-17__arrow {
transition: color 160ms ease;
}
.mu-17__link:hover .mu-17__arrow,
.mu-17__link:focus-visible .mu-17__arrow,
.mu-17__link:hover .mu-17__name,
.mu-17__link:focus-visible .mu-17__name {
translate: none;
}
}
@media (min-width: 44rem) {
.mu-17__link {
grid-template-columns: minmax(0, 1fr) minmax(0, 14rem) 4rem 1.5rem;
align-items: center;
}
.mu-17__meta {
grid-column: 2;
grid-row: 1;
}
.mu-17__year {
grid-column: 3;
justify-self: end;
}
.mu-17__arrow {
grid-column: 4;
justify-self: end;
}
}
@media (min-width: 56rem) {
.mu-17__stage {
padding-inline: 3rem;
}
}Here's a working CSS Minimalist UI from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Minimalist Portfolio Project Listing
Source: https://codefronts.com/design-styles/css-minimalist-ui/vera-lindstrom-portfolio/
A project list as rows, where the hover state is the only decorative moment in the whole page. It also argues the case: when items are text-led, a list beats a card grid, because a grid spends most of its space on containers and thumbnails you did not need — and the whole row is one link, one tab stop, at a 44px minimum height.
## HTML
```html
<section class="mu-17" aria-labelledby="mu-17-title">
<h2 class="mu-17__sr" id="mu-17-title">Selected work, 2022–2026</h2>
<div class="mu-17__theme">
<input class="mu-17__theme-input" type="checkbox" id="mu-17-theme">
<label class="mu-17__theme-label" for="mu-17-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
<span class="mu-17__sr">Toggle dark theme</span>
</label>
</div>
<div class="mu-17__stage">
<nav class="mu-17__nav" aria-label="Project index">
<ul class="mu-17__list">
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Meridian platform rebuild</span>
<span class="mu-17__meta">Product design · 8 months</span>
<span class="mu-17__year">2026</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Basalt identity and type system</span>
<span class="mu-17__meta">Brand · with Priya Sharma</span>
<span class="mu-17__year">2025</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Orbit developer documentation</span>
<span class="mu-17__meta">Information architecture</span>
<span class="mu-17__year">2025</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Fern onboarding, mobile first</span>
<span class="mu-17__meta">Product design · iOS and Android</span>
<span class="mu-17__year">2024</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Quill editorial commissioning tool</span>
<span class="mu-17__meta">Interface design · with Jordan Lee</span>
<span class="mu-17__year">2023</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Vale annual report</span>
<span class="mu-17__meta">Editorial · print and web</span>
<span class="mu-17__year">2022</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
</ul>
</nav>
</div>
</section>
```
## CSS
```css
.mu-17 {
--mu-17-row-pad: 1.125rem;
--mu-17-travel: 4px;
--mu-17-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--mu-17-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--mu-17-bg: #eef0f1;
--mu-17-bg: oklch(94.5% 0.003 220);
--mu-17-ink: #15181a;
--mu-17-ink: oklch(19.5% 0.005 240);
--mu-17-ink-2: #4e565b;
--mu-17-ink-2: oklch(45% 0.008 240);
--mu-17-line: #c9ced1;
--mu-17-line: oklch(84.5% 0.005 240);
--mu-17-acc: #15181a;
--mu-17-acc: oklch(19.5% 0.005 240);
--mu-17-ring: #1f5fd0;
--mu-17-ring: oklch(48% 0.17 262);
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--mu-17-bg);
color: var(--mu-17-ink);
font-family: var(--mu-17-ui);
line-height: 1.5;
-webkit-text-size-adjust: 100%;
}
.mu-17 *,
.mu-17 *::before,
.mu-17 *::after {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.mu-17 {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
}
.mu-17[data-theme="dark"],
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
@media (prefers-color-scheme: light) {
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
}
@media (prefers-color-scheme: dark) {
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #eef0f1;
--mu-17-bg: oklch(94.5% 0.003 220);
--mu-17-ink: #15181a;
--mu-17-ink: oklch(19.5% 0.005 240);
--mu-17-ink-2: #4e565b;
--mu-17-ink-2: oklch(45% 0.008 240);
--mu-17-line: #c9ced1;
--mu-17-line: oklch(84.5% 0.005 240);
--mu-17-acc: #15181a;
--mu-17-acc: oklch(19.5% 0.005 240);
--mu-17-ring: #1f5fd0;
--mu-17-ring: oklch(48% 0.17 262);
}
}
.mu-17__sr {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.mu-17__theme {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
z-index: 5;
}
.mu-17__theme-input {
position: absolute;
inset: 0;
inline-size: 44px;
block-size: 44px;
margin: 0;
opacity: 0;
cursor: pointer;
}
.mu-17__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--mu-17-line);
border-radius: 2px;
background: transparent;
color: var(--mu-17-ink-2);
cursor: pointer;
}
.mu-17__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.mu-17__theme-input:focus-visible + .mu-17__theme-label {
outline: 2px solid var(--mu-17-ring);
outline-offset: 2px;
}
.mu-17__stage {
max-inline-size: 62rem;
margin-inline: auto;
padding: 4.5rem 1.25rem 5rem;
display: grid;
gap: 2.5rem;
grid-template-columns: minmax(0, 1fr);
}
.mu-17__nav {
min-inline-size: 0;
}
.mu-17__list {
margin: 0;
padding: 0;
list-style: none;
border-block-start: 1px solid var(--mu-17-line);
}
.mu-17__row {
min-inline-size: 0;
border-block-end: 1px solid var(--mu-17-line);
}
.mu-17__link {
min-inline-size: 0;
min-block-size: 44px;
display: grid;
align-items: baseline;
gap: 0.125rem 1rem;
grid-template-columns: minmax(0, 1fr) auto 1.25rem;
padding-block: var(--mu-17-row-pad);
color: var(--mu-17-ink);
text-decoration: none;
transition: color 160ms ease;
}
.mu-17__name {
grid-column: 1;
min-inline-size: 0;
font-size: clamp(1.0625rem, 1rem + 0.5vw, 1.375rem);
line-height: 1.2;
letter-spacing: -0.018em;
font-weight: 500;
text-wrap: pretty;
transition: translate 180ms ease;
}
.mu-17__meta {
grid-column: 1;
grid-row: 2;
min-inline-size: 0;
font-size: 0.8125rem;
color: var(--mu-17-ink-2);
}
.mu-17__year {
grid-column: 2;
grid-row: 1;
font-family: var(--mu-17-mono);
font-size: 0.8125rem;
color: var(--mu-17-ink-2);
font-variant-numeric: tabular-nums;
}
.mu-17__arrow {
grid-column: 3;
grid-row: 1;
inline-size: 20px;
block-size: 20px;
color: var(--mu-17-ink-2);
transition: translate 180ms ease, color 160ms ease;
}
.mu-17__row:hover {
border-block-end-color: var(--mu-17-acc);
}
.mu-17__link:hover .mu-17__arrow,
.mu-17__link:focus-visible .mu-17__arrow {
translate: var(--mu-17-travel) 0;
color: var(--mu-17-acc);
}
.mu-17__link:hover .mu-17__name,
.mu-17__link:focus-visible .mu-17__name {
translate: calc(var(--mu-17-travel) / 2) 0;
}
.mu-17__link:focus-visible {
outline: 2px solid var(--mu-17-ring);
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.mu-17__link,
.mu-17__name,
.mu-17__arrow {
transition: color 160ms ease;
}
.mu-17__link:hover .mu-17__arrow,
.mu-17__link:focus-visible .mu-17__arrow,
.mu-17__link:hover .mu-17__name,
.mu-17__link:focus-visible .mu-17__name {
translate: none;
}
}
@media (min-width: 44rem) {
.mu-17__link {
grid-template-columns: minmax(0, 1fr) minmax(0, 14rem) 4rem 1.5rem;
align-items: center;
}
.mu-17__meta {
grid-column: 2;
grid-row: 1;
}
.mu-17__year {
grid-column: 3;
justify-self: end;
}
.mu-17__arrow {
grid-column: 4;
justify-self: end;
}
}
@media (min-width: 56rem) {
.mu-17__stage {
padding-inline: 3rem;
}
}
```Here's a working CSS Minimalist UI from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Minimalist Portfolio Project Listing
Source: https://codefronts.com/design-styles/css-minimalist-ui/vera-lindstrom-portfolio/
A project list as rows, where the hover state is the only decorative moment in the whole page. It also argues the case: when items are text-led, a list beats a card grid, because a grid spends most of its space on containers and thumbnails you did not need — and the whole row is one link, one tab stop, at a 44px minimum height.
## HTML
```html
<section class="mu-17" aria-labelledby="mu-17-title">
<h2 class="mu-17__sr" id="mu-17-title">Selected work, 2022–2026</h2>
<div class="mu-17__theme">
<input class="mu-17__theme-input" type="checkbox" id="mu-17-theme">
<label class="mu-17__theme-label" for="mu-17-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
<span class="mu-17__sr">Toggle dark theme</span>
</label>
</div>
<div class="mu-17__stage">
<nav class="mu-17__nav" aria-label="Project index">
<ul class="mu-17__list">
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Meridian platform rebuild</span>
<span class="mu-17__meta">Product design · 8 months</span>
<span class="mu-17__year">2026</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Basalt identity and type system</span>
<span class="mu-17__meta">Brand · with Priya Sharma</span>
<span class="mu-17__year">2025</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Orbit developer documentation</span>
<span class="mu-17__meta">Information architecture</span>
<span class="mu-17__year">2025</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Fern onboarding, mobile first</span>
<span class="mu-17__meta">Product design · iOS and Android</span>
<span class="mu-17__year">2024</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Quill editorial commissioning tool</span>
<span class="mu-17__meta">Interface design · with Jordan Lee</span>
<span class="mu-17__year">2023</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
<li class="mu-17__row">
<a class="mu-17__link" href="#mu-17-title">
<span class="mu-17__name">Vale annual report</span>
<span class="mu-17__meta">Editorial · print and web</span>
<span class="mu-17__year">2022</span>
<svg class="mu-17__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 12h14m0 0-6-6m6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</a>
</li>
</ul>
</nav>
</div>
</section>
```
## CSS
```css
.mu-17 {
--mu-17-row-pad: 1.125rem;
--mu-17-travel: 4px;
--mu-17-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--mu-17-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--mu-17-bg: #eef0f1;
--mu-17-bg: oklch(94.5% 0.003 220);
--mu-17-ink: #15181a;
--mu-17-ink: oklch(19.5% 0.005 240);
--mu-17-ink-2: #4e565b;
--mu-17-ink-2: oklch(45% 0.008 240);
--mu-17-line: #c9ced1;
--mu-17-line: oklch(84.5% 0.005 240);
--mu-17-acc: #15181a;
--mu-17-acc: oklch(19.5% 0.005 240);
--mu-17-ring: #1f5fd0;
--mu-17-ring: oklch(48% 0.17 262);
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--mu-17-bg);
color: var(--mu-17-ink);
font-family: var(--mu-17-ui);
line-height: 1.5;
-webkit-text-size-adjust: 100%;
}
.mu-17 *,
.mu-17 *::before,
.mu-17 *::after {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.mu-17 {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
}
.mu-17[data-theme="dark"],
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
@media (prefers-color-scheme: light) {
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #121416;
--mu-17-bg: oklch(18% 0.005 240);
--mu-17-ink: #eef1f2;
--mu-17-ink: oklch(95.5% 0.003 240);
--mu-17-ink-2: #a2aab0;
--mu-17-ink-2: oklch(74% 0.008 240);
--mu-17-line: #333a3e;
--mu-17-line: oklch(33% 0.009 240);
--mu-17-acc: #eef1f2;
--mu-17-acc: oklch(95.5% 0.003 240);
--mu-17-ring: #8ab4ff;
--mu-17-ring: oklch(80% 0.12 262);
}
}
@media (prefers-color-scheme: dark) {
.mu-17:has(.mu-17__theme-input:checked) {
--mu-17-bg: #eef0f1;
--mu-17-bg: oklch(94.5% 0.003 220);
--mu-17-ink: #15181a;
--mu-17-ink: oklch(19.5% 0.005 240);
--mu-17-ink-2: #4e565b;
--mu-17-ink-2: oklch(45% 0.008 240);
--mu-17-line: #c9ced1;
--mu-17-line: oklch(84.5% 0.005 240);
--mu-17-acc: #15181a;
--mu-17-acc: oklch(19.5% 0.005 240);
--mu-17-ring: #1f5fd0;
--mu-17-ring: oklch(48% 0.17 262);
}
}
.mu-17__sr {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.mu-17__theme {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
z-index: 5;
}
.mu-17__theme-input {
position: absolute;
inset: 0;
inline-size: 44px;
block-size: 44px;
margin: 0;
opacity: 0;
cursor: pointer;
}
.mu-17__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--mu-17-line);
border-radius: 2px;
background: transparent;
color: var(--mu-17-ink-2);
cursor: pointer;
}
.mu-17__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.mu-17__theme-input:focus-visible + .mu-17__theme-label {
outline: 2px solid var(--mu-17-ring);
outline-offset: 2px;
}
.mu-17__stage {
max-inline-size: 62rem;
margin-inline: auto;
padding: 4.5rem 1.25rem 5rem;
display: grid;
gap: 2.5rem;
grid-template-columns: minmax(0, 1fr);
}
.mu-17__nav {
min-inline-size: 0;
}
.mu-17__list {
margin: 0;
padding: 0;
list-style: none;
border-block-start: 1px solid var(--mu-17-line);
}
.mu-17__row {
min-inline-size: 0;
border-block-end: 1px solid var(--mu-17-line);
}
.mu-17__link {
min-inline-size: 0;
min-block-size: 44px;
display: grid;
align-items: baseline;
gap: 0.125rem 1rem;
grid-template-columns: minmax(0, 1fr) auto 1.25rem;
padding-block: var(--mu-17-row-pad);
color: var(--mu-17-ink);
text-decoration: none;
transition: color 160ms ease;
}
.mu-17__name {
grid-column: 1;
min-inline-size: 0;
font-size: clamp(1.0625rem, 1rem + 0.5vw, 1.375rem);
line-height: 1.2;
letter-spacing: -0.018em;
font-weight: 500;
text-wrap: pretty;
transition: translate 180ms ease;
}
.mu-17__meta {
grid-column: 1;
grid-row: 2;
min-inline-size: 0;
font-size: 0.8125rem;
color: var(--mu-17-ink-2);
}
.mu-17__year {
grid-column: 2;
grid-row: 1;
font-family: var(--mu-17-mono);
font-size: 0.8125rem;
color: var(--mu-17-ink-2);
font-variant-numeric: tabular-nums;
}
.mu-17__arrow {
grid-column: 3;
grid-row: 1;
inline-size: 20px;
block-size: 20px;
color: var(--mu-17-ink-2);
transition: translate 180ms ease, color 160ms ease;
}
.mu-17__row:hover {
border-block-end-color: var(--mu-17-acc);
}
.mu-17__link:hover .mu-17__arrow,
.mu-17__link:focus-visible .mu-17__arrow {
translate: var(--mu-17-travel) 0;
color: var(--mu-17-acc);
}
.mu-17__link:hover .mu-17__name,
.mu-17__link:focus-visible .mu-17__name {
translate: calc(var(--mu-17-travel) / 2) 0;
}
.mu-17__link:focus-visible {
outline: 2px solid var(--mu-17-ring);
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.mu-17__link,
.mu-17__name,
.mu-17__arrow {
transition: color 160ms ease;
}
.mu-17__link:hover .mu-17__arrow,
.mu-17__link:focus-visible .mu-17__arrow,
.mu-17__link:hover .mu-17__name,
.mu-17__link:focus-visible .mu-17__name {
translate: none;
}
}
@media (min-width: 44rem) {
.mu-17__link {
grid-template-columns: minmax(0, 1fr) minmax(0, 14rem) 4rem 1.5rem;
align-items: center;
}
.mu-17__meta {
grid-column: 2;
grid-row: 1;
}
.mu-17__year {
grid-column: 3;
justify-self: end;
}
.mu-17__arrow {
grid-column: 4;
justify-self: end;
}
}
@media (min-width: 56rem) {
.mu-17__stage {
padding-inline: 3rem;
}
}
```How this works
When the content is text, a list is more information per screen than a grid. A card grid spends its area on containers, image slots and repeated padding. Twelve projects as rows fit on one screen with room for a title, a discipline, a year and a client; the same twelve as cards need two screens and say less on each. So the honest question is not which looks nicer, it is whether the image is doing work. If there is no image worth showing at thumbnail size, use rows.
Give the page exactly one decorative moment. Here it is the row hover: the arrow travels 4px, the rule darkens to full ink, the title shifts a hair. That is enough to feel responsive and cheap enough that it never becomes noise across thirty rows. Adding a second moment — a background wash, a scale, a fade — makes the first one stop registering.
Make the whole row the target. One <a> per row, filling the row, at 44px minimum height and with the year inside the link so the tap area covers the full width. This is also why the markup avoids nesting a second link inside the row: two links in one row means two tab stops for one destination, which is slower for keyboard users, not faster.
Where a grid wins. Visual work — photography, illustration, motion — where the thumbnail is the information. Then the image earns the space and a list actively hides the portfolio. The rule is simple: if a visitor can judge the work from a thumbnail, show thumbnails. If they have to read to judge it, show rows and let the type do the work.
Make it yours
- Change
--mu-17-row-padto tighten or open the list; keep the row at 44px minimum. - Add a discipline column by extending
grid-template-columns— theminmax(0, 1fr)title track absorbs the change. - Set
--mu-17-travelto0for a completely still list, or raise it for a more playful feel. - Swap the arrow for a plus or an external-link glyph; it is a single inline SVG per row.
- Add a thumbnail on hover by giving the row a
::afterwith a background image if your work is visual. - Recolour by editing the two
--mu-17-accdeclarations, hex first then oklch.
Gotchas — read before shipping
- Nesting a second link inside a row doubles the tab stops for one destination and makes the list slower to navigate by keyboard.
- A row under 44px looks tighter and misses on a phone. Use padding rather than a fixed height so long titles can wrap without breaking the floor.
- Multiple hover effects on one row cancel each other out perceptually. One moment, repeated consistently, reads as intent.
- A list hides visual work. If a visitor can judge the project from a thumbnail, a grid is the correct choice and this pattern is the wrong one.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by :has(), oklch(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
oklch() and the :has() theme toggle set the floor. The list itself is grid plus :hover and :focus-visible — Baseline since 2017 and 2022 respectively — so it works well below this floor with the sRGB hex fallbacks declared first. The travel animation is behind prefers-reduced-motion and degrades to a colour change with no layout impact.