« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/misc-templates.js27
-rw-r--r--src/page/album-commentary.js24
-rw-r--r--src/page/album.js34
-rw-r--r--src/page/artist.js28
-rw-r--r--src/page/flash.js61
-rw-r--r--src/page/group.js16
-rw-r--r--src/page/homepage.js6
-rw-r--r--src/page/listing.js12
-rw-r--r--src/page/news.js81
-rw-r--r--src/page/static.js14
-rw-r--r--src/page/tag.js7
-rw-r--r--src/page/track.js27
-rw-r--r--src/static/site2.css9
-rwxr-xr-xsrc/upd8.js2
-rw-r--r--src/write/bind-utilities.js16
-rw-r--r--src/write/page-template.js48
16 files changed, 185 insertions, 227 deletions
diff --git a/src/misc-templates.js b/src/misc-templates.js
index 7cfdc86c..9c089978 100644
--- a/src/misc-templates.js
+++ b/src/misc-templates.js
@@ -220,7 +220,7 @@ function unbound_generateChronologyLinks(currentThing, {
 
 // Content warning tags
 
-function unbound_getRevealStringFromWarnings(warnings, {
+function unbound_getRevealStringFromContentWarningMessage(warnings, {
   html,
   language,
 }) {
@@ -232,14 +232,13 @@ function unbound_getRevealStringFromWarnings(warnings, {
   );
 }
 
-function unbound_getRevealStringFromTags(tags, {
+function unbound_getRevealStringFromArtTags(tags, {
+  getRevealStringFromContentWarningMessage,
   language,
-
-  getRevealStringFromWarnings,
 }) {
   return (
     tags?.some(tag => tag.isContentWarning) &&
-      getRevealStringFromWarnings(
+      getRevealStringFromContentWarningMessage(
         language.formatUnitList(
           tags
             .filter(tag => tag.isContentWarning)
@@ -255,7 +254,7 @@ function unbound_generateCoverLink({
   language,
   link,
 
-  getRevealStringFromTags,
+  getRevealStringFromArtTags,
 
   alt,
   path,
@@ -284,7 +283,7 @@ function unbound_generateCoverLink({
       id: 'cover-art',
       link: true,
       square: true,
-      reveal: getRevealStringFromTags(tags),
+      reveal: getRevealStringFromArtTags(tags),
     }),
 
     wikiInfo.enableArtTagUI &&
@@ -572,7 +571,7 @@ function unbound_getGridHTML({
   html,
   language,
 
-  getRevealStringFromTags,
+  getRevealStringFromArtTags,
 
   entries,
   srcFn,
@@ -593,7 +592,7 @@ function unbound_getGridHTML({
             thumb: 'medium',
             lazy: typeof lazy === 'number' ? i >= lazy : lazy,
             square: true,
-            reveal: getRevealStringFromTags(item.artTags, {language}),
+            reveal: getRevealStringFromArtTags(item.artTags, {language}),
             noSrcText: noSrcTextFn(item),
           }),
           html.tag('span', item.name),
@@ -929,14 +928,14 @@ function unbound_generateNavigationLinks(current, {
 // Sticky heading, ooooo
 
 function unbound_generateStickyHeadingContainer({
-  getRevealStringFromTags,
+  getRevealStringFromArtTags,
   html,
   img,
 
   class: classes,
   coverSrc,
   coverAlt,
-  coverTags,
+  coverArtTags,
   title,
 }) {
   return html.tag('div',
@@ -957,7 +956,7 @@ function unbound_generateStickyHeadingContainer({
                 thumb: 'small',
                 link: false,
                 square: true,
-                reveal: getRevealStringFromTags(coverTags),
+                reveal: getRevealStringFromArtTags(coverArtTags),
               }))),
       ]),
 
@@ -1012,8 +1011,8 @@ export {
 
   unbound_generateChronologyLinks as generateChronologyLinks,
 
-  unbound_getRevealStringFromWarnings as getRevealStringFromWarnings,
-  unbound_getRevealStringFromTags as getRevealStringFromTags,
+  unbound_getRevealStringFromContentWarningMessage as getRevealStringFromContentWarningMessage,
+  unbound_getRevealStringFromArtTags as getRevealStringFromArtTags,
 
   unbound_generateCoverLink as generateCoverLink,
 
diff --git a/src/page/album-commentary.js b/src/page/album-commentary.js
index 5ac6cd26..5fd78beb 100644
--- a/src/page/album-commentary.js
+++ b/src/page/album-commentary.js
@@ -24,7 +24,6 @@ export function write(album) {
     type: 'page',
     path: ['albumCommentary', album.directory],
     page: ({
-      generateStickyHeadingContainer,
       getAlbumStylesheet,
       getLinkThemeString,
       getThemeString,
@@ -38,13 +37,10 @@ export function write(album) {
       theme: getThemeString(album.color),
 
       main: {
-        content: html.tag('div', {class: 'long-content'}, [
-          generateStickyHeadingContainer({
-            title: language.$('albumCommentaryPage.title', {
-              album: album.name,
-            }),
-          }),
+        classes: ['long-content'],
+        headingMode: 'sticky',
 
+        content: [
           html.tag('p',
             language.$('albumCommentaryPage.infoLine', {
               words: html.tag('b', language.formatWordCount(words, {unit: true})),
@@ -70,7 +66,7 @@ export function write(album) {
               {style: getLinkThemeString(track.color)},
               transformMultiline(track.commentary)),
           ])
-        ]),
+        ],
       },
 
       nav: generateAlbumExtrasPageNav(album, 'commentary', {
@@ -112,20 +108,24 @@ export function writeTargetless({wikiData}) {
       title: language.$('commentaryIndex.title'),
 
       main: {
-        content: html.tag('div', {class: 'long-content'}, [
-          html.tag('h1', language.$('commentaryIndex.title')),
+        classes: ['long-content'],
+        headingMode: 'static',
+
+        content: [
           html.tag('p', language.$('commentaryIndex.infoLine', {
             words: html.tag('b', language.formatWordCount(totalWords, {unit: true})),
             entries: html.tag('b', language.countCommentaryEntries(totalEntries, {unit: true})),
           })),
+
           html.tag('p', language.$('commentaryIndex.albumList.title')),
+
           html.tag('ul', data.map(({album, entries, words}) =>
             html.tag('li', language.$('commentaryIndex.albumList.item', {
               album: link.albumCommentary(album),
               words: language.formatWordCount(words, {unit: true}),
               entries: language.countCommentaryEntries(entries.length, {unit: true}),
-            }))))
-        ]),
+            })))),
+        ],
       },
 
       nav: {simple: true},
diff --git a/src/page/album.js b/src/page/album.js
index 90f6afa5..7a7f35bc 100644
--- a/src/page/album.js
+++ b/src/page/album.js
@@ -130,9 +130,7 @@ export function write(album, {wikiData}) {
       generateAdditionalFilesShortcut,
       generateAdditionalFilesList,
       generateChronologyLinks,
-      generateCoverLink,
       generateNavigationLinks,
-      generateStickyHeadingContainer,
       getAlbumCover,
       getAlbumStylesheet,
       getArtistString,
@@ -153,8 +151,6 @@ export function write(album, {wikiData}) {
         link,
       });
 
-      const cover = getAlbumCover(album);
-
       return {
         title: language.$('albumPage.title', {album: album.name}),
         stylesheet: getAlbumStylesheet(album),
@@ -197,22 +193,16 @@ export function write(album, {wikiData}) {
           position: 'top',
         },
 
-        main: {
-          content: [
-            generateStickyHeadingContainer({
-              title: language.$('albumPage.title', {album: album.name}),
-
-              coverSrc: cover,
-              coverAlt: language.$('misc.alt.albumCover'),
-              coverTags: album.artTags,
-            }),
+        cover: {
+          src: getAlbumCover(album),
+          alt: language.$('misc.alt.albumCover'),
+          artTags: album.artTags,
+        },
 
-            cover && generateCoverLink({
-              src: cover,
-              alt: language.$('misc.alt.albumCover'),
-              tags: album.artTags,
-            }),
+        main: {
+          headingMode: 'sticky',
 
+          content: [
             html.tag('p',
               {
                 [html.onlyIfContent]: true,
@@ -256,6 +246,7 @@ export function write(album, {wikiData}) {
                     date: language.formatDate(album.date),
                   }),
 
+                album.hasCoverArt &&
                 album.coverArtDate &&
                 +album.coverArtDate !== +album.date &&
                   language.$('releaseInfo.artReleased', {
@@ -436,12 +427,9 @@ export function write(album, {wikiData}) {
 
       main: {
         classes: ['top-index'],
-        content: [
-          html.tag('h1',
-            language.$('albumGalleryPage.title', {
-              album: album.name,
-            })),
+        headingMode: 'static',
 
+        content: [
           html.tag('p',
             {class: 'quick-info'},
             (album.date
diff --git a/src/page/artist.js b/src/page/artist.js
index af0c7c43..87859c89 100644
--- a/src/page/artist.js
+++ b/src/page/artist.js
@@ -321,9 +321,7 @@ export function write(artist, {wikiData}) {
     path: ['artist', artist.directory],
     page: ({
       fancifyURL,
-      generateCoverLink,
       generateInfoGalleryLinks,
-      generateStickyHeadingContainer,
       getArtistAvatar,
       getArtistString,
       html,
@@ -341,20 +339,15 @@ export function write(artist, {wikiData}) {
       return {
         title: language.$('artistPage.title', {artist: name}),
 
-        main: {
-          content: [
-            artist.hasAvatar &&
-              generateCoverLink({
-                src: getArtistAvatar(artist),
-                alt: language.$('misc.alt.artistAvatar'),
-              }),
+        cover: artist.hasAvatar && {
+          src: getArtistAvatar(artist),
+          alt: language.$('misc.alt.artistAvatar'),
+        },
 
-            generateStickyHeadingContainer({
-              title: language.$('artistPage.title', {
-                artist: name,
-              }),
-            }),
+        main: {
+          headingMode: 'sticky',
 
+          content: [
             ...html.fragment(
               contextNotes && [
                 html.tag('p',
@@ -610,12 +603,9 @@ export function write(artist, {wikiData}) {
 
       main: {
         classes: ['top-index'],
-        content: [
-          html.tag('h1',
-            language.$('artistGalleryPage.title', {
-              artist: name,
-            })),
+        headingMode: 'static',
 
+        content: [
           html.tag('p',
             {class: 'quick-info'},
             language.$('artistGalleryPage.infoLine', {
diff --git a/src/page/flash.js b/src/page/flash.js
index 8f2506fc..a9ce053d 100644
--- a/src/page/flash.js
+++ b/src/page/flash.js
@@ -20,9 +20,7 @@ export function write(flash, {wikiData}) {
     page: ({
       fancifyFlashURL,
       generateChronologyLinks,
-      generateCoverLink,
       generateNavigationLinks,
-      generateStickyHeadingContainer,
       getArtistString,
       getFlashCover,
       getThemeString,
@@ -40,19 +38,15 @@ export function write(flash, {wikiData}) {
           ],
         }),
 
-      main: {
-        content: [
-          generateStickyHeadingContainer({
-            title: language.$('flashPage.title', {
-              flash: flash.name,
-            }),
-          }),
+      cover: {
+        src: getFlashCover(flash),
+        alt: language.$('misc.alt.flashArt'),
+      },
 
-          generateCoverLink({
-            src: getFlashCover(flash),
-            alt: language.$('misc.alt.flashArt'),
-          }),
+      main: {
+        headingMode: 'sticky',
 
+        content: [
           html.tag('p',
             language.$('releaseInfo.released', {
               date: language.formatDate(flash.date),
@@ -146,30 +140,25 @@ export function writeTargetless({
 
       main: {
         classes: ['flash-index'],
-        content: [
-          html.tag('h1',
-            language.$('flashIndex.title')),
+        headingMode: 'static',
 
-          html.tag('div',
-            {class: 'long-content'},
-            [
-              html.tag('p',
-                {class: 'quick-info'},
-                language.$('misc.jumpTo')),
-
-              html.tag('ul',
-                {class: 'quick-info'},
-                flashActData
-                  .filter(act => act.jump)
-                  .map(({anchor, jump, jumpColor}) =>
-                    html.tag('li',
-                      html.tag('a',
-                        {
-                          href: '#' + anchor,
-                          style: getLinkThemeString(jumpColor),
-                        },
-                        jump)))),
-            ]),
+        content: [
+          html.tag('p',
+            {class: 'quick-info'},
+            language.$('misc.jumpTo')),
+
+          html.tag('ul',
+            {class: 'quick-info'},
+            flashActData
+              .filter(act => act.jump)
+              .map(({anchor, jump, jumpColor}) =>
+                html.tag('li',
+                  html.tag('a',
+                    {
+                      href: '#' + anchor,
+                      style: getLinkThemeString(jumpColor),
+                    },
+                    jump)))),
 
           ...flashActData.flatMap((act, i) => [
             html.tag('h2',
diff --git a/src/page/group.js b/src/page/group.js
index 54a8358e..37ebb789 100644
--- a/src/page/group.js
+++ b/src/page/group.js
@@ -34,7 +34,6 @@ export function write(group, {wikiData}) {
       fancifyURL,
       generateInfoGalleryLinks,
       generateNavigationLinks,
-      generateStickyHeadingContainer,
       getLinkThemeString,
       getThemeString,
       html,
@@ -48,13 +47,9 @@ export function write(group, {wikiData}) {
       theme: getThemeString(group.color),
 
       main: {
-        content: [
-          generateStickyHeadingContainer({
-            title: language.$('groupInfoPage.title', {
-              group: group.name
-            }),
-          }),
+        headingMode: 'sticky',
 
+        content: [
           !empty(group.urls) &&
             html.tag('p',
               language.$('releaseInfo.visitOn', {
@@ -147,12 +142,9 @@ export function write(group, {wikiData}) {
 
       main: {
         classes: ['top-index'],
-        content: [
-          html.tag('h1',
-            language.$('groupGalleryPage.title', {
-              group: group.name,
-            })),
+        headingMode: 'static',
 
+        content: [
           getCarouselHTML({
             items: group.featuredAlbums.slice(0, 12 + 1),
             srcFn: getAlbumCover,
diff --git a/src/page/homepage.js b/src/page/homepage.js
index 9722f105..465152aa 100644
--- a/src/page/homepage.js
+++ b/src/page/homepage.js
@@ -84,7 +84,9 @@ export function writeTargetless({wikiData}) {
 
       main: {
         classes: ['top-index'],
-        content: html.fragment([
+        headingMode: 'none',
+
+        content: [
           html.tag('h1',
             wikiInfo.name),
 
@@ -136,7 +138,7 @@ export function writeTargetless({wikiData}) {
                           })),
                     ]),
                 ]))),
-        ]),
+        ],
       },
 
       sidebarLeft: homepageLayout.sidebarContent && {
diff --git a/src/page/listing.js b/src/page/listing.js
index 37f4d155..2412efe6 100644
--- a/src/page/listing.js
+++ b/src/page/listing.js
@@ -34,7 +34,6 @@ export function write(listing, {wikiData}) {
     path: ['listing', listing.directory],
     page: (opts) => {
       const {
-        generateStickyHeadingContainer,
         getLinkThemeString,
         html,
         language,
@@ -47,11 +46,9 @@ export function write(listing, {wikiData}) {
         title: language.$(titleKey),
 
         main: {
-          content: [
-            generateStickyHeadingContainer({
-              title: language.$(titleKey),
-            }),
+          headingMode: 'sticky',
 
+          content: [
             ...html.fragment(
               listing.html &&
                 (listing.data
@@ -111,10 +108,9 @@ export function writeTargetless({wikiData}) {
       title: language.$('listingIndex.title'),
 
       main: {
-        content: [
-          html.tag('h1',
-            language.$('listingIndex.title')),
+        headingMode: 'static',
 
+        content: [
           html.tag('p',
             language.$('listingIndex.infoLine', {
               wiki: wikiInfo.name,
diff --git a/src/page/news.js b/src/page/news.js
index e042e8ae..00d1e4dc 100644
--- a/src/page/news.js
+++ b/src/page/news.js
@@ -16,7 +16,6 @@ export function write(entry, {wikiData}) {
     path: ['newsEntry', entry.directory],
     page: ({
       generateNavigationLinks,
-      generateStickyHeadingContainer,
       html,
       language,
       link,
@@ -25,22 +24,16 @@ export function write(entry, {wikiData}) {
       title: language.$('newsEntryPage.title', {entry: entry.name}),
 
       main: {
+        classes: ['long-content'],
+        headingMode: 'sticky',
+
         content: [
-          generateStickyHeadingContainer({
-            class: ['long-content'],
-            title: language.$('newsEntryPage.title', {
-              entry: entry.name,
-            }),
-          }),
-
-          html.tag('div', {class: 'long-content'}, [
-            html.tag('p',
-              language.$('newsEntryPage.published', {
-                date: language.formatDate(entry.date),
-              })),
-
-            transformMultiline(entry.content),
-          ]),
+          html.tag('p',
+            language.$('newsEntryPage.published', {
+              date: language.formatDate(entry.date),
+            })),
+
+          transformMultiline(entry.content),
         ],
       },
 
@@ -64,7 +57,6 @@ export function writeTargetless({wikiData}) {
     type: 'page',
     path: ['newsIndex'],
     page: ({
-      generateStickyHeadingContainer,
       html,
       language,
       link,
@@ -73,37 +65,30 @@ export function writeTargetless({wikiData}) {
       title: language.$('newsIndex.title'),
 
       main: {
-        content: [
-          generateStickyHeadingContainer({
-            class: ['long-content'],
-            title: language.$('newsIndex.title'),
-          }),
-
-          html.tag('div',
-            {class: ['long-content', 'news-index']},
-            [
-              ...newsData.map(entry =>
-                html.tag('article',
-                  {id: entry.directory},
-                  [
-                    html.tag('h2', [
-                      html.tag('time',
-                        language.formatDate(entry.date)),
-                      link.newsEntry(entry),
-                    ]),
-
-                    transformMultiline(entry.contentShort),
-
-                    entry.contentShort !== entry.content &&
-                      html.tag('p',
-                        link.newsEntry(entry, {
-                          text: language.$(
-                            'newsIndex.entry.viewRest'
-                          ),
-                        })),
-                  ])),
-            ]),
-        ],
+        classes: ['long-content', 'news-index'],
+        headingMode: 'sticky',
+
+        content:
+          newsData.map(entry =>
+            html.tag('article',
+              {id: entry.directory},
+              [
+                html.tag('h2', [
+                  html.tag('time',
+                    language.formatDate(entry.date)),
+                  link.newsEntry(entry),
+                ]),
+
+                transformMultiline(entry.contentShort),
+
+                entry.contentShort !== entry.content &&
+                  html.tag('p',
+                    link.newsEntry(entry, {
+                      text: language.$(
+                        'newsIndex.entry.viewRest'
+                      ),
+                    })),
+              ])),
       },
 
       nav: {simple: true},
diff --git a/src/page/static.js b/src/page/static.js
index 2da71b74..8572db4e 100644
--- a/src/page/static.js
+++ b/src/page/static.js
@@ -13,22 +13,16 @@ export function write(staticPage) {
     type: 'page',
     path: ['staticPage', staticPage.directory],
     page: ({
-      generateStickyHeadingContainer,
-      html,
       transformMultiline,
     }) => ({
       title: staticPage.name,
       stylesheet: staticPage.stylesheet,
 
       main: {
-        content: [
-          generateStickyHeadingContainer({
-            class: ['long-content'],
-            title: staticPage.name,
-          }),
-          html.tag('div', {class: 'long-content'},
-            transformMultiline(staticPage.content)),
-        ],
+        classes: ['long-content'],
+        headingMode: 'sticky',
+
+        content: transformMultiline(staticPage.content),
       },
 
       nav: {simple: true},
diff --git a/src/page/tag.js b/src/page/tag.js
index ee62038e..81db6137 100644
--- a/src/page/tag.js
+++ b/src/page/tag.js
@@ -36,12 +36,9 @@ export function write(tag, {wikiData}) {
 
       main: {
         classes: ['top-index'],
-        content: [
-          html.tag('h1',
-            language.$('tagPage.title', {
-              tag: tag.name,
-            })),
+        headingMode: 'static',
 
+        content: [
           html.tag('p',
             {class: 'quick-info'},
             language.$('tagPage.infoLine', {
diff --git a/src/page/track.js b/src/page/track.js
index 495bf42d..caba3668 100644
--- a/src/page/track.js
+++ b/src/page/track.js
@@ -162,9 +162,7 @@ export function write(track, {wikiData}) {
       absoluteTo,
       fancifyURL,
       generateChronologyLinks,
-      generateCoverLink,
       generateNavigationLinks,
-      generateStickyHeadingContainer,
       generateTrackListDividedByGroups,
       getAlbumStylesheet,
       getArtistString,
@@ -185,7 +183,6 @@ export function write(track, {wikiData}) {
         language,
         link,
       });
-      const cover = getTrackCover(track);
 
       return {
         title: language.$('trackPage.title', {track: track.name}),
@@ -224,22 +221,16 @@ export function write(track, {wikiData}) {
         },
         */
 
-        main: {
-          content: [
-            generateStickyHeadingContainer({
-              title: language.$('trackPage.title', {track: track.name}),
-
-              coverSrc: cover,
-              coverAlt: language.$('misc.alt.trackCover'),
-              coverTags: track.artTags,
-            }),
+        cover: {
+          src: getTrackCover(track),
+          alt: language.$('misc.alt.trackCover'),
+          artTags: track.artTags,
+        },
 
-            cover && generateCoverLink({
-              src: cover,
-              alt: language.$('misc.alt.trackCover'),
-              tags: track.artTags,
-            }),
+        main: {
+          headingMode: 'sticky',
 
+          content: [
             html.tag('p',
               {
                 [html.onlyIfContent]: true,
@@ -267,7 +258,7 @@ export function write(track, {wikiData}) {
                     date: language.formatDate(track.date),
                   }),
 
-                cover &&
+                track.hasCoverArt &&
                 track.coverArtDate &&
                 +track.coverArtDate !== +track.date &&
                   language.$('releaseInfo.artReleased', {
diff --git a/src/static/site2.css b/src/static/site2.css
index 287bbd66..cc853b65 100644
--- a/src/static/site2.css
+++ b/src/static/site2.css
@@ -626,7 +626,8 @@ blockquote {
   margin-right: 0;
 }
 
-.long-content {
+main.long-content .main-content-container,
+main.long-content > h1 {
   padding-left: 12%;
   padding-right: 12%;
 }
@@ -1149,13 +1150,13 @@ html[data-url-key="localized.home"] .carousel-container {
   transform: translateY(-5px);
 }
 
-.content-sticky-heading-container.long-content {
+main.long-content .content-sticky-heading-container {
   padding-left: 0;
   padding-right: 0;
 }
 
-.content-sticky-heading-container.long-content .content-sticky-heading-row,
-.content-sticky-heading-container.long-content .content-sticky-subheading-row {
+main.long-content .content-sticky-heading-container .content-sticky-heading-row,
+main.long-content .content-sticky-heading-container .content-sticky-subheading-row {
   padding-left: calc(0.12 * (100% - 2 * var(--content-padding)) + var(--content-padding));
   padding-right: calc(0.12 * (100% - 2 * var(--content-padding)) + var(--content-padding));
 }
diff --git a/src/upd8.js b/src/upd8.js
index 3bcdf884..39372833 100755
--- a/src/upd8.js
+++ b/src/upd8.js
@@ -81,7 +81,7 @@ import FileSizePreloader from './file-size-preloader.js';
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
-const CACHEBUST = 17;
+const CACHEBUST = 18;
 
 let COMMIT;
 try {
diff --git a/src/write/bind-utilities.js b/src/write/bind-utilities.js
index 6212b824..127afe2c 100644
--- a/src/write/bind-utilities.js
+++ b/src/write/bind-utilities.js
@@ -13,8 +13,8 @@ import {
   getCarouselHTML,
   getFlashGridHTML,
   getGridHTML,
-  getRevealStringFromTags,
-  getRevealStringFromWarnings,
+  getRevealStringFromArtTags,
+  getRevealStringFromContentWarningMessage,
   getThemeString,
   generateAdditionalFilesList,
   generateAdditionalFilesShortcut,
@@ -142,15 +142,15 @@ export function bindUtilities({
     fancifyURL: bound.fancifyURL,
   });
 
-  bound.getRevealStringFromWarnings = bindOpts(getRevealStringFromWarnings, {
+  bound.getRevealStringFromContentWarningMessage = bindOpts(getRevealStringFromContentWarningMessage, {
     html,
     language,
   });
 
-  bound.getRevealStringFromTags = bindOpts(getRevealStringFromTags, {
+  bound.getRevealStringFromArtTags = bindOpts(getRevealStringFromArtTags, {
     language,
 
-    getRevealStringFromWarnings: bound.getRevealStringFromWarnings,
+    getRevealStringFromContentWarningMessage: bound.getRevealStringFromContentWarningMessage,
   });
 
   bound.getArtistString = bindOpts(getArtistString, {
@@ -194,7 +194,7 @@ export function bindUtilities({
 
   bound.generateStickyHeadingContainer = bindOpts(generateStickyHeadingContainer, {
     [bindOpts.bindIndex]: 0,
-    getRevealStringFromTags: bound.getRevealStringFromTags,
+    getRevealStringFromArtTags: bound.getRevealStringFromArtTags,
     html,
     img: bound.img,
   });
@@ -217,7 +217,7 @@ export function bindUtilities({
     to,
     wikiData,
 
-    getRevealStringFromTags: bound.getRevealStringFromTags,
+    getRevealStringFromArtTags: bound.getRevealStringFromArtTags,
   });
 
   bound.generateInfoGalleryLinks = bindOpts(generateInfoGalleryLinks, {
@@ -238,7 +238,7 @@ export function bindUtilities({
     html,
     language,
 
-    getRevealStringFromTags: bound.getRevealStringFromTags,
+    getRevealStringFromArtTags: bound.getRevealStringFromArtTags,
   });
 
   bound.getAlbumGridHTML = bindOpts(getAlbumGridHTML, {
diff --git a/src/write/page-template.js b/src/write/page-template.js
index 6ed9fcf5..e4bd9774 100644
--- a/src/write/page-template.js
+++ b/src/write/page-template.js
@@ -5,7 +5,7 @@ import {getColors} from '../util/colors.js';
 
 import {
   getFooterLocalizationLinks,
-  getRevealStringFromWarnings,
+  getRevealStringFromContentWarningMessage,
   img,
 } from '../misc-templates.js';
 
@@ -49,6 +49,8 @@ export function generateDocumentHTML(pageInfo, {
   cachebust,
   defaultLanguage,
   developersComment,
+  generateCoverLink,
+  generateStickyHeadingContainer,
   getThemeString,
   language,
   languages,
@@ -74,6 +76,7 @@ export function generateDocumentHTML(pageInfo, {
     // missing properties are auto-filled, see below!
     body = {},
     banner = {},
+    cover = {},
     main = {},
     sidebarLeft = {},
     sidebarRight = {},
@@ -95,6 +98,11 @@ export function generateDocumentHTML(pageInfo, {
 
   main.classes ??= [];
   main.content ??= '';
+  main.headingMode ??= 'none';
+
+  cover.src ??= '';
+  cover.alt ??= '';
+  cover.artTags ??= [];
 
   sidebarLeft ??= {};
   sidebarRight ??= {};
@@ -139,13 +147,39 @@ export function generateDocumentHTML(pageInfo, {
     sidebarLeft.collapse !== false && sidebarRight.collapse !== false;
 
   const mainHTML =
-    main.content &&
-      html.tag('main',
+    html.tag('main', {
+      id: 'content',
+      class: main.classes,
+    }, [
+      ...html.fragment(
+          !title ?
+            null
+        : main.headingMode === 'sticky' ?
+            generateStickyHeadingContainer({
+              coverSrc: cover.src,
+              coverAlt: cover.alt,
+              coverArtTags: cover.artTags,
+              title,
+            })
+        : main.headingMode === 'static' ?
+            html.tag('h1', title)
+        : null),
+
+      ...html.fragment(
+        cover.src &&
+          generateCoverLink({
+            src: cover.src,
+            alt: cover.alt,
+            tags: cover.artTags,
+          })),
+
+      html.tag('div',
         {
-          id: 'content',
-          class: main.classes,
+          [html.onlyIfContent]: true,
+          class: 'main-content-container',
         },
-        main.content);
+        main.content),
+    ]);
 
   const footerHTML =
     html.tag('footer',
@@ -378,7 +412,7 @@ export function generateDocumentHTML(pageInfo, {
             src: '',
             link: true,
             square: true,
-            reveal: getRevealStringFromWarnings(
+            reveal: getRevealStringFromContentWarningMessage(
               html.tag('span', {class: 'info-card-art-warnings'}),
               {html, language}),
           })),