diff --git a/assets/app.js b/assets/app.js index e5006b13..6c68bcaa 100644 --- a/assets/app.js +++ b/assets/app.js @@ -873,13 +873,13 @@ function bmRender() { const total = bmFiltered.length; if (total === 0) { list.innerHTML = '
No matching reference pairs.
'; - meta.textContent = '0 reference pairs'; pager.style.display = 'none'; return; + meta.textContent = BROWSE_KIND_LABELS[browseKind] + ': 0 reference pairs'; pager.style.display = 'none'; return; } const totalPages = Math.max(1, Math.ceil(total / BM_PAGE_SIZE)); if (bmPage >= totalPages) bmPage = totalPages - 1; const start = bmPage * BM_PAGE_SIZE; const end = Math.min(total, start + BM_PAGE_SIZE); list.innerHTML = bmFiltered.slice(start, end).map(bmCardHtml).join(''); - meta.innerHTML = `Showing ${(start + 1).toLocaleString('en-GB')}–${end.toLocaleString('en-GB')} of ${total.toLocaleString()} reference ${total === 1 ? "pair" : "pairs"}`; + meta.innerHTML = `${BROWSE_KIND_LABELS[browseKind]}: Showing ${(start + 1).toLocaleString('en-GB')}–${end.toLocaleString('en-GB')} of ${total.toLocaleString()} reference ${total === 1 ? "pair" : "pairs"}`; info.textContent = `Page ${bmPage + 1} of ${totalPages}`; prev.disabled = bmPage === 0; next.disabled = bmPage >= totalPages - 1; pager.style.display = totalPages > 1 ? 'flex' : 'none'; @@ -892,8 +892,14 @@ function setupBrowseMobile(data) { const next = document.getElementById('bm-next'); let debounceTimer; input.addEventListener('input', () => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => bmApplySearch(input.value), 200); }); - prev.addEventListener('click', () => { if (bmPage > 0) { bmPage--; bmRender(); syncBrowseUrl(); document.querySelector('.app-main').scrollTo({ top: 0, behavior: 'smooth' }); } }); - next.addEventListener('click', () => { bmPage++; bmRender(); syncBrowseUrl(); document.querySelector('.app-main').scrollTo({ top: 0, behavior: 'smooth' }); }); + function showPage(delta) { + bmPage += delta; bmRender(); syncBrowseUrl(); + const heading = document.getElementById('browse-results-heading'); + heading.focus({preventScroll:true}); + heading.scrollIntoView({block:'start', behavior:matchMedia('(prefers-reduced-motion: reduce)').matches ? 'instant' : 'smooth'}); + } + prev.addEventListener('click', () => { if (bmPage > 0) showPage(-1); }); + next.addEventListener('click', () => { if (!next.disabled) showPage(1); }); input.value = browseQuery; bmApplySearch(browseQuery); } @@ -961,7 +967,11 @@ function syncBrowseUrl() { } // ===== Browse kind filter ===== -let browseKind = new URLSearchParams(location.search).get('kind') || 'all'; +const BROWSE_KIND_LABELS = {all: 'All studies', replication: 'Replications', reproduction: 'Reproductions', 'reproduction-numerical': 'Numerical Reproductions', 'reproduction-robustness': 'Robustness Reproductions'}; +function normalizeBrowseKind(kind) { + return Object.hasOwn(BROWSE_KIND_LABELS, kind) ? kind : 'all'; +} +let browseKind = normalizeBrowseKind(new URLSearchParams(location.search).get('kind')); let browseQuery = new URLSearchParams(location.search).get('q') || ''; let browseOutcomeChart = null; let browseComputationalChart = null; @@ -1002,7 +1012,7 @@ function renderBrowseOutcomeCharts() { // Zero matches get a visible explanation and recovery actions instead of an empty table and // chart. FLoRA is incomplete, so absence here is not evidence that no repetition exists. -const BROWSE_KIND_NOUNS = {all: 'reference pairs', replication: 'replications', 'reproduction-numerical': 'numerical reproductions', 'reproduction-robustness': 'robustness reproductions'}; +const BROWSE_KIND_NOUNS = {all: 'reference pairs', replication: 'replications', reproduction: 'reproductions', 'reproduction-numerical': 'numerical reproductions', 'reproduction-robustness': 'robustness reproductions'}; function renderBrowseEmpty(n) { const el = document.getElementById('browse-empty'); if (!el) return; el.hidden = n > 0; @@ -1056,7 +1066,8 @@ function trendsFilteredData() { return filterByKind(fullRowData, trendsKind); } function updateTrendsCount() { const el = document.getElementById('trend-filter-count'); if (!el) return; const n = trendsFilteredData().length; const total = fullRowData.length; - el.textContent = trendsKind === 'all' ? `${n.toLocaleString()} reference ${n === 1 ? "pair" : "pairs"}` : `${n.toLocaleString()} of ${total.toLocaleString()} reference ${total === 1 ? "pair" : "pairs"}`; + const count = trendsKind === 'all' ? `${n.toLocaleString()} reference ${n === 1 ? "pair" : "pairs"}` : `${n.toLocaleString()} of ${total.toLocaleString()} reference ${total === 1 ? "pair" : "pairs"}`; + el.innerHTML = `${BROWSE_KIND_LABELS[trendsKind]}: ${count}`; } // Plain per-category counts (year/journal/field) - no outcome breakdown. A prior version @@ -1346,7 +1357,7 @@ function renderMcCharts() { const statCards = buckets.filter(b => ov[b.overviewKey] > 0).map(b => { const n = ov[b.overviewKey] || 0; const pct = FloraCharts.percent(n, ov.n_total); - return `
${n.toLocaleString()}${b.label} (${escapeHtml(pct)}%)
`; + return `
${n.toLocaleString()}${b.label} (${escapeHtml(pct)}%)
`; }).join(''); document.getElementById('mc-overview').innerHTML = `
${ov.n_total.toLocaleString()}Reference pairs with OMC
diff --git a/assets/chart-accessibility.js b/assets/chart-accessibility.js index e5eb3078..a2ede154 100644 --- a/assets/chart-accessibility.js +++ b/assets/chart-accessibility.js @@ -59,7 +59,7 @@ window.FloraCharts = (() => { || el.closest('.trend-block')?.querySelector('.trend-desc h3') || el.closest('.mc-chart-card, .plot-card')?.querySelector('.mc-chart-title, h3') || el.closest('.modal-body')?.querySelector('h3'); - const prefix = el.id.startsWith('browse-') ? 'Browse results: ' : ''; + const prefix = el.id.startsWith('browse-') ? 'Browse results: ' : /^plot-(cit|cocit)/.test(el.id) ? (el.id.endsWith('-model') ? 'Adjusted association: ' : 'Raw trajectory: ') : ''; return heading ? prefix + plain(heading.textContent) : el.id.replace(/-/g, ' '); } // Removes a chart together with everything that describes or exports it, so an @@ -84,7 +84,7 @@ window.FloraCharts = (() => { anchor.insertAdjacentElement('afterend', details); } const wasOpen = details.open; - details.innerHTML = `View chart data

${escape(summary)}

${escape(context(el))}

${headers.map(h=>``).join('')}${rows.map(row=>`${row.map((v,i)=>i ? `` : ``).join('')}`).join('')}
${escape(visibleTitle(el))}
${escape(h)}
${escape(fmt(v, headers[i]))}${escape(fmt(v, headers[i]))}
`; + details.innerHTML = `View chart data

${escape(summary)}

${escape(context(el))}

${headers.map(h=>``).join('')}${rows.map(row=>`${row.map((v,i)=>i ? `` : ``).join('')}`).join('')}
${escape(visibleTitle(el))}
${escape(h)}
${escape(fmt(v, headers[i]))}${escape(fmt(v, headers[i]))}
`; details.open = wasOpen; const label = el.closest('.outcome-chart-row')?.querySelector('.outcome-chart-label') || el.closest('.trend-block')?.querySelector('.trend-title'); let action = document.getElementById(id + '-copy'); @@ -108,7 +108,7 @@ window.FloraCharts = (() => { try { await navigator.clipboard.writeText(csvText(headers, rows, summary + '\n' + context(el))); action.title = 'Chart CSV copied'; - action.setAttribute('aria-label', 'Chart CSV copied'); + action.setAttribute('aria-label', 'Chart CSV copied for ' + visibleTitle(el)); status.textContent = 'Chart CSV copied'; setTimeout(() => { action.title = 'Copy chart CSV'; action.setAttribute('aria-label', 'Copy chart CSV for ' + visibleTitle(el)); }, 2000); } catch { @@ -130,6 +130,8 @@ window.FloraCharts = (() => { table(el,rows,['Category',...datasets.map(d=>d.label)],sample); if (config.options.scales) for (const axis of Object.values(config.options.scales)) if (axis.ticks) axis.ticks.autoSkip = !config.options.indexAxis || config.options.indexAxis !== 'y'; if (config.options.plugins?.legend) config.options.plugins.legend.reverse = false; + Chart.defaults.font.family = theme().fontFamily; + if (matchMedia('(prefers-reduced-motion: reduce)').matches) config.options.animation = false; return new Chart(ctx, config); } // options.table = {headers, rows, summary} replaces the generic per-trace table, for @@ -179,6 +181,7 @@ window.FloraCharts = (() => { toggle.onclick=()=>{modes.set(id,modes.get(id)==='count'?'percent':'count');const args=originals.get(id);plot(el,...args);}; } toggle.textContent=counts?'Show percentages':'Show counts'; + toggle.setAttribute('aria-label', toggle.textContent + ' for ' + visibleTitle(el)); } if (window.innerWidth < 600) { layout.margin={...layout.margin,l:60,r:layout.yaxis2 ? 65 : 20,b:100}; diff --git a/assets/citation-impact.js b/assets/citation-impact.js index e3753bad..2bfb8d1a 100644 --- a/assets/citation-impact.js +++ b/assets/citation-impact.js @@ -368,7 +368,7 @@ `; }).join(''); return `
-

${title}

+

${title}

${rows} @@ -398,6 +398,11 @@ } function renderTable() { + const focused = document.activeElement; + const pageAction = focused.closest('#pagination button')?.dataset.action; + const pageNumber = focused.closest('#pagination button')?.dataset.p; + const timelineRow = focused.closest('#originals-table tbody tr'); + const rowIndex = timelineRow ? [...timelineRow.parentElement.children].indexOf(timelineRow) : -1; const q = document.getElementById('search-input').value || ''; const tokens = searchTokens(q); const outFilter = document.getElementById('filter-outcome').value; @@ -420,7 +425,13 @@ th.classList.remove('sort-asc', 'sort-desc'); if (th.dataset.sort === col) th.classList.add(dir === 'asc' ? 'sort-asc' : 'sort-desc'); }); + document.querySelectorAll('#originals-table th').forEach(th => { + const active = th.querySelector(`[data-sort="${col}"]`); + if (active) th.setAttribute('aria-sort', dir === 'asc' ? 'ascending' : 'descending'); + else th.removeAttribute('aria-sort'); + }); document.querySelectorAll('.cocit-sort-btn').forEach(btn => { + btn.setAttribute('aria-pressed', String(btn.dataset.sort === col)); btn.classList.remove('sort-asc', 'sort-desc'); if (btn.dataset.sort === col) btn.classList.add(dir === 'asc' ? 'sort-asc' : 'sort-desc'); }); @@ -457,10 +468,12 @@ const winStart = Math.max(1, CI.page - Math.floor(winSize / 2)); const winEnd = Math.min(pages, winStart + winSize - 1); let pHtml = ''; - if (CI.page > 1) pHtml += ``; - for (let i = winStart; i <= winEnd; i++) pHtml += ``; - if (CI.page < pages) pHtml += ``; + if (CI.page > 1) pHtml += ``; + for (let i = winStart; i <= winEnd; i++) pHtml += ``; + if (CI.page < pages) pHtml += ``; pag.innerHTML = pHtml; + if (pageNumber) (pag.querySelector(pageAction ? `[data-action="${pageAction}"]` : `[data-p="${pageNumber}"]`) || pag.querySelector('[aria-current]'))?.focus({preventScroll:true}); + else if (rowIndex >= 0) (tbody.rows[Math.min(rowIndex, tbody.rows.length - 1)]?.querySelector('.timeline-open') || document.getElementById('originals-table')).focus(); } // Fetch one original's full record (citation timeline and replication list), @@ -656,12 +669,7 @@ CI.page = 1; renderTable(); } - document.querySelectorAll('#originals-table thead th[data-sort]').forEach(th => { - th.tabIndex = 0; th.setAttribute('role','button'); - th.addEventListener('click', () => sortBy(th.dataset.sort)); - th.addEventListener('keydown', e => { if(e.key === 'Enter' || e.key === ' ') {e.preventDefault();sortBy(th.dataset.sort);} }); - }); - document.querySelectorAll('.cocit-sort-btn').forEach(btn => { + document.querySelectorAll('#originals-table button[data-sort]').forEach(btn => { btn.addEventListener('click', () => sortBy(btn.dataset.sort)); }); document.querySelector('#originals-table tbody').addEventListener('click', e => { diff --git a/assets/ds-tokens.css b/assets/ds-tokens.css index f536d5e4..e43e4173 100644 --- a/assets/ds-tokens.css +++ b/assets/ds-tokens.css @@ -88,12 +88,14 @@ --color-plot-band-original: rgba(37, 99, 235, .15); /* study-original band */ --color-plot-point: rgba(80, 80, 80, .15); /* jittered observations */ --color-plot-rule: rgba(100, 100, 100, .4); /* reference lines */ - --color-plot-rule-label: rgba(100, 100, 100, .6); + --color-plot-rule-label: #6b7280; /* Chart series colours (FLoRA explorer). Light values keep the established outcome palette; dark values below keep series marks at >= 3:1 against --color-surface-raised, except --color-chart-not-coded, which stays deliberately faint (2.6:1) for unassessed rows. */ + --color-badge-successful: #246f3d; + --color-badge-on-outcome: #fff; --color-chart-successful: #2f8f4f; --color-chart-failed: #b3331e; --color-chart-mixed: #d49b1d; @@ -291,10 +293,13 @@ --color-plot-band-original: rgba(147, 180, 253, .18); --color-plot-point: rgba(210, 210, 220, .2); --color-plot-rule: rgba(200, 200, 210, .45); - --color-plot-rule-label: rgba(200, 200, 210, .6); + --color-plot-rule-label: #c3bac0; - --color-chart-successful: #4ade80; - --color-chart-failed: #f08a7c; + /* Lightness separates success and failure under red/green colour-vision loss. */ + --color-badge-successful: var(--color-chart-successful); + --color-badge-on-outcome: #17131a; + --color-chart-successful: #86efac; + --color-chart-failed: #ed7465; --color-chart-mixed: #e0b040; --color-chart-inconclusive: #a3a9b6; --color-chart-qualified: #c0a8fa; diff --git a/assets/styles.css b/assets/styles.css index d86acbf7..3462fd2c 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -172,8 +172,8 @@ h1, .serif, .card-header, .stats-card .number, /* Badges */ .badge { font-weight: 500; padding: 0.4em 0.7em; border-radius: 4px; font-size: 0.78rem; letter-spacing: 0.02em; } -.badge-successful { background-color: #246f3d; color: #fff; } -.badge-failed { background-color: #b3331e; color: #fff; } +.badge-successful { background-color: var(--color-badge-successful); color: var(--color-badge-on-outcome); } +.badge-failed { background-color: var(--color-chart-failed); color: var(--color-badge-on-outcome); } .badge-mixed { background-color: #d49b1d; color: #201800; } .badge-inconclusive, .badge-unknown { background-color: var(--color-muted-fg); color: white; } @@ -450,6 +450,7 @@ tr.shown td.details-control .study-details::before { transform: rotate(90deg); } .table-wrap { background: var(--color-surface-raised); border: 1px solid var(--color-border); border-radius: 8px; overflow-x: auto; } #originals-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; } #originals-table thead { background: var(--color-surface-subtle); } +#originals-table th { position: relative; } #originals-table th, #originals-table td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid var(--color-border); } #originals-table tbody tr { cursor: pointer; transition: background 0.1s; } #originals-table tbody tr:hover { background: var(--color-surface-alt); } @@ -457,8 +458,8 @@ tr.shown td.details-control .study-details::before { transform: rotate(90deg); } .title-cell { font-weight: 600; color: var(--color-text); font-family: var(--font-display); } .author-cell { font-size: 0.78rem; color: var(--color-muted-fg); margin-top: 0.15rem; } .outcome-badge { display: inline-block; padding: 0.15em 0.55em; border-radius: 4px; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-right: 0.25rem; } -.outcome-badge.failed { background: rgba(179,51,30,0.15); color: var(--color-error); } -.outcome-badge.successful { background: rgba(47,143,79,0.15); color: var(--color-success-strong); } +.outcome-badge.failed { background: var(--color-chart-failed); color: var(--color-badge-on-outcome); } +.outcome-badge.successful { background: var(--color-badge-successful); color: var(--color-badge-on-outcome); } .outcome-badge.mixed { background: rgba(212,155,29,0.18); color: var(--color-warning); } #originals-table th.sortable { cursor: pointer; user-select: none; white-space: nowrap; } @@ -476,7 +477,7 @@ tr.shown td.details-control .study-details::before { transform: rotate(90deg); } .cocit-sort-btn.sort-desc::after { content: ' ▼'; } .cocit-breakdown-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.25rem; margin-top: 0.75rem; } -.cocit-breakdown-table h4 { margin: 0 0 0.5rem; font-size: 0.88rem; color: var(--color-primary); font-family: var(--font-display); font-weight: 600; } +.cocit-breakdown-table h3 { margin: 0 0 0.5rem; font-size: 0.88rem; color: var(--color-primary); font-family: var(--font-display); font-weight: 600; } .cocit-breakdown-table table { width: 100%; border-collapse: collapse; font-size: 0.86rem; } .cocit-breakdown-table th, .cocit-breakdown-table td { padding: 0.5rem 0.6rem; text-align: left; border-bottom: 1px solid var(--color-border); } .cocit-breakdown-table th:not(:first-child), .cocit-breakdown-table td:not(:first-child) { text-align: right; } @@ -487,9 +488,9 @@ tr.shown td.details-control .study-details::before { transform: rotate(90deg); } .rep-bar-wrap { display: flex; align-items: center; gap: 0.45rem; } .rep-bar { display: flex; height: 10px; border-radius: 4px; overflow: hidden; min-width: 80px; flex: 1; background: var(--color-border); } .rep-seg { height: 100%; min-width: 3px; transition: width 0.2s; } -.rep-seg.failed { background: #b3331e; } -.rep-seg.mixed { background: #d49b1d; } -.rep-seg.successful { background: #2f8f4f; } +.rep-seg.failed { background: var(--color-chart-failed); } +.rep-seg.mixed { background: var(--color-chart-mixed); } +.rep-seg.successful { background: var(--color-chart-successful); } .rep-count { font-size: 0.78rem; color: var(--color-muted-fg); white-space: nowrap; } .cocit-pct { font-size: 0.78rem; color: var(--color-muted-fg); } @@ -633,7 +634,7 @@ h1,.card-header,.mc-chart-title {font-family:var(--font-display);} .table-scroll caption {caption-side:top;color:var(--color-text);} .chart-data .table-scroll th {text-transform:none;} .badge {white-space:normal;line-height:1.4;text-transform:none;} -.badge-successful,.outcome-badge.successful {background:#246f3d!important;color:#fff!important;} +.badge-successful,.outcome-badge.successful {background:var(--color-badge-successful)!important;color:var(--color-badge-on-outcome)!important;} .badge-mixed,.outcome-badge.mixed {background:#d49b1d!important;color:#201800!important;} .badge-qualified {background:#684491!important;color:#fff!important;} .badge-other,.badge-unknown,.badge-inconclusive {background:#626875!important;color:#fff!important;} @@ -716,3 +717,41 @@ h1,.card-header,.mc-chart-title {font-family:var(--font-display);} /* Citation timeline: numbered event markers match the numbered replication list. */ .rep-marker { display: inline-flex; align-items: center; justify-content: center; min-width: 1.4rem; height: 1.4rem; margin-right: 0.4rem; border-radius: 50%; font-size: 0.75rem; font-weight: 700; border: 1.5px solid currentColor; } .kpi-note { grid-column: 1 / -1; margin: 0; font-size: 0.85rem; } + +/* Keyboard access uses opaque rings against the surface and the dark chrome. */ +:root { --color-focus: var(--color-link); --color-focus-on-chrome: #fff; } +:focus-visible, input:focus-visible, select:focus-visible, .form-control:focus-visible, +#search-input:focus-visible, .dataTables_filter input:focus-visible { + outline: 3px solid var(--color-focus); outline-offset: 3px; box-shadow: none; +} +.top-bar :focus-visible, .footer-bar :focus-visible { + outline: 3px solid var(--color-focus-on-chrome); outline-offset: -3px; box-shadow: none; +} +.skip-link { position: fixed; top: .5rem; left: .5rem; z-index: 1300; padding: .5rem 1rem; background: var(--color-surface-raised); color: var(--color-text); transform: translateY(-200%); } +.skip-link:focus { transform: none; } +.info-icon { width: 24px; height: 24px; flex-shrink: 0; } +.gloss { display: inline-block; min-width: 24px; min-height: 24px; } +.footer-bar a { display: inline-flex; align-items: center; min-height: 24px; } +.study-details, .btn-close, .bm-evidence summary { min-width: 24px; min-height: 24px; } +.data-stamp a { text-decoration: underline; } +.study-type-note { color: var(--color-text-secondary); } +.table-sort { border: 0; background: none; color: inherit; font: inherit; padding: .15rem; min-width: 24px; min-height: 24px; text-align: left; } +#originals-table th.sortable::after { content: none; } +.table-sort::after { content: ' ⇅'; } +.sort-asc .table-sort::after { content: ' ▲'; } +.sort-desc .table-sort::after { content: ' ▼'; } +.cocit-sort-btn { min-width: 24px; min-height: 24px; opacity: 1; } + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { scroll-behavior: auto !important; animation: none !important; transition: none !important; } +} +@media (forced-colors: active) { + .chip.active, .trend-filter-btn.active { outline: 3px solid Highlight; outline-offset: -4px; border-color: Highlight; } + .nav-tabs .nav-link.active { border-bottom: 5px double Highlight; } + .rep-bar { border: 1px solid CanvasText; } + .rep-seg { forced-color-adjust: none; background: Canvas !important; border-right: 1px solid CanvasText; } + .rep-seg.failed { background: CanvasText !important; } + .rep-seg.successful { background: repeating-linear-gradient(90deg, Canvas 0 2px, CanvasText 2px 3px) !important; } + .rep-seg.mixed { background: repeating-linear-gradient(45deg, Canvas 0 2px, CanvasText 2px 4px) !important; } + :focus-visible { outline-color: Highlight; } +} diff --git a/assets/view-controls.js b/assets/view-controls.js index 66db441b..41eca6eb 100644 --- a/assets/view-controls.js +++ b/assets/view-controls.js @@ -42,8 +42,7 @@ // Back/forward always dismisses a timeline, including its shareable DOI. url.searchParams.delete('doi'); } - const kinds=['all','replication','reproduction-numerical','reproduction-robustness']; - const kind=url.searchParams.get('kind') || 'all';browseKind=kinds.includes(kind)?kind:'all'; + browseKind=normalizeBrowseKind(url.searchParams.get('kind')); browseQuery=url.searchParams.get('q') || '';document.getElementById('browse-mobile-input').value=browseQuery; document.querySelectorAll('.browse-kind-btn').forEach(b=>b.classList.toggle('active',b.dataset.kind===browseKind)); const sort=url.searchParams.get('sort')?.split(':'); @@ -81,7 +80,13 @@ } document.addEventListener('flora-ready',()=>restore(initial),{once:true}); document.addEventListener('click',e=>{ - if(e.target.closest('.chip, .trend-filter-btn')) {pressed();recordState();} + const chip = e.target.closest('.chip, .trend-filter-btn'); + if(chip) { + pressed();recordState(); + // Announce user choices by their group label; state restored from the URL stays silent. + const group = chip.closest('#ci-study-type, #outcome-chips, #mc-study-type, #ao-study-type, #pubtype-study-type'); + if (group && !restoring) document.getElementById('analysis-feedback').textContent = group.getAttribute('aria-label') + ': ' + chip.textContent.trim() + ' selected.'; + } }); document.addEventListener('change',e=>{if(Object.keys(controls).includes(e.target.id))recordState();}); document.getElementById('search-input')?.addEventListener('input',recordState); @@ -135,7 +140,7 @@ `Unit: reference pair\n${stamp}\nStudy kind: ${browseKind}\nQuery: ${browseQuery}\nIncluded: ${rows.length.toLocaleString('en-GB')}; excluded by filters: ${(fullRowData.length-rows.length).toLocaleString('en-GB')}\nQualified success is separate from unqualified success; reproduction dimensions overlap.`); }; // Associate static and dynamically inserted glossary definitions with their triggers. - let tipNumber = 0; + let tipNumber = 0, openingTip = null; function describeTips() { document.querySelectorAll('.info-icon, .gloss').forEach(trigger => { const tip = trigger.querySelector('.info-tip, .gloss-tip'); @@ -143,6 +148,13 @@ tip.id ||= 'flora-tip-' + (++tipNumber); tip.setAttribute('role', 'tooltip'); trigger.setAttribute('aria-describedby', tip.id); + trigger.addEventListener('focus', () => { + trigger.classList.remove('tip-dismissed'); + // Focusing an off-screen definition scrolls it into view before paint. + openingTip = trigger; + requestAnimationFrame(() => requestAnimationFrame(() => { if (openingTip === trigger) openingTip = null; })); + }); + trigger.addEventListener('click', () => trigger.classList.remove('tip-dismissed')); trigger.addEventListener('mouseleave', () => trigger.classList.remove('tip-dismissed')); trigger.addEventListener('blur', () => trigger.classList.remove('tip-dismissed')); }); @@ -151,6 +163,10 @@ document.addEventListener('keydown', event => { if (event.key === 'Escape') document.querySelectorAll('.info-icon:hover, .info-icon:focus, .gloss:hover, .gloss:focus').forEach(el => el.classList.add('tip-dismissed')); }); + document.addEventListener('scroll', event => { + if (!matchMedia('(max-width: 600px)').matches || event.target.closest?.('.info-tip, .gloss-tip')) return; + document.querySelectorAll('.info-icon:hover, .info-icon:focus, .gloss:hover, .gloss:focus').forEach(el => { if (el !== openingTip) el.classList.add('tip-dismissed'); }); + }, true); describeTips(); pressed(); })(); diff --git a/index.html b/index.html index 2dc41cad..24b1fc2d 100644 --- a/index.html +++ b/index.html @@ -32,10 +32,12 @@ + + -
- -
+ + -
+
+
@@ -111,7 +114,7 @@

Outcome Breakdown

Computational Reproducibility - + Covers reproductions only: were the original results obtained? Reproduction coverage is still small, so this chart will fill in as more are added. @@ -124,7 +127,7 @@

Outcome Breakdown

GroupN originalsPer-paper meanMedianWeighted mean
- + @@ -424,11 +428,11 @@

Studies Database

-
+
- +
@@ -438,7 +442,8 @@

Studies Database

Study evidence Original Study Year Replication or reproduction study
- + @@ -592,7 +598,7 @@

Aggregate impact of replications

Replication outcome -
+
@@ -650,23 +656,23 @@

Browse all originals

-
Outcome Originals Citations to original Citations to reproduction
+
- - - - + + + - +
OriginalYearReplicationsCitations, all yearsCo-citations since first recorded replication + Co-citations since first recorded replication Timeline
- + @@ -697,6 +703,7 @@

About this analysis

+

Mean Citedness

Citation and reuse guidance @@ -711,7 +718,7 @@

Journal Mean Citedness and Recorded Outcomes

OpenAlex Mean Citedness (OMC) of an original study's journal predicts replication success. OMC is a proxy for the Journal Impact Factor. Mean Citedness is a journal-level measure from OpenAlex: the mean citations to its works published in the previous two years. It is retrieved at enrichment time, not matched to the original publication year, and is not the original article’s citation count. The update stamp identifies the analysis snapshot. Any fitted model describes an association.

-
+
@@ -743,6 +750,7 @@

Success probability among successful / failed replica
+

Authorship Overlap

Citation and reuse guidance @@ -760,7 +768,7 @@

Authorship Overlap and Outcomes

This may produce false positives for common family names when two unrelated researchers share a surname.

-
+
@@ -794,11 +802,12 @@

+

Publication Type

Citation and reuse guidance
How to cite
Wallrich, L., & Röseler, L. (2026). FLoRA Explorer [Website]. https://forrt.org/flora-explorer/
-
+
@@ -996,13 +1005,13 @@

Outcomes by report size

-
+