Skip to content

Commit 289f919

Browse files
jeremymanningclaude
andcommitted
Fix HTML margin note positioning and personnel two-column layout
Margin notes: - CSS: dd margin notes use position:absolute relative to parent dl, with left:110% to align with non-dl margin notes - JS: detect and fix overlapping adjacent notes by nudging down Personnel lists: - JS: split raw text nodes in .columns-2 into individual spans so CSS grid can lay them out in two columns - CSS: wider columns (80%, no margin notes in this section), white-space:nowrap to prevent date ranges from splitting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3540b55 commit 289f919

3 files changed

Lines changed: 70 additions & 38 deletions

File tree

lab-manual.css

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,18 @@ p.noindent img {
9191
div.columns-2,
9292
.columns-2,
9393
.personnel-list {
94-
width: 55%;
94+
width: 80%;
9595
display: grid;
9696
grid-template-columns: 1fr 1fr;
97-
gap: 0.3em 2em;
98-
line-height: 1.6;
97+
gap: 0.2em 2em;
9998
}
10099

101100
/* Individual personnel items (wrapped by JavaScript) */
102101
.personnel-item {
103102
display: block;
103+
font-size: 1.4rem;
104+
line-height: 1.8;
105+
white-space: nowrap;
104106
}
105107

106108
/* Margin notes styling - positioned in right margin */
@@ -131,9 +133,12 @@ label.sidenote-number {
131133
}
132134

133135
/* Definition lists (used for numbered items) */
134-
/* Only top-level dl gets 55% width; nested dl inherits parent width */
136+
/* Only top-level dl gets 55% width; nested dl inherits parent width.
137+
position: relative makes this the containing block for absolutely
138+
positioned margin notes inside dd elements. */
135139
article > dl {
136140
width: 55%;
141+
position: relative;
137142
}
138143

139144
dt {
@@ -153,13 +158,16 @@ dd dl {
153158
width: 100%;
154159
}
155160

156-
/* Margin notes inside definition lists need wider offset since
157-
dd elements are narrower than article-level paragraphs.
158-
These notes are repositioned by JavaScript (see toc.js) to
159-
prevent them from expanding dd height or overlapping. */
160-
dl .marginnote, dl .sidenote {
161-
margin-right: -80%;
162-
width: 40%;
161+
/* Margin notes inside definition lists: absolutely positioned relative
162+
to the top-level dl. This removes them from flow so dd elements don't
163+
expand vertically. left:110% aligns with non-dl margin notes. */
164+
dd .marginnote, dd .sidenote {
165+
position: absolute;
166+
left: 110%;
167+
width: 50%;
168+
float: none;
169+
clear: none;
170+
margin-right: 0;
163171
}
164172

165173
/* Checklist styling - make checkboxes visible */

lab_manual.pdf

0 Bytes
Binary file not shown.

toc.js

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ document.addEventListener('DOMContentLoaded', function() {
33
var toc = document.getElementById('toc-list');
44
if (!toc) return;
55

6-
// Clear existing TOC items (tex4ht generates broken links)
76
while (toc.firstChild) toc.removeChild(toc.firstChild);
87

98
var headings = document.querySelectorAll('h2.chapterHead');
109
headings.forEach(function(h, index) {
11-
// Generate an ID if heading doesn't have one or it's empty
1210
if (!h.id || h.id === '') {
13-
// Create slug from heading text
1411
var slug = h.textContent
1512
.toLowerCase()
1613
.replace(/[^a-z0-9]+/g, '-')
@@ -25,35 +22,62 @@ document.addEventListener('DOMContentLoaded', function() {
2522
li.appendChild(a);
2623
toc.appendChild(li);
2724
});
28-
});
2925

30-
// Fix margin notes inside definition lists.
31-
// Floated margin notes inside dd elements expand the dd height,
32-
// causing large gaps in the left-side text. We reposition them
33-
// as absolutely positioned elements relative to the article.
34-
// Runs on window load (after layout) so getBoundingClientRect is accurate.
35-
window.addEventListener('load', function() {
36-
var article = document.querySelector('article');
37-
if (!article) return;
26+
// Wrap personnel names in .columns-2 sections into individual spans.
27+
// tex4ht outputs names as raw text in a <p>; CSS grid needs elements.
28+
// Each name matches "Name (year – )" or "Name (year – year)" or "Name (year)".
29+
var cols = document.querySelectorAll('.columns-2');
30+
cols.forEach(function(col) {
31+
var ps = col.querySelectorAll('p');
32+
ps.forEach(function(p) {
33+
var text = p.textContent.trim();
34+
if (!text) return;
3835

39-
article.style.position = 'relative';
36+
// Split on the pattern: end of one entry ")" followed by start of next
37+
var names = text.match(/[^)]+\)/g);
38+
if (!names || names.length === 0) return;
4039

41-
var ddNotes = document.querySelectorAll('dd .marginnote, dd .sidenote');
42-
var articleRect = article.getBoundingClientRect();
40+
// Replace the <p> contents with wrapped spans
41+
while (p.firstChild) p.removeChild(p.firstChild);
42+
names.forEach(function(name) {
43+
var span = document.createElement('span');
44+
span.className = 'personnel-item';
45+
span.textContent = name.trim();
46+
p.appendChild(span);
47+
});
48+
});
4349

44-
ddNotes.forEach(function(note) {
45-
var parent = note.parentElement;
46-
var anchorRect = parent.getBoundingClientRect();
47-
var topOffset = anchorRect.top - articleRect.top;
50+
// Move children out of <p> into the columns-2 div directly for grid layout
51+
var items = col.querySelectorAll('.personnel-item');
52+
items.forEach(function(item) {
53+
col.appendChild(item);
54+
});
55+
// Remove now-empty <p> elements
56+
var emptyPs = col.querySelectorAll('p');
57+
emptyPs.forEach(function(p) {
58+
if (p.textContent.trim() === '') p.remove();
59+
});
60+
});
61+
});
4862

49-
note.style.position = 'absolute';
50-
note.style.top = topOffset + 'px';
51-
note.style.right = '0';
52-
note.style.width = '25%';
53-
note.style.marginRight = '0';
54-
note.style.float = 'none';
55-
note.style.clear = 'none';
63+
// Prevent overlapping margin notes inside definition lists.
64+
// CSS positions dd margin notes with position:absolute (removing them
65+
// from flow so dd doesn't expand). But adjacent notes can overlap.
66+
// This script nudges overlapping notes down with a small gap.
67+
window.addEventListener('load', function() {
68+
var dls = document.querySelectorAll('article > dl');
69+
dls.forEach(function(dl) {
70+
var notes = dl.querySelectorAll('dd .marginnote, dd .sidenote');
71+
if (notes.length < 2) return;
5672

57-
article.appendChild(note);
73+
for (var i = 1; i < notes.length; i++) {
74+
var prev = notes[i - 1].getBoundingClientRect();
75+
var curr = notes[i].getBoundingClientRect();
76+
var overlap = prev.bottom - curr.top;
77+
if (overlap > 0) {
78+
var currentTop = parseFloat(getComputedStyle(notes[i]).top) || 0;
79+
notes[i].style.top = (currentTop + overlap + 8) + 'px';
80+
}
81+
}
5882
});
5983
});

0 commit comments

Comments
 (0)