oefenhub_generiek_03_open_bericht_v3_5.html
Korte beschrijving
Deze HTML-mockup toont een geopend privébericht met de inhoud van het geselecteerde bericht en, wanneer aanwezig, de bijbehorende threadhistorie. Naast gewone berichten kunnen ook systeemmatige threadgebeurtenissen zichtbaar zijn, zoals een onderwerpwijziging.
Gerelateerde documentatie
- Bekijk de schermdocumentatie voor meer functionele context.
Bestand
Broncode
/mockups_html/generiek/oefenhub_generiek_03_open_bericht_v3_5.html
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>OefenHub - Ontvangen bericht</title>
<link href="./oefenhub_shared_logo.css" rel="stylesheet">
<style>
:root {
--blue-1: #8fd0ff;
--blue-2: #5bb3ff;
--blue-3: #1f82e9;
--blue-4: #0d5db8;
--text: #114f95;
--muted: #5f7ea5;
--line: rgba(17,79,149,.12);
--orange-1: #ffbb4d;
--orange-2: #ff9620;
--green-1: #8adf67;
--green-2: #57ba3c;
--yellow-1: #ffd04e;
--red-text: #b74d4d;
--shadow: 0 12px 28px rgba(35, 100, 177, 0.12);
--shadow-soft: 0 8px 18px rgba(35, 100, 177, 0.08);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
color: var(--text);
background:
radial-gradient(circle at 8% 14%, rgba(255,255,255,.68) 0 12%, transparent 13%),
radial-gradient(circle at 18% 10%, rgba(255,255,255,.45) 0 9%, transparent 10%),
radial-gradient(circle at 86% 17%, rgba(255,255,255,.58) 0 11%, transparent 12%),
linear-gradient(180deg, #7ec2f8 0%, #bfe2fd 15%, #edf7ff 34%, #f7fbff 100%);
min-height: 100vh;
overflow-x: hidden;
}
.wrap { width: min(1220px, calc(100% - 30px)); margin: 0 auto; }
.header {
padding: 10px 0 12px;
background: linear-gradient(180deg, rgba(100,171,238,.92), rgba(120,192,248,.85));
border-bottom: 1px solid rgba(255,255,255,.38);
box-shadow: 0 8px 20px rgba(28, 100, 177, .08);
position: relative;
z-index: 10;
}
.header-row {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 10px;
}
.brand {
display: flex;
flex-direction: column;
align-items: flex-start;
min-width: 250px;
}
.welcome-text {
font-size: 12px;
color: rgba(17,79,149,.82);
font-weight: 700;
margin-top: -10px;
margin-left: 53px;
}
.nav-pills {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
flex-wrap: nowrap;
white-space: nowrap;
}
.nav-item { position: relative; display: inline-flex; align-items: center; }
.pill {
border: 0;
border-radius: 12px;
padding: 9px 14px;
color: #fff;
font-size: 13px;
font-weight: 900;
box-shadow: var(--shadow-soft);
line-height: 1;
cursor: pointer;
flex: 0 0 auto;
}
.pill.orange { background: linear-gradient(180deg, #ffbb4d, #ff7f12); }
.pill.blue { background: linear-gradient(180deg, #2f88ea, #176acc); }
.pill.yellow { background: linear-gradient(180deg, #ffd04d, #f4ab08); }
.pill.green { background: linear-gradient(180deg, #8adf67, #58ba3d); }
.pill.has-chevron {
display: inline-flex;
align-items: center;
gap: 6px;
padding-right: 14px;
}
.pill-caret {
font-size: 12px;
line-height: 1;
transform: translateY(1px);
}
.dropdown-menu {
position: absolute;
top: calc(100% + 8px);
left: 0;
width: 306px;
background: linear-gradient(180deg, rgba(255,255,255,.97), rgba(244,250,255,.98));
border-radius: 20px;
box-shadow: 0 12px 24px rgba(35,100,177,.16);
border: 1px solid rgba(170, 204, 238, .55);
padding: 10px 12px;
z-index: 40;
opacity: 0;
visibility: hidden;
transform: translateY(-4px);
transition: opacity .18s ease, transform .18s ease, visibility .18s ease;
}
.dropdown-menu::before {
content: "";
position: absolute;
top: -10px;
left: 36px;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid rgba(255,255,255,.97);
filter: drop-shadow(0 -1px 0 rgba(170, 204, 238, .45));
}
.nav-item:hover .dropdown-menu,
.nav-item:focus-within .dropdown-menu,
.nav-item.open .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.menu-list { display: flex; flex-direction: column; gap: 0; }
.menu-link {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 9px;
padding: 8px 11px;
border-radius: 13px;
text-decoration: none;
color: #18518f;
font-size: 15px;
font-weight: 900;
box-shadow: inset 0 -1px 0 rgba(17,79,149,.10);
transition: background .18s ease, transform .18s ease;
}
.menu-link:last-child { box-shadow: none; }
.menu-link:hover,
.menu-link.is-active { background: linear-gradient(180deg, #edf5ff, #dcecff); }
.menu-link:hover { transform: translateX(2px); }
.menu-icon {
width: 23px;
height: 23px;
border-radius: 7px;
display: grid;
place-items: center;
color: #fff;
font-size: 14px;
font-weight: 900;
box-shadow: inset 0 1px 1px rgba(255,255,255,.35), 0 4px 8px rgba(0,0,0,.10);
}
.menu-icon.green { background: linear-gradient(180deg, #8add68, #4baa35); }
.menu-icon.blue { background: linear-gradient(180deg, #49a4ff, #1f7fe2); }
.menu-icon.yellow { background: linear-gradient(180deg, #ffd45a, #f0b000); color: #8a5d00; }
.menu-icon.purple { background: linear-gradient(180deg, #8d6cff, #6147d8); }
.menu-arrow {
color: #18518f;
font-size: 22px;
line-height: 1;
font-weight: 700;
}
.header-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
min-width: 120px;
}
.mail-btn,
.profile-chip {
border: 0;
box-shadow: var(--shadow-soft);
cursor: pointer;
}
.mail-btn {
position: relative;
width: 40px;
height: 40px;
border-radius: 999px;
background: linear-gradient(180deg, #ffffff, #eef6ff);
color: var(--blue-4);
font-size: 17px;
}
.badge {
position: absolute;
right: -2px;
bottom: -2px;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 999px;
background: #e23838;
color: #fff;
font-size: 11px;
font-weight: 800;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid #fff;
}
.profile-chip {
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 999px;
background: linear-gradient(180deg, #ffffff, #edf6ff);
padding: 0;
}
.avatar {
width: 28px;
height: 28px;
border-radius: 999px;
display: grid;
place-items: center;
color: #fff;
font-size: 13px;
font-weight: 900;
background: linear-gradient(180deg, #ffb247, #ff8120);
}
main.wrap { padding: 22px 0 42px; }
.breadcrumb-bar {
display: flex;
align-items: center;
gap: 8px;
margin: 0 0 12px;
font-size: 13px;
font-weight: 700;
color: rgba(17,79,149,.78);
}
.breadcrumb-bar a {
color: rgba(17,79,149,.82);
text-decoration: none;
}
.breadcrumb-bar a:hover { text-decoration: underline; }
.breadcrumb-separator { opacity: .55; }
.message-shell {
position: relative;
background: linear-gradient(180deg, rgba(255,255,255,.96), rgba(243,249,255,.96));
border: 1px solid rgba(170,204,238,.62);
border-radius: 30px;
box-shadow: var(--shadow);
overflow: hidden;
}
.message-content { padding: 22px 22px 26px; }
.page-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 16px;
margin-bottom: 18px;
}
.page-title-wrap h1 {
margin: 0 0 10px;
font-size: 24px;
line-height: 1.08;
color: #0d5db8;
}
.page-title-wrap p {
margin: 0;
max-width: 760px;
font-size: 15px;
line-height: 1.55;
color: var(--text);
}
.open-message-card {
border: 1px solid rgba(170,204,238,.62);
border-radius: 22px;
background: linear-gradient(180deg, #ffffff, #f8fbff);
box-shadow: var(--shadow-soft);
overflow: hidden;
}
.open-message-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px 14px;
padding: 10px 18px;
border-bottom: 1px solid rgba(17,79,149,.08);
background: linear-gradient(180deg, rgba(91,179,255,.12), rgba(255,255,255,.48));
font-size: 13px;
color: var(--muted);
}
.open-message-meta strong {
color: var(--text);
font-weight: 700;
}
.type-tag {
display: inline-flex;
align-items: center;
min-height: 24px;
padding: 0 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 900;
}
.type-tag.private { background: #e3f7ea; color: #23804a; }
.open-message-body {
padding: 14px 18px 18px;
display: grid;
gap: 14px;
}
.open-message-subject {
margin: 0;
font-size: 17px;
line-height: 1.25;
color: var(--blue-4);
}
.open-message-box {
border-radius: 20px;
padding: 12px 18px;
border: 1px solid rgba(170,204,238,.50);
background: #fff;
font-size: 14px;
line-height: 1.72;
color: var(--text);
}
.open-message-box p { margin: 0 0 12px; }
.open-message-box p:last-child { margin-bottom: 0; }
.open-message-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.open-message-actions-right {
display: flex;
align-items: center;
gap: 10px;
margin-left: auto;
flex-wrap: wrap;
}
.ghost-action,
.back-btn,
.primary-action {
appearance: none;
min-height: 44px;
padding: 0 16px;
border-radius: 14px;
font-size: 14px;
font-weight: 800;
cursor: pointer;
font-family: inherit;
}
.ghost-action,
.back-btn {
border: 1px solid rgba(17,79,149,.14);
background: #fff;
color: var(--blue-4);
box-shadow: 0 8px 18px rgba(17,79,149,.08);
}
.primary-action {
border: 0;
color: #fff;
background: linear-gradient(180deg, #3fb670 0%, #2c9b59 100%);
box-shadow: 0 12px 24px rgba(47, 155, 89, .24);
}
.delete-action {
border: 2px solid #b13e46;
color: #b95760;
background: #fff6f6;
box-shadow: inset 0 0 0 1px rgba(177,62,70,.08);
}
.delete-action:hover {
background: #fdeeee;
border-color: #7d2329;
color: #ad3f48;
}
.thread-shell {
display: grid;
gap: 12px;
margin-top: 18px;
}
.thread-summary {
width: fit-content;
min-width: 0;
margin: 0 auto;
padding: 12px 18px;
border-radius: 18px;
border: 1px solid rgba(170,204,238,.55);
background: linear-gradient(180deg, #ffffff, #f8fbff);
box-shadow: var(--shadow-soft);
color: var(--blue-4);
font-weight: 800;
text-align: center;
}
.thread-summary .count {
font-size: 12px;
color: var(--muted);
font-weight: 700;
margin-left: 6px;
}
.thread-panel {
border: 1px solid rgba(170,204,238,.62);
border-radius: 22px;
background: linear-gradient(180deg, #ffffff, #f8fbff);
box-shadow: var(--shadow-soft);
padding: 18px;
}
.thread-list {
display: grid;
gap: 12px;
}
.thread-bubble {
width: 80%;
border-radius: 18px;
border: 1px solid rgba(170,204,238,.48);
box-shadow: 0 8px 18px rgba(17,79,149,.08);
overflow: hidden;
}
.thread-bubble.other {
justify-self: start;
background: linear-gradient(180deg, #ffffff, #f6fbff);
}
.thread-bubble.self {
justify-self: end;
background: linear-gradient(180deg, #f7fcf3, #eef8e8);
border-color: rgba(44,147,72,.18);
}
.thread-head {
padding: 6px 16px;
font-size: 12px;
font-weight: 700;
color: var(--muted);
border-bottom: 1px solid rgba(17,79,149,.08);
background: rgba(255,255,255,.58);
}
.thread-bubble.self .thread-head {
background: rgba(255,255,255,.42);
border-bottom-color: rgba(44,147,72,.12);
}
.thread-body {
padding: 10px 16px 12px;
font-size: 13px;
line-height: 1.65;
color: var(--text);
}
.thread-system {
justify-self: center;
width: min(76%, 560px);
border-radius: 16px;
padding: 10px 14px;
background: linear-gradient(180deg, rgba(255,247,231,.98), rgba(255,239,205,.98));
border: 1px solid rgba(233,163,64,.34);
box-shadow: 0 8px 18px rgba(233,163,64,.12);
text-align: center;
color: #7a5a1b;
font-size: 13px;
line-height: 1.5;
}
.thread-system strong { color: #6a4d17; }
.site-footer {
margin-top: 26px;
background: linear-gradient(180deg, rgba(255,255,255,.86), rgba(255,255,255,.96));
border-top: 1px solid rgba(17,79,149,.10);
box-shadow: var(--shadow);
border-radius: 26px 26px 0 0;
padding: 22px 0 18px;
overflow: visible;
}
.footer-grid {
display: grid;
grid-template-columns: 1.1fr 1fr 1fr;
gap: 36px;
align-items: start;
padding: 0;
}
.footer-col h4 {
margin: 0 0 12px;
font-size: 15px;
color: #0d5db8;
font-weight: 900;
padding-bottom: 8px;
border-bottom: 1px solid rgba(17,79,149,.14);
}
.footer-col p {
margin: 0;
font-size: 13px;
line-height: 1.55;
color: var(--muted);
font-weight: 600;
}
.footer-links {
display: flex;
flex-direction: column;
gap: 10px;
}
.footer-links a {
color: #18518f;
text-decoration: none;
font-size: 13px;
font-weight: 700;
transition: color .18s ease;
}
.footer-links a:hover {
color: var(--blue-4);
text-decoration: underline;
}
.footer-bottom {
margin-top: 18px;
padding-top: 14px;
border-top: 1px solid rgba(17,79,149,.10);
display: flex;
justify-content: space-between;
gap: 12px;
align-items: center;
flex-wrap: wrap;
font-size: 12px;
color: var(--muted);
font-weight: 600;
}
@media (max-width: 900px) {
.dropdown-menu { width: 266px; }
.header-row { grid-template-columns: 1fr; gap: 14px; }
.brand { justify-self: center; align-items: center; min-width: 0; }
.welcome-text { margin-left: 0; margin-top: -6px; }
.header-actions { justify-content: center; }
.nav-pills { overflow-x: auto; justify-content: flex-start; padding-bottom: 2px; width: 100%; }
.footer-grid { grid-template-columns: 1fr; gap: 24px; }
}
@media (max-width: 760px) {
.message-content { padding: 18px; }
.page-head {
flex-direction: column;
align-items: flex-start;
}
.open-message-actions {
flex-direction: column;
align-items: stretch;
}
.open-message-actions-right {
margin-left: 0;
width: 100%;
}
.open-message-actions-right > button,
.open-message-actions > button {
width: 100%;
}
.thread-bubble,
.thread-system {
width: 100%;
}
.footer-bottom { justify-content: flex-start; }
}
@media (max-width: 640px) {
.wrap { width: min(100%, calc(100% - 18px)); }
main.wrap { padding-top: 18px; }
.message-shell { border-radius: 24px; }
}
</style>
</head>
<body>
<header class="header">
<div class="wrap">
<div class="header-row">
<div class="brand">
<div class="brand-logo" aria-label="OefenHub logo" role="img"></div>
<div class="welcome-text">Welkom terug, Emma</div>
</div>
<div class="nav-pills">
<div class="nav-item">
<button aria-controls="rekenen-menu" aria-expanded="false" class="pill orange has-chevron" type="button">🔢 Rekenen <span class="pill-caret">▾</span></button>
<div class="dropdown-menu" id="rekenen-menu">
<div class="menu-list">
<a class="menu-link is-active" href="#"><span class="menu-icon green">+</span><span>Optellen</span><span class="menu-arrow">›</span></a>
<a class="menu-link" href="#"><span class="menu-icon blue">−</span><span>Aftrekken</span></a>
<a class="menu-link" href="#"><span class="menu-icon yellow">×</span><span>Vermenigvuldigen</span></a>
<a class="menu-link" href="#"><span class="menu-icon purple">÷</span><span>Delen</span></a>
</div>
</div>
</div>
<button class="pill blue">📖 Taal</button>
<button class="pill blue">🇬🇧 Engels</button>
<button class="pill yellow">✏️ Spelling</button>
<button class="pill green">🌍 Topografie</button>
</div>
<div class="header-actions">
<button aria-label="Berichten" class="mail-btn">✉️<span class="badge">3</span></button>
<button aria-label="Profiel" class="profile-chip"><span class="avatar">E</span></button>
</div>
</div>
</div>
</header>
<main class="wrap">
<nav class="breadcrumb-bar" aria-label="Breadcrumb">
<a href="#">Home</a>
<span class="breadcrumb-separator">›</span>
<a href="#">Berichten</a>
<span class="breadcrumb-separator">›</span>
<span>Ontvangen bericht</span>
</nav>
<section class="message-shell">
<div class="message-content">
<div class="page-head">
<div class="page-title-wrap">
<h1>Ontvangen bericht</h1>
<p>Bekijk een ontvangen privébericht. Onder het huidige bericht staan de eerdere berichten uit dezelfde conversatie.</p>
</div>
</div>
<section class="open-message-card">
<div class="open-message-meta">
<span class="type-tag private">Privé</span>
<span><strong>Van:</strong> Sanne de Vries</span>
<span><strong>Aan:</strong> Emma Jansen</span>
<span><strong>Ontvangen:</strong> Vandaag · 12:08</span>
</div>
<div class="open-message-body">
<h2 class="open-message-subject">Goed bezig met optellen</h2>
<div class="open-message-box">
<p>Hoi Emma,</p>
<p>Ik zag dat je vandaag al meerdere oefeningen voor optellen hebt gemaakt. Dat gaat echt de goede kant op. Blijf vooral rustig werken en neem de tijd om de som eerst even goed te lezen voordat je antwoord geeft.</p>
<p>Morgen kijken we samen nog even naar de sommen waarbij je moest twijfelen. Als je wilt, kun je me alvast laten weten welke soort optelsommen je nog lastig vindt.</p>
<p>Groetjes,<br/>Sanne</p>
</div>
<div class="open-message-actions">
<button class="ghost-action delete-action" type="button">Verwijder bericht</button>
<div class="open-message-actions-right">
<button class="back-btn" type="button">Terug naar berichten</button>
<button class="primary-action" type="button">Beantwoorden</button>
</div>
</div>
</div>
</section>
<div class="thread-shell">
<div class="thread-summary">Eerdere berichten <span class="count">(3)</span></div>
<section class="thread-panel">
<div class="thread-list">
<article class="thread-system"><strong>Sanne de Vries</strong> veranderde het onderwerp van <strong>‘Lekker bezig’</strong> naar <strong>‘Goed bezig met optellen’</strong> <span style="white-space:nowrap;">(Vandaag · 12:08)</span></article>
<article class="thread-bubble other">
<div class="thread-head">Vandaag · 12:08 (Sanne de Vries)</div>
<div class="thread-body">Hoi Emma,<br/><br/>Ik zag dat je vandaag al meerdere oefeningen voor optellen hebt gemaakt. Dat gaat echt de goede kant op.</div>
</article>
<article class="thread-bubble self">
<div class="thread-head">Vandaag · 11:42 (Jij)</div>
<div class="thread-body">Dank je wel! Ik vind vooral de sommen met tientallen soms nog lastig.</div>
</article>
<article class="thread-bubble other">
<div class="thread-head">Vandaag · 10:31 (Sanne de Vries)</div>
<div class="thread-body">Netjes gewerkt vanochtend. Laat het gerust weten als je wilt dat ik straks nog even meekijk.</div>
</article>
</div>
</section>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="wrap">
<div class="footer-grid">
<div class="footer-col">
<h4>OefenHub</h4>
<p>OefenHub helpt kinderen spelenderwijs schoolvakken oefenen, herhalen en beter begrijpen. Alles overzichtelijk op één plek.</p>
</div>
<div class="footer-col">
<h4>Handige Links</h4>
<div class="footer-links">
<a href="#">Over OefenHub</a>
<a href="#">Contact</a>
<a href="#">Privacybeleid</a>
</div>
</div>
<div class="footer-col">
<h4>Snel naar</h4>
<div class="footer-links">
<a href="#">Profiel</a>
<a href="#">Berichten</a>
<a href="#">Meld een probleem</a>
</div>
</div>
</div>
<div class="footer-bottom">
<span>© 2026 OefenHub</span>
</div>
</div>
</footer>
<script>
(function () {
const navItem = document.querySelector('.nav-item');
const trigger = navItem?.querySelector('.pill');
if (!navItem || !trigger) return;
trigger.addEventListener('click', function (event) {
event.preventDefault();
const isOpen = navItem.classList.toggle('open');
trigger.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
});
document.addEventListener('click', function (event) {
if (!navItem.contains(event.target)) {
navItem.classList.remove('open');
trigger.setAttribute('aria-expanded', 'false');
}
});
})();
</script>
</body>
</html>