From d9c1bfa6c11cd1f623c330f07130c3130f5ef91f Mon Sep 17 00:00:00 2001
From: Mat Jordan <mat@northwestern.edu>
Date: Fri, 8 Nov 2024 09:10:34 -0500
Subject: [PATCH] Add common IIIF share component.

---
 components/Clover/ViewerWrapper.styled.ts     |  10 +
 components/Clover/ViewerWrapper.tsx           |  39 ++--
 components/Collection/Collection.styled.ts    |  28 ++-
 components/Collection/NavTabs.styled.ts       |   2 +-
 components/Figure/Figure.styled.ts            |   1 +
 components/Heading/Heading.styled.ts          |   3 +-
 components/Hero/Hero.styled.ts                |   9 +-
 components/Hero/Hero.tsx                      |   2 +-
 components/Homepage/Collections.styled.ts     |   3 -
 components/Homepage/Overview.styled.ts        |  17 +-
 components/Search/Results.tsx                 |  16 +-
 components/Search/Search.styled.ts            |  10 +-
 components/Shared/CopyText.styled.ts          |  17 +-
 components/Shared/DefinitionList.styled.ts    |   6 +-
 components/Shared/Expand/Expand.styled.ts     |   6 +-
 components/Shared/Expand/Expand.tsx           |   7 +-
 components/Shared/IIIF/Share.test.tsx         |  56 +++++
 components/Shared/IIIF/Share.tsx              | 204 ++++++++++++++++++
 components/Shared/IIIF/ViewerLink.test.tsx    |  36 ++++
 components/Shared/IIIF/ViewerLink.tsx         |  26 +++
 components/Shared/Icon.tsx                    |  31 ++-
 components/Shared/SVG/Icons.tsx               |   8 +
 .../Shared/WorkCount/WorkCount.styled.ts      |   1 -
 .../DownloadAndShare/IIIFManifest.tsx         |  23 +-
 components/Work/TopInfo.styled.ts             |  50 +++--
 components/Work/TopInfo.tsx                   |  20 +-
 hooks/useCopyToClipboard.ts                   |  24 ++-
 lib/dc-api.test.ts                            |  46 ++++
 lib/dc-api.ts                                 |  29 +++
 mocks/sample-collection1.ts                   |   2 +
 package-lock.json                             |   8 +-
 package.json                                  |   2 +-
 pages/collections/[id].tsx                    |  19 +-
 33 files changed, 654 insertions(+), 107 deletions(-)
 create mode 100644 components/Shared/IIIF/Share.test.tsx
 create mode 100644 components/Shared/IIIF/Share.tsx
 create mode 100644 components/Shared/IIIF/ViewerLink.test.tsx
 create mode 100644 components/Shared/IIIF/ViewerLink.tsx
 create mode 100644 lib/dc-api.test.ts

diff --git a/components/Clover/ViewerWrapper.styled.ts b/components/Clover/ViewerWrapper.styled.ts
index 2d580346..f1d1c560 100644
--- a/components/Clover/ViewerWrapper.styled.ts
+++ b/components/Clover/ViewerWrapper.styled.ts
@@ -10,6 +10,16 @@ const ViewerWrapperStyled = styled("section", {
     background: "#f0f0f0",
   },
 
+  ".clover-viewer-header": {
+    display: "none",
+  },
+
+  ".clover-viewer-media-wrapper": {
+    "div[role='radiogroup']": {
+      paddingBottom: "0",
+    },
+  },
+
   "& label[for='information-toggle']": {
     boxShadow: "none",
   },
diff --git a/components/Clover/ViewerWrapper.tsx b/components/Clover/ViewerWrapper.tsx
index 24076213..4a6cf573 100644
--- a/components/Clover/ViewerWrapper.tsx
+++ b/components/Clover/ViewerWrapper.tsx
@@ -8,6 +8,7 @@ import type {
 } from "@samvera/clover-iiif";
 
 import Announcement from "@/components/Shared/Announcement";
+import Container from "../Shared/Container";
 import { IconInfo } from "@/components/Shared/SVG/Icons";
 import React from "react";
 import { UserContext } from "@/context/user-context";
@@ -76,24 +77,26 @@ const WorkViewerWrapper: React.FC<WrapperProps> = ({
   };
 
   return (
-    <ViewerWrapperStyled data-testid="work-viewer-wrapper">
-      {manifestId && (
-        <CloverViewer
-          // @ts-ignore
-          customTheme={customTheme}
-          iiifContent={manifestId}
-          options={options}
-        />
-      )}
-      {isWorkRestricted && userAuth?.user?.isReadingRoom && (
-        <Announcement>
-          <AnnouncementContent>
-            <IconInfo />
-            <p>You have access to Work because you are in the reading room</p>
-          </AnnouncementContent>
-        </Announcement>
-      )}
-    </ViewerWrapperStyled>
+    <Container containerType="wide">
+      <ViewerWrapperStyled data-testid="work-viewer-wrapper">
+        {manifestId && (
+          <CloverViewer
+            // @ts-ignore
+            customTheme={customTheme}
+            iiifContent={manifestId}
+            options={options}
+          />
+        )}
+        {isWorkRestricted && userAuth?.user?.isReadingRoom && (
+          <Announcement>
+            <AnnouncementContent>
+              <IconInfo />
+              <p>You have access to Work because you are in the reading room</p>
+            </AnnouncementContent>
+          </Announcement>
+        )}
+      </ViewerWrapperStyled>
+    </Container>
   );
 };
 
diff --git a/components/Collection/Collection.styled.ts b/components/Collection/Collection.styled.ts
index 9423ef76..0328651b 100644
--- a/components/Collection/Collection.styled.ts
+++ b/components/Collection/Collection.styled.ts
@@ -2,12 +2,32 @@ import { styled } from "@/stitches.config";
 
 /* eslint sort-keys: 0 */
 
+const CollectionHeader = styled("div", {
+  display: "flex",
+  justifyContent: "space-between",
+  marginTop: "1.5em",
+  gap: "$gr4",
+
+  "div:last-child": {
+    flexShrink: 0,
+    flexGrow: 0,
+    marginBottom: "$gr2",
+  },
+
+  "@sm": {
+    flexDirection: "column",
+    gap: "0",
+  },
+});
+
 const Description = styled("p", {
-  fontSize: "$gr5",
-  fontFamily: "$northwesternSansLight",
   lineHeight: "1.55em",
-  margin: "$gr2 0 $gr5",
+  margin: "$gr2 0 $gr6",
   color: "$black50",
+
+  "@sm": {
+    fontSize: "$gr3",
+  },
 });
 
 const HeroWrapper = styled("div", {
@@ -22,4 +42,4 @@ const Interstitial = styled("div", {
   padding: "$gr3 0",
 });
 
-export { Description, Interstitial, HeroWrapper };
+export { CollectionHeader, Description, Interstitial, HeroWrapper };
diff --git a/components/Collection/NavTabs.styled.ts b/components/Collection/NavTabs.styled.ts
index 941ee844..eac53e3d 100644
--- a/components/Collection/NavTabs.styled.ts
+++ b/components/Collection/NavTabs.styled.ts
@@ -1,4 +1,5 @@
 import * as TabsPrimitive from "@radix-ui/react-tabs";
+
 import { styled } from "@/stitches.config";
 
 /* eslint sort-keys: 0 */
@@ -63,7 +64,6 @@ const StyledContent = styled(TabsPrimitive.Content, {
   borderBottomLeftRadius: 6,
   borderBottomRightRadius: 6,
   outline: "none",
-  "&:focus": { boxShadow: `0 0 0 2px #f0f0f0` },
 
   "& a": {
     color: "$purple",
diff --git a/components/Figure/Figure.styled.ts b/components/Figure/Figure.styled.ts
index 1d2e8c54..7c11f9fd 100644
--- a/components/Figure/Figure.styled.ts
+++ b/components/Figure/Figure.styled.ts
@@ -93,6 +93,7 @@ const FigureTitle = styled("span", {
   color: "$purple",
   display: "flex",
   alignItems: "flex-start",
+  lineHeight: "1.25em",
 });
 
 const FigureText = styled("div", {
diff --git a/components/Heading/Heading.styled.ts b/components/Heading/Heading.styled.ts
index 2cac8d43..123e4b7c 100644
--- a/components/Heading/Heading.styled.ts
+++ b/components/Heading/Heading.styled.ts
@@ -35,9 +35,10 @@ const StyledHeading = styled("h2", {
   },
 
   "&[data-level=h2]": {
-    color: "$purple",
     fontFamily: "$northwesternDisplayBold",
+    color: "$black80",
     fontSize: "$gr7",
+    letterSpacing: "-0.015em",
     fontWeight: "400",
     marginBottom: "$gr5",
   },
diff --git a/components/Hero/Hero.styled.ts b/components/Hero/Hero.styled.ts
index 4eb41ff1..dafc85fa 100644
--- a/components/Hero/Hero.styled.ts
+++ b/components/Hero/Hero.styled.ts
@@ -37,6 +37,7 @@ const HeroStyled = styled("div", {
     width: "100%",
     height: "100%",
     position: "relative",
+    zIndex: "1",
 
     ".swiper-wrapper": {
       "&::before": {
@@ -115,7 +116,7 @@ const HeroStyled = styled("div", {
           display: "flex",
           flexDirection: "column",
           alignItems: "flex-start",
-          textShadow: "2px 2px 2px #000",
+          textShadow: "3px 3px 8px #0006",
           maxWidth: "$gr11",
           textAlign: "left",
 
@@ -135,10 +136,12 @@ const HeroStyled = styled("div", {
 
           ".slide-label": {
             fontFamily: "$northwesternDisplayBold",
-            fontSize: "$gr8",
+            fontWeight: "400",
+            fontSize: "$gr7",
             display: "block",
             margin: "0 0 $gr2",
             lineHeight: "1em",
+            letterSpacing: "-0.015em",
 
             "@sm": {
               fontSize: "$gr7",
@@ -146,7 +149,7 @@ const HeroStyled = styled("div", {
           },
 
           ".slide-summary": {
-            fontFamily: "$northwesternSansLightItalic",
+            fontFamily: "$northwesternSansRegular",
             fontSize: "$gr4",
             display: "block",
             color: "$black20",
diff --git a/components/Hero/Hero.tsx b/components/Hero/Hero.tsx
index a1bacaea..029bad32 100644
--- a/components/Hero/Hero.tsx
+++ b/components/Hero/Hero.tsx
@@ -30,7 +30,7 @@ const Hero: React.FC<HeroProps> = ({ collection }) => {
           clickable: true,
         }}
         slidesPerView={1}
-        speed={1000}
+        speed={300}
       >
         {collection.items.map((item) => (
           <SwiperSlide key={item.id}>
diff --git a/components/Homepage/Collections.styled.ts b/components/Homepage/Collections.styled.ts
index f82e32bf..c60e9229 100644
--- a/components/Homepage/Collections.styled.ts
+++ b/components/Homepage/Collections.styled.ts
@@ -4,9 +4,6 @@ import { FigureTitle } from "@/components/Figure/Figure.styled";
 import { styled } from "@/stitches.config";
 
 const HomepageCollectionsStyled = styled("section", {
-  backgroundColor: "$purple10",
-  padding: "$gr5 0",
-
   [`${FigureTitle}`]: {
     lineHeight: "1.25em",
     fontSize: "$gr4",
diff --git a/components/Homepage/Overview.styled.ts b/components/Homepage/Overview.styled.ts
index 02d0e450..8771ae48 100644
--- a/components/Homepage/Overview.styled.ts
+++ b/components/Homepage/Overview.styled.ts
@@ -13,18 +13,20 @@ const Content = styled("div", {
 
   h2: {
     fontFamily: "$northwesternDisplayBold",
-    margin: "0 0 $gr2",
+    margin: "0 0 $gr3",
     fontSize: "$gr6",
     fontWeight: "400",
     lineHeight: "1.15",
+    letterSpacing: "-0.015em",
   },
 
   p: {
-    fontFamily: "$northwesternSansLight",
-    fontSize: "$4",
+    fontFamily: "$northwesternSansRegular",
+    fontSize: "$gr4",
     lineHeight: "1.55em",
     margin: "0 0 $gr4",
     padding: "0",
+    color: "$black50",
   },
 });
 
@@ -34,11 +36,9 @@ const Images = styled("div", {
   gridTemplateColumns: "1fr 2fr 3fr 3fr 3fr 1fr",
   gridTemplateRows: "repeat(6,auto)",
   gridGap: "$gr2",
-  marginLeft: "$gr5",
 
   "@sm": {
-    width: "100%",
-    marginLeft: "0",
+    width: "300px",
   },
 
   "img, video": {
@@ -82,13 +82,14 @@ const Images = styled("div", {
 
 const Inner = styled("div", {
   display: "flex",
-  padding: "$gr6",
+  padding: "$gr5",
   width: "auto",
   alignItems: "center",
+  gap: "$gr5",
 
   "@sm": {
     flexDirection: "column-reverse",
-    padding: "$gr4 $gr3",
+    gap: "$gr3",
   },
 });
 
diff --git a/components/Search/Results.tsx b/components/Search/Results.tsx
index f3753075..11624703 100644
--- a/components/Search/Results.tsx
+++ b/components/Search/Results.tsx
@@ -2,13 +2,18 @@ import {
   NoResultsMessage,
   ResultsMessage,
   ResultsWrapper,
+  ResultsWrapperHeader,
 } from "@/components/Search/Search.styled";
 
 import Grid from "@/components/Grid/Grid";
+import IIIFShare from "../Shared/IIIF/Share";
 import PaginationAltCounts from "@/components/Search/PaginationAltCounts";
+import { SEARCH_RESULTS_PER_PAGE } from "@/lib/constants/common";
 import { SearchResultsState } from "@/types/components/search";
+import { iiifSearchUri } from "@/lib/dc-api";
 import { pluralize } from "@/lib/utils/count-helpers";
 import useGenerativeAISearchToggle from "@/hooks/useGenerativeAISearchToggle";
+import { useRouter } from "next/router";
 
 const SearchResults: React.FC<SearchResultsState> = ({
   data,
@@ -16,7 +21,9 @@ const SearchResults: React.FC<SearchResultsState> = ({
   loading,
 }) => {
   const { isChecked: isAI } = useGenerativeAISearchToggle();
+  const router = useRouter();
 
+  const iiifCollection = iiifSearchUri(router.query, SEARCH_RESULTS_PER_PAGE);
   const totalResults = data?.pagination?.total_hits;
 
   return (
@@ -27,9 +34,12 @@ const SearchResults: React.FC<SearchResultsState> = ({
         <>
           {!isAI &&
             (totalResults ? (
-              <ResultsMessage data-testid="results-count">
-                {pluralize("result", totalResults)}
-              </ResultsMessage>
+              <ResultsWrapperHeader>
+                <ResultsMessage data-testid="results-count">
+                  {pluralize("result", totalResults)}
+                </ResultsMessage>
+                <IIIFShare uri={iiifCollection} />
+              </ResultsWrapperHeader>
             ) : (
               <NoResultsMessage>
                 <strong>Your search did not match any results.</strong> Please
diff --git a/components/Search/Search.styled.ts b/components/Search/Search.styled.ts
index 113bbd14..12187f81 100644
--- a/components/Search/Search.styled.ts
+++ b/components/Search/Search.styled.ts
@@ -87,7 +87,6 @@ const Button = styled("button", {
 
 const ResultsMessage = styled("span", {
   color: "$black50",
-  padding: "0 $gr4 $gr4",
   fontSize: "$gr3",
 
   "@lg": {
@@ -126,6 +125,14 @@ const ResultsWrapper = styled("div", {
   minHeight: "80vh",
 });
 
+const ResultsWrapperHeader = styled("header", {
+  display: "flex",
+  flexDirection: "row",
+  justifyContent: "space-between",
+  alignItems: "center",
+  padding: "0 $gr4 $gr4",
+});
+
 const StyledResponseWrapper = styled("div", {
   padding: "0 0 $gr6",
 });
@@ -135,6 +142,7 @@ export {
   NoResultsMessage,
   ResultsMessage,
   ResultsWrapper,
+  ResultsWrapperHeader,
   SearchStyled,
   StyledResponseWrapper,
 };
diff --git a/components/Shared/CopyText.styled.ts b/components/Shared/CopyText.styled.ts
index d4aee8aa..64237fca 100644
--- a/components/Shared/CopyText.styled.ts
+++ b/components/Shared/CopyText.styled.ts
@@ -6,13 +6,12 @@ const StyledStatus = styled("span", {
   display: "flex",
   alignContent: "center",
   alignItems: "center",
-  padding: "0 $gr1",
-  marginLeft: "$gr1",
-  backgroundColor: "$darkBlueA",
-  color: "$white",
+  color: "$darkBlueA",
   borderRadius: "3px",
   fontSize: "$gr1",
   textTransform: "uppercase",
+  position: "absolute",
+  right: "-1.25em",
 });
 
 const StyledCopyText = styled("button", {
@@ -26,15 +25,17 @@ const StyledCopyText = styled("button", {
   fontFamily: "$northwesternSans",
   fontSize: "$gr3",
   whiteSpace: "nowrap",
+  textDecoration: "underline",
+  textDecorationThickness: "min(2px,max(1px,.05em))",
+  textUnderlineOffset: "calc(.05em + 2px)",
+  textDecorationColor: "$purple10",
+  position: "relative",
+  zIndex: "0",
 
   svg: {
     height: "calc($gr3 - 3px)",
     marginRight: "$gr1",
   },
-
-  "&:hover": {
-    textDecoration: "underline",
-  },
 });
 
 export { StyledCopyText, StyledStatus };
diff --git a/components/Shared/DefinitionList.styled.ts b/components/Shared/DefinitionList.styled.ts
index 808b8923..0dfa6b36 100644
--- a/components/Shared/DefinitionList.styled.ts
+++ b/components/Shared/DefinitionList.styled.ts
@@ -4,15 +4,15 @@ import { styled } from "@/stitches.config";
 
 const DefinitionListWrapper = styled("div", {
   lineHeight: "1.47em",
+  fontSize: "$gr3",
 
   "& dt": {
-    fontSize: "$gr3",
     color: "$black",
-    fontFamily: "$northwesternDisplayBold",
+    fontFamily: "$northwesternSansBold",
   },
   "& dd": {
     marginInlineStart: "0",
-    paddingBottom: "$gr2",
+    paddingBottom: "$gr3",
   },
 });
 
diff --git a/components/Shared/Expand/Expand.styled.ts b/components/Shared/Expand/Expand.styled.ts
index 7890055c..c4ae989c 100644
--- a/components/Shared/Expand/Expand.styled.ts
+++ b/components/Shared/Expand/Expand.styled.ts
@@ -1,10 +1,10 @@
 import { VariantProps, styled } from "@/stitches.config";
+
 import { Button } from "@nulib/design-system";
 
 /* eslint sort-keys: 0 */
 
 const ExpandButton = styled(Button, {
-  margin: "0 auto",
   opacity: "1",
   transition: "$dcAll",
 });
@@ -14,9 +14,9 @@ const ExpandEdge = styled("div", {
   width: "100%",
   bottom: "0",
   display: "flex",
-  justifyContent: "center",
-  padding: "$4 0 0",
   backgroundColor: "$white",
+  justifyContent: "flex-start",
+  paddingTop: "$gr7",
   background: "linear-gradient(to bottom, #fff0 0%, #fff 61.8%)",
   transition: "$dcAll",
   overflow: "hidden",
diff --git a/components/Shared/Expand/Expand.tsx b/components/Shared/Expand/Expand.tsx
index 4f852f5e..0b8b1d4e 100644
--- a/components/Shared/Expand/Expand.tsx
+++ b/components/Shared/Expand/Expand.tsx
@@ -38,7 +38,12 @@ const Expand: React.FC<ExpandProps & ExpandVariants> = ({
     >
       <ExpandContent ref={contentRef}>{children}</ExpandContent>
       <ExpandEdge>
-        <ExpandButton onClick={handleExpand} isLowercase disabled={isExpanded}>
+        <ExpandButton
+          onClick={handleExpand}
+          isLowercase
+          isPrimary
+          disabled={isExpanded}
+        >
           {buttonText}
         </ExpandButton>
       </ExpandEdge>
diff --git a/components/Shared/IIIF/Share.test.tsx b/components/Shared/IIIF/Share.test.tsx
new file mode 100644
index 00000000..830c2fff
--- /dev/null
+++ b/components/Shared/IIIF/Share.test.tsx
@@ -0,0 +1,56 @@
+import { render, screen } from "@/test-utils";
+
+import IIIFShare from "@/components/Shared/IIIF/Share";
+import React from "react";
+
+describe("IIIFShare", () => {
+  const uri =
+    "https://iiif.io/api/cookbook/recipe/0001-mvm-image/manifest.json";
+
+  it("renders a dropdown with IIIF viewers", async () => {
+    render(<IIIFShare uri={uri} />);
+
+    // Verify that initial elements are present
+    expect(screen.getByTestId("iiif-share")).toBeInTheDocument();
+
+    const trigger = screen.getByTestId("iiif-share-trigger");
+    expect(trigger).toHaveTextContent("View as IIIF");
+  });
+
+  it("renders dropdown content with expected items", async () => {
+    render(<IIIFShare uri={uri} dropdownMenuProps={{ open: true }} />);
+
+    const content = screen.getByTestId("iiif-share-content");
+    expect(screen.getByText("View in...")).toBeInTheDocument();
+
+    const links = Array.from(content.querySelectorAll("a"));
+    const expectedLinks = {
+      "Clover IIIF":
+        "https://samvera-labs.github.io/clover-iiif/docs/viewer/demo?iiif-content=https%3A%2F%2Fiiif.io%2Fapi%2Fcookbook%2Frecipe%2F0001-mvm-image%2Fmanifest.json",
+      Mirador:
+        "https://projectmirador.org/embed?iiif-content=https%3A%2F%2Fiiif.io%2Fapi%2Fcookbook%2Frecipe%2F0001-mvm-image%2Fmanifest.json",
+      Theseus:
+        "https://theseusviewer.org/?iiif-content=https%3A%2F%2Fiiif.io%2Fapi%2Fcookbook%2Frecipe%2F0001-mvm-image%2Fmanifest.json",
+      "View Raw JSON": uri,
+      "What is IIIF?": "https://iiif.io/get-started/why-iiif/",
+    };
+
+    // verify that the links have the expected hrefs
+    expect(links.length).toEqual(Object.keys(expectedLinks).length);
+    links.forEach((link) => {
+      expect(link).toBeInTheDocument();
+      expect(link).toBeInstanceOf(HTMLAnchorElement);
+      expect(link).toHaveAttribute("target", "_blank");
+      const key = link.textContent as keyof typeof expectedLinks;
+      const expectedHref = expectedLinks[key];
+      if (expectedHref) {
+        expect(link).toHaveAttribute("href", expectedHref);
+      }
+    });
+
+    // verify that the copy button is present
+    const copyText = screen.getByText("Copy IIIF JSON");
+    expect(copyText).toBeInTheDocument();
+    expect(copyText).toBeInstanceOf(HTMLButtonElement);
+  });
+});
diff --git a/components/Shared/IIIF/Share.tsx b/components/Shared/IIIF/Share.tsx
new file mode 100644
index 00000000..e58720cd
--- /dev/null
+++ b/components/Shared/IIIF/Share.tsx
@@ -0,0 +1,204 @@
+import * as Dropdown from "@radix-ui/react-dropdown-menu";
+
+import { IconChevronDown, IconExternalLink } from "../SVG/Icons";
+
+import CopyText from "../CopyText";
+import IIIFLogo from "../SVG/IIIF";
+import IIIFViewerLink from "./ViewerLink";
+import Icon from "../Icon";
+import Link from "next/link";
+import { styled } from "@/stitches.config";
+
+const IIIFShare = ({
+  uri,
+  dropdownMenuProps,
+}: {
+  uri: string;
+  dropdownMenuProps?: Dropdown.DropdownMenuProps;
+}) => {
+  return (
+    <StyledIIIFShare data-iiif-uri={uri} data-testid="iiif-share">
+      <Dropdown.Root {...dropdownMenuProps}>
+        <Dropdown.Trigger data-testid="iiif-share-trigger">
+          <Icon>
+            <IIIFLogo />
+          </Icon>
+          <em>View as IIIF</em>
+          <Icon>
+            <IconChevronDown />
+          </Icon>
+        </Dropdown.Trigger>
+        <StyledIIIFShareContent
+          data-testid="iiif-share-content"
+          side="bottom"
+          sideOffset={3}
+          collisionPadding={19}
+        >
+          <StyledDropdownLabel>View in...</StyledDropdownLabel>
+          <Dropdown.Item>
+            <IIIFViewerLink
+              viewer={{
+                label: "Clover IIIF",
+                href: "https://samvera-labs.github.io/clover-iiif/docs/viewer/demo",
+              }}
+              uri={uri}
+            />
+          </Dropdown.Item>
+          <Dropdown.Item>
+            <IIIFViewerLink
+              viewer={{
+                label: "Mirador",
+                href: "https://projectmirador.org/embed",
+              }}
+              uri={uri}
+            />
+          </Dropdown.Item>
+          <Dropdown.Item>
+            <IIIFViewerLink
+              viewer={{
+                label: "Theseus",
+                href: "https://theseusviewer.org",
+              }}
+              uri={uri}
+            />
+          </Dropdown.Item>
+          <StyledDropdownSeparator />
+          <Dropdown.Item>
+            <Link href={uri} target="_blank" rel="noreferrer">
+              View Raw JSON
+            </Link>
+          </Dropdown.Item>
+          <Dropdown.Item>
+            <CopyText textPrompt="Copy IIIF JSON" textToCopy={uri} />
+          </Dropdown.Item>
+          <StyledDropdownSeparator />
+          <Dropdown.Item>
+            <Link
+              href="https://iiif.io/get-started/why-iiif/"
+              target="_blank"
+              rel="noreferrer"
+              data-id="what-is-iiif"
+            >
+              What is IIIF?
+              <Icon
+                style={{
+                  display: "inline-flex",
+                  width: "12px",
+                  height: "12px",
+                  color: "$black50",
+                  fill: "$black50",
+                  marginLeft: "0.25em",
+                }}
+                hasSVGPadding={false}
+              >
+                <IconExternalLink />
+              </Icon>
+            </Link>
+          </Dropdown.Item>
+        </StyledIIIFShareContent>
+      </Dropdown.Root>
+    </StyledIIIFShare>
+  );
+};
+
+const StyledIIIFShare = styled("div", {
+  position: "relative",
+  zIndex: 1,
+
+  "> button": {
+    backgroundColor: "transparent",
+    color: "$black50",
+    fontFamily: "$northwesternSansRegular",
+    fontSize: "$gr3",
+    borderRadius: "38px",
+    border: "none",
+    display: "flex",
+    alignItems: "center",
+    justifyContent: "space-between",
+    cursor: "pointer",
+    gap: "$gr1",
+    padding: "0 $gr1",
+    margin: "0",
+
+    "> span": {
+      svg: {
+        padding: "7px",
+        path: {
+          fill: "$purple !important",
+        },
+      },
+
+      "&:last-child": {
+        display: "inline-flex",
+        alignItems: "center",
+        gap: "$gr1",
+
+        "svg path": {
+          stroke: "$black50 !important",
+          fill: "none !important",
+        },
+      },
+    },
+
+    em: {
+      fontStyle: "normal",
+      display: "inline-flex",
+      marginBottom: "-3px",
+    },
+
+    "&:hover, &:active ": {
+      color: "$purple",
+      fill: "$black",
+      backgroundColor: "$purple10",
+
+      "> span:last-child svg path": {
+        stroke: "$purple !important",
+      },
+    },
+  },
+});
+
+const StyledIIIFShareContent = styled(Dropdown.Content, {
+  zIndex: 1,
+  backgroundColor: "$white",
+  padding: "$gr3",
+  borderRadius: "3px",
+  boxShadow: "5px 5px 19px 0 #0002",
+  display: "flex",
+  flexDirection: "column",
+  fontSize: "$gr2 ",
+  minWidth: "160px",
+  gap: "$gr2",
+
+  a: {
+    color: "$purple",
+    display: "flex",
+
+    svg: {
+      color: "$purple",
+      fill: "$purple",
+    },
+  },
+
+  button: {
+    fontSize: "$gr2",
+    margin: "0 !important",
+    padding: "0 !important",
+    fontWeight: "400",
+    lineHeight: "inherit !important",
+    textDecoration: "none",
+    color: "$purple",
+  },
+});
+
+const StyledDropdownLabel = styled(Dropdown.Separator, {
+  fontSize: "$gr2 ",
+  color: "$black50",
+});
+
+const StyledDropdownSeparator = styled(Dropdown.Separator, {
+  height: "1px",
+  backgroundColor: "$gray6",
+});
+
+export default IIIFShare;
diff --git a/components/Shared/IIIF/ViewerLink.test.tsx b/components/Shared/IIIF/ViewerLink.test.tsx
new file mode 100644
index 00000000..f2d5c777
--- /dev/null
+++ b/components/Shared/IIIF/ViewerLink.test.tsx
@@ -0,0 +1,36 @@
+import { render, screen } from "@testing-library/react";
+
+import React from "react";
+import ViewerLink from "./ViewerLink";
+
+describe("ViewerLink", () => {
+  const uri =
+    "https://iiif.io/api/cookbook/recipe/0001-mvm-image/manifest.json";
+  const viewer = {
+    label: "IIIF Viewer",
+    href: "https://example.org/iiif-viewer",
+  };
+
+  it("renders an `<a>` element to IIIF Viewer", () => {
+    render(<ViewerLink viewer={viewer} uri={uri} />);
+    expect(screen.getByText(viewer.label)).toBeInTheDocument();
+    expect(screen.getByText(viewer.label).closest("a")).toHaveAttribute(
+      "href",
+      `${viewer.href}?iiif-content=${encodeURIComponent(uri)}`,
+    );
+  });
+
+  it("renders an `<a>` element to IIIF Viewer with custom iiif-content param", () => {
+    render(
+      <ViewerLink
+        viewer={{ ...viewer, iiifContentParam: "manifest" }}
+        uri={uri}
+      />,
+    );
+    expect(screen.getByText(viewer.label)).toBeInTheDocument();
+    expect(screen.getByText(viewer.label).closest("a")).toHaveAttribute(
+      "href",
+      `${viewer.href}?manifest=${encodeURIComponent(uri)}`,
+    );
+  });
+});
diff --git a/components/Shared/IIIF/ViewerLink.tsx b/components/Shared/IIIF/ViewerLink.tsx
new file mode 100644
index 00000000..18417799
--- /dev/null
+++ b/components/Shared/IIIF/ViewerLink.tsx
@@ -0,0 +1,26 @@
+import Link from "next/link";
+
+interface IIIFViewerLinkProps {
+  viewer: {
+    label: string;
+    href: string;
+    iiifContentParam?: string;
+  };
+  uri: string;
+}
+
+const IIIFViewerLink: React.FC<IIIFViewerLinkProps> = ({ viewer, uri }) => {
+  const iiifContent = new URL(viewer.href);
+  iiifContent.searchParams.set(
+    viewer.iiifContentParam ? viewer.iiifContentParam : "iiif-content",
+    uri,
+  );
+
+  return (
+    <Link href={iiifContent.toString()} target="_blank" rel="noreferrer">
+      {viewer.label}
+    </Link>
+  );
+};
+
+export default IIIFViewerLink;
diff --git a/components/Shared/Icon.tsx b/components/Shared/Icon.tsx
index fbd94278..794f8a61 100644
--- a/components/Shared/Icon.tsx
+++ b/components/Shared/Icon.tsx
@@ -1,12 +1,23 @@
+import { CSS } from "@stitches/react";
 import { ReactNode } from "react";
 import { styled } from "@/stitches.config";
 
 interface IconProps {
   children: ReactNode;
+  style?: CSS;
+  hasSVGPadding?: boolean;
 }
 
-const Icon: React.FC<IconProps> = ({ children }) => {
-  return <IconStyled>{children}</IconStyled>;
+const Icon: React.FC<IconProps> = ({
+  children,
+  style,
+  hasSVGPadding = true,
+}) => {
+  return (
+    <IconStyled css={{ ...style }} hasSVGPadding={hasSVGPadding}>
+      {children}
+    </IconStyled>
+  );
 };
 
 /* eslint sort-keys: 0 */
@@ -32,7 +43,21 @@ export const IconStyled = styled("span", {
     color: "inherit",
     fill: "inherit",
     stroke: "inherit",
-    padding: "8px",
+  },
+
+  variants: {
+    hasSVGPadding: {
+      true: {
+        svg: {
+          padding: "0.5em",
+        },
+      },
+      false: {
+        svg: {
+          padding: "0",
+        },
+      },
+    },
   },
 });
 
diff --git a/components/Shared/SVG/Icons.tsx b/components/Shared/SVG/Icons.tsx
index 7e5344b6..0df72d2e 100644
--- a/components/Shared/SVG/Icons.tsx
+++ b/components/Shared/SVG/Icons.tsx
@@ -79,6 +79,13 @@ const IconClear: React.FC = () => (
   </svg>
 );
 
+const IconExternalLink: React.FC = () => (
+  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+    <path d="M224 304a16 16 0 01-11.31-27.31l157.94-157.94A55.7 55.7 0 00344 112H104a56.06 56.06 0 00-56 56v240a56.06 56.06 0 0056 56h240a56.06 56.06 0 0056-56V168a55.7 55.7 0 00-6.75-26.63L235.31 299.31A15.92 15.92 0 01224 304z" />
+    <path d="M448 48H336a16 16 0 000 32h73.37l-38.74 38.75a56.35 56.35 0 0122.62 22.62L432 102.63V176a16 16 0 0032 0V64a16 16 0 00-16-16z" />
+  </svg>
+);
+
 const IconFilter: React.FC = () => (
   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
     <title>Filter</title>
@@ -228,6 +235,7 @@ export {
   IconCheck,
   IconChevronDown,
   IconClear,
+  IconExternalLink,
   IconFilter,
   IconImage,
   IconInfo,
diff --git a/components/Shared/WorkCount/WorkCount.styled.ts b/components/Shared/WorkCount/WorkCount.styled.ts
index 345f8690..94c05943 100644
--- a/components/Shared/WorkCount/WorkCount.styled.ts
+++ b/components/Shared/WorkCount/WorkCount.styled.ts
@@ -4,7 +4,6 @@ import { styled } from "@/stitches.config";
 
 const WorkCountStyled = styled("div", {
   display: "inline-flex",
-  backgroundColor: "$purple10",
   color: "$white",
   fontSize: "$gr1",
   borderRadius: "1rem",
diff --git a/components/Work/ActionsDialog/DownloadAndShare/IIIFManifest.tsx b/components/Work/ActionsDialog/DownloadAndShare/IIIFManifest.tsx
index cd84a852..ad8a9bcc 100644
--- a/components/Work/ActionsDialog/DownloadAndShare/IIIFManifest.tsx
+++ b/components/Work/ActionsDialog/DownloadAndShare/IIIFManifest.tsx
@@ -48,18 +48,19 @@ const IIIFManifest: React.FC<IIIFManifestProps> = ({ manifest, work }) => {
             manifestId={manifest.id}
           />
         </ShareURLActions>
-        {(isWorkInstitution || isWorkPrivate) && (
-          <Announcement
-            css={{
-              marginTop: "1rem",
-            }}
-            data-testid="mirador-announcement"
-          >
-            Opening in external tools like Mirador is not supported for works
-            that require authentication.
-          </Announcement>
-        )}
       </ShareURL>
+
+      {(isWorkInstitution || isWorkPrivate) && (
+        <Announcement
+          css={{
+            marginTop: "1rem",
+          }}
+          data-testid="mirador-announcement"
+        >
+          Opening in external tools like Mirador is not supported for works that
+          require authentication.
+        </Announcement>
+      )}
     </>
   );
 };
diff --git a/components/Work/TopInfo.styled.ts b/components/Work/TopInfo.styled.ts
index 44586359..0b0cabdc 100644
--- a/components/Work/TopInfo.styled.ts
+++ b/components/Work/TopInfo.styled.ts
@@ -11,7 +11,6 @@ const ActionButtons = styled("div", {
 
   button: {
     marginRight: "$gr3",
-    fontFamily: "$northwesternSansLight",
     paddingTop: "$gr3",
 
     "&:last-child": {
@@ -45,7 +44,7 @@ const TopInfoContent = styled("div", {
 });
 
 const TopInfoWrapper = styled("section", {
-  margin: "$gr5 0",
+  margin: "$gr5 0 $gr6",
 
   [`> header`]: {
     display: "flex",
@@ -53,25 +52,42 @@ const TopInfoWrapper = styled("section", {
 
     h1: {
       lineHeight: "1em",
+      fontWeight: "400",
       fontFamily: "$northwesternDisplayBold",
-      fontSize: "$8",
-      letterSpacing: "-0.015em",
+      fontSize: "$gr7",
+      letterSpacing: "-0.025em",
       margin: "0",
-
-      "@sm": {
-        fontSize: "$gr7",
-      },
     },
 
     p: {
-      fontSize: "$gr5",
+      fontSize: "$gr4",
       color: "$black50",
-      fontFamily: "$northwesternSansLight",
       lineHeight: "1.47em",
+    },
+  },
+});
 
-      "@sm": {
-        fontSize: "$gr4",
-      },
+const TopInfoHeaderContent = styled("div", {
+  display: "flex",
+  justifyContent: "space-between",
+  padding: "0 0 $gr3",
+  gap: "$gr2",
+
+  "@sm": {
+    flexDirection: "column",
+    gap: "0",
+  },
+
+  "> div:last-child": {
+    flexShrink: 0,
+    display: "flex",
+    alignItems: "flex-start",
+    justifyContent: "flex-end",
+    width: "38.2%",
+
+    "@sm": {
+      justifyContent: "center",
+      width: "100%",
     },
   },
 });
@@ -86,4 +102,10 @@ const TopInfoCollection = styled("div", {
   },
 });
 
-export { ActionButtons, TopInfoCollection, TopInfoContent, TopInfoWrapper };
+export {
+  ActionButtons,
+  TopInfoCollection,
+  TopInfoContent,
+  TopInfoHeaderContent,
+  TopInfoWrapper,
+};
diff --git a/components/Work/TopInfo.tsx b/components/Work/TopInfo.tsx
index e4962656..04a5ad24 100644
--- a/components/Work/TopInfo.tsx
+++ b/components/Work/TopInfo.tsx
@@ -2,6 +2,7 @@ import {
   ActionButtons,
   TopInfoCollection,
   TopInfoContent,
+  TopInfoHeaderContent,
   TopInfoWrapper,
 } from "@/components//Work/TopInfo.styled";
 import {
@@ -15,6 +16,7 @@ import { Button } from "@nulib/design-system";
 import Card from "@/components/Shared/Card";
 import { DefinitionListWrapper } from "@/components/Shared/DefinitionList.styled";
 import Expand from "@/components/Shared/Expand/Expand";
+import IIIFShare from "../Shared/IIIF/Share";
 import { Manifest } from "@iiif/presentation-3";
 import type { Work } from "@nulib/dcapi-types";
 import WorkActionsDialog from "@/components/Work/ActionsDialog/ActionsDialog";
@@ -71,10 +73,20 @@ const WorkTopInfo: React.FC<TopInfoProps> = ({
   return (
     <TopInfoWrapper>
       <header>
-        <Label label={manifest.label} as="h1" data-testid="title" />
-        {manifest?.summary && (
-          <Summary summary={manifest.summary} as="p" data-testid="summary" />
-        )}
+        <TopInfoHeaderContent>
+          <div>
+            <Label label={manifest.label} as="h1" data-testid="title" />
+            {manifest?.summary && (
+              <Summary
+                summary={manifest.summary}
+                as="p"
+                data-testid="summary"
+              />
+            )}
+          </div>
+          <IIIFShare uri={manifest.id} />
+        </TopInfoHeaderContent>
+
         <ActionButtons>
           <Button
             name="find"
diff --git a/hooks/useCopyToClipboard.ts b/hooks/useCopyToClipboard.ts
index 6660a88d..eddfd308 100644
--- a/hooks/useCopyToClipboard.ts
+++ b/hooks/useCopyToClipboard.ts
@@ -1,18 +1,22 @@
-import { useCallback, useEffect, useState } from "react";
+import { MouseEvent, useCallback, useEffect, useState } from "react";
 
-export type CopyStatus = "copied" | "failed" | undefined;
+export type CopyStatus = "✔" | "✗" | undefined;
 
 export const useCopyToClipboard = (
   text: string,
-  notifyTimeout = 2500,
-): [CopyStatus, () => void] => {
+  notifyTimeout = 5000,
+): [CopyStatus, (event: MouseEvent) => void] => {
   const [copyStatus, setCopyStatus] = useState<CopyStatus>();
-  const copy = useCallback(() => {
-    navigator.clipboard.writeText(text).then(
-      () => setCopyStatus("copied"),
-      () => setCopyStatus("failed"),
-    );
-  }, [text]);
+  const copy = useCallback(
+    (event: MouseEvent) => {
+      event?.preventDefault();
+      navigator.clipboard.writeText(text).then(
+        () => setCopyStatus("✔"),
+        () => setCopyStatus("✗"),
+      );
+    },
+    [text],
+  );
 
   useEffect(() => {
     if (!copyStatus) {
diff --git a/lib/dc-api.test.ts b/lib/dc-api.test.ts
new file mode 100644
index 00000000..fef1c12e
--- /dev/null
+++ b/lib/dc-api.test.ts
@@ -0,0 +1,46 @@
+import { DC_API_SEARCH_URL } from "@/lib/constants/endpoints";
+import { URLSearchParams } from "url";
+import { iiifSearchUri } from "@/lib/dc-api";
+
+describe("iiifSearchUri", () => {
+  it("returns the expected uri", () => {
+    const query = { q: "Joan Baez" };
+    const uri = new URL(iiifSearchUri(query));
+    const params = new URLSearchParams(uri.search);
+
+    // check that the URI has the expected origin and path
+    expect(DC_API_SEARCH_URL).toContain(uri.origin);
+    expect(DC_API_SEARCH_URL).toContain(uri.pathname);
+
+    // check that the query string has the expected params
+    expect(params.get("query")).toEqual("Joan Baez");
+    expect(params.get("as")).toEqual("iiif");
+  });
+
+  it("returns the expected uri with a custom size", () => {
+    const query = { q: "John Fahey" };
+    const uri = new URL(iiifSearchUri(query, 100));
+    const params = new URLSearchParams(uri.search);
+
+    // check that the size param is set to 100
+    expect(params.get("query")).toEqual("John Fahey");
+    expect(params.get("as")).toEqual("iiif");
+    expect(params.get("size")).toEqual("100");
+  });
+
+  it("returns the expected uri with appended facets as params", () => {
+    const query = {
+      q: "Muddy Waters",
+      workType: "Image",
+      genre: "photographs",
+    };
+    const uri = new URL(iiifSearchUri(query));
+    const params = new URLSearchParams(uri.search);
+
+    // check that the facets are appended as params
+    expect(params.get("query")).toEqual("Muddy Waters");
+    expect(params.get("as")).toEqual("iiif");
+    expect(params.get("workType")).toEqual("Image");
+    expect(params.get("genre")).toEqual("photographs");
+  });
+});
diff --git a/lib/dc-api.ts b/lib/dc-api.ts
index afe93d79..ac45b992 100644
--- a/lib/dc-api.ts
+++ b/lib/dc-api.ts
@@ -1,6 +1,8 @@
+import { DCAPI_ENDPOINT, DC_API_SEARCH_URL } from "./constants/endpoints";
 import axios, { AxiosError, RawAxiosRequestHeaders } from "axios";
 
 import type { ApiSearchRequestBody } from "@/types/api/request";
+import { NextRouter } from "next/router";
 
 interface ApiGetRequestParams {
   url: string;
@@ -72,6 +74,31 @@ async function getIIIFResource<R>(
   }
 }
 
+function iiifSearchUri(query: NextRouter["query"], size?: number): string {
+  const url = new URL(DC_API_SEARCH_URL);
+  Object.keys(query).forEach((key) => {
+    url.searchParams.append(key, query[key] as string);
+    url.searchParams.delete("q");
+    query.q ? url.searchParams.append("query", query.q as string) : null;
+  });
+
+  if (size) url.searchParams.append("size", size.toString());
+  url.searchParams.append("as", "iiif");
+
+  return url.toString();
+}
+
+function iiifCollectionUri(id?: string, size?: number): string | undefined {
+  if (!id) return;
+
+  const url = new URL(`${DCAPI_ENDPOINT}/collections/${id}`);
+
+  if (size) url.searchParams.append("size", size.toString());
+  url.searchParams.append("as", "iiif");
+
+  return url.toString();
+}
+
 function handleError(err: unknown) {
   const error = err as AxiosError;
   if (error.response) {
@@ -96,4 +123,6 @@ export {
   apiPostRequest,
   getIIIFResource,
   handleError,
+  iiifCollectionUri,
+  iiifSearchUri,
 };
diff --git a/mocks/sample-collection1.ts b/mocks/sample-collection1.ts
index 6f724e8f..8f690ba1 100644
--- a/mocks/sample-collection1.ts
+++ b/mocks/sample-collection1.ts
@@ -10,6 +10,8 @@ export const sampleCollection1 = {
   featured: true,
   finding_aid_url: null,
   id: "04c17199-1b74-4f9f-853c-1c069f1c4f2e",
+  iiif_collection:
+    "https://api.dc.library.northwestern.edu/api/v2/works/04c17199-1b74-4f9f-853c-1c069f1c4f2e?as=iiif",
   indexed_at: "what goes here",
   keywords: [],
   modified_date: "2022-02-24T23:51:15.854797Z",
diff --git a/package-lock.json b/package-lock.json
index 2bcc6a76..bd55ff88 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
         "@next/bundle-analyzer": "^14.0.3",
         "@next/font": "^14.0.3",
         "@next/third-parties": "^14.2.3",
-        "@nulib/dcapi-types": "^2.5.0",
+        "@nulib/dcapi-types": "^2.6.0",
         "@nulib/design-system": "^1.6.2",
         "@nulib/use-markdown": "^0.2.1",
         "@radix-ui/colors": "^3.0.0",
@@ -2198,9 +2198,9 @@
       }
     },
     "node_modules/@nulib/dcapi-types": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/@nulib/dcapi-types/-/dcapi-types-2.5.0.tgz",
-      "integrity": "sha512-Ai1AJioCC4y4lpGdvzziC7zDcrzUbIislM0InmRx9nuj4I/0Old9eJJyBrfiDv9uGdWwFGqBaleAUtZk6pQcTQ=="
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/@nulib/dcapi-types/-/dcapi-types-2.6.0.tgz",
+      "integrity": "sha512-TaOhHfbTXnlHzurpgrCVCQNU13oCmzR8C51yKZ3iKBtf6mzVzXsnCDhIUbQ69iuXWeeuHGcEO6oK3XnVeUqb2A=="
     },
     "node_modules/@nulib/design-system": {
       "version": "1.6.2",
diff --git a/package.json b/package.json
index d8d7df2b..9554fe01 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,7 @@
     "@next/bundle-analyzer": "^14.0.3",
     "@next/font": "^14.0.3",
     "@next/third-parties": "^14.2.3",
-    "@nulib/dcapi-types": "^2.5.0",
+    "@nulib/dcapi-types": "^2.6.0",
     "@nulib/design-system": "^1.6.2",
     "@nulib/use-markdown": "^0.2.1",
     "@radix-ui/colors": "^3.0.0",
diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx
index 026981c7..8b0a0e73 100644
--- a/pages/collections/[id].tsx
+++ b/pages/collections/[id].tsx
@@ -1,4 +1,5 @@
 import {
+  CollectionHeader,
   Description,
   HeroWrapper,
   Interstitial,
@@ -32,11 +33,14 @@ import { Collection as CollectionType } from "@nulib/dcapi-types";
 import Container from "@/components/Shared/Container";
 import Facts from "@/components/Shared/Facts";
 import Head from "next/head";
+import Heading from "@/components/Heading/Heading";
 import Hero from "@/components/Hero/Hero";
+import IIIFShare from "@/components/Shared/IIIF/Share";
 import Layout from "components/layout";
 import ReadMore from "@/components/Shared/ReadMore";
 import { buildDataLayer } from "@/lib/ga/data-layer";
 import { getHeroCollection } from "@/lib/iiif/collection-helpers";
+import { iiifCollectionUri } from "@/lib/dc-api";
 import { loadCollectionStructuredData } from "@/lib/json-ld";
 import useGenerativeAISearchToggle from "@/hooks/useGenerativeAISearchToggle";
 import { useRouter } from "next/router";
@@ -57,6 +61,8 @@ const Collection: NextPage = () => {
 
   const description = collection?.description;
 
+  const iiifResource = iiifCollectionUri(collection?.id);
+
   /** Get the Collection */
   useEffect(() => {
     async function getData() {
@@ -182,9 +188,20 @@ const Collection: NextPage = () => {
                   <TabsTrigger value="metadata">All Subjects</TabsTrigger>
                 </TabsList>
                 <TabsContent value="explore">
+                  <CollectionHeader>
+                    <Heading
+                      as="h2"
+                      css={{
+                        margin: "0 0 $gr2 !important",
+                      }}
+                    >
+                      {collection?.title}
+                    </Heading>{" "}
+                    <IIIFShare uri={String(collection.iiif_collection)} />
+                  </CollectionHeader>
                   {description && (
                     <Description data-testid="description">
-                      <ReadMore text={description} words={55} />
+                      {<ReadMore text={description} words={55} />}
                     </Description>
                   )}
                   {topMetadata.length > 0 && (