19 / 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 Mobile App Screen in a Frame
A single app screen inside a phone frame, where minimalism is under real pressure: 390 points of width, a 44px touch floor, and a thumb that has to reach everything. It shows what gets cut first when space runs out — imagery, repeated metadata, tertiary actions — and why the touch floor is never the thing you cut. The tab list is a real widget with arrow keys, in 24 lines of vanilla script.
Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time
Untitled UI · Affiliate link
The code
<section class="mu-19" aria-labelledby="mu-19-title">
<h2 class="mu-19__sr" id="mu-19-title">Vale account</h2>
<div class="mu-19__theme">
<input class="mu-19__theme-input" type="checkbox" id="mu-19-theme">
<label class="mu-19__theme-label" for="mu-19-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-19__sr">Toggle dark theme</span>
</label>
</div>
<div class="mu-19__stage">
<div class="mu-19__frame-wrap">
<div class="mu-19__frame">
<div class="mu-19__screen" id="mu-19-screen">
<header class="mu-19__app-head">
<p class="mu-19__app-label">Vale</p>
<button class="mu-19__icon-btn" type="button" aria-label="Account settings">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="8" r="3.4" fill="none" stroke="currentColor" stroke-width="1.7"/><path d="M5.5 20a6.5 6.5 0 0 1 13 0" fill="none" stroke="currentColor" stroke-width="1.7"/></svg>
</button>
</header>
<div class="mu-19__balance">
<p class="mu-19__balance-label">Available balance</p>
<p class="mu-19__balance-value">£4,182.60</p>
<p class="mu-19__balance-note">£620.00 pending · settles Tuesday</p>
</div>
<div class="mu-19__tabs" role="tablist" aria-label="Account view">
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-1" aria-controls="mu-19-panel-1" aria-selected="true">Activity</button>
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-2" aria-controls="mu-19-panel-2" aria-selected="false" tabindex="-1">Scheduled</button>
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-3" aria-controls="mu-19-panel-3" aria-selected="false" tabindex="-1">Cards</button>
</div>
<div class="mu-19__panels">
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-1" aria-labelledby="mu-19-tab-1" tabindex="0">
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Basalt Coffee Roasters</span><span class="mu-19__row-meta">Today</span><span class="mu-19__row-amt">−£4.80</span></li>
<li><span class="mu-19__row-name">Meridian salary</span><span class="mu-19__row-meta">28 Aug</span><span class="mu-19__row-amt mu-19__row-amt--in">+£3,410.00</span></li>
<li><span class="mu-19__row-name">Orbit Broadband monthly</span><span class="mu-19__row-meta">27 Aug</span><span class="mu-19__row-amt">−£38.00</span></li>
<li><span class="mu-19__row-name">Fern Grocery, Islington</span><span class="mu-19__row-meta">26 Aug</span><span class="mu-19__row-amt">−£61.24</span></li>
<li><span class="mu-19__row-name">Transfer to savings</span><span class="mu-19__row-meta">25 Aug</span><span class="mu-19__row-amt">−£400.00</span></li>
</ul>
</div>
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-2" aria-labelledby="mu-19-tab-2" tabindex="0" hidden>
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Rent, Talon Property</span><span class="mu-19__row-meta">1 Sep</span><span class="mu-19__row-amt">−£1,240.00</span></li>
<li><span class="mu-19__row-name">Prism Energy</span><span class="mu-19__row-meta">4 Sep</span><span class="mu-19__row-amt">−£92.50</span></li>
<li><span class="mu-19__row-name">Savings standing order</span><span class="mu-19__row-meta">6 Sep</span><span class="mu-19__row-amt">−£400.00</span></li>
</ul>
</div>
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-3" aria-labelledby="mu-19-tab-3" tabindex="0" hidden>
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Physical · 4412</span><span class="mu-19__row-meta">Active</span><span class="mu-19__row-amt">£500 / day</span></li>
<li><span class="mu-19__row-name">Virtual · subscriptions</span><span class="mu-19__row-meta">Active</span><span class="mu-19__row-amt">£120 / mo</span></li>
<li><span class="mu-19__row-name">Virtual · travel</span><span class="mu-19__row-meta">Frozen</span><span class="mu-19__row-amt">—</span></li>
</ul>
</div>
</div>
<footer class="mu-19__bar">
<button class="mu-19__primary" type="button">Send money</button>
</footer>
</div>
</div>
</div>
</div>
</section><section class="mu-19" aria-labelledby="mu-19-title">
<h2 class="mu-19__sr" id="mu-19-title">Vale account</h2>
<div class="mu-19__theme">
<input class="mu-19__theme-input" type="checkbox" id="mu-19-theme">
<label class="mu-19__theme-label" for="mu-19-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-19__sr">Toggle dark theme</span>
</label>
</div>
<div class="mu-19__stage">
<div class="mu-19__frame-wrap">
<div class="mu-19__frame">
<div class="mu-19__screen" id="mu-19-screen">
<header class="mu-19__app-head">
<p class="mu-19__app-label">Vale</p>
<button class="mu-19__icon-btn" type="button" aria-label="Account settings">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="8" r="3.4" fill="none" stroke="currentColor" stroke-width="1.7"/><path d="M5.5 20a6.5 6.5 0 0 1 13 0" fill="none" stroke="currentColor" stroke-width="1.7"/></svg>
</button>
</header>
<div class="mu-19__balance">
<p class="mu-19__balance-label">Available balance</p>
<p class="mu-19__balance-value">£4,182.60</p>
<p class="mu-19__balance-note">£620.00 pending · settles Tuesday</p>
</div>
<div class="mu-19__tabs" role="tablist" aria-label="Account view">
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-1" aria-controls="mu-19-panel-1" aria-selected="true">Activity</button>
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-2" aria-controls="mu-19-panel-2" aria-selected="false" tabindex="-1">Scheduled</button>
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-3" aria-controls="mu-19-panel-3" aria-selected="false" tabindex="-1">Cards</button>
</div>
<div class="mu-19__panels">
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-1" aria-labelledby="mu-19-tab-1" tabindex="0">
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Basalt Coffee Roasters</span><span class="mu-19__row-meta">Today</span><span class="mu-19__row-amt">−£4.80</span></li>
<li><span class="mu-19__row-name">Meridian salary</span><span class="mu-19__row-meta">28 Aug</span><span class="mu-19__row-amt mu-19__row-amt--in">+£3,410.00</span></li>
<li><span class="mu-19__row-name">Orbit Broadband monthly</span><span class="mu-19__row-meta">27 Aug</span><span class="mu-19__row-amt">−£38.00</span></li>
<li><span class="mu-19__row-name">Fern Grocery, Islington</span><span class="mu-19__row-meta">26 Aug</span><span class="mu-19__row-amt">−£61.24</span></li>
<li><span class="mu-19__row-name">Transfer to savings</span><span class="mu-19__row-meta">25 Aug</span><span class="mu-19__row-amt">−£400.00</span></li>
</ul>
</div>
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-2" aria-labelledby="mu-19-tab-2" tabindex="0" hidden>
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Rent, Talon Property</span><span class="mu-19__row-meta">1 Sep</span><span class="mu-19__row-amt">−£1,240.00</span></li>
<li><span class="mu-19__row-name">Prism Energy</span><span class="mu-19__row-meta">4 Sep</span><span class="mu-19__row-amt">−£92.50</span></li>
<li><span class="mu-19__row-name">Savings standing order</span><span class="mu-19__row-meta">6 Sep</span><span class="mu-19__row-amt">−£400.00</span></li>
</ul>
</div>
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-3" aria-labelledby="mu-19-tab-3" tabindex="0" hidden>
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Physical · 4412</span><span class="mu-19__row-meta">Active</span><span class="mu-19__row-amt">£500 / day</span></li>
<li><span class="mu-19__row-name">Virtual · subscriptions</span><span class="mu-19__row-meta">Active</span><span class="mu-19__row-amt">£120 / mo</span></li>
<li><span class="mu-19__row-name">Virtual · travel</span><span class="mu-19__row-meta">Frozen</span><span class="mu-19__row-amt">—</span></li>
</ul>
</div>
</div>
<footer class="mu-19__bar">
<button class="mu-19__primary" type="button">Send money</button>
</footer>
</div>
</div>
</div>
</div>
</section>.mu-19 {
--mu-19-frame-w: 390px;
--mu-19-radius: 40px;
--mu-19-tab-underline: 2px;
--mu-19-round: ui-rounded, "SF Pro Rounded", "Hiragino Maru Gothic ProN", system-ui, -apple-system, "Segoe UI", sans-serif;
--mu-19-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--mu-19-bg: #eaefe9;
--mu-19-bg: oklch(94% 0.016 145);
--mu-19-shell: #d7ddd5;
--mu-19-shell: oklch(88% 0.014 145);
--mu-19-surface: #ffffff;
--mu-19-surface: oklch(100% 0 0);
--mu-19-ink: #14180f;
--mu-19-ink: oklch(19% 0.012 130);
--mu-19-ink-2: #4d5548;
--mu-19-ink-2: oklch(44% 0.014 135);
--mu-19-line: #dfe3dc;
--mu-19-line: oklch(90% 0.008 140);
--mu-19-acc: #1d5c3a;
--mu-19-acc: oklch(42% 0.1 158);
--mu-19-acc-ink: #ffffff;
--mu-19-acc-ink: oklch(100% 0 0);
--mu-19-in: #14663f;
--mu-19-in: oklch(43% 0.11 158);
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--mu-19-bg);
color: var(--mu-19-ink);
font-family: var(--mu-19-round);
line-height: 1.5;
-webkit-text-size-adjust: 100%;
}
.mu-19 *,
.mu-19 *::before,
.mu-19 *::after {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.mu-19 {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
}
.mu-19[data-theme="dark"],
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
@media (prefers-color-scheme: light) {
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
}
@media (prefers-color-scheme: dark) {
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #eaefe9;
--mu-19-bg: oklch(94% 0.016 145);
--mu-19-shell: #d7ddd5;
--mu-19-shell: oklch(88% 0.014 145);
--mu-19-surface: #ffffff;
--mu-19-surface: oklch(100% 0 0);
--mu-19-ink: #14180f;
--mu-19-ink: oklch(19% 0.012 130);
--mu-19-ink-2: #4d5548;
--mu-19-ink-2: oklch(44% 0.014 135);
--mu-19-line: #dfe3dc;
--mu-19-line: oklch(90% 0.008 140);
--mu-19-acc: #1d5c3a;
--mu-19-acc: oklch(42% 0.1 158);
--mu-19-acc-ink: #ffffff;
--mu-19-acc-ink: oklch(100% 0 0);
--mu-19-in: #14663f;
--mu-19-in: oklch(43% 0.11 158);
}
}
.mu-19__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-19__theme {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
z-index: 5;
}
.mu-19__theme-input {
position: absolute;
inset: 0;
inline-size: 44px;
block-size: 44px;
margin: 0;
opacity: 0;
cursor: pointer;
}
.mu-19__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--mu-19-line);
border-radius: 50%;
background: var(--mu-19-surface);
color: var(--mu-19-ink-2);
cursor: pointer;
}
.mu-19__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.mu-19__theme-input:focus-visible + .mu-19__theme-label {
outline: 2px solid var(--mu-19-acc);
outline-offset: 2px;
}
.mu-19__stage {
max-inline-size: 68rem;
margin-inline: auto;
padding: 2.5rem 1.25rem 3rem;
display: grid;
grid-template-columns: minmax(0, 1fr);
}
.mu-19__frame-wrap {
display: grid;
min-inline-size: 0;
}
.mu-19__frame {
inline-size: 100%;
max-inline-size: var(--mu-19-frame-w);
margin-inline: auto;
padding: 10px;
border-radius: var(--mu-19-radius);
background: var(--mu-19-shell);
min-inline-size: 0;
}
.mu-19__screen {
min-inline-size: 0;
aspect-ratio: 390 / 844;
display: grid;
grid-template-rows: auto auto auto minmax(0, 1fr) auto;
background: var(--mu-19-surface);
border-radius: calc(var(--mu-19-radius) - 10px);
overflow: hidden;
}
.mu-19__app-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.75rem 1.25rem 0.25rem;
}
.mu-19__app-label {
margin: 0;
font-size: 0.9375rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.mu-19__icon-btn {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
min-inline-size: 44px;
min-block-size: 44px;
padding: 0;
border: 0;
border-radius: 50%;
background: transparent;
color: var(--mu-19-ink);
cursor: pointer;
}
.mu-19__icon-btn svg {
inline-size: 22px;
block-size: 22px;
}
.mu-19__icon-btn:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: 2px;
}
.mu-19__balance {
padding: 0.5rem 1.25rem 1.25rem;
display: grid;
gap: 0.25rem;
}
.mu-19__balance p {
margin: 0;
min-inline-size: 0;
}
.mu-19__balance-label {
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--mu-19-ink-2);
}
.mu-19__balance-value {
font-size: clamp(1.875rem, 8cqi, 2.375rem);
line-height: 1.05;
letter-spacing: -0.03em;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.mu-19__balance-note {
font-size: 0.75rem;
color: var(--mu-19-ink-2);
}
.mu-19__tabs {
display: flex;
gap: 0.25rem;
padding-inline: 1.25rem;
border-block-end: 1px solid var(--mu-19-line);
}
.mu-19__tab {
min-block-size: 44px;
min-inline-size: 44px;
padding-inline: 0.5rem;
border: 0;
border-block-end: var(--mu-19-tab-underline) solid transparent;
background: transparent;
color: var(--mu-19-ink-2);
font: inherit;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
}
.mu-19__tab[aria-selected="true"] {
color: var(--mu-19-ink);
border-block-end-color: var(--mu-19-acc);
}
.mu-19__tab:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: -2px;
}
.mu-19__panels {
min-inline-size: 0;
min-block-size: 0;
overflow-y: auto;
}
.mu-19__panel {
min-inline-size: 0;
}
.mu-19__panel:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: -2px;
}
.mu-19__rows {
margin: 0;
padding: 0.5rem 1.25rem;
list-style: none;
display: grid;
gap: 0;
}
.mu-19__rows li {
min-inline-size: 0;
min-block-size: 44px;
display: grid;
align-items: center;
gap: 0 0.75rem;
grid-template-columns: minmax(0, 1fr) auto;
padding-block: 0.625rem;
border-block-end: 1px solid var(--mu-19-line);
}
.mu-19__rows li:last-child {
border-block-end: 0;
}
.mu-19__row-name {
grid-column: 1;
min-inline-size: 0;
font-size: 0.875rem;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mu-19__row-meta {
grid-column: 1;
grid-row: 2;
font-size: 0.75rem;
color: var(--mu-19-ink-2);
}
.mu-19__row-amt {
grid-column: 2;
grid-row: 1 / span 2;
font-size: 0.875rem;
font-weight: 600;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.mu-19__row-amt--in {
color: var(--mu-19-in);
}
.mu-19__bar {
padding: 0.75rem 1.25rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
border-block-start: 1px solid var(--mu-19-line);
}
.mu-19__primary {
inline-size: 100%;
min-block-size: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
border-radius: 12px;
background: var(--mu-19-acc);
color: var(--mu-19-acc-ink);
font: inherit;
font-size: 0.9375rem;
font-weight: 700;
cursor: pointer;
}
.mu-19__primary:focus-visible {
outline: 2px solid var(--mu-19-ink);
outline-offset: 2px;
}
@supports (container-type: inline-size) {
.mu-19__screen {
container-type: inline-size;
}
}
@media (min-width: 56rem) {
.mu-19__stage {
grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
padding-inline: 3rem;
gap: 3rem;
}
.mu-19__frame-wrap {
grid-row: span 2;
}
}.mu-19 {
--mu-19-frame-w: 390px;
--mu-19-radius: 40px;
--mu-19-tab-underline: 2px;
--mu-19-round: ui-rounded, "SF Pro Rounded", "Hiragino Maru Gothic ProN", system-ui, -apple-system, "Segoe UI", sans-serif;
--mu-19-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--mu-19-bg: #eaefe9;
--mu-19-bg: oklch(94% 0.016 145);
--mu-19-shell: #d7ddd5;
--mu-19-shell: oklch(88% 0.014 145);
--mu-19-surface: #ffffff;
--mu-19-surface: oklch(100% 0 0);
--mu-19-ink: #14180f;
--mu-19-ink: oklch(19% 0.012 130);
--mu-19-ink-2: #4d5548;
--mu-19-ink-2: oklch(44% 0.014 135);
--mu-19-line: #dfe3dc;
--mu-19-line: oklch(90% 0.008 140);
--mu-19-acc: #1d5c3a;
--mu-19-acc: oklch(42% 0.1 158);
--mu-19-acc-ink: #ffffff;
--mu-19-acc-ink: oklch(100% 0 0);
--mu-19-in: #14663f;
--mu-19-in: oklch(43% 0.11 158);
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--mu-19-bg);
color: var(--mu-19-ink);
font-family: var(--mu-19-round);
line-height: 1.5;
-webkit-text-size-adjust: 100%;
}
.mu-19 *,
.mu-19 *::before,
.mu-19 *::after {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.mu-19 {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
}
.mu-19[data-theme="dark"],
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
@media (prefers-color-scheme: light) {
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
}
@media (prefers-color-scheme: dark) {
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #eaefe9;
--mu-19-bg: oklch(94% 0.016 145);
--mu-19-shell: #d7ddd5;
--mu-19-shell: oklch(88% 0.014 145);
--mu-19-surface: #ffffff;
--mu-19-surface: oklch(100% 0 0);
--mu-19-ink: #14180f;
--mu-19-ink: oklch(19% 0.012 130);
--mu-19-ink-2: #4d5548;
--mu-19-ink-2: oklch(44% 0.014 135);
--mu-19-line: #dfe3dc;
--mu-19-line: oklch(90% 0.008 140);
--mu-19-acc: #1d5c3a;
--mu-19-acc: oklch(42% 0.1 158);
--mu-19-acc-ink: #ffffff;
--mu-19-acc-ink: oklch(100% 0 0);
--mu-19-in: #14663f;
--mu-19-in: oklch(43% 0.11 158);
}
}
.mu-19__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-19__theme {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
z-index: 5;
}
.mu-19__theme-input {
position: absolute;
inset: 0;
inline-size: 44px;
block-size: 44px;
margin: 0;
opacity: 0;
cursor: pointer;
}
.mu-19__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--mu-19-line);
border-radius: 50%;
background: var(--mu-19-surface);
color: var(--mu-19-ink-2);
cursor: pointer;
}
.mu-19__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.mu-19__theme-input:focus-visible + .mu-19__theme-label {
outline: 2px solid var(--mu-19-acc);
outline-offset: 2px;
}
.mu-19__stage {
max-inline-size: 68rem;
margin-inline: auto;
padding: 2.5rem 1.25rem 3rem;
display: grid;
grid-template-columns: minmax(0, 1fr);
}
.mu-19__frame-wrap {
display: grid;
min-inline-size: 0;
}
.mu-19__frame {
inline-size: 100%;
max-inline-size: var(--mu-19-frame-w);
margin-inline: auto;
padding: 10px;
border-radius: var(--mu-19-radius);
background: var(--mu-19-shell);
min-inline-size: 0;
}
.mu-19__screen {
min-inline-size: 0;
aspect-ratio: 390 / 844;
display: grid;
grid-template-rows: auto auto auto minmax(0, 1fr) auto;
background: var(--mu-19-surface);
border-radius: calc(var(--mu-19-radius) - 10px);
overflow: hidden;
}
.mu-19__app-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.75rem 1.25rem 0.25rem;
}
.mu-19__app-label {
margin: 0;
font-size: 0.9375rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.mu-19__icon-btn {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
min-inline-size: 44px;
min-block-size: 44px;
padding: 0;
border: 0;
border-radius: 50%;
background: transparent;
color: var(--mu-19-ink);
cursor: pointer;
}
.mu-19__icon-btn svg {
inline-size: 22px;
block-size: 22px;
}
.mu-19__icon-btn:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: 2px;
}
.mu-19__balance {
padding: 0.5rem 1.25rem 1.25rem;
display: grid;
gap: 0.25rem;
}
.mu-19__balance p {
margin: 0;
min-inline-size: 0;
}
.mu-19__balance-label {
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--mu-19-ink-2);
}
.mu-19__balance-value {
font-size: clamp(1.875rem, 8cqi, 2.375rem);
line-height: 1.05;
letter-spacing: -0.03em;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.mu-19__balance-note {
font-size: 0.75rem;
color: var(--mu-19-ink-2);
}
.mu-19__tabs {
display: flex;
gap: 0.25rem;
padding-inline: 1.25rem;
border-block-end: 1px solid var(--mu-19-line);
}
.mu-19__tab {
min-block-size: 44px;
min-inline-size: 44px;
padding-inline: 0.5rem;
border: 0;
border-block-end: var(--mu-19-tab-underline) solid transparent;
background: transparent;
color: var(--mu-19-ink-2);
font: inherit;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
}
.mu-19__tab[aria-selected="true"] {
color: var(--mu-19-ink);
border-block-end-color: var(--mu-19-acc);
}
.mu-19__tab:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: -2px;
}
.mu-19__panels {
min-inline-size: 0;
min-block-size: 0;
overflow-y: auto;
}
.mu-19__panel {
min-inline-size: 0;
}
.mu-19__panel:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: -2px;
}
.mu-19__rows {
margin: 0;
padding: 0.5rem 1.25rem;
list-style: none;
display: grid;
gap: 0;
}
.mu-19__rows li {
min-inline-size: 0;
min-block-size: 44px;
display: grid;
align-items: center;
gap: 0 0.75rem;
grid-template-columns: minmax(0, 1fr) auto;
padding-block: 0.625rem;
border-block-end: 1px solid var(--mu-19-line);
}
.mu-19__rows li:last-child {
border-block-end: 0;
}
.mu-19__row-name {
grid-column: 1;
min-inline-size: 0;
font-size: 0.875rem;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mu-19__row-meta {
grid-column: 1;
grid-row: 2;
font-size: 0.75rem;
color: var(--mu-19-ink-2);
}
.mu-19__row-amt {
grid-column: 2;
grid-row: 1 / span 2;
font-size: 0.875rem;
font-weight: 600;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.mu-19__row-amt--in {
color: var(--mu-19-in);
}
.mu-19__bar {
padding: 0.75rem 1.25rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
border-block-start: 1px solid var(--mu-19-line);
}
.mu-19__primary {
inline-size: 100%;
min-block-size: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
border-radius: 12px;
background: var(--mu-19-acc);
color: var(--mu-19-acc-ink);
font: inherit;
font-size: 0.9375rem;
font-weight: 700;
cursor: pointer;
}
.mu-19__primary:focus-visible {
outline: 2px solid var(--mu-19-ink);
outline-offset: 2px;
}
@supports (container-type: inline-size) {
.mu-19__screen {
container-type: inline-size;
}
}
@media (min-width: 56rem) {
.mu-19__stage {
grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
padding-inline: 3rem;
gap: 3rem;
}
.mu-19__frame-wrap {
grid-row: span 2;
}
}(() => {
const screen = document.querySelector('#mu-19-screen');
if (!screen) return;
const tabs = [...screen.querySelectorAll('[role="tab"]')];
if (!tabs.length) return;
const select = (tab) => {
for (const t of tabs) {
const on = t === tab;
t.setAttribute('aria-selected', String(on));
t.tabIndex = on ? 0 : -1;
const panel = screen.querySelector('#' + t.getAttribute('aria-controls'));
if (panel) panel.hidden = !on;
}
tab.focus();
};
const onClick = (e) => select(e.currentTarget);
const onKey = (e) => {
const i = tabs.indexOf(e.currentTarget);
if (e.key === 'ArrowRight') select(tabs[(i + 1) % tabs.length]);
else if (e.key === 'ArrowLeft') select(tabs[(i - 1 + tabs.length) % tabs.length]);
else return;
e.preventDefault();
};
for (const t of tabs) {
t.addEventListener('click', onClick);
t.addEventListener('keydown', onKey);
}
screen.__muCleanup = () => {
for (const t of tabs) {
t.removeEventListener('click', onClick);
t.removeEventListener('keydown', onKey);
}
};
})();(() => {
const screen = document.querySelector('#mu-19-screen');
if (!screen) return;
const tabs = [...screen.querySelectorAll('[role="tab"]')];
if (!tabs.length) return;
const select = (tab) => {
for (const t of tabs) {
const on = t === tab;
t.setAttribute('aria-selected', String(on));
t.tabIndex = on ? 0 : -1;
const panel = screen.querySelector('#' + t.getAttribute('aria-controls'));
if (panel) panel.hidden = !on;
}
tab.focus();
};
const onClick = (e) => select(e.currentTarget);
const onKey = (e) => {
const i = tabs.indexOf(e.currentTarget);
if (e.key === 'ArrowRight') select(tabs[(i + 1) % tabs.length]);
else if (e.key === 'ArrowLeft') select(tabs[(i - 1 + tabs.length) % tabs.length]);
else return;
e.preventDefault();
};
for (const t of tabs) {
t.addEventListener('click', onClick);
t.addEventListener('keydown', onKey);
}
screen.__muCleanup = () => {
for (const t of tabs) {
t.removeEventListener('click', onClick);
t.removeEventListener('keydown', onKey);
}
};
})();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 Mobile App Screen in a Frame
Source: https://codefronts.com/design-styles/css-minimalist-ui/elara-fintech/
A single app screen inside a phone frame, where minimalism is under real pressure: 390 points of width, a 44px touch floor, and a thumb that has to reach everything. It shows what gets cut first when space runs out — imagery, repeated metadata, tertiary actions — and why the touch floor is never the thing you cut. The tab list is a real widget with arrow keys, in 24 lines of vanilla script.
## HTML
```html
<section class="mu-19" aria-labelledby="mu-19-title">
<h2 class="mu-19__sr" id="mu-19-title">Vale account</h2>
<div class="mu-19__theme">
<input class="mu-19__theme-input" type="checkbox" id="mu-19-theme">
<label class="mu-19__theme-label" for="mu-19-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-19__sr">Toggle dark theme</span>
</label>
</div>
<div class="mu-19__stage">
<div class="mu-19__frame-wrap">
<div class="mu-19__frame">
<div class="mu-19__screen" id="mu-19-screen">
<header class="mu-19__app-head">
<p class="mu-19__app-label">Vale</p>
<button class="mu-19__icon-btn" type="button" aria-label="Account settings">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="8" r="3.4" fill="none" stroke="currentColor" stroke-width="1.7"/><path d="M5.5 20a6.5 6.5 0 0 1 13 0" fill="none" stroke="currentColor" stroke-width="1.7"/></svg>
</button>
</header>
<div class="mu-19__balance">
<p class="mu-19__balance-label">Available balance</p>
<p class="mu-19__balance-value">£4,182.60</p>
<p class="mu-19__balance-note">£620.00 pending · settles Tuesday</p>
</div>
<div class="mu-19__tabs" role="tablist" aria-label="Account view">
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-1" aria-controls="mu-19-panel-1" aria-selected="true">Activity</button>
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-2" aria-controls="mu-19-panel-2" aria-selected="false" tabindex="-1">Scheduled</button>
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-3" aria-controls="mu-19-panel-3" aria-selected="false" tabindex="-1">Cards</button>
</div>
<div class="mu-19__panels">
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-1" aria-labelledby="mu-19-tab-1" tabindex="0">
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Basalt Coffee Roasters</span><span class="mu-19__row-meta">Today</span><span class="mu-19__row-amt">−£4.80</span></li>
<li><span class="mu-19__row-name">Meridian salary</span><span class="mu-19__row-meta">28 Aug</span><span class="mu-19__row-amt mu-19__row-amt--in">+£3,410.00</span></li>
<li><span class="mu-19__row-name">Orbit Broadband monthly</span><span class="mu-19__row-meta">27 Aug</span><span class="mu-19__row-amt">−£38.00</span></li>
<li><span class="mu-19__row-name">Fern Grocery, Islington</span><span class="mu-19__row-meta">26 Aug</span><span class="mu-19__row-amt">−£61.24</span></li>
<li><span class="mu-19__row-name">Transfer to savings</span><span class="mu-19__row-meta">25 Aug</span><span class="mu-19__row-amt">−£400.00</span></li>
</ul>
</div>
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-2" aria-labelledby="mu-19-tab-2" tabindex="0" hidden>
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Rent, Talon Property</span><span class="mu-19__row-meta">1 Sep</span><span class="mu-19__row-amt">−£1,240.00</span></li>
<li><span class="mu-19__row-name">Prism Energy</span><span class="mu-19__row-meta">4 Sep</span><span class="mu-19__row-amt">−£92.50</span></li>
<li><span class="mu-19__row-name">Savings standing order</span><span class="mu-19__row-meta">6 Sep</span><span class="mu-19__row-amt">−£400.00</span></li>
</ul>
</div>
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-3" aria-labelledby="mu-19-tab-3" tabindex="0" hidden>
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Physical · 4412</span><span class="mu-19__row-meta">Active</span><span class="mu-19__row-amt">£500 / day</span></li>
<li><span class="mu-19__row-name">Virtual · subscriptions</span><span class="mu-19__row-meta">Active</span><span class="mu-19__row-amt">£120 / mo</span></li>
<li><span class="mu-19__row-name">Virtual · travel</span><span class="mu-19__row-meta">Frozen</span><span class="mu-19__row-amt">—</span></li>
</ul>
</div>
</div>
<footer class="mu-19__bar">
<button class="mu-19__primary" type="button">Send money</button>
</footer>
</div>
</div>
</div>
</div>
</section>
```
## CSS
```css
.mu-19 {
--mu-19-frame-w: 390px;
--mu-19-radius: 40px;
--mu-19-tab-underline: 2px;
--mu-19-round: ui-rounded, "SF Pro Rounded", "Hiragino Maru Gothic ProN", system-ui, -apple-system, "Segoe UI", sans-serif;
--mu-19-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--mu-19-bg: #eaefe9;
--mu-19-bg: oklch(94% 0.016 145);
--mu-19-shell: #d7ddd5;
--mu-19-shell: oklch(88% 0.014 145);
--mu-19-surface: #ffffff;
--mu-19-surface: oklch(100% 0 0);
--mu-19-ink: #14180f;
--mu-19-ink: oklch(19% 0.012 130);
--mu-19-ink-2: #4d5548;
--mu-19-ink-2: oklch(44% 0.014 135);
--mu-19-line: #dfe3dc;
--mu-19-line: oklch(90% 0.008 140);
--mu-19-acc: #1d5c3a;
--mu-19-acc: oklch(42% 0.1 158);
--mu-19-acc-ink: #ffffff;
--mu-19-acc-ink: oklch(100% 0 0);
--mu-19-in: #14663f;
--mu-19-in: oklch(43% 0.11 158);
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--mu-19-bg);
color: var(--mu-19-ink);
font-family: var(--mu-19-round);
line-height: 1.5;
-webkit-text-size-adjust: 100%;
}
.mu-19 *,
.mu-19 *::before,
.mu-19 *::after {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.mu-19 {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
}
.mu-19[data-theme="dark"],
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
@media (prefers-color-scheme: light) {
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
}
@media (prefers-color-scheme: dark) {
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #eaefe9;
--mu-19-bg: oklch(94% 0.016 145);
--mu-19-shell: #d7ddd5;
--mu-19-shell: oklch(88% 0.014 145);
--mu-19-surface: #ffffff;
--mu-19-surface: oklch(100% 0 0);
--mu-19-ink: #14180f;
--mu-19-ink: oklch(19% 0.012 130);
--mu-19-ink-2: #4d5548;
--mu-19-ink-2: oklch(44% 0.014 135);
--mu-19-line: #dfe3dc;
--mu-19-line: oklch(90% 0.008 140);
--mu-19-acc: #1d5c3a;
--mu-19-acc: oklch(42% 0.1 158);
--mu-19-acc-ink: #ffffff;
--mu-19-acc-ink: oklch(100% 0 0);
--mu-19-in: #14663f;
--mu-19-in: oklch(43% 0.11 158);
}
}
.mu-19__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-19__theme {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
z-index: 5;
}
.mu-19__theme-input {
position: absolute;
inset: 0;
inline-size: 44px;
block-size: 44px;
margin: 0;
opacity: 0;
cursor: pointer;
}
.mu-19__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--mu-19-line);
border-radius: 50%;
background: var(--mu-19-surface);
color: var(--mu-19-ink-2);
cursor: pointer;
}
.mu-19__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.mu-19__theme-input:focus-visible + .mu-19__theme-label {
outline: 2px solid var(--mu-19-acc);
outline-offset: 2px;
}
.mu-19__stage {
max-inline-size: 68rem;
margin-inline: auto;
padding: 2.5rem 1.25rem 3rem;
display: grid;
grid-template-columns: minmax(0, 1fr);
}
.mu-19__frame-wrap {
display: grid;
min-inline-size: 0;
}
.mu-19__frame {
inline-size: 100%;
max-inline-size: var(--mu-19-frame-w);
margin-inline: auto;
padding: 10px;
border-radius: var(--mu-19-radius);
background: var(--mu-19-shell);
min-inline-size: 0;
}
.mu-19__screen {
min-inline-size: 0;
aspect-ratio: 390 / 844;
display: grid;
grid-template-rows: auto auto auto minmax(0, 1fr) auto;
background: var(--mu-19-surface);
border-radius: calc(var(--mu-19-radius) - 10px);
overflow: hidden;
}
.mu-19__app-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.75rem 1.25rem 0.25rem;
}
.mu-19__app-label {
margin: 0;
font-size: 0.9375rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.mu-19__icon-btn {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
min-inline-size: 44px;
min-block-size: 44px;
padding: 0;
border: 0;
border-radius: 50%;
background: transparent;
color: var(--mu-19-ink);
cursor: pointer;
}
.mu-19__icon-btn svg {
inline-size: 22px;
block-size: 22px;
}
.mu-19__icon-btn:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: 2px;
}
.mu-19__balance {
padding: 0.5rem 1.25rem 1.25rem;
display: grid;
gap: 0.25rem;
}
.mu-19__balance p {
margin: 0;
min-inline-size: 0;
}
.mu-19__balance-label {
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--mu-19-ink-2);
}
.mu-19__balance-value {
font-size: clamp(1.875rem, 8cqi, 2.375rem);
line-height: 1.05;
letter-spacing: -0.03em;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.mu-19__balance-note {
font-size: 0.75rem;
color: var(--mu-19-ink-2);
}
.mu-19__tabs {
display: flex;
gap: 0.25rem;
padding-inline: 1.25rem;
border-block-end: 1px solid var(--mu-19-line);
}
.mu-19__tab {
min-block-size: 44px;
min-inline-size: 44px;
padding-inline: 0.5rem;
border: 0;
border-block-end: var(--mu-19-tab-underline) solid transparent;
background: transparent;
color: var(--mu-19-ink-2);
font: inherit;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
}
.mu-19__tab[aria-selected="true"] {
color: var(--mu-19-ink);
border-block-end-color: var(--mu-19-acc);
}
.mu-19__tab:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: -2px;
}
.mu-19__panels {
min-inline-size: 0;
min-block-size: 0;
overflow-y: auto;
}
.mu-19__panel {
min-inline-size: 0;
}
.mu-19__panel:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: -2px;
}
.mu-19__rows {
margin: 0;
padding: 0.5rem 1.25rem;
list-style: none;
display: grid;
gap: 0;
}
.mu-19__rows li {
min-inline-size: 0;
min-block-size: 44px;
display: grid;
align-items: center;
gap: 0 0.75rem;
grid-template-columns: minmax(0, 1fr) auto;
padding-block: 0.625rem;
border-block-end: 1px solid var(--mu-19-line);
}
.mu-19__rows li:last-child {
border-block-end: 0;
}
.mu-19__row-name {
grid-column: 1;
min-inline-size: 0;
font-size: 0.875rem;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mu-19__row-meta {
grid-column: 1;
grid-row: 2;
font-size: 0.75rem;
color: var(--mu-19-ink-2);
}
.mu-19__row-amt {
grid-column: 2;
grid-row: 1 / span 2;
font-size: 0.875rem;
font-weight: 600;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.mu-19__row-amt--in {
color: var(--mu-19-in);
}
.mu-19__bar {
padding: 0.75rem 1.25rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
border-block-start: 1px solid var(--mu-19-line);
}
.mu-19__primary {
inline-size: 100%;
min-block-size: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
border-radius: 12px;
background: var(--mu-19-acc);
color: var(--mu-19-acc-ink);
font: inherit;
font-size: 0.9375rem;
font-weight: 700;
cursor: pointer;
}
.mu-19__primary:focus-visible {
outline: 2px solid var(--mu-19-ink);
outline-offset: 2px;
}
@supports (container-type: inline-size) {
.mu-19__screen {
container-type: inline-size;
}
}
@media (min-width: 56rem) {
.mu-19__stage {
grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
padding-inline: 3rem;
gap: 3rem;
}
.mu-19__frame-wrap {
grid-row: span 2;
}
}
```
## JavaScript
```js
(() => {
const screen = document.querySelector('#mu-19-screen');
if (!screen) return;
const tabs = [...screen.querySelectorAll('[role="tab"]')];
if (!tabs.length) return;
const select = (tab) => {
for (const t of tabs) {
const on = t === tab;
t.setAttribute('aria-selected', String(on));
t.tabIndex = on ? 0 : -1;
const panel = screen.querySelector('#' + t.getAttribute('aria-controls'));
if (panel) panel.hidden = !on;
}
tab.focus();
};
const onClick = (e) => select(e.currentTarget);
const onKey = (e) => {
const i = tabs.indexOf(e.currentTarget);
if (e.key === 'ArrowRight') select(tabs[(i + 1) % tabs.length]);
else if (e.key === 'ArrowLeft') select(tabs[(i - 1 + tabs.length) % tabs.length]);
else return;
e.preventDefault();
};
for (const t of tabs) {
t.addEventListener('click', onClick);
t.addEventListener('keydown', onKey);
}
screen.__muCleanup = () => {
for (const t of tabs) {
t.removeEventListener('click', onClick);
t.removeEventListener('keydown', onKey);
}
};
})();
```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 Mobile App Screen in a Frame
Source: https://codefronts.com/design-styles/css-minimalist-ui/elara-fintech/
A single app screen inside a phone frame, where minimalism is under real pressure: 390 points of width, a 44px touch floor, and a thumb that has to reach everything. It shows what gets cut first when space runs out — imagery, repeated metadata, tertiary actions — and why the touch floor is never the thing you cut. The tab list is a real widget with arrow keys, in 24 lines of vanilla script.
## HTML
```html
<section class="mu-19" aria-labelledby="mu-19-title">
<h2 class="mu-19__sr" id="mu-19-title">Vale account</h2>
<div class="mu-19__theme">
<input class="mu-19__theme-input" type="checkbox" id="mu-19-theme">
<label class="mu-19__theme-label" for="mu-19-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-19__sr">Toggle dark theme</span>
</label>
</div>
<div class="mu-19__stage">
<div class="mu-19__frame-wrap">
<div class="mu-19__frame">
<div class="mu-19__screen" id="mu-19-screen">
<header class="mu-19__app-head">
<p class="mu-19__app-label">Vale</p>
<button class="mu-19__icon-btn" type="button" aria-label="Account settings">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="8" r="3.4" fill="none" stroke="currentColor" stroke-width="1.7"/><path d="M5.5 20a6.5 6.5 0 0 1 13 0" fill="none" stroke="currentColor" stroke-width="1.7"/></svg>
</button>
</header>
<div class="mu-19__balance">
<p class="mu-19__balance-label">Available balance</p>
<p class="mu-19__balance-value">£4,182.60</p>
<p class="mu-19__balance-note">£620.00 pending · settles Tuesday</p>
</div>
<div class="mu-19__tabs" role="tablist" aria-label="Account view">
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-1" aria-controls="mu-19-panel-1" aria-selected="true">Activity</button>
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-2" aria-controls="mu-19-panel-2" aria-selected="false" tabindex="-1">Scheduled</button>
<button class="mu-19__tab" type="button" role="tab" id="mu-19-tab-3" aria-controls="mu-19-panel-3" aria-selected="false" tabindex="-1">Cards</button>
</div>
<div class="mu-19__panels">
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-1" aria-labelledby="mu-19-tab-1" tabindex="0">
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Basalt Coffee Roasters</span><span class="mu-19__row-meta">Today</span><span class="mu-19__row-amt">−£4.80</span></li>
<li><span class="mu-19__row-name">Meridian salary</span><span class="mu-19__row-meta">28 Aug</span><span class="mu-19__row-amt mu-19__row-amt--in">+£3,410.00</span></li>
<li><span class="mu-19__row-name">Orbit Broadband monthly</span><span class="mu-19__row-meta">27 Aug</span><span class="mu-19__row-amt">−£38.00</span></li>
<li><span class="mu-19__row-name">Fern Grocery, Islington</span><span class="mu-19__row-meta">26 Aug</span><span class="mu-19__row-amt">−£61.24</span></li>
<li><span class="mu-19__row-name">Transfer to savings</span><span class="mu-19__row-meta">25 Aug</span><span class="mu-19__row-amt">−£400.00</span></li>
</ul>
</div>
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-2" aria-labelledby="mu-19-tab-2" tabindex="0" hidden>
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Rent, Talon Property</span><span class="mu-19__row-meta">1 Sep</span><span class="mu-19__row-amt">−£1,240.00</span></li>
<li><span class="mu-19__row-name">Prism Energy</span><span class="mu-19__row-meta">4 Sep</span><span class="mu-19__row-amt">−£92.50</span></li>
<li><span class="mu-19__row-name">Savings standing order</span><span class="mu-19__row-meta">6 Sep</span><span class="mu-19__row-amt">−£400.00</span></li>
</ul>
</div>
<div class="mu-19__panel" role="tabpanel" id="mu-19-panel-3" aria-labelledby="mu-19-tab-3" tabindex="0" hidden>
<ul class="mu-19__rows">
<li><span class="mu-19__row-name">Physical · 4412</span><span class="mu-19__row-meta">Active</span><span class="mu-19__row-amt">£500 / day</span></li>
<li><span class="mu-19__row-name">Virtual · subscriptions</span><span class="mu-19__row-meta">Active</span><span class="mu-19__row-amt">£120 / mo</span></li>
<li><span class="mu-19__row-name">Virtual · travel</span><span class="mu-19__row-meta">Frozen</span><span class="mu-19__row-amt">—</span></li>
</ul>
</div>
</div>
<footer class="mu-19__bar">
<button class="mu-19__primary" type="button">Send money</button>
</footer>
</div>
</div>
</div>
</div>
</section>
```
## CSS
```css
.mu-19 {
--mu-19-frame-w: 390px;
--mu-19-radius: 40px;
--mu-19-tab-underline: 2px;
--mu-19-round: ui-rounded, "SF Pro Rounded", "Hiragino Maru Gothic ProN", system-ui, -apple-system, "Segoe UI", sans-serif;
--mu-19-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--mu-19-bg: #eaefe9;
--mu-19-bg: oklch(94% 0.016 145);
--mu-19-shell: #d7ddd5;
--mu-19-shell: oklch(88% 0.014 145);
--mu-19-surface: #ffffff;
--mu-19-surface: oklch(100% 0 0);
--mu-19-ink: #14180f;
--mu-19-ink: oklch(19% 0.012 130);
--mu-19-ink-2: #4d5548;
--mu-19-ink-2: oklch(44% 0.014 135);
--mu-19-line: #dfe3dc;
--mu-19-line: oklch(90% 0.008 140);
--mu-19-acc: #1d5c3a;
--mu-19-acc: oklch(42% 0.1 158);
--mu-19-acc-ink: #ffffff;
--mu-19-acc-ink: oklch(100% 0 0);
--mu-19-in: #14663f;
--mu-19-in: oklch(43% 0.11 158);
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--mu-19-bg);
color: var(--mu-19-ink);
font-family: var(--mu-19-round);
line-height: 1.5;
-webkit-text-size-adjust: 100%;
}
.mu-19 *,
.mu-19 *::before,
.mu-19 *::after {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.mu-19 {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
}
.mu-19[data-theme="dark"],
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
@media (prefers-color-scheme: light) {
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #0f1310;
--mu-19-bg: oklch(17.5% 0.01 145);
--mu-19-shell: #1c211c;
--mu-19-shell: oklch(24% 0.012 145);
--mu-19-surface: #171c17;
--mu-19-surface: oklch(21.5% 0.011 145);
--mu-19-ink: #edf1ea;
--mu-19-ink: oklch(95.5% 0.01 140);
--mu-19-ink-2: #a3ac9d;
--mu-19-ink-2: oklch(73.5% 0.014 140);
--mu-19-line: #2c332b;
--mu-19-line: oklch(29% 0.012 145);
--mu-19-acc: #74d3a1;
--mu-19-acc: oklch(82% 0.12 158);
--mu-19-acc-ink: #0d1a12;
--mu-19-acc-ink: oklch(17% 0.02 158);
--mu-19-in: #74d3a1;
--mu-19-in: oklch(82% 0.12 158);
}
}
@media (prefers-color-scheme: dark) {
.mu-19:has(.mu-19__theme-input:checked) {
--mu-19-bg: #eaefe9;
--mu-19-bg: oklch(94% 0.016 145);
--mu-19-shell: #d7ddd5;
--mu-19-shell: oklch(88% 0.014 145);
--mu-19-surface: #ffffff;
--mu-19-surface: oklch(100% 0 0);
--mu-19-ink: #14180f;
--mu-19-ink: oklch(19% 0.012 130);
--mu-19-ink-2: #4d5548;
--mu-19-ink-2: oklch(44% 0.014 135);
--mu-19-line: #dfe3dc;
--mu-19-line: oklch(90% 0.008 140);
--mu-19-acc: #1d5c3a;
--mu-19-acc: oklch(42% 0.1 158);
--mu-19-acc-ink: #ffffff;
--mu-19-acc-ink: oklch(100% 0 0);
--mu-19-in: #14663f;
--mu-19-in: oklch(43% 0.11 158);
}
}
.mu-19__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-19__theme {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
z-index: 5;
}
.mu-19__theme-input {
position: absolute;
inset: 0;
inline-size: 44px;
block-size: 44px;
margin: 0;
opacity: 0;
cursor: pointer;
}
.mu-19__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--mu-19-line);
border-radius: 50%;
background: var(--mu-19-surface);
color: var(--mu-19-ink-2);
cursor: pointer;
}
.mu-19__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.mu-19__theme-input:focus-visible + .mu-19__theme-label {
outline: 2px solid var(--mu-19-acc);
outline-offset: 2px;
}
.mu-19__stage {
max-inline-size: 68rem;
margin-inline: auto;
padding: 2.5rem 1.25rem 3rem;
display: grid;
grid-template-columns: minmax(0, 1fr);
}
.mu-19__frame-wrap {
display: grid;
min-inline-size: 0;
}
.mu-19__frame {
inline-size: 100%;
max-inline-size: var(--mu-19-frame-w);
margin-inline: auto;
padding: 10px;
border-radius: var(--mu-19-radius);
background: var(--mu-19-shell);
min-inline-size: 0;
}
.mu-19__screen {
min-inline-size: 0;
aspect-ratio: 390 / 844;
display: grid;
grid-template-rows: auto auto auto minmax(0, 1fr) auto;
background: var(--mu-19-surface);
border-radius: calc(var(--mu-19-radius) - 10px);
overflow: hidden;
}
.mu-19__app-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.75rem 1.25rem 0.25rem;
}
.mu-19__app-label {
margin: 0;
font-size: 0.9375rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.mu-19__icon-btn {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
min-inline-size: 44px;
min-block-size: 44px;
padding: 0;
border: 0;
border-radius: 50%;
background: transparent;
color: var(--mu-19-ink);
cursor: pointer;
}
.mu-19__icon-btn svg {
inline-size: 22px;
block-size: 22px;
}
.mu-19__icon-btn:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: 2px;
}
.mu-19__balance {
padding: 0.5rem 1.25rem 1.25rem;
display: grid;
gap: 0.25rem;
}
.mu-19__balance p {
margin: 0;
min-inline-size: 0;
}
.mu-19__balance-label {
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--mu-19-ink-2);
}
.mu-19__balance-value {
font-size: clamp(1.875rem, 8cqi, 2.375rem);
line-height: 1.05;
letter-spacing: -0.03em;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.mu-19__balance-note {
font-size: 0.75rem;
color: var(--mu-19-ink-2);
}
.mu-19__tabs {
display: flex;
gap: 0.25rem;
padding-inline: 1.25rem;
border-block-end: 1px solid var(--mu-19-line);
}
.mu-19__tab {
min-block-size: 44px;
min-inline-size: 44px;
padding-inline: 0.5rem;
border: 0;
border-block-end: var(--mu-19-tab-underline) solid transparent;
background: transparent;
color: var(--mu-19-ink-2);
font: inherit;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
}
.mu-19__tab[aria-selected="true"] {
color: var(--mu-19-ink);
border-block-end-color: var(--mu-19-acc);
}
.mu-19__tab:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: -2px;
}
.mu-19__panels {
min-inline-size: 0;
min-block-size: 0;
overflow-y: auto;
}
.mu-19__panel {
min-inline-size: 0;
}
.mu-19__panel:focus-visible {
outline: 2px solid var(--mu-19-acc);
outline-offset: -2px;
}
.mu-19__rows {
margin: 0;
padding: 0.5rem 1.25rem;
list-style: none;
display: grid;
gap: 0;
}
.mu-19__rows li {
min-inline-size: 0;
min-block-size: 44px;
display: grid;
align-items: center;
gap: 0 0.75rem;
grid-template-columns: minmax(0, 1fr) auto;
padding-block: 0.625rem;
border-block-end: 1px solid var(--mu-19-line);
}
.mu-19__rows li:last-child {
border-block-end: 0;
}
.mu-19__row-name {
grid-column: 1;
min-inline-size: 0;
font-size: 0.875rem;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mu-19__row-meta {
grid-column: 1;
grid-row: 2;
font-size: 0.75rem;
color: var(--mu-19-ink-2);
}
.mu-19__row-amt {
grid-column: 2;
grid-row: 1 / span 2;
font-size: 0.875rem;
font-weight: 600;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.mu-19__row-amt--in {
color: var(--mu-19-in);
}
.mu-19__bar {
padding: 0.75rem 1.25rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
border-block-start: 1px solid var(--mu-19-line);
}
.mu-19__primary {
inline-size: 100%;
min-block-size: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
border-radius: 12px;
background: var(--mu-19-acc);
color: var(--mu-19-acc-ink);
font: inherit;
font-size: 0.9375rem;
font-weight: 700;
cursor: pointer;
}
.mu-19__primary:focus-visible {
outline: 2px solid var(--mu-19-ink);
outline-offset: 2px;
}
@supports (container-type: inline-size) {
.mu-19__screen {
container-type: inline-size;
}
}
@media (min-width: 56rem) {
.mu-19__stage {
grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
padding-inline: 3rem;
gap: 3rem;
}
.mu-19__frame-wrap {
grid-row: span 2;
}
}
```
## JavaScript
```js
(() => {
const screen = document.querySelector('#mu-19-screen');
if (!screen) return;
const tabs = [...screen.querySelectorAll('[role="tab"]')];
if (!tabs.length) return;
const select = (tab) => {
for (const t of tabs) {
const on = t === tab;
t.setAttribute('aria-selected', String(on));
t.tabIndex = on ? 0 : -1;
const panel = screen.querySelector('#' + t.getAttribute('aria-controls'));
if (panel) panel.hidden = !on;
}
tab.focus();
};
const onClick = (e) => select(e.currentTarget);
const onKey = (e) => {
const i = tabs.indexOf(e.currentTarget);
if (e.key === 'ArrowRight') select(tabs[(i + 1) % tabs.length]);
else if (e.key === 'ArrowLeft') select(tabs[(i - 1 + tabs.length) % tabs.length]);
else return;
e.preventDefault();
};
for (const t of tabs) {
t.addEventListener('click', onClick);
t.addEventListener('keydown', onKey);
}
screen.__muCleanup = () => {
for (const t of tabs) {
t.removeEventListener('click', onClick);
t.removeEventListener('keydown', onKey);
}
};
})();
```How this works
A phone is where restraint becomes arithmetic. At 390 points, with 20 points of margin each side, you have 350 points of usable width. A 44×44 target costs 44 of them. Four such targets plus gaps is essentially the whole row. There is no room for a decorative element that does not carry information, which is why mobile design is the most honest test of whether your hierarchy actually works.
What gets cut first, in order. Decorative imagery. Secondary metadata that repeats what the primary already implies. Labels on universally understood icons — and only those. Tertiary actions, moved behind a menu. What never gets cut: the touch target size, the label on any icon whose meaning is not universal, and the primary action, which stays visible and reachable in the bottom third where a thumb naturally rests.
The tab list is a real widget, not styled links. role="tablist" with aria-selected and arrow-key navigation gives one tab stop for the group and left/right movement inside it, which is what a screen-reader user expects from tabs. Twenty-four lines of script, one cleanup function, and no library. The visual treatment slims the tabs down to a hairline underline while the hit area stays 44px tall — which is the general answer to “44px looks heavy”: keep the target, change the paint.
Where mobile minimalism goes wrong. Hiding navigation behind a hamburger on a five-item app measurably reduces use of every item behind it. Removing labels from non-obvious icons makes a screen that only its designers can operate. And a bottom bar that ignores env(safe-area-inset-bottom) puts your primary action under the home indicator, where it looks perfect in a screenshot and cannot be tapped on the device.
Make it yours
- Change
--mu-19-frame-wto mock a different device; the layout is container-driven and reflows rather than scaling. - Set
--mu-19-tab-underlinethicker for a heavier selected state without touching the 44px hit area. - Swap the transaction list for a card list by changing one
grid-template-columns. - Add a fourth tab; the script derives its tabs from the DOM and needs no edits.
- Set
--mu-19-radiusto28pxfor an older device frame, or44pxfor a current one. - Recolour by editing the two
--mu-19-accdeclarations, hex first then oklch.
Gotchas — read before shipping
- A 32px tab because 44px looked heavy fails SC 2.5.8 and is measurably harder to hit while walking. Keep the target and slim the paint.
- Ignoring
env(safe-area-inset-bottom)puts the primary action under the home indicator on every modern iPhone. - Long merchant names in a transaction row will widen a grid track unless the text cell carries
min-inline-size: 0. - Hiding a five-item navigation behind a hamburger reduces the use of everything behind it. A tab bar with four visible items is almost always better.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16+ | 121+ | 111+ |
container-type: inline-size sets the Safari floor at 16 (Chrome 105, Firefox 110) and is used only so the frame scales; a @media fallback is declared first, so smaller engines get a fixed-width frame with identical content. oklch() needs Chrome 111 / Safari 15.4 / Firefox 113 and the :has() theme toggle needs Firefox 121. env(safe-area-inset-*) is Safari 11 and Chrome 69. With script blocked the first tab panel stays visible and the screen still reads.