« 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/content/dependencies/generateArtTagGalleryPage.js78
-rw-r--r--src/content/dependencies/generateArtTagNavLinks.js80
-rw-r--r--src/content/dependencies/generateCoverArtwork.js14
-rw-r--r--src/content/dependencies/image.js4
-rw-r--r--src/content/dependencies/linkArtTag.js2
-rw-r--r--src/content/dependencies/linkArtTagGallery.js8
-rw-r--r--src/content/dependencies/listTagsByName.js15
-rw-r--r--src/content/dependencies/listTagsByUses.js49
-rw-r--r--src/content/dependencies/transformContent.js9
-rw-r--r--src/data/composite/things/art-tag/index.js2
-rw-r--r--src/data/composite/things/art-tag/withAllDescendantArtTags.js (renamed from src/data/composite/things/art-tag/withAllDescendantTags.js)20
-rw-r--r--src/data/things/art-tag.js24
-rw-r--r--src/data/yaml.js4
-rw-r--r--src/find.js8
-rw-r--r--src/page/art-tag.js (renamed from src/page/tag.js)4
-rw-r--r--src/page/index.js2
-rw-r--r--src/strings-default.json10
-rw-r--r--src/url-spec.js5
-rw-r--r--src/util/wiki-data.js2
-rw-r--r--test/snapshot/linkThing.js2
20 files changed, 214 insertions, 128 deletions
diff --git a/src/content/dependencies/generateArtTagGalleryPage.js b/src/content/dependencies/generateArtTagGalleryPage.js
index c37fe710..fcdf917b 100644
--- a/src/content/dependencies/generateArtTagGalleryPage.js
+++ b/src/content/dependencies/generateArtTagGalleryPage.js
@@ -3,12 +3,14 @@ import {sortAlbumsTracksChronologically} from '#wiki-data';
 
 export default {
   contentDependencies: [
+    'generateArtTagNavLinks',
     'generateCoverGrid',
     'generatePageLayout',
     'generateQuickDescription',
     'image',
     'linkAlbum',
     'linkArtTag',
+    'linkArtTagGallery',
     'linkTrack',
   ],
 
@@ -20,9 +22,9 @@ export default {
     };
   },
 
-  query(sprawl, tag) {
-    const directThings = tag.directlyTaggedInThings;
-    const indirectThings = tag.indirectlyTaggedInThings;
+  query(sprawl, artTag) {
+    const directThings = artTag.directlyTaggedInThings;
+    const indirectThings = artTag.indirectlyTaggedInThings;
     const allThings = unique([...directThings, ...indirectThings]);
 
     sortAlbumsTracksChronologically(allThings, {
@@ -33,57 +35,57 @@ export default {
     return {directThings, indirectThings, allThings};
   },
 
-  relations(relation, query, sprawl, tag) {
+  relations(relation, query, sprawl, artTag) {
     const relations = {};
 
     relations.layout =
       relation('generatePageLayout');
 
-    relations.artTagMainLink =
-      relation('linkArtTag', tag);
+    relations.navLinks =
+      relation('generateArtTagNavLinks', artTag);
 
-    // TODO: linkArtTagInfo
     relations.infoPageLink =
-      relation('linkArtTag', tag);
+      relation('linkArtTag', artTag);
 
     relations.quickDescription =
-      relation('generateQuickDescription', tag);
+      relation('generateQuickDescription', artTag);
 
-    if (!empty(tag.directAncestorTags)) {
+    if (!empty(artTag.directAncestorArtTags)) {
       relations.ancestorLinks =
-        tag.directAncestorTags.map(tag =>
-          relation('linkArtTag', tag));
+        artTag.directAncestorArtTags
+          .map(artTag => relation('linkArtTagGallery', artTag));
     }
 
-    if (!empty(tag.directDescendantTags)) {
+    if (!empty(artTag.directDescendantArtTags)) {
       relations.descendantLinks =
-        tag.directDescendantTags.map(tag =>
-          relation('linkArtTag', tag));
+        artTag.directDescendantArtTags
+          .map(artTag => relation('linkArtTagGallery', artTag));
     }
 
     relations.coverGrid =
       relation('generateCoverGrid');
 
     relations.links =
-      query.allThings.map(thing =>
-        (thing.album
-          ? relation('linkTrack', thing)
-          : relation('linkAlbum', thing)));
+      query.allThings
+        .map(thing =>
+          (thing.album
+            ? relation('linkTrack', thing)
+            : relation('linkAlbum', thing)));
 
     relations.images =
-      query.allThings.map(thing =>
-        relation('image', thing.artTags));
+      query.allThings
+        .map(thing => relation('image', thing.artTags));
 
     return relations;
   },
 
-  data(query, sprawl, tag) {
+  data(query, sprawl, artTag) {
     const data = {};
 
     data.enableListings = sprawl.enableListings;
 
-    data.name = tag.name;
-    data.color = tag.color;
+    data.name = artTag.name;
+    data.color = artTag.color;
 
     data.numArtworks = query.allThings.length;
 
@@ -107,7 +109,7 @@ export default {
     return relations.layout
       .slots({
         title:
-          language.$('tagPage.title', {
+          language.$('artTagGalleryPage.title', {
             tag: data.name,
           }),
 
@@ -121,7 +123,7 @@ export default {
             .slot('infoPageLink', relations.infoPageLink),
 
           html.tag('p', {class: 'quick-info'},
-            language.$('tagPage.infoLine', {
+            language.$('artTagGalleryPage.infoLine', {
               coverArts: language.countArtworks(data.numArtworks, {
                 unit: true,
               }),
@@ -129,13 +131,13 @@ export default {
 
           relations.ancestorLinks &&
             html.tag('p', {class: 'quick-info'},
-              language.$('tagPage.descendsFrom', {
+              language.$('artTagGalleryPage.descendsFrom', {
                 tags: language.formatConjunctionList(relations.ancestorLinks),
               })),
 
           relations.descendantLinks &&
             html.tag('p', {clasS: 'quick-info'},
-              language.$('tagPage.desendants', {
+              language.$('artTagGalleryPage.desendants', {
                 tags: language.formatUnitList(relations.descendantLinks),
               })),
 
@@ -159,22 +161,10 @@ export default {
         ],
 
         navLinkStyle: 'hierarchical',
-        navLinks: [
-          {auto: 'home'},
-
-          data.enableListings &&
-            {
-              path: ['localized.listingIndex'],
-              title: language.$('listingIndex.title'),
-            },
-
-          {
-            html:
-              language.$('tagPage.nav.tag', {
-                tag: relations.artTagMainLink,
-              }),
-          },
-        ],
+        navLinks:
+          relations.navLinks
+            .slot('currentExtra', 'gallery')
+            .content,
       });
   },
 };
diff --git a/src/content/dependencies/generateArtTagNavLinks.js b/src/content/dependencies/generateArtTagNavLinks.js
new file mode 100644
index 00000000..368b36fe
--- /dev/null
+++ b/src/content/dependencies/generateArtTagNavLinks.js
@@ -0,0 +1,80 @@
+import {empty} from '#sugar';
+
+export default {
+  contentDependencies: [
+    'linkArtTag',
+    'linkArtTagGallery',
+  ],
+
+  extraDependencies: ['html', 'language', 'wikiData'],
+
+  sprawl: ({wikiInfo}) =>
+    ({enableListings: wikiInfo.enableListings}),
+
+  relations: (relation, sprawl, tag) => ({
+    mainLink:
+      relation('linkArtTag', tag),
+
+    infoLink:
+      relation('linkArtTag', tag),
+
+    galleryLink:
+      relation('linkArtTagGallery', tag),
+  }),
+
+  data: (sprawl) =>
+    ({enableListings: sprawl.enableListings}),
+
+  slots: {
+    currentExtra: {
+      validate: v => v.is('gallery'),
+    },
+  },
+
+  generate(data, relations, slots, {language}) {
+    if (!data.enableListings) {
+      return [
+        {auto: 'home'},
+        {auto: 'current'},
+      ];
+    }
+
+    const infoLink =
+      relations.infoLink.slots({
+        attributes: {class: slots.currentExtra === null && 'current'},
+        content: language.$('misc.nav.info'),
+      });
+
+    const extraLinks = [
+      relations.galleryLink?.slots({
+        attributes: {class: slots.currentExtra === 'gallery' && 'current'},
+        content: language.$('misc.nav.gallery'),
+      }),
+    ];
+
+    const extrasPart =
+      (empty(extraLinks)
+        ? ''
+        : language.formatUnitList([infoLink, ...extraLinks]));
+
+    const accent = `(${extrasPart})`;
+
+    return [
+      {auto: 'home'},
+
+      data.enableListings &&
+        {
+          path: ['localized.listingIndex'],
+          title: language.$('listingIndex.title'),
+        },
+
+      {
+        accent,
+        html:
+          language.$('artTagPage.nav.tag', {
+            tag: relations.mainLink,
+          }),
+      },
+    ].filter(Boolean);
+  },
+};
diff --git a/src/content/dependencies/generateCoverArtwork.js b/src/content/dependencies/generateCoverArtwork.js
index 4060c6b0..392d8c25 100644
--- a/src/content/dependencies/generateCoverArtwork.js
+++ b/src/content/dependencies/generateCoverArtwork.js
@@ -1,7 +1,7 @@
 import {empty} from '#sugar';
 
 export default {
-  contentDependencies: ['image', 'linkArtTag'],
+  contentDependencies: ['image', 'linkArtTagGallery'],
   extraDependencies: ['html', 'language'],
 
   relations(relation, artTags) {
@@ -11,12 +11,12 @@ export default {
       relation('image', artTags);
 
     if (artTags) {
-      relations.tagLinks =
+      relations.artTagLinks =
         artTags
-          .filter(tag => !tag.isContentWarning)
-          .map(tag => relation('linkArtTag', tag));
+          .filter(artTag => !artTag.isContentWarning)
+          .map(artTag => relation('linkArtTagGallery', artTag));
     } else {
-      relations.tagLinks = null;
+      relations.artTagLinks = null;
     }
 
     return relations;
@@ -52,12 +52,12 @@ export default {
               square: true,
             }),
 
-          !empty(relations.tagLinks) &&
+          !empty(relations.artTagLinks) &&
             html.tag('p',
               language.$('releaseInfo.artTags.inline', {
                 tags:
                   language.formatUnitList(
-                    relations.tagLinks
+                    relations.artTagLinks
                       .map(tagLink => tagLink.slot('preferShortName', true))),
               })),
           ]);
diff --git a/src/content/dependencies/image.js b/src/content/dependencies/image.js
index 6c0aeecd..006be156 100644
--- a/src/content/dependencies/image.js
+++ b/src/content/dependencies/image.js
@@ -20,8 +20,8 @@ export default {
     if (artTags) {
       data.contentWarnings =
         artTags
-          .filter(tag => tag.isContentWarning)
-          .map(tag => tag.name);
+          .filter(artTag => artTag.isContentWarning)
+          .map(artTag => artTag.name);
     } else {
       data.contentWarnings = null;
     }
diff --git a/src/content/dependencies/linkArtTag.js b/src/content/dependencies/linkArtTag.js
index 7ddb7786..409cb3c0 100644
--- a/src/content/dependencies/linkArtTag.js
+++ b/src/content/dependencies/linkArtTag.js
@@ -2,7 +2,7 @@ export default {
   contentDependencies: ['linkThing'],
 
   relations: (relation, artTag) =>
-    ({link: relation('linkThing', 'localized.tag', artTag)}),
+    ({link: relation('linkThing', 'localized.artTagInfo', artTag)}),
 
   generate: (relations) => relations.link,
 };
diff --git a/src/content/dependencies/linkArtTagGallery.js b/src/content/dependencies/linkArtTagGallery.js
new file mode 100644
index 00000000..a92b69c1
--- /dev/null
+++ b/src/content/dependencies/linkArtTagGallery.js
@@ -0,0 +1,8 @@
+export default {
+  contentDependencies: ['linkThing'],
+
+  relations: (relation, artTag) =>
+    ({link: relation('linkThing', 'localized.artTagGallery', artTag)}),
+
+  generate: (relations) => relations.link,
+};
diff --git a/src/content/dependencies/listTagsByName.js b/src/content/dependencies/listTagsByName.js
index 8571ccd0..9bec9eaa 100644
--- a/src/content/dependencies/listTagsByName.js
+++ b/src/content/dependencies/listTagsByName.js
@@ -1,8 +1,8 @@
-import {stitchArrays} from '#sugar';
+import {stitchArrays, unique} from '#sugar';
 import {sortAlphabetically} from '#wiki-data';
 
 export default {
-  contentDependencies: ['generateListingPage', 'linkArtTag'],
+  contentDependencies: ['generateListingPage', 'linkArtTagGallery'],
   extraDependencies: ['language', 'wikiData'],
 
   sprawl({artTagData}) {
@@ -16,7 +16,7 @@ export default {
       artTags:
         sortAlphabetically(
           artTagData
-            .filter(tag => !tag.isContentWarning)),
+            .filter(artTag => !artTag.isContentWarning)),
     };
   },
 
@@ -26,15 +26,18 @@ export default {
 
       artTagLinks:
         query.artTags
-          .map(tag => relation('linkArtTag', tag)),
+          .map(artTag => relation('linkArtTagGallery', artTag)),
     };
   },
 
   data(query) {
     return {
       counts:
-        query.artTags
-          .map(tag => tag.taggedInThings.length),
+        query.artTags.map(artTag =>
+          unique([
+            ...artTag.indirectlyTaggedInThings,
+            ...artTag.directlyTaggedInThings,
+          ]).length),
     };
   },
 
diff --git a/src/content/dependencies/listTagsByUses.js b/src/content/dependencies/listTagsByUses.js
index 98a50b89..9eb6f185 100644
--- a/src/content/dependencies/listTagsByUses.js
+++ b/src/content/dependencies/listTagsByUses.js
@@ -1,23 +1,25 @@
-import {stitchArrays} from '#sugar';
+import {stitchArrays, unique} from '#sugar';
 import {filterByCount, sortAlphabetically, sortByCount} from '#wiki-data';
 
 export default {
-  contentDependencies: ['generateListingPage', 'linkArtTag'],
+  contentDependencies: ['generateListingPage', 'linkArtTagGallery'],
   extraDependencies: ['language', 'wikiData'],
 
-  sprawl({artTagData}) {
-    return {artTagData};
-  },
+  sprawl: ({artTagData}) =>
+    ({artTagData}),
 
   query({artTagData}, spec) {
     const artTags =
       sortAlphabetically(
         artTagData
-          .filter(tag => !tag.isContentWarning));
+          .filter(artTag => !artTag.isContentWarning));
 
     const counts =
-      artTags
-        .map(tag => tag.taggedInThings.length);
+      artTags.map(artTag =>
+        unique([
+          ...artTag.directlyTaggedInThings,
+          ...artTag.indirectlyTaggedInThings,
+        ]).length);
 
     filterByCount(artTags, counts);
     sortByCount(artTags, counts, {greatestFirst: true});
@@ -25,26 +27,20 @@ export default {
     return {spec, artTags, counts};
   },
 
-  relations(relation, query) {
-    return {
-      page: relation('generateListingPage', query.spec),
+  relations: (relation, query) => ({
+    page:
+      relation('generateListingPage', query.spec),
 
-      artTagLinks:
-        query.artTags
-          .map(tag => relation('linkArtTag', tag)),
-    };
-  },
+    artTagLinks:
+      query.artTags
+        .map(artTag => relation('linkArtTagGallery', artTag)),
+  }),
 
-  data(query) {
-    return {
-      counts:
-        query.artTags
-          .map(tag => tag.taggedInThings.length),
-    };
-  },
+  data: (query) =>
+    ({counts: query.counts}),
 
-  generate(data, relations, {language}) {
-    return relations.page.slots({
+  generate: (data, relations, {language}) =>
+    relations.page.slots({
       type: 'rows',
       rows:
         stitchArrays({
@@ -54,6 +50,5 @@ export default {
             tag: link,
             timesUsed: language.countTimesUsed(count, {unit: true}),
           })),
-    });
-  },
+    }),
 };
diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js
index 9a5ac456..087520ce 100644
--- a/src/content/dependencies/transformContent.js
+++ b/src/content/dependencies/transformContent.js
@@ -104,7 +104,11 @@ export const replacerSpec = {
   },
   tag: {
     find: 'artTag',
-    link: 'tag',
+    link: 'artTag',
+  },
+  'tag-info': {
+    find: 'artTag',
+    link: 'artTagInfo',
   },
   track: {
     find: 'track',
@@ -118,13 +122,14 @@ const linkThingRelationMap = {
   albumGallery: 'linkAlbumGallery',
   artist: 'linkArtist',
   artistGallery: 'linkArtistGallery',
+  artTag: 'linkArtTagGallery',
+  artTagInfo: 'linkArtTag',
   flash: 'linkFlash',
   groupInfo: 'linkGroup',
   groupGallery: 'linkGroupGallery',
   listing: 'linkListing',
   newsEntry: 'linkNewsEntry',
   staticPage: 'linkStaticPage',
-  tag: 'linkArtTag',
   track: 'linkTrack',
 };
 
diff --git a/src/data/composite/things/art-tag/index.js b/src/data/composite/things/art-tag/index.js
index aedc3a0c..0c365ce2 100644
--- a/src/data/composite/things/art-tag/index.js
+++ b/src/data/composite/things/art-tag/index.js
@@ -1 +1 @@
-export {default as withAllDescendantTags} from './withAllDescendantTags.js';
+export {default as withAllDescendantArtTags} from './withAllDescendantArtTags.js';
diff --git a/src/data/composite/things/art-tag/withAllDescendantTags.js b/src/data/composite/things/art-tag/withAllDescendantArtTags.js
index b832e529..c643cf23 100644
--- a/src/data/composite/things/art-tag/withAllDescendantTags.js
+++ b/src/data/composite/things/art-tag/withAllDescendantArtTags.js
@@ -1,4 +1,4 @@
-// Gets all the tags which descend from this one - that means its own direct
+// Gets all the art tags which descend from this one - that means its own direct
 // descendants, but also all the direct and indirect desceands of each of those!
 // The results aren't specially sorted, but they won't contain any duplicates
 // (for example if two descendant tags both route deeper to end up including
@@ -12,19 +12,19 @@ import {raiseOutputWithoutDependency} from '#composite/control-flow';
 import {withResolvedReferenceList} from '#composite/wiki-data';
 
 export default templateCompositeFrom({
-  annotation: `withAllDescendantTags`,
+  annotation: `withAllDescendantArtTags`,
 
-  outputs: ['#allDescendantTags'],
+  outputs: ['#allDescendantArtTags'],
 
   steps: () => [
     raiseOutputWithoutDependency({
-      dependency: 'directDescendantTags',
+      dependency: 'directDescendantArtTags',
       mode: input.value('empty'),
-      output: input.value({'#allDescendantTags': []})
+      output: input.value({'#allDescendantArtTags': []})
     }),
 
     withResolvedReferenceList({
-      list: 'directDescendantTags',
+      list: 'directDescendantArtTags',
       data: 'artTagData',
       find: input.value(find.artTag),
     }),
@@ -32,12 +32,12 @@ export default templateCompositeFrom({
     {
       dependencies: ['#resolvedReferenceList'],
       compute: (continuation, {
-        ['#resolvedReferenceList']: directDescendantTags,
+        ['#resolvedReferenceList']: directDescendantArtTags,
       }) => continuation({
-        ['#allDescendantTags']:
+        ['#allDescendantArtTags']:
           unique([
-            ...directDescendantTags,
-            ...directDescendantTags.flatMap(tag => tag.allDescendantTags),
+            ...directDescendantArtTags,
+            ...directDescendantArtTags.flatMap(artTag => artTag.allDescendantArtTags),
           ]),
       }),
     },
diff --git a/src/data/things/art-tag.js b/src/data/things/art-tag.js
index d86800de..50526691 100644
--- a/src/data/things/art-tag.js
+++ b/src/data/things/art-tag.js
@@ -18,7 +18,7 @@ import {
   wikiData,
 } from '#composite/wiki-properties';
 
-import {withAllDescendantTags} from '#composite/things/art-tag';
+import {withAllDescendantArtTags} from '#composite/things/art-tag';
 
 import Thing from './thing.js';
 
@@ -47,7 +47,7 @@ export class ArtTag extends Thing {
 
     description: simpleString(),
 
-    directDescendantTags: referenceList({
+    directDescendantArtTags: referenceList({
       class: input.value(ArtTag),
       find: input.value(find.artTag),
       data: 'artTagData',
@@ -88,23 +88,25 @@ export class ArtTag extends Thing {
     },
 
     indirectlyTaggedInThings: [
-      withAllDescendantTags(),
+      withAllDescendantArtTags(),
 
       {
-        dependencies: ['#allDescendantTags'],
-        compute: ({'#allDescendantTags': allDescendantTags}) =>
-          unique(allDescendantTags.flatMap(tag => tag.directlyTaggedInThings)),
+        dependencies: ['#allDescendantArtTags'],
+        compute: ({'#allDescendantArtTags': allDescendantArtTags}) =>
+          unique(
+            allDescendantArtTags
+              .flatMap(artTag => artTag.directlyTaggedInThings)),
       },
     ],
 
-    allDescendantTags: [
-      withAllDescendantTags(),
-      exposeDependency({dependency: '#allDescendantTags'}),
+    allDescendantArtTags: [
+      withAllDescendantArtTags(),
+      exposeDependency({dependency: '#allDescendantArtTags'}),
     ],
 
-    directAncestorTags: reverseReferenceList({
+    directAncestorArtTags: reverseReferenceList({
       data: 'artTagData',
-      list: input.value('directDescendantTags'),
+      list: input.value('directDescendantArtTags'),
     }),
   });
 }
diff --git a/src/data/yaml.js b/src/data/yaml.js
index c8916fb8..61a9308d 100644
--- a/src/data/yaml.js
+++ b/src/data/yaml.js
@@ -464,7 +464,7 @@ export const processArtTagDocument = makeProcessDocument(T.ArtTag, {
     color: 'Color',
     isContentWarning: 'Is CW',
 
-    directDescendantTags: 'Direct Descendant Tags',
+    directDescendantArtTags: 'Direct Descendant Tags',
   },
 });
 
@@ -1481,7 +1481,7 @@ export function filterReferenceErrors(wikiData) {
     }],
 
     ['artTagData', processArtTagDocument, {
-      directDescendantTags: 'artTag',
+      directDescendantArtTags: 'artTag',
     }],
 
     ['trackData', processTrackDocument, {
diff --git a/src/find.js b/src/find.js
index 716aca5e..141dd599 100644
--- a/src/find.js
+++ b/src/find.js
@@ -134,10 +134,10 @@ const find = {
   artTag: findHelper({
     referenceTypes: ['tag'],
 
-    getMatchableNames: tag =>
-      (tag.isContentWarning
-        ? [`cw: ${tag.name}`]
-        : [tag.name]),
+    getMatchableNames: artTag =>
+      (artTag.isContentWarning
+        ? [`cw: ${artTag.name}`]
+        : [artTag.name]),
   }),
 
   flash: findHelper({
diff --git a/src/page/tag.js b/src/page/art-tag.js
index 8942aea9..32e869e1 100644
--- a/src/page/tag.js
+++ b/src/page/art-tag.js
@@ -1,6 +1,6 @@
 // Art tag page specification.
 
-export const description = `per-artwork-tag gallery pages`;
+export const description = `per-art-tag gallery pages`;
 
 export function condition({wikiData}) {
   return wikiData.wikiInfo.enableArtTagUI;
@@ -14,7 +14,7 @@ export function pathsForTarget(tag) {
   return [
     {
       type: 'page',
-      path: ['tag', tag.directory],
+      path: ['artTagGallery', tag.directory],
 
       contentFunction: {
         name: 'generateArtTagGalleryPage',
diff --git a/src/page/index.js b/src/page/index.js
index 48e22d2e..557384a6 100644
--- a/src/page/index.js
+++ b/src/page/index.js
@@ -1,11 +1,11 @@
 export * as album from './album.js';
 export * as artist from './artist.js';
 export * as artistAlias from './artist-alias.js';
+export * as artTag from './art-tag.js';
 export * as flash from './flash.js';
 export * as group from './group.js';
 export * as homepage from './homepage.js';
 export * as listing from './listing.js';
 export * as news from './news.js';
 export * as static from './static.js';
-export * as tag from './tag.js';
 export * as track from './track.js';
diff --git a/src/strings-default.json b/src/strings-default.json
index a12753b0..3d662657 100644
--- a/src/strings-default.json
+++ b/src/strings-default.json
@@ -317,6 +317,11 @@
   "artistPage.nav.artist": "Artist: {ARTIST}",
   "artistGalleryPage.title": "{ARTIST} - Gallery",
   "artistGalleryPage.infoLine": "Contributed to {COVER_ARTS}.",
+  "artTagPage.nav.tag": "Tag: {TAG}",
+  "artTagGalleryPage.title": "{TAG}",
+  "artTagGalleryPage.infoLine": "Appears in {COVER_ARTS}.",
+  "artTagGalleryPage.descendsFrom": "Descends from {TAGS}.",
+  "artTagGalleryPage.desendants": "Direct descendants: {TAGS}.",
   "commentaryIndex.title": "Commentary",
   "commentaryIndex.infoLine": "{WORDS} across {ENTRIES}, in all.",
   "commentaryIndex.albumList.title": "Choose an album:",
@@ -497,11 +502,6 @@
   "newsEntryPage.published": "(Published {DATE}.)",
   "redirectPage.title": "Moved to {TITLE}",
   "redirectPage.infoLine": "This page has been moved to {TARGET}.",
-  "tagPage.title": "{TAG}",
-  "tagPage.infoLine": "Appears in {COVER_ARTS}.",
-  "tagPage.descendsFrom": "Descends from {TAGS}.",
-  "tagPage.desendants": "Direct descendants: {TAGS}.",
-  "tagPage.nav.tag": "Tag: {TAG}",
   "trackPage.title": "{TRACK}",
   "trackPage.referenceList.fandom": "Fandom:",
   "trackPage.referenceList.official": "Official:",
diff --git a/src/url-spec.js b/src/url-spec.js
index 4d103134..e4c0c65b 100644
--- a/src/url-spec.js
+++ b/src/url-spec.js
@@ -47,7 +47,10 @@ const urlSpec = {
       newsEntry: 'news/<>/',
 
       staticPage: '<>/',
-      tag: 'tag/<>/',
+
+      artTagInfo: 'tag/<>/info/',
+      artTagGallery: 'tag/<>/',
+
       track: 'track/<>/',
     },
   },
diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js
index ac652b27..fecf94e1 100644
--- a/src/util/wiki-data.js
+++ b/src/util/wiki-data.js
@@ -871,7 +871,7 @@ export function filterItemsForCarousel(items) {
 
   return items
     .filter(item => item.hasCoverArt)
-    .filter(item => item.artTags.every(tag => !tag.isContentWarning))
+    .filter(item => item.artTags.every(artTag => !artTag.isContentWarning))
     .slice(0, maxCarouselLayoutItems + 1);
 }
 
diff --git a/test/snapshot/linkThing.js b/test/snapshot/linkThing.js
index 195d8c0e..a55b8586 100644
--- a/test/snapshot/linkThing.js
+++ b/test/snapshot/linkThing.js
@@ -20,7 +20,7 @@ testContentFunctions(t, 'linkThing (snapshot)', async (t, evaluate) => {
   });
 
   quickSnapshot('preferShortName', {
-    args: ['localized.tag', {
+    args: ['localized.artTagGallery', {
       directory: 'five-oceanfalls',
       name: 'Five (Oceanfalls)',
       nameShort: 'Five',