diff --git a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx
new file mode 100644
index 000000000..015e7d96c
--- /dev/null
+++ b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx
@@ -0,0 +1,354 @@
+import {
+ Box,
+ VStack,
+ HStack,
+ SpanBox,
+ Blockquote,
+} from '../../elements/LayoutPrimitives'
+import { LabelChip } from '../../elements/LabelChip'
+import type { LinkedItemCardProps } from './CardTypes'
+import { CoverImage } from '../../elements/CoverImage'
+import dayjs from 'dayjs'
+import relativeTime from 'dayjs/plugin/relativeTime'
+import { Fragment, useMemo, useState } from 'react'
+import {
+ CaretCircleDown,
+ CaretDown,
+ CaretUp,
+ DotsThreeVertical,
+} from 'phosphor-react'
+import Link from 'next/link'
+import { CardMenu } from '../CardMenu'
+import {
+ AuthorInfoStyle,
+ DescriptionStyle,
+ MenuStyle,
+ MetaStyle,
+ TitleStyle,
+} from './LibraryCardStyles'
+import { Separator } from '../../elements/Separator'
+import { styled } from '@stitches/react'
+import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
+import { StyledText } from '../../elements/StyledText'
+import { Highlight } from '../../../lib/networking/fragments/highlightFragment'
+import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
+import {
+ LibraryItem,
+ LibraryItemNode,
+} from '../../../lib/networking/queries/useGetLibraryItemsQuery'
+import { useRouter } from 'next/router'
+import { Label } from '../../../lib/networking/fragments/labelFragment'
+import { Button } from '../../elements/Button'
+import { theme } from '../../tokens/stitches.config'
+
+dayjs.extend(relativeTime)
+
+const timeAgo = (date: string | undefined): string => {
+ if (!date) {
+ return ''
+ }
+ return dayjs(date).fromNow()
+}
+
+export const GridSeparator = styled(Box, {
+ height: '1px',
+ marginTop: '15px',
+ backgroundColor: '$thBorderColor',
+})
+
+type LibraryHighlightGridCardProps = {
+ viewer: UserBasicData
+ item: LibraryItemNode
+}
+
+// Component
+export function LibraryHighlightGridCard(
+ props: LibraryHighlightGridCardProps
+): JSX.Element {
+ const [isHovered, setIsHovered] = useState(false)
+ const [expanded, setExpanded] = useState(false)
+
+ const higlightCount = props.item.highlights?.length ?? 0
+
+ return (
+ {
+ setIsHovered(true)
+ }}
+ onMouseLeave={() => {
+ setIsHovered(false)
+ }}
+ >
+ {!expanded && (
+
+
+ {timeAgo(props.item.savedAt)}
+ {` `}
+ {props.item.wordsCount ?? 0 > 0
+ ? ` • ${Math.max(
+ 1,
+ Math.round((props.item.wordsCount ?? 0) / 235)
+ )} min read`
+ : null}
+ {props.item.highlights?.length ?? 0 > 0
+ ? ` • ${props.item.highlights?.length} highlights`
+ : null}
+
+
+ )}
+
+
+ {props.item.title}
+
+ {expanded && (
+ <>
+
+
+ {(props.item.highlights ?? []).map((highlight) => (
+
+ ))}
+
+ >
+ )}
+
+ {!expanded ? (
+
+ ) : (
+
+ )}
+
+
+
+ )
+}
+
+type HighlightItemCardProps = {
+ highlight: Highlight
+ viewer: UserBasicData | undefined
+ item: LibraryItemNode
+}
+
+const StyledQuote = styled(Blockquote, {
+ margin: '0px',
+ fontSize: '16px',
+ fontFamily: '$inter',
+ lineHeight: '1.50',
+ color: '$thHighContrast',
+ paddingLeft: '15px',
+ borderLeft: '2px solid $omnivoreCtaYellow',
+})
+
+function HighlightItemCard(props: HighlightItemCardProps): JSX.Element {
+ const router = useRouter()
+ const [hover, setHover] = useState(false)
+ const [isEditing, setIsEditing] = useState(false)
+
+ const lines = useMemo(
+ () => props.highlight.quote.split('\n'),
+ [props.highlight.quote]
+ )
+
+ return (
+ setHover(true)}
+ onMouseLeave={() => setHover(false)}
+ >
+ {
+ if (router && props.viewer) {
+ const dest = `/${props.viewer}/${props.item.slug}#${props.highlight.id}`
+ router.push(dest)
+ }
+ event.preventDefault()
+ }}
+ >
+
+
+ {lines.map((line: string, index: number) => (
+
+ {line}
+ {index !== lines.length - 1 && (
+ <>
+
+
+ >
+ )}
+
+ ))}
+
+
+
+
+ {props.highlight.labels?.map((label: Label, index: number) => (
+
+ ))}
+
+
+ setIsEditing(true)}
+ >
+ {props.highlight.annotation
+ ? props.highlight.annotation
+ : 'Add your notes...'}
+
+
+
+
+
+
+ )
+}
+
+function HighlightsMenu(): JSX.Element {
+ return (
+
+
+
+ }
+ >
+ {
+ console.log('copy')
+ }}
+ title="Copy"
+ />
+ {
+ console.log('labels')
+ }}
+ title="Labels"
+ />
+ {
+ console.log('delete')
+ }}
+ title="Delete"
+ />
+
+ )
+}
diff --git a/packages/web/components/templates/article/HighlightHoverCard.tsx b/packages/web/components/templates/article/HighlightHoverCard.tsx
new file mode 100644
index 000000000..ccaa5a3cd
--- /dev/null
+++ b/packages/web/components/templates/article/HighlightHoverCard.tsx
@@ -0,0 +1,37 @@
+import { Box } from '../../elements/LayoutPrimitives'
+import { theme } from '../../tokens/stitches.config'
+import { Highlight } from '../../../lib/networking/fragments/highlightFragment'
+import { HighlightView } from '../../patterns/HighlightView'
+
+type PageCoordinates = {
+ pageX: number
+ pageY: number
+}
+
+type HighlightHoverCardProps = {
+ highlight: Highlight
+ anchorCoordinates: PageCoordinates
+}
+
+export function HighlightHoverCard(
+ props: HighlightHoverCardProps
+): JSX.Element {
+ return (
+
+
+
+ )
+}