Commit b84fa9d h
Changes: 2 files +20 -63
Changes (Side-by-Side)
share/jenny/layout/post.sh +7 -30
render_footer_nav() { | |||
| 90 | printf '</footer>\n' |
90 | printf '</footer>\n' |
| 91 | } |
91 | } |
| 92 | |
92 | |
| 93 | # post_crumb |
||
| 94 | # The post's own segment of the breadcrumb, matching what a page renders: an |
||
| 95 | # arrow, then the slug with dashes read as spaces. |
||
| 96 | # |
||
| 97 | # The slug rather than the title because the breadcrumb is a picture of the |
||
| 98 | # URL -- it says where you are, and the address is what the reader can act on. |
||
| 99 | # A long title would also wrap the header, which the trail is meant to survive. |
||
| 100 | # |
||
| 101 | # Marked current the way a page's last segment is, so the same rule underlines |
||
| 102 | # it and the two headers behave identically. |
||
| 103 | post_crumb() { |
||
| 104 | [ -z "${IS_PAGE:-}" ] || return 0 |
||
| 105 | local url=${POST_URL:-} slug |
||
| 106 | [ -n "$url" ] || return 0 |
||
| 107 | slug=${url##*/} |
||
| 108 | # Under URL_STYLE=html the URL carries the extension, which is part of the |
||
| 109 | # address but not of the post's name -- without this the crumb read |
||
| 110 | # "hello world.html". |
||
| 111 | slug=${slug%.html} |
||
| 112 | [ -n "$slug" ] || return 0 |
||
| 113 | # Built from URL_PREFIX rather than POST_URL, which is site-absolute: the |
||
| 114 | # site title beside it is relative, and a header that mixes the two breaks |
||
| 115 | # the moment the site is served from a subdirectory. |
||
| 116 | # The href keeps the URL's own ending; only the label drops it. |
||
| 117 | printf ' →︎ <a href="%s/post/%s" class="current">%s</a>' \ |
||
| 118 | "$URL_PREFIX" "$(html_escape "${url##*/}")" \ |
||
| 119 | "$(html_escape "$(printf '%s' "$slug" | tr '-' ' ')")" |
||
| 120 | } |
||
| 121 | |
||
| 122 | html_escape() { |
93 | html_escape() { |
| 123 | local s=$1 |
94 | local s=$1 |
| 124 | # The replacement text must escape &, because bash >= 5.2 expands a bare & |
95 | # The replacement text must escape &, because bash >= 5.2 expands a bare & |
cat << _EOF_ | |||
| 607 | </head> |
578 | </head> |
| 608 | <body> |
579 | <body> |
| 609 | <a href="#" class="theme-toggle" aria-label="Toggle dark mode">☼︎</a> |
580 | <a href="#" class="theme-toggle" aria-label="Toggle dark mode">☼︎</a> |
| 610 | <header><a href="$URL_PREFIX/"$([ -n "${IS_PAGE:-}" ] && echo ' class="current"')>$(html_escape "${BLOG_TITLE:-}")</a>$(post_crumb)</header> |
||
| 581 | <!-- No breadcrumb on a post. Its own segment would be the slug, which |
||
| 582 | usually restates the title sitting directly beneath it -- and making |
||
| 583 | that read well would mean choosing titles that differ from their |
||
| 584 | slugs, which is the layout dictating what a post may be called. A |
||
| 585 | page's trail earns its place because the path has segments the title |
||
| 586 | does not repeat. --> |
||
| 587 | <header><a href="$URL_PREFIX/">$(html_escape "${BLOG_TITLE:-}")</a></header> |
||
| 611 | <article> |
588 | <article> |
| 612 | <div class="heading$([ -n "${IS_PAGE:-}" ] && echo ' heading-page')"><a href="${POST_URL:-}">$([ -z "${IS_PAGE:-}" ] && printf '<span class="stamp" data-iso="%s"><span class="stamp-slide"><span class="stamp-abs">%s</span><span class="stamp-rel" aria-hidden="true"></span></span></span>' "$(html_escape "${POST_DATE_RFC822:-}")" "$("$DATE" -d "${POST_DATE_RFC822:-}" +%m/%d/%Y 2>/dev/null || printf '%s' "${POST_DATE:-}")")<h1 class="title">$(html_escape "${POST_TITLE:-}")</h1></a></div> |
589 | <div class="heading$([ -n "${IS_PAGE:-}" ] && echo ' heading-page')"><a href="${POST_URL:-}">$([ -z "${IS_PAGE:-}" ] && printf '<span class="stamp" data-iso="%s"><span class="stamp-slide"><span class="stamp-abs">%s</span><span class="stamp-rel" aria-hidden="true"></span></span></span>' "$(html_escape "${POST_DATE_RFC822:-}")" "$("$DATE" -d "${POST_DATE_RFC822:-}" +%m/%d/%Y 2>/dev/null || printf '%s' "${POST_DATE:-}")")<h1 class="title">$(html_escape "${POST_TITLE:-}")</h1></a></div> |
| 613 | <div class="body-row"> |
590 | <div class="body-row"> |
test/regressions.sh +13 -33
assert_file_contains "$tl_post" '.citations ol { padding-left: 1.4em; margin-top | |||
| 913 | it "the build is clean" |
913 | it "the build is clean" |
| 914 | assert_eq "$stderr_output" "" |
914 | assert_eq "$stderr_output" "" |
| 915 | |
915 | |
| 916 | suite "a post names itself in the breadcrumb" |
||
| 916 | suite "a post has no breadcrumb" |
||
| 917 | |
917 | |
| 918 | # A page's header shows the trail to where you are; a post's showed only the |
||
| 919 | # site title, so the two headers disagreed about what a header is for. |
||
| 918 | # A post's own segment would be its slug, which usually restates the title |
||
| 919 | # directly beneath it. Making that read well would mean choosing titles that |
||
| 920 | # differ from their slugs -- the layout dictating what a post may be called. |
||
| 921 | # A page's trail earns its place because the path has segments the title does |
||
| 922 | # not repeat. |
||
| 920 | |
923 | |
| 921 | bc="$TMPROOT/breadcrumb" |
924 | bc="$TMPROOT/breadcrumb" |
| 922 | make_blog_min "$bc" 1 10 |
925 | make_blog_min "$bc" 1 10 |
| 926 | mkdir -p "$bc/pages" |
||
| 927 | printf '# Plain\n\nBody.\n' > "$bc/pages/plain.md" |
||
| 923 | run_jenny_split "$bc" |
928 | run_jenny_split "$bc" |
| 924 | bc_post=$(find "$bc/.dist/post" -name '*.html' | head -1) |
929 | bc_post=$(find "$bc/.dist/post" -name '*.html' | head -1) |
| 925 | |
930 | |
| 926 | it "the post's own segment follows the site title" |
||
| 927 | assert_file_contains "$bc_post" '→︎ <a href=' |
||
| 928 | |
||
| 929 | it "the segment reads the slug with dashes as spaces" |
||
| 930 | # The slug rather than the title: the breadcrumb is a picture of the URL, it |
||
| 931 | # says where you are, and a long title would wrap the header. |
||
| 932 | assert_file_contains "$bc_post" 'class="current">post 01</a>' |
||
| 933 | |
||
| 934 | it "the segment is marked current, like a page's last one" |
||
| 935 | # So the same rule underlines it and the two headers behave identically. |
||
| 936 | assert_file_contains "$bc_post" 'class="current">' |
||
| 937 | |
||
| 938 | it "the crumb link is relative, like the site title beside it" |
||
| 939 | # POST_URL is site-absolute; a header mixing the two breaks the moment the |
||
| 940 | # site is served from a subdirectory. Scoped to the header: the heading's own |
||
| 941 | # link is absolute and was so before this, which is a separate question. |
||
| 931 | it "a post's header is the site title alone" |
||
| 942 | bc_header=$("$GREP" -o '<header>.*</header>' "$bc_post" | head -1) |
932 | bc_header=$("$GREP" -o '<header>.*</header>' "$bc_post" | head -1) |
| 943 | assert_contains "$bc_header" 'href="../post/' |
||
| 944 | assert_not_contains "$bc_header" 'href="/post/' |
||
| 933 | assert_not_contains "$bc_header" 'class="current"' |
||
| 934 | assert_not_contains "$bc_header" '→︎' |
||
| 945 | |
935 | |
| 946 | it "the label drops the extension the href keeps" |
||
| 947 | # Under URL_STYLE=html the URL carries .html, which is part of the address but |
||
| 948 | # not of the post's name -- the crumb read "hello world.html". |
||
| 949 | printf 'URL_STYLE=html\n' >> "$bc/.blogrc" |
||
| 950 | run_jenny_split "$bc" |
||
| 951 | assert_file_contains "$bc_post" 'class="current">post 01</a>' |
||
| 952 | assert_file_contains "$bc_post" 'href="../post/post-01.html"' |
||
| 953 | |
||
| 954 | it "a page's breadcrumb is unchanged" |
||
| 955 | mkdir -p "$bc/pages" |
||
| 956 | printf '# Plain\n\nBody.\n' > "$bc/pages/plain.md" |
||
| 957 | run_jenny_split "$bc" |
||
| 936 | it "a page still has its trail" |
||
| 937 | # The two headers differ on purpose, not by omission. |
||
| 958 | assert_file_contains "$bc/.dist/plain.html" 'class="current">plain</a>' |
938 | assert_file_contains "$bc/.dist/plain.html" 'class="current">plain</a>' |
| 959 | |
939 | |
| 960 | it "the build is clean" |
940 | it "the build is clean" |
Raw
commit b84fa9d9f3753e9038ff864afc8a7fd973495478
Author: Pat Ambrosio <cp.ambrosio@gmail.com>
Date: Wed Aug 26 15:31:31 2026 +0800
Drop the post breadcrumb
Added one commit ago and wrong. A post's own segment is its slug, which
usually restates the title sitting directly beneath it -- the header read
"DEAD KEY -> ON PLAIN TEXT" above a title saying "On Plain Text".
Worse than the redundancy: making it read well would mean choosing titles
that differ from their slugs, so the layout would be dictating what a post
may be called. A theme should not constrain the writing that way.
A page keeps its trail, which earns its place: the path has segments --
study, notes/method -- that the title does not repeat, and a page has no
date line to identify it either.
The tests become the rule they replaced: a post's header is the site title
alone, and a page's still has its trail, so the two differ on purpose
rather than by omission. Checked by mutation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnSAstgnDYdKPwLGv1YZE5
diff --git a/share/jenny/layout/post.sh b/share/jenny/layout/post.sh
index f22979c..396c355 100755
--- a/share/jenny/layout/post.sh
+++ b/share/jenny/layout/post.sh
@@ -90,35 +90,6 @@ render_footer_nav() {
printf '</footer>\n'
}
-# post_crumb
-# The post's own segment of the breadcrumb, matching what a page renders: an
-# arrow, then the slug with dashes read as spaces.
-#
-# The slug rather than the title because the breadcrumb is a picture of the
-# URL -- it says where you are, and the address is what the reader can act on.
-# A long title would also wrap the header, which the trail is meant to survive.
-#
-# Marked current the way a page's last segment is, so the same rule underlines
-# it and the two headers behave identically.
-post_crumb() {
- [ -z "${IS_PAGE:-}" ] || return 0
- local url=${POST_URL:-} slug
- [ -n "$url" ] || return 0
- slug=${url##*/}
- # Under URL_STYLE=html the URL carries the extension, which is part of the
- # address but not of the post's name -- without this the crumb read
- # "hello world.html".
- slug=${slug%.html}
- [ -n "$slug" ] || return 0
- # Built from URL_PREFIX rather than POST_URL, which is site-absolute: the
- # site title beside it is relative, and a header that mixes the two breaks
- # the moment the site is served from a subdirectory.
- # The href keeps the URL's own ending; only the label drops it.
- printf ' →︎ <a href="%s/post/%s" class="current">%s</a>' \
- "$URL_PREFIX" "$(html_escape "${url##*/}")" \
- "$(html_escape "$(printf '%s' "$slug" | tr '-' ' ')")"
-}
-
html_escape() {
local s=$1
# The replacement text must escape &, because bash >= 5.2 expands a bare &
@@ -607,7 +578,13 @@ cat << _EOF_
</head>
<body>
<a href="#" class="theme-toggle" aria-label="Toggle dark mode">☼︎</a>
- <header><a href="$URL_PREFIX/"$([ -n "${IS_PAGE:-}" ] && echo ' class="current"')>$(html_escape "${BLOG_TITLE:-}")</a>$(post_crumb)</header>
+ <!-- No breadcrumb on a post. Its own segment would be the slug, which
+ usually restates the title sitting directly beneath it -- and making
+ that read well would mean choosing titles that differ from their
+ slugs, which is the layout dictating what a post may be called. A
+ page's trail earns its place because the path has segments the title
+ does not repeat. -->
+ <header><a href="$URL_PREFIX/">$(html_escape "${BLOG_TITLE:-}")</a></header>
<article>
<div class="heading$([ -n "${IS_PAGE:-}" ] && echo ' heading-page')"><a href="${POST_URL:-}">$([ -z "${IS_PAGE:-}" ] && printf '<span class="stamp" data-iso="%s"><span class="stamp-slide"><span class="stamp-abs">%s</span><span class="stamp-rel" aria-hidden="true"></span></span></span>' "$(html_escape "${POST_DATE_RFC822:-}")" "$("$DATE" -d "${POST_DATE_RFC822:-}" +%m/%d/%Y 2>/dev/null || printf '%s' "${POST_DATE:-}")")<h1 class="title">$(html_escape "${POST_TITLE:-}")</h1></a></div>
<div class="body-row">
diff --git a/test/regressions.sh b/test/regressions.sh
index 31d059e..98cc3ab 100755
--- a/test/regressions.sh
+++ b/test/regressions.sh
@@ -913,48 +913,28 @@ assert_file_contains "$tl_post" '.citations ol { padding-left: 1.4em; margin-top
it "the build is clean"
assert_eq "$stderr_output" ""
-suite "a post names itself in the breadcrumb"
+suite "a post has no breadcrumb"
-# A page's header shows the trail to where you are; a post's showed only the
-# site title, so the two headers disagreed about what a header is for.
+# A post's own segment would be its slug, which usually restates the title
+# directly beneath it. Making that read well would mean choosing titles that
+# differ from their slugs -- the layout dictating what a post may be called.
+# A page's trail earns its place because the path has segments the title does
+# not repeat.
bc="$TMPROOT/breadcrumb"
make_blog_min "$bc" 1 10
+mkdir -p "$bc/pages"
+printf '# Plain\n\nBody.\n' > "$bc/pages/plain.md"
run_jenny_split "$bc"
bc_post=$(find "$bc/.dist/post" -name '*.html' | head -1)
-it "the post's own segment follows the site title"
-assert_file_contains "$bc_post" '→︎ <a href='
-
-it "the segment reads the slug with dashes as spaces"
-# The slug rather than the title: the breadcrumb is a picture of the URL, it
-# says where you are, and a long title would wrap the header.
-assert_file_contains "$bc_post" 'class="current">post 01</a>'
-
-it "the segment is marked current, like a page's last one"
-# So the same rule underlines it and the two headers behave identically.
-assert_file_contains "$bc_post" 'class="current">'
-
-it "the crumb link is relative, like the site title beside it"
-# POST_URL is site-absolute; a header mixing the two breaks the moment the
-# site is served from a subdirectory. Scoped to the header: the heading's own
-# link is absolute and was so before this, which is a separate question.
+it "a post's header is the site title alone"
bc_header=$("$GREP" -o '<header>.*</header>' "$bc_post" | head -1)
-assert_contains "$bc_header" 'href="../post/'
-assert_not_contains "$bc_header" 'href="/post/'
+assert_not_contains "$bc_header" 'class="current"'
+assert_not_contains "$bc_header" '→︎'
-it "the label drops the extension the href keeps"
-# Under URL_STYLE=html the URL carries .html, which is part of the address but
-# not of the post's name -- the crumb read "hello world.html".
-printf 'URL_STYLE=html\n' >> "$bc/.blogrc"
-run_jenny_split "$bc"
-assert_file_contains "$bc_post" 'class="current">post 01</a>'
-assert_file_contains "$bc_post" 'href="../post/post-01.html"'
-
-it "a page's breadcrumb is unchanged"
-mkdir -p "$bc/pages"
-printf '# Plain\n\nBody.\n' > "$bc/pages/plain.md"
-run_jenny_split "$bc"
+it "a page still has its trail"
+# The two headers differ on purpose, not by omission.
assert_file_contains "$bc/.dist/plain.html" 'class="current">plain</a>'
it "the build is clean"