ssh git@grit.deadkey.org man

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