Science — Publications List
A research-group publications index styled like an academic bibliography page, presenting a chronological list of fictional articles, conference papers, and preprints grouped by year. A sticky masthead and bibliometric summary sit above live filter controls for year, topic, author, and type, flag checkboxes for peer-reviewed, preprint, and award entries, plus a search box that highlights matches across titles, venues, and authors. Each entry bolds group members, carries colored badges, PDF, DOI, code, and BibTeX-copy links, citation counts, and an active-filter tag bar with a sortable order control.
MCP
Code
:root {
--bg: #ffffff;
--bg-alt: #f6f8fb;
--ink: #0f1b2d;
--ink-2: #33445c;
--muted: #697892;
--accent: #1a4f8a;
--accent-d: #123a66;
--accent-50: #e9f0f9;
--teal: #0f7d78;
--teal-50: #e4f3f1;
--line: rgba(15, 27, 45, 0.12);
--line-2: rgba(15, 27, 45, 0.2);
--ok: #2f9e6f;
--warn: #c9821f;
--danger: #cf4538;
--r-sm: 6px;
--r-md: 10px;
--r-lg: 16px;
--sh-1: 0 1px 2px rgba(15, 27, 45, 0.04), 0 1px 3px rgba(15, 27, 45, 0.06);
--sh-2: 0 4px 16px rgba(15, 27, 45, 0.08), 0 2px 6px rgba(15, 27, 45, 0.05);
--serif: "Source Serif 4", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
--mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: var(--serif);
line-height: 1.6;
font-size: 16px;
}
a {
color: var(--accent);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: 3px;
}
.mono {
font-family: var(--mono);
font-variant-numeric: tabular-nums;
}
.skip-link {
position: absolute;
left: -999px;
top: 0;
background: var(--accent);
color: #fff;
padding: 8px 14px;
border-radius: var(--r-sm);
font-family: var(--sans);
z-index: 50;
}
.skip-link:focus {
left: 12px;
top: 12px;
}
/* ── Masthead ─────────────────────────────── */
.masthead {
border-bottom: 1px solid var(--line);
background: var(--bg);
position: sticky;
top: 0;
z-index: 20;
}
.masthead-inner {
max-width: 1000px;
margin: 0 auto;
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.brand {
display: flex;
align-items: center;
gap: 14px;
}
.brand-mark {
font-family: var(--mono);
font-weight: 500;
font-size: 14px;
letter-spacing: 0.04em;
color: #fff;
background: linear-gradient(135deg, var(--accent), var(--accent-d));
width: 44px;
height: 44px;
border-radius: var(--r-md);
display: grid;
place-items: center;
flex: none;
}
.brand-lab {
margin: 0;
font-family: var(--sans);
font-weight: 700;
font-size: 15px;
color: var(--ink);
letter-spacing: -0.01em;
}
.brand-dept {
margin: 2px 0 0;
font-family: var(--sans);
font-size: 12.5px;
color: var(--muted);
}
.masthead-nav .nav-current {
font-family: var(--sans);
font-weight: 600;
font-size: 14px;
color: var(--accent);
padding-bottom: 4px;
border-bottom: 2px solid var(--accent);
}
/* ── Page layout ──────────────────────────── */
.page {
max-width: 1000px;
margin: 0 auto;
padding: 36px 24px 64px;
}
.intro h1 {
font-family: var(--serif);
font-weight: 700;
font-size: 38px;
line-height: 1.1;
margin: 0 0 12px;
letter-spacing: -0.02em;
}
.lede {
margin: 0;
max-width: 64ch;
color: var(--ink-2);
font-size: 17px;
}
.metrics {
margin: 28px 0 0;
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;
}
.metric {
background: var(--bg-alt);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 14px 16px;
}
.metric dt {
font-family: var(--sans);
font-size: 11.5px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
font-weight: 600;
}
.metric dd {
margin: 4px 0 0;
font-family: var(--mono);
font-weight: 500;
font-size: 24px;
color: var(--accent-d);
font-variant-numeric: tabular-nums;
}
/* ── Controls ─────────────────────────────── */
.controls {
margin: 32px 0 24px;
padding: 20px;
border: 1px solid var(--line);
border-radius: var(--r-lg);
background: var(--bg);
box-shadow: var(--sh-1);
}
.search-row {
display: flex;
gap: 12px;
align-items: center;
}
.search-field {
position: relative;
flex: 1;
display: flex;
align-items: center;
}
.search-icon {
position: absolute;
left: 14px;
width: 18px;
height: 18px;
color: var(--muted);
pointer-events: none;
}
#search {
width: 100%;
font-family: var(--sans);
font-size: 15px;
color: var(--ink);
padding: 12px 40px 12px 42px;
border: 1px solid var(--line-2);
border-radius: var(--r-md);
background: var(--bg);
}
#search::placeholder {
color: var(--muted);
}
#search:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-50);
outline: none;
}
.search-clear {
position: absolute;
right: 8px;
width: 26px;
height: 26px;
border: none;
background: var(--bg-alt);
color: var(--ink-2);
border-radius: 50%;
font-size: 18px;
line-height: 1;
cursor: pointer;
}
.search-clear:hover {
background: var(--line);
}
.btn {
font-family: var(--sans);
font-weight: 600;
font-size: 14px;
padding: 11px 16px;
border-radius: var(--r-md);
border: 1px solid var(--line-2);
background: var(--bg);
color: var(--ink-2);
cursor: pointer;
white-space: nowrap;
transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.btn:hover {
background: var(--bg-alt);
border-color: var(--accent);
color: var(--accent);
}
.btn.ghost {
flex: none;
}
.filter-grid {
margin-top: 16px;
display: grid;
grid-template-columns: repeat(4, 1fr) auto;
gap: 12px;
align-items: end;
}
.select-field,
.sort-field {
display: flex;
flex-direction: column;
gap: 5px;
}
.select-label {
font-family: var(--sans);
font-size: 11.5px;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
color: var(--muted);
}
select {
font-family: var(--sans);
font-size: 14px;
color: var(--ink);
padding: 9px 12px;
border: 1px solid var(--line-2);
border-radius: var(--r-md);
background: var(--bg);
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23697892' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 30px;
}
select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-50);
outline: none;
}
.badge-filters {
grid-column: 1 / -1;
margin: 0;
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: var(--r-md);
background: var(--bg-alt);
display: flex;
flex-wrap: wrap;
gap: 6px 18px;
align-items: center;
}
.badge-filters legend {
font-family: var(--sans);
font-size: 11.5px;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
color: var(--muted);
padding: 0 6px;
}
.chk {
font-family: var(--sans);
font-size: 13.5px;
color: var(--ink-2);
display: inline-flex;
align-items: center;
gap: 7px;
cursor: pointer;
}
.chk input {
accent-color: var(--accent);
width: 15px;
height: 15px;
}
.result-bar {
margin-top: 18px;
padding-top: 16px;
border-top: 1px solid var(--line);
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.result-count {
margin: 0;
font-family: var(--sans);
font-size: 14px;
color: var(--ink-2);
font-weight: 500;
}
.result-count strong {
color: var(--ink);
font-weight: 700;
}
.sort-field {
flex-direction: row;
align-items: center;
gap: 8px;
}
.active-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 14px;
}
.active-tags:empty {
margin-top: 0;
}
.tag {
font-family: var(--sans);
font-size: 12.5px;
font-weight: 500;
color: var(--accent-d);
background: var(--accent-50);
border: 1px solid rgba(26, 79, 138, 0.22);
border-radius: 999px;
padding: 4px 8px 4px 11px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.tag button {
border: none;
background: rgba(26, 79, 138, 0.14);
color: var(--accent-d);
width: 17px;
height: 17px;
border-radius: 50%;
font-size: 13px;
line-height: 1;
cursor: pointer;
}
.tag button:hover {
background: rgba(26, 79, 138, 0.28);
}
/* ── Results ──────────────────────────────── */
.year-groups {
list-style: none;
margin: 0;
padding: 0;
}
.year-group {
margin-top: 36px;
}
.year-group:first-child {
margin-top: 8px;
}
.year-head {
display: flex;
align-items: baseline;
gap: 12px;
margin: 0 0 14px;
padding-bottom: 8px;
border-bottom: 2px solid var(--ink);
}
.year-head h2 {
font-family: var(--mono);
font-weight: 500;
font-size: 26px;
margin: 0;
color: var(--ink);
letter-spacing: -0.01em;
}
.year-head .year-tally {
font-family: var(--sans);
font-size: 12.5px;
color: var(--muted);
}
.pubs {
list-style: none;
margin: 0;
padding: 0;
}
.pub {
display: grid;
grid-template-columns: 56px 1fr;
gap: 18px;
padding: 18px 0;
border-bottom: 1px solid var(--line);
}
.pub:last-child {
border-bottom: none;
}
.pub-num {
font-family: var(--mono);
font-size: 13px;
color: var(--muted);
padding-top: 2px;
text-align: right;
}
.pub-num .pub-type {
display: block;
margin-top: 6px;
font-family: var(--sans);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--muted);
text-align: right;
}
.pub-body {
min-width: 0;
}
.pub-title {
font-family: var(--serif);
font-weight: 600;
font-size: 18px;
line-height: 1.35;
margin: 0 0 6px;
color: var(--ink);
}
.pub-title a {
color: var(--ink);
}
.pub-title a:hover {
color: var(--accent);
text-decoration: none;
}
.pub-authors {
font-family: var(--sans);
font-size: 14px;
color: var(--ink-2);
margin: 0 0 6px;
}
.pub-authors .me {
font-weight: 700;
color: var(--ink);
}
.pub-venue {
font-family: var(--sans);
font-size: 13.5px;
color: var(--muted);
margin: 0 0 10px;
}
.pub-venue em {
font-style: italic;
color: var(--ink-2);
}
.pub-venue .mono {
color: var(--ink-2);
}
.badges {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 10px;
}
.badge {
font-family: var(--sans);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
padding: 3px 8px;
border-radius: var(--r-sm);
border: 1px solid transparent;
}
.badge.peer {
color: var(--ok);
background: rgba(47, 158, 111, 0.1);
border-color: rgba(47, 158, 111, 0.28);
}
.badge.preprint {
color: var(--warn);
background: rgba(201, 130, 31, 0.1);
border-color: rgba(201, 130, 31, 0.28);
}
.badge.award {
color: var(--accent-d);
background: var(--accent-50);
border-color: rgba(26, 79, 138, 0.28);
}
.badge.oa {
color: var(--teal);
background: var(--teal-50);
border-color: rgba(15, 125, 120, 0.28);
}
.badge.topic {
color: var(--ink-2);
background: var(--bg-alt);
border-color: var(--line);
text-transform: none;
letter-spacing: 0;
font-weight: 500;
}
.pub-links {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.pub-link {
font-family: var(--sans);
font-size: 12.5px;
font-weight: 600;
color: var(--accent);
background: var(--bg);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 5px 10px;
display: inline-flex;
align-items: center;
gap: 5px;
cursor: pointer;
transition: background 0.15s, border-color 0.15s;
}
.pub-link:hover {
background: var(--accent-50);
border-color: var(--accent);
text-decoration: none;
}
.pub-link svg {
width: 13px;
height: 13px;
}
.pub-link.cite {
color: var(--teal);
}
.pub-link.cite:hover {
background: var(--teal-50);
border-color: var(--teal);
}
.pub-cited {
font-family: var(--sans);
font-size: 12px;
color: var(--muted);
margin-left: auto;
align-self: center;
}
.pub-cited strong {
font-family: var(--mono);
color: var(--ink-2);
}
.empty-state {
text-align: center;
padding: 48px 20px;
color: var(--muted);
font-size: 16px;
}
.page-foot {
margin-top: 56px;
padding-top: 20px;
border-top: 1px solid var(--line);
font-family: var(--sans);
font-size: 12.5px;
color: var(--muted);
}
.page-foot p {
margin: 0;
}
/* ── Toast ────────────────────────────────── */
.toast {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%) translateY(20px);
background: var(--ink);
color: #fff;
font-family: var(--sans);
font-size: 13.5px;
font-weight: 500;
padding: 11px 18px;
border-radius: var(--r-md);
box-shadow: var(--sh-2);
opacity: 0;
pointer-events: none;
transition: opacity 0.22s, transform 0.22s;
z-index: 60;
max-width: 88vw;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* ── Highlight ────────────────────────────── */
mark {
background: #fff3bf;
color: inherit;
border-radius: 2px;
padding: 0 1px;
}
/* ── Responsive ───────────────────────────── */
@media (max-width: 860px) {
.metrics {
grid-template-columns: repeat(3, 1fr);
}
.filter-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 640px) {
body {
font-size: 15px;
}
.masthead-inner {
padding: 12px 16px;
}
.brand-dept {
display: none;
}
.page {
padding: 24px 16px 48px;
}
.intro h1 {
font-size: 30px;
}
.lede {
font-size: 15.5px;
}
.metrics {
grid-template-columns: repeat(2, 1fr);
}
.controls {
padding: 16px;
}
.search-row {
flex-direction: column;
align-items: stretch;
}
.btn.ghost {
width: 100%;
}
.filter-grid {
grid-template-columns: 1fr 1fr;
}
.result-bar {
flex-direction: column;
align-items: stretch;
}
.pub {
grid-template-columns: 1fr;
gap: 8px;
}
.pub-num {
text-align: left;
display: flex;
gap: 10px;
align-items: baseline;
}
.pub-num .pub-type {
margin-top: 0;
text-align: left;
}
.pub-cited {
margin-left: 0;
}
.year-head h2 {
font-size: 22px;
}
}
@media (prefers-reduced-motion: reduce) {
* {
transition: none !important;
}
}/* Computational Soft Matter Group — publications index
Vanilla JS. Live filtering, search, sort, BibTeX/citation copy. */
(function () {
"use strict";
// Group members — names matched against author lists to bold them.
var MEMBERS = [
"M. Okonkwo-Reyes",
"L. Vasquez",
"T. Aaltonen",
"R. Devi",
"S. Brennan",
"K. Yamazaki",
];
// Seeded, fictional bibliography.
var PUBS = [
{
id: "okonkwo2026shear",
type: "journal",
year: 2026,
authors: ["M. Okonkwo-Reyes", "P. Lindqvist", "R. Devi", "S. Brennan"],
title: "Shear-induced crystallisation pathways in dense colloidal suspensions",
venue: "Physical Review X",
volume: "16",
pages: "021034",
doi: "10.1109/prx.2026.021034",
topic: "Colloids",
peer: true,
oa: true,
award: "Editors' Suggestion",
cited: 4,
hasCode: true,
hasPdf: true,
},
{
id: "vasquez2026active",
type: "preprint",
year: 2026,
authors: ["L. Vasquez", "M. Okonkwo-Reyes"],
title: "Active stress regulation in confined bacterial suspensions: a continuum model",
venue: "arXiv:2603.14988 [cond-mat.soft]",
doi: "10.48550/arXiv.2603.14988",
topic: "Active matter",
peer: false,
oa: true,
cited: 1,
hasCode: true,
hasPdf: true,
},
{
id: "aaltonen2025glass",
type: "journal",
year: 2025,
authors: ["T. Aaltonen", "K. Yamazaki", "M. Okonkwo-Reyes"],
title: "Dynamic heterogeneity near the glass transition of binary polymer melts",
venue: "Soft Matter",
volume: "21",
pages: "3877–3891",
doi: "10.1039/sm2025.03877",
topic: "Glass physics",
peer: true,
oa: false,
cited: 17,
hasCode: true,
hasPdf: true,
},
{
id: "devi2025membrane",
type: "journal",
year: 2025,
authors: ["R. Devi", "S. Brennan", "M. Okonkwo-Reyes"],
title: "Curvature-coupled phase separation in lipid bilayer membranes",
venue: "Biophysical Journal",
volume: "128",
pages: "1142–1156",
doi: "10.1016/j.bpj.2025.01142",
topic: "Membranes",
peer: true,
oa: true,
cited: 9,
hasCode: false,
hasPdf: true,
},
{
id: "yamazaki2025md",
type: "conference",
year: 2025,
authors: ["K. Yamazaki", "T. Aaltonen"],
title: "Scalable molecular dynamics for polydisperse soft spheres on heterogeneous GPUs",
venue: "Proc. Int. Conf. on Computational Physics (ICCP 25)",
pages: "88–97",
doi: "10.1145/iccp25.0088",
topic: "Methods & software",
peer: true,
oa: false,
award: "Best Paper",
cited: 6,
hasCode: true,
hasPdf: true,
},
{
id: "brennan2024gel",
type: "journal",
year: 2024,
authors: ["S. Brennan", "L. Vasquez", "M. Okonkwo-Reyes", "H. Nakamura"],
title: "Rheological signatures of arrested phase separation in attractive colloidal gels",
venue: "Journal of Rheology",
volume: "68",
pages: "455–472",
doi: "10.1122/jor.2024.0455",
topic: "Colloids",
peer: true,
oa: false,
cited: 23,
hasCode: true,
hasPdf: true,
},
{
id: "okonkwo2024review",
type: "journal",
year: 2024,
authors: ["M. Okonkwo-Reyes", "T. Aaltonen"],
title: "Coarse-grained models of soft matter: a practitioner's review",
venue: "Annual Review of Condensed Matter Physics",
volume: "15",
pages: "201–229",
doi: "10.1146/arcmp.2024.00201",
topic: "Methods & software",
peer: true,
oa: true,
cited: 41,
hasCode: false,
hasPdf: true,
},
{
id: "devi2023active",
type: "preprint",
year: 2023,
authors: ["R. Devi", "M. Okonkwo-Reyes"],
title: "Topological defects as control variables in active nematic films",
venue: "arXiv:2310.07712 [cond-mat.soft]",
doi: "10.48550/arXiv.2310.07712",
topic: "Active matter",
peer: false,
oa: true,
cited: 14,
hasCode: true,
hasPdf: true,
},
{
id: "vasquez2023swim",
type: "journal",
year: 2023,
authors: ["L. Vasquez", "K. Yamazaki", "M. Okonkwo-Reyes"],
title: "Hydrodynamic clustering of microswimmers under acoustic confinement",
venue: "Physical Review Fluids",
volume: "8",
pages: "094201",
doi: "10.1103/prfluids.8.094201",
topic: "Active matter",
peer: true,
oa: false,
cited: 19,
hasCode: true,
hasPdf: true,
},
{
id: "aaltonen2022chapter",
type: "chapter",
year: 2022,
authors: ["T. Aaltonen", "M. Okonkwo-Reyes"],
title: "Free-energy methods for soft interfaces",
venue: "In: Handbook of Statistical Mechanics of Soft Materials, ch. 7",
pages: "211–248",
doi: "10.1007/hbssm.2022.07",
topic: "Glass physics",
peer: true,
oa: false,
cited: 11,
hasCode: false,
hasPdf: false,
},
{
id: "brennan2022droplet",
type: "journal",
year: 2022,
authors: ["S. Brennan", "R. Devi"],
title: "Coalescence statistics of surfactant-laden emulsion droplets in turbulent flow",
venue: "Journal of Fluid Mechanics",
volume: "942",
pages: "A18",
doi: "10.1017/jfm.2022.0a18",
topic: "Membranes",
peer: true,
oa: true,
cited: 27,
hasCode: false,
hasPdf: true,
},
{
id: "yamazaki2021bench",
type: "conference",
year: 2021,
authors: ["K. Yamazaki", "L. Vasquez", "M. Okonkwo-Reyes"],
title: "Benchmarking lattice-Boltzmann kernels for soft-matter simulation at exascale",
venue: "Proc. Supercomputing Workshop on Mesoscale Methods (SC-MM 21)",
pages: "31–40",
doi: "10.1145/scmm21.0031",
topic: "Methods & software",
peer: true,
oa: false,
cited: 33,
hasCode: true,
hasPdf: true,
},
];
// ── DOM refs ──────────────────────────────
var $ = function (sel) { return document.querySelector(sel); };
var search = $("#search");
var searchClear = $("#search-clear");
var fYear = $("#f-year");
var fTopic = $("#f-topic");
var fAuthor = $("#f-author");
var fType = $("#f-type");
var fPeer = $("#f-peer");
var fPreprint = $("#f-preprint");
var fBest = $("#f-best");
var fSort = $("#f-sort");
var resetAll = $("#reset-all");
var groupsEl = $("#year-groups");
var emptyEl = $("#empty-state");
var countEl = $("#result-count");
var activeTags = $("#active-tags");
var resultsEl = $("#results");
var toastEl = $("#toast");
var TYPE_LABEL = {
journal: "Article",
conference: "Conference",
preprint: "Preprint",
chapter: "Chapter",
};
// ── Toast ─────────────────────────────────
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2200);
}
function authorList(authors) {
return authors
.map(function (a) {
var isMember = MEMBERS.indexOf(a) !== -1;
return isMember ? '<span class="me">' + esc(a) + "</span>" : esc(a);
})
.join(", ");
}
function esc(s) {
return String(s).replace(/[&<>"]/g, function (c) {
return { "&": "&", "<": "<", ">": ">", '"': """ }[c];
});
}
// ── BibTeX + citation generation ──────────
function bibtex(p) {
var entryType =
p.type === "conference" ? "inproceedings" : p.type === "chapter" ? "incollection" : "article";
var fields = [
["author", p.authors.join(" and ")],
["title", p.title],
[p.type === "conference" ? "booktitle" : "journal", p.venue],
["year", String(p.year)],
];
if (p.volume) fields.push(["volume", p.volume]);
if (p.pages) fields.push(["pages", p.pages]);
fields.push(["doi", p.doi]);
var lines = fields.map(function (f) {
return " " + f[0] + " = {" + f[1] + "}";
});
return "@" + entryType + "{" + p.id + ",\n" + lines.join(",\n") + "\n}";
}
function citation(p) {
var auth = p.authors
.map(function (a, i) {
return i === p.authors.length - 1 && p.authors.length > 1 ? "& " + a : a;
})
.join(", ");
var s = auth + " (" + p.year + "). " + p.title + ". ";
s += p.venue;
if (p.volume) s += ", " + p.volume;
if (p.pages) s += ", " + p.pages;
s += ". https://doi.org/" + p.doi;
return s;
}
function copy(text, msg) {
var done = function () { toast(msg); };
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(done, fallbackCopy.bind(null, text, done));
} else {
fallbackCopy(text, done);
}
}
function fallbackCopy(text, done) {
var ta = document.createElement("textarea");
ta.value = text;
ta.style.position = "fixed";
ta.style.opacity = "0";
document.body.appendChild(ta);
ta.select();
try { document.execCommand("copy"); } catch (e) {}
document.body.removeChild(ta);
done();
}
// ── Populate filter selects ───────────────
function uniqueSorted(arr) {
return arr.filter(function (v, i) { return arr.indexOf(v) === i; });
}
function populateSelects() {
var years = uniqueSorted(PUBS.map(function (p) { return p.year; })).sort(function (a, b) { return b - a; });
years.forEach(function (y) { fYear.appendChild(opt(y, y)); });
var topics = uniqueSorted(PUBS.map(function (p) { return p.topic; })).sort();
topics.forEach(function (t) { fTopic.appendChild(opt(t, t)); });
var authors = [];
PUBS.forEach(function (p) { p.authors.forEach(function (a) { authors.push(a); }); });
uniqueSorted(authors)
.sort()
.forEach(function (a) { fAuthor.appendChild(opt(a, a)); });
}
function opt(value, label) {
var o = document.createElement("option");
o.value = value;
o.textContent = label;
return o;
}
// ── Filtering ─────────────────────────────
function getState() {
return {
q: search.value.trim().toLowerCase(),
year: fYear.value,
topic: fTopic.value,
author: fAuthor.value,
type: fType.value,
peer: fPeer.checked,
preprint: fPreprint.checked,
best: fBest.checked,
sort: fSort.value,
};
}
function matches(p, st) {
if (st.year && String(p.year) !== st.year) return false;
if (st.topic && p.topic !== st.topic) return false;
if (st.author && p.authors.indexOf(st.author) === -1) return false;
if (st.type && p.type !== st.type) return false;
if (st.peer && !p.peer) return false;
if (st.preprint && p.type !== "preprint") return false;
if (st.best && !p.award) return false;
if (st.q) {
var hay = (
p.title +
" " +
p.authors.join(" ") +
" " +
p.venue +
" " +
p.topic +
" " +
p.doi
).toLowerCase();
if (hay.indexOf(st.q) === -1) return false;
}
return true;
}
function highlight(text, q) {
if (!q) return esc(text);
var idx = text.toLowerCase().indexOf(q);
if (idx === -1) return esc(text);
return esc(text.slice(0, idx)) + "<mark>" + esc(text.slice(idx, idx + q.length)) + "</mark>" + highlight(text.slice(idx + q.length), q);
}
var ICONS = {
pdf: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>',
doi: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/></svg>',
code: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 18l6-6-6-6M8 6l-6 6 6 6"/></svg>',
bib: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',
cite: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3-1 5-3 5-7V5H3v9h4M14 21c3-1 5-3 5-7V5h-5v9h4"/></svg>',
};
function pubHTML(p, num, q) {
var badges = "";
if (p.peer) badges += '<span class="badge peer">Peer-reviewed</span>';
if (p.type === "preprint") badges += '<span class="badge preprint">Preprint</span>';
if (p.award) badges += '<span class="badge award">' + esc(p.award) + "</span>";
if (p.oa) badges += '<span class="badge oa">Open access</span>';
badges += '<span class="badge topic">' + esc(p.topic) + "</span>";
var venue = "<em>" + highlight(p.venue, q) + "</em>";
if (p.volume) venue += ' <span class="mono">' + esc(p.volume) + "</span>";
if (p.pages) venue += ', <span class="mono">' + esc(p.pages) + "</span>";
venue += " (" + p.year + ").";
var links = "";
if (p.hasPdf) links += '<a class="pub-link" href="#" data-act="pdf" data-id="' + p.id + '">' + ICONS.pdf + "PDF</a>";
links += '<a class="pub-link" href="https://doi.org/' + esc(p.doi) + '" target="_blank" rel="noopener">' + ICONS.doi + "DOI</a>";
if (p.hasCode) links += '<a class="pub-link" href="#" data-act="code" data-id="' + p.id + '">' + ICONS.code + "Code</a>";
links += '<button class="pub-link" type="button" data-act="bib" data-id="' + p.id + '">' + ICONS.bib + "BibTeX</button>";
links += '<button class="pub-link cite" type="button" data-act="cite" data-id="' + p.id + '">' + ICONS.cite + "Cite</button>";
return (
'<li class="pub">' +
'<div class="pub-num">[' + num + ']<span class="pub-type">' + TYPE_LABEL[p.type] + "</span></div>" +
'<div class="pub-body">' +
'<h3 class="pub-title">' + highlight(p.title, q) + "</h3>" +
'<p class="pub-authors">' + authorList(p.authors) + "</p>" +
'<p class="pub-venue">' + venue + "</p>" +
'<div class="badges">' + badges + "</div>" +
'<div class="pub-links">' + links +
'<span class="pub-cited"><strong>' + p.cited + "</strong> citations</span>" +
"</div>" +
"</div>" +
"</li>"
);
}
function render() {
var st = getState();
searchClear.hidden = !search.value;
var filtered = PUBS.filter(function (p) { return matches(p, st); });
// sort
filtered.sort(function (a, b) {
if (st.sort === "cited") return b.cited - a.cited;
if (st.sort === "oldest") return a.year - b.year || a.title.localeCompare(b.title);
return b.year - a.year || a.title.localeCompare(b.title); // newest
});
renderTags(st);
renderCount(filtered.length, st);
if (!filtered.length) {
groupsEl.innerHTML = "";
emptyEl.hidden = false;
return;
}
emptyEl.hidden = true;
var num = 1;
var html;
if (st.sort === "cited") {
// flat list, no year grouping
html =
'<li class="year-group"><div class="year-head"><h2>By citations</h2>' +
'<span class="year-tally">most cited first</span></div><ol class="pubs">';
filtered.forEach(function (p) { html += pubHTML(p, num++, st.q); });
html += "</ol></li>";
} else {
// group by year
var order = [];
var byYear = {};
filtered.forEach(function (p) {
if (!byYear[p.year]) { byYear[p.year] = []; order.push(p.year); }
byYear[p.year].push(p);
});
html = order
.map(function (y) {
var items = byYear[y];
var rows = items.map(function (p) { return pubHTML(p, num++, st.q); }).join("");
return (
'<li class="year-group"><div class="year-head"><h2>' + y + "</h2>" +
'<span class="year-tally">' + items.length + (items.length === 1 ? " publication" : " publications") + "</span></div>" +
'<ol class="pubs">' + rows + "</ol></li>"
);
})
.join("");
}
groupsEl.innerHTML = html;
}
function renderCount(n, st) {
var active = countActive(st);
var base = "<strong>" + n + "</strong> of " + PUBS.length + " publication" + (PUBS.length === 1 ? "" : "s");
countEl.innerHTML = active ? base + " · " + active + " filter" + (active === 1 ? "" : "s") + " active" : base;
}
function countActive(st) {
var n = 0;
if (st.q) n++;
if (st.year) n++;
if (st.topic) n++;
if (st.author) n++;
if (st.type) n++;
if (st.peer) n++;
if (st.preprint) n++;
if (st.best) n++;
return n;
}
function renderTags(st) {
var tags = [];
if (st.q) tags.push(["q", '"' + st.q + '"']);
if (st.year) tags.push(["year", "Year: " + st.year]);
if (st.topic) tags.push(["topic", st.topic]);
if (st.author) tags.push(["author", st.author]);
if (st.type) tags.push(["type", TYPE_LABEL[st.type]]);
if (st.peer) tags.push(["peer", "Peer-reviewed"]);
if (st.preprint) tags.push(["preprint", "Preprints only"]);
if (st.best) tags.push(["best", "Award"]);
activeTags.innerHTML = tags
.map(function (t) {
return '<span class="tag">' + esc(t[1]) + '<button type="button" data-clear="' + t[0] + '" aria-label="Remove filter ' + esc(t[1]) + '">×</button></span>';
})
.join("");
}
function clearOne(key) {
switch (key) {
case "q": search.value = ""; break;
case "year": fYear.value = ""; break;
case "topic": fTopic.value = ""; break;
case "author": fAuthor.value = ""; break;
case "type": fType.value = ""; break;
case "peer": fPeer.checked = false; break;
case "preprint": fPreprint.checked = false; break;
case "best": fBest.checked = false; break;
}
}
function resetFilters() {
search.value = "";
fYear.value = "";
fTopic.value = "";
fAuthor.value = "";
fType.value = "";
fPeer.checked = false;
fPreprint.checked = false;
fBest.checked = false;
fSort.value = "newest";
}
// ── Events ────────────────────────────────
[search].forEach(function (el) { el.addEventListener("input", render); });
[fYear, fTopic, fAuthor, fType, fSort].forEach(function (el) { el.addEventListener("change", render); });
[fPeer, fPreprint, fBest].forEach(function (el) { el.addEventListener("change", render); });
searchClear.addEventListener("click", function () {
search.value = "";
search.focus();
render();
});
resetAll.addEventListener("click", function () {
resetFilters();
render();
toast("Filters reset");
});
activeTags.addEventListener("click", function (e) {
var btn = e.target.closest("[data-clear]");
if (!btn) return;
clearOne(btn.getAttribute("data-clear"));
render();
});
// delegated link/button actions inside results
resultsEl.addEventListener("click", function (e) {
var el = e.target.closest("[data-act]");
if (!el) return;
var act = el.getAttribute("data-act");
var p = PUBS.filter(function (x) { return x.id === el.getAttribute("data-id"); })[0];
if (!p) return;
if (act === "bib") {
e.preventDefault();
copy(bibtex(p), "BibTeX copied to clipboard");
} else if (act === "cite") {
e.preventDefault();
copy(citation(p), "Citation copied to clipboard");
} else if (act === "pdf") {
e.preventDefault();
toast("PDF (demo): " + p.id + ".pdf — fictional document");
} else if (act === "code") {
e.preventDefault();
toast("Repository (demo): github.com/csm-lab/" + p.id);
}
});
// ── Metrics ───────────────────────────────
function fillMetrics() {
var total = PUBS.length;
var peer = PUBS.filter(function (p) { return p.peer; }).length;
var pre = PUBS.filter(function (p) { return p.type === "preprint"; }).length;
var citations = PUBS.reduce(function (s, p) { return s + p.cited; }, 0);
var cites = PUBS.map(function (p) { return p.cited; }).sort(function (a, b) { return b - a; });
var h = 0;
for (var i = 0; i < cites.length; i++) { if (cites[i] >= i + 1) h = i + 1; }
set("total", total);
set("peer", peer);
set("preprint", pre);
set("citations", citations);
set("hindex", h);
}
function set(name, val) {
var el = document.querySelector('[data-metric="' + name + '"]');
if (el) el.textContent = val;
}
// ── Init ──────────────────────────────────
populateSelects();
fillMetrics();
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Publications — Computational Soft Matter Group</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Source+Serif+4:ital,wght@0,400;0,600;0,700;1,400&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#results">Skip to publications</a>
<header class="masthead">
<div class="masthead-inner">
<div class="brand">
<span class="brand-mark" aria-hidden="true">CSM</span>
<div class="brand-text">
<p class="brand-lab">Computational Soft Matter Group</p>
<p class="brand-dept">Dept. of Applied Physics · Vannevar Institute of Technology</p>
</div>
</div>
<nav class="masthead-nav" aria-label="Section">
<span class="nav-current" aria-current="page">Publications</span>
</nav>
</div>
</header>
<main class="page">
<section class="intro">
<h1>Publications</h1>
<p class="lede">
A complete, chronological record of peer-reviewed articles, conference papers, and
preprints authored by members of the group. Bold names denote current or former group
members. Use the filters and search to narrow the list.
</p>
<dl class="metrics" aria-label="Bibliometrics summary">
<div class="metric">
<dt>Total</dt>
<dd><span data-metric="total">0</span></dd>
</div>
<div class="metric">
<dt>Peer-reviewed</dt>
<dd><span data-metric="peer">0</span></dd>
</div>
<div class="metric">
<dt>Preprints</dt>
<dd><span data-metric="preprint">0</span></dd>
</div>
<div class="metric">
<dt>Citations</dt>
<dd><span data-metric="citations">0</span></dd>
</div>
<div class="metric">
<dt>h-index</dt>
<dd><span data-metric="hindex">0</span></dd>
</div>
</dl>
</section>
<section class="controls" aria-label="Filter publications">
<div class="search-row">
<div class="search-field">
<svg class="search-icon" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="11" cy="11" r="7" fill="none" stroke="currentColor" stroke-width="2" />
<line x1="16.5" y1="16.5" x2="21" y2="21" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
<input
id="search"
type="search"
placeholder="Search titles, authors, venues, keywords…"
autocomplete="off"
aria-label="Search publications"
/>
<button type="button" class="search-clear" id="search-clear" aria-label="Clear search" hidden>×</button>
</div>
<button type="button" class="btn ghost" id="reset-all">Reset filters</button>
</div>
<div class="filter-grid">
<label class="select-field">
<span class="select-label">Year</span>
<select id="f-year" aria-label="Filter by year">
<option value="">All years</option>
</select>
</label>
<label class="select-field">
<span class="select-label">Topic</span>
<select id="f-topic" aria-label="Filter by topic">
<option value="">All topics</option>
</select>
</label>
<label class="select-field">
<span class="select-label">Author</span>
<select id="f-author" aria-label="Filter by author">
<option value="">All authors</option>
</select>
</label>
<label class="select-field">
<span class="select-label">Type</span>
<select id="f-type" aria-label="Filter by publication type">
<option value="">All types</option>
<option value="journal">Journal article</option>
<option value="conference">Conference paper</option>
<option value="preprint">Preprint</option>
<option value="chapter">Book chapter</option>
</select>
</label>
<fieldset class="badge-filters">
<legend>Flags</legend>
<label class="chk"><input type="checkbox" id="f-peer" /> Peer-reviewed</label>
<label class="chk"><input type="checkbox" id="f-preprint" /> Preprint</label>
<label class="chk"><input type="checkbox" id="f-best" /> Award</label>
</fieldset>
</div>
<div class="result-bar" aria-live="polite">
<p class="result-count" id="result-count">Loading…</p>
<label class="sort-field">
<span class="select-label">Sort</span>
<select id="f-sort" aria-label="Sort order">
<option value="newest">Newest first</option>
<option value="oldest">Oldest first</option>
<option value="cited">Most cited</option>
</select>
</label>
</div>
<div class="active-tags" id="active-tags" aria-label="Active filters"></div>
</section>
<section id="results" class="results" aria-label="Publication list" tabindex="-1">
<ol class="year-groups" id="year-groups"></ol>
<p class="empty-state" id="empty-state" hidden>
No publications match the current filters. Try broadening your search.
</p>
</section>
<footer class="page-foot">
<p>
Listing generated from the group BibTeX database. DOIs resolve via
<span class="mono">doi.org</span>. Last compiled 2026-06-15.
</p>
</footer>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Publications List
A complete bibliography page for a fictional Computational Soft Matter Group. A sticky masthead carries the lab identity, and a summary band reports computed bibliometrics — total works, peer-reviewed count, preprints, aggregate citations, and an h-index derived live from the seeded data. Below it, a control panel offers four dropdowns (year, topic, author, type), three flag checkboxes (peer-reviewed, preprint-only, award), a sort selector (newest, oldest, most cited), and a search field that matches titles, authors, venues, topics, and DOIs.
The list is grouped by year with a tally per year and sequential reference numbers; switching to “most cited” collapses the grouping into a single ranked list. Every entry bolds the names of group members, shows an italic-serif title with mono volume and page numbers, colored badges (peer-reviewed, preprint, award, open access, topic), a live citation count, and an action row with PDF, DOI, Code, BibTeX, and Cite links. Search terms are highlighted inline with <mark>, and an active-filter tag bar lets you remove individual filters with one click.
Interactions are vanilla JS only: live filtering and sorting, computed metrics and counts, clipboard copy of generated BibTeX entries and formatted citations (with an execCommand fallback), removable filter tags, and a small toast() helper confirming each action. The layout reflows from a five-up metrics grid to a single-column entry list down to 360px, and all controls are keyboard-usable with visible focus.
Illustrative UI only — fictional authors, data, and figures; not real scientific results.