Commit 9eebc20 h
Pat Ambrosio · 26-08-28 14:05
Shorten the section mark to a tick above the heading
Changes: 3 files
+41
-15
Changes (Side-by-Side)
share/jenny/layout/page.sh +17 -6
cat << _EOF_ |
| 861 |
rest. Scoped to .contents because the title itself lives in |
861 |
rest. Scoped to .contents because the title itself lives in |
| 862 |
.heading, outside it. */ |
862 |
.heading, outside it. */ |
| 863 |
.contents :is(h1, h2) { |
863 |
.contents :is(h1, h2) { |
| 864 |
border-top: 1px solid var(--border-light); |
| |
| 865 |
padding-top: 1.5em; |
| |
| |
864 |
position: relative; |
| |
865 |
margin-top: 3em; |
| |
866 |
} |
| |
867 |
/* A short mark rather than a border. border-top spans the whole box, |
| |
868 |
which drew a full-measure line and read as a divider between two |
| |
869 |
halves of the page instead of as a mark belonging to the heading |
| |
870 |
under it. 3ch sits at the width of two or three characters, so it |
| |
871 |
reads as a tick, and 0.6em above keeps it close enough to belong to |
| |
872 |
the heading rather than to float between the two. */ |
| |
873 |
.contents :is(h1, h2)::before { |
| |
874 |
content: ""; |
| |
875 |
position: absolute; |
| |
876 |
top: -0.6em; |
| |
877 |
left: 0; |
| |
878 |
width: 3ch; |
| |
879 |
border-top: 1px solid var(--border-medium); |
| 866 |
} |
880 |
} |
| 867 |
/* The one exception: a page repeats its title as the body's first |
881 |
/* The one exception: a page repeats its title as the body's first |
| 868 |
heading, and an h1 with no heading before it is exactly that repeat. |
882 |
heading, and an h1 with no heading before it is exactly that repeat. |
| 869 |
A rule there draws a line immediately under the title in the header. |
883 |
A rule there draws a line immediately under the title in the header. |
| 870 |
A post has no repeat -- its title is lifted into the header -- so its |
884 |
A post has no repeat -- its title is lifted into the header -- so its |
| 871 |
opening h2 is a real section and keeps its rule. */ |
885 |
opening h2 is a real section and keeps its rule. */ |
| 872 |
.contents h1:not(:is(h1, h2) ~ *) { |
| |
| 873 |
border-top: 0; |
| |
| 874 |
padding-top: 0; |
| |
| 875 |
} |
| |
| |
886 |
.contents h1:not(:is(h1, h2) ~ *)::before { content: none; } |
| 876 |
ol, ul { padding-left: 1em; } |
887 |
ol, ul { padding-left: 1em; } |
| 877 |
/* Notes and references are two different lists, so each is labelled. |
888 |
/* Notes and references are two different lists, so each is labelled. |
| 878 |
The wording lives here rather than in the parser: md2html.awk marks |
889 |
The wording lives here rather than in the parser: md2html.awk marks |
share/jenny/layout/post.sh +17 -6
cat << _EOF_ |
| 682 |
rest. Scoped to .contents because the title itself lives in |
682 |
rest. Scoped to .contents because the title itself lives in |
| 683 |
.heading, outside it. */ |
683 |
.heading, outside it. */ |
| 684 |
.contents :is(h1, h2) { |
684 |
.contents :is(h1, h2) { |
| 685 |
border-top: 1px solid var(--border-light); |
| |
| 686 |
padding-top: 1.5em; |
| |
| |
685 |
position: relative; |
| |
686 |
margin-top: 3em; |
| |
687 |
} |
| |
688 |
/* A short mark rather than a border. border-top spans the whole box, |
| |
689 |
which drew a full-measure line and read as a divider between two |
| |
690 |
halves of the page instead of as a mark belonging to the heading |
| |
691 |
under it. 3ch sits at the width of two or three characters, so it |
| |
692 |
reads as a tick, and 0.6em above keeps it close enough to belong to |
| |
693 |
the heading rather than to float between the two. */ |
| |
694 |
.contents :is(h1, h2)::before { |
| |
695 |
content: ""; |
| |
696 |
position: absolute; |
| |
697 |
top: -0.6em; |
| |
698 |
left: 0; |
| |
699 |
width: 3ch; |
| |
700 |
border-top: 1px solid var(--border-medium); |
| 687 |
} |
701 |
} |
| 688 |
/* The one exception: a page repeats its title as the body's first |
702 |
/* The one exception: a page repeats its title as the body's first |
| 689 |
heading, and an h1 with no heading before it is exactly that repeat. |
703 |
heading, and an h1 with no heading before it is exactly that repeat. |
| 690 |
A rule there draws a line immediately under the title in the header. |
704 |
A rule there draws a line immediately under the title in the header. |
| 691 |
A post has no repeat -- its title is lifted into the header -- so its |
705 |
A post has no repeat -- its title is lifted into the header -- so its |
| 692 |
opening h2 is a real section and keeps its rule. */ |
706 |
opening h2 is a real section and keeps its rule. */ |
| 693 |
.contents h1:not(:is(h1, h2) ~ *) { |
| |
| 694 |
border-top: 0; |
| |
| 695 |
padding-top: 0; |
| |
| 696 |
} |
| |
| |
707 |
.contents h1:not(:is(h1, h2) ~ *)::before { content: none; } |
| 697 |
ol, ul { padding-left: 1em; } |
708 |
ol, ul { padding-left: 1em; } |
| 698 |
/* Notes and references are two different lists, so each is labelled. |
709 |
/* Notes and references are two different lists, so each is labelled. |
| 699 |
The wording lives here rather than in the parser: md2html.awk marks |
710 |
The wording lives here rather than in the parser: md2html.awk marks |
test/regressions.sh +7 -3
it "a section heading is marked by a rule above it" |
| 1376 |
# green -- the mutation looked survivable when it was simply unobserved. |
1376 |
# green -- the mutation looked survivable when it was simply unobserved. |
| 1377 |
assert_file_contains "$dg/.dist/chart.html" '.contents :is(h1, h2) {' |
1377 |
assert_file_contains "$dg/.dist/chart.html" '.contents :is(h1, h2) {' |
| 1378 |
assert_file_contains "$dg/.dist/post/charted.html" '.contents :is(h1, h2) {' |
1378 |
assert_file_contains "$dg/.dist/post/charted.html" '.contents :is(h1, h2) {' |
| 1379 |
assert_file_contains "$dg/.dist/chart.html" 'border-top: 1px solid var(--border-light);' |
| |
| |
1379 |
# A short mark, not a border. border-top spans the whole box, which drew a |
| |
1380 |
# full-measure line reading as a divider between two halves of the page rather |
| |
1381 |
# than as a mark belonging to the heading beneath it. |
| |
1382 |
assert_file_contains "$dg/.dist/chart.html" 'width: 3ch;' |
| |
1383 |
assert_file_contains "$dg/.dist/chart.html" '.contents :is(h1, h2)::before {' |
| 1380 |
|
1384 |
|
| 1381 |
it "the body's repeat of the page title carries no rule" |
1385 |
it "the body's repeat of the page title carries no rule" |
| 1382 |
# A page repeats its title as the body's opening h1, and a rule there draws a |
1386 |
# A page repeats its title as the body's opening h1, and a rule there draws a |
it "the body's repeat of the page title carries no rule" |
| 1386 |
# another heading" fixed pages while breaking posts -- a post's title is lifted |
1390 |
# another heading" fixed pages while breaking posts -- a post's title is lifted |
| 1387 |
# into the header, so its opening h2 lost its rule and the first section read |
1391 |
# into the header, so its opening h2 lost its rule and the first section read |
| 1388 |
# as preamble. Verified in Chrome on both layouts. |
1392 |
# as preamble. Verified in Chrome on both layouts. |
| 1389 |
assert_file_contains "$dg/.dist/chart.html" '.contents h1:not(:is(h1, h2) ~ *) {' |
| |
| 1390 |
assert_file_contains "$dg/.dist/post/charted.html" '.contents h1:not(:is(h1, h2) ~ *) {' |
| |
| |
1393 |
assert_file_contains "$dg/.dist/chart.html" '.contents h1:not(:is(h1, h2) ~ *)::before { content: none; }' |
| |
1394 |
assert_file_contains "$dg/.dist/post/charted.html" '.contents h1:not(:is(h1, h2) ~ *)::before { content: none; }' |
| 1391 |
|
1395 |
|
| 1392 |
it "an unrendered diagram degrades to readable source" |
1396 |
it "an unrendered diagram degrades to readable source" |
| 1393 |
# If the script is absent or blocked, the element still holds what the author |
1397 |
# If the script is absent or blocked, the element still holds what the author |
Raw
commit 9eebc2045ad3536eab94799381d123f6acc8103d
Author: Pat Ambrosio <cp.ambrosio@gmail.com>
Date: Fri Aug 28 14:05:35 2026 +0800
Shorten the section mark to a tick above the heading
A full-measure line reads as a divider between two halves of the page
rather than as a mark belonging to the heading beneath it. Three
characters wide, close enough above to sit with the heading rather than
float between it and the paragraph before.
border-top cannot do this -- it always spans the whole box -- so the mark
is a ::before, and the title-repeat exemption suppresses its content
rather than a border. The space that a 1.5em gap used to provide now
comes from margin-top, which goes to 3em: with the mark tight against the
heading, the separation between sections has to live above the mark
instead of below it.
--border-medium rather than --border-light: at 27px the lighter value
almost disappears. Measured in both themes -- #999 on the dark ground,
#ddd on the light one.
diff --git a/share/jenny/layout/page.sh b/share/jenny/layout/page.sh
index ceabb79..1b5b78e 100755
--- a/share/jenny/layout/page.sh
+++ b/share/jenny/layout/page.sh
@@ -861,18 +861,29 @@ cat << _EOF_
rest. Scoped to .contents because the title itself lives in
.heading, outside it. */
.contents :is(h1, h2) {
- border-top: 1px solid var(--border-light);
- padding-top: 1.5em;
+ position: relative;
+ margin-top: 3em;
+ }
+ /* A short mark rather than a border. border-top spans the whole box,
+ which drew a full-measure line and read as a divider between two
+ halves of the page instead of as a mark belonging to the heading
+ under it. 3ch sits at the width of two or three characters, so it
+ reads as a tick, and 0.6em above keeps it close enough to belong to
+ the heading rather than to float between the two. */
+ .contents :is(h1, h2)::before {
+ content: "";
+ position: absolute;
+ top: -0.6em;
+ left: 0;
+ width: 3ch;
+ border-top: 1px solid var(--border-medium);
}
/* The one exception: a page repeats its title as the body's first
heading, and an h1 with no heading before it is exactly that repeat.
A rule there draws a line immediately under the title in the header.
A post has no repeat -- its title is lifted into the header -- so its
opening h2 is a real section and keeps its rule. */
- .contents h1:not(:is(h1, h2) ~ *) {
- border-top: 0;
- padding-top: 0;
- }
+ .contents h1:not(:is(h1, h2) ~ *)::before { content: none; }
ol, ul { padding-left: 1em; }
/* Notes and references are two different lists, so each is labelled.
The wording lives here rather than in the parser: md2html.awk marks
diff --git a/share/jenny/layout/post.sh b/share/jenny/layout/post.sh
index 6d258b3..ebbda04 100755
--- a/share/jenny/layout/post.sh
+++ b/share/jenny/layout/post.sh
@@ -682,18 +682,29 @@ cat << _EOF_
rest. Scoped to .contents because the title itself lives in
.heading, outside it. */
.contents :is(h1, h2) {
- border-top: 1px solid var(--border-light);
- padding-top: 1.5em;
+ position: relative;
+ margin-top: 3em;
+ }
+ /* A short mark rather than a border. border-top spans the whole box,
+ which drew a full-measure line and read as a divider between two
+ halves of the page instead of as a mark belonging to the heading
+ under it. 3ch sits at the width of two or three characters, so it
+ reads as a tick, and 0.6em above keeps it close enough to belong to
+ the heading rather than to float between the two. */
+ .contents :is(h1, h2)::before {
+ content: "";
+ position: absolute;
+ top: -0.6em;
+ left: 0;
+ width: 3ch;
+ border-top: 1px solid var(--border-medium);
}
/* The one exception: a page repeats its title as the body's first
heading, and an h1 with no heading before it is exactly that repeat.
A rule there draws a line immediately under the title in the header.
A post has no repeat -- its title is lifted into the header -- so its
opening h2 is a real section and keeps its rule. */
- .contents h1:not(:is(h1, h2) ~ *) {
- border-top: 0;
- padding-top: 0;
- }
+ .contents h1:not(:is(h1, h2) ~ *)::before { content: none; }
ol, ul { padding-left: 1em; }
/* Notes and references are two different lists, so each is labelled.
The wording lives here rather than in the parser: md2html.awk marks
diff --git a/test/regressions.sh b/test/regressions.sh
index f90ead3..d929380 100755
--- a/test/regressions.sh
+++ b/test/regressions.sh
@@ -1376,7 +1376,11 @@ it "a section heading is marked by a rule above it"
# green -- the mutation looked survivable when it was simply unobserved.
assert_file_contains "$dg/.dist/chart.html" '.contents :is(h1, h2) {'
assert_file_contains "$dg/.dist/post/charted.html" '.contents :is(h1, h2) {'
-assert_file_contains "$dg/.dist/chart.html" 'border-top: 1px solid var(--border-light);'
+# A short mark, not a border. border-top spans the whole box, which drew a
+# full-measure line reading as a divider between two halves of the page rather
+# than as a mark belonging to the heading beneath it.
+assert_file_contains "$dg/.dist/chart.html" 'width: 3ch;'
+assert_file_contains "$dg/.dist/chart.html" '.contents :is(h1, h2)::before {'
it "the body's repeat of the page title carries no rule"
# A page repeats its title as the body's opening h1, and a rule there draws a
@@ -1386,8 +1390,8 @@ it "the body's repeat of the page title carries no rule"
# another heading" fixed pages while breaking posts -- a post's title is lifted
# into the header, so its opening h2 lost its rule and the first section read
# as preamble. Verified in Chrome on both layouts.
-assert_file_contains "$dg/.dist/chart.html" '.contents h1:not(:is(h1, h2) ~ *) {'
-assert_file_contains "$dg/.dist/post/charted.html" '.contents h1:not(:is(h1, h2) ~ *) {'
+assert_file_contains "$dg/.dist/chart.html" '.contents h1:not(:is(h1, h2) ~ *)::before { content: none; }'
+assert_file_contains "$dg/.dist/post/charted.html" '.contents h1:not(:is(h1, h2) ~ *)::before { content: none; }'
it "an unrendered diagram degrades to readable source"
# If the script is absent or blocked, the element still holds what the author