Skip to content

Commit 45b40d3

Browse files
committed
Add carousel to tag page, remove tag from testimonials, case, studies, and services, and reorganize tag categories
1 parent f95fc33 commit 45b40d3

65 files changed

Lines changed: 440 additions & 682 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cache/pages.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,13 @@
3737
},
3838
{
3939
"tags": [
40-
"apiDesign",
41-
"cms",
42-
"code",
43-
"crm",
44-
"graphql",
45-
"online-learning",
46-
"react",
47-
"services",
40+
"api-platforms-and-edge",
41+
"cloud-infrastructure",
42+
"legacy-systems-and-modernization",
43+
"observability",
44+
"platform-engineering-and-developer-experience",
45+
"release-engineering",
46+
"reliability-engineering",
4847
"typescript"
4948
]
5049
}

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"MTTR",
7676
"nanostores",
7777
"navigations",
78+
"ndots",
7879
"nektosact",
7980
"Neue",
8081
"Niklas",
@@ -94,6 +95,7 @@
9495
"Postg",
9596
"postgrest",
9697
"psql",
98+
"Pulumi",
9799
"pylint",
98100
"qpart",
99101
"qparts",
@@ -130,6 +132,7 @@
130132
"tsdoc",
131133
"tseslint",
132134
"tshirt",
135+
"Turborepo",
133136
"Tursa",
134137
"Turso",
135138
"tydata",

_TODO.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ if (window.matchMedia) {
8888
}
8989
```
9090

91-
## Tags cards
91+
Our site has tag pages that are categories for article content. Each of the individual tag pages should have a paragraph or two in the markdown field that describes what the tag (category) is about and covers. Right now at the top of the content area on each page, there are some bullet points or a brief description. Please refer to ./CONTENT.md for the voice and focus of content on the site. Then please do the following:
9292

93-
Put the tags on the all-categories top level page into carousels by category
93+
1. Rewrite the content between the last frontmatter code fence (`---`) and the "Article Ideas" header as described above, into an introduction to this category.
94+
95+
2. Rewrite the "TODO" in the "description" front matter to feature a brief description of the category that can be shown in tooltips when a user hovers over a tag name on an article page and that's used in SEO metadata like OpenGraph descriptions.
96+
97+
3. In the "Cover Prompt" header, add four or five AI prompts that can be used to generate a cover image for this category. I don't have a defined visual style for the site yet except that it is technical, modern, interesting (so cartoon balloon speech tags are ok), and highly professional. The suggested AI prompts for image generation should be very detailed. I'm hoping to find a visual style from evaluating prompt suggestions for images.
98+
99+
4. At the very bottom of the tag / category page, add a header named "Slug and displayName Suggestions" and add four or five alternate suggestions for the slug and displayName (alternatives to the `slug` and `displayName` already defined in frontmatter properties and the slug the directory the index.mdx file is named after).
100+
101+
5. On most tag / category pages, ignore the content between the "Article Ideas" header and the "Cover Prompt" header. However, for api-platforms-and-edge, we are very weak on article ideas. Please add 8 more article ideas for this tag / category. It seems like maybe we should have something about swagger, and something about managing costs associated with API endpoints.

src/components/Carousel/@types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ type CollectionEntryMap = {
1515

1616
export interface CarouselProps<T extends CollectionSlug = CollectionSlug> {
1717
title?: string
18+
titleHref?: string
1819
limit?: number
1920
variant?: CarouselVariant
2021
currentSlug: string
2122
type: T
23+
items?: Array<ItemType<T>>
2224
}
2325

2426
export type ItemType<T extends CollectionSlug = CollectionSlug> = CollectionEntryMap[T]

src/components/Carousel/client/__tests__/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ vi.mock('@components/scripts/store', () => ({
6060
}))
6161

6262
type CarouselComponentModule = WebComponentModule<CarouselElement>
63-
type ConcreteCarouselProps = Required<CarouselProps<'articles'>>
63+
type ConcreteCarouselProps = CarouselProps<'articles'>
6464

6565
vi.mock('astro:content', () => ({
6666
getCollection: vi.fn(async () => sampleCollection),

src/components/Carousel/client/__tests__/selectors.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ vi.mock('embla-carousel-autoplay', () => ({
7676

7777
type CarouselModule = WebComponentModule<CarouselElement>
7878

79-
type ConcreteCarouselProps = Required<CarouselProps<'articles'>>
79+
type ConcreteCarouselProps = CarouselProps<'articles'>
8080

8181
const defaultCarouselProps: ConcreteCarouselProps = {
8282
title: 'Featured Articles',

src/components/Carousel/index.astro

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,25 @@ import { prepareItems } from './server'
1010
1111
export interface Props {
1212
title?: string
13+
titleHref?: string
1314
limit?: number
1415
variant?: CarouselVariant
1516
currentSlug: string
1617
type: CollectionSlug
18+
items?: ItemType[]
1719
}
1820
1921
const {
2022
title = 'Featured Content',
23+
titleHref,
2124
limit = 3,
2225
variant = 'featured',
2326
currentSlug,
2427
type,
28+
items: itemsOverride,
2529
} = Astro.props
2630
27-
const allItems = await getCollection(collectionMap[type])
31+
const allItems = itemsOverride ?? (await getCollection(collectionMap[type]))
2832
const items = prepareItems(allItems, variant, currentSlug, limit)
2933
3034
const carouselId = `carousel-${type}-${variant}-${currentSlug}`
@@ -36,7 +40,13 @@ const carouselId = `carousel-${type}-${variant}-${currentSlug}`
3640
<section class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
3741
<header class="text-center mb-12">
3842
<h2 id={`${carouselId}-title`} class="text-3xl md:text-4xl font-bold">
39-
{title}
43+
{titleHref ? (
44+
<a href={titleHref} class="hover:text-primary transition-colors duration-200">
45+
{title}
46+
</a>
47+
) : (
48+
title
49+
)}
4050
</h2>
4151
</header>
4252

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,67 @@
11
---
2+
import { getCollection } from 'astro:content'
3+
24
export interface Props {
35
tags: string[]
46
}
57
68
const { tags } = Astro.props
9+
10+
const allTags = await getCollection('tags')
11+
12+
type TagMeta = {
13+
slug: string
14+
displayName: string
15+
}
16+
17+
const tagMetaByIdentifier = new Map<string, TagMeta>()
18+
19+
for (const tagEntry of allTags) {
20+
const meta: TagMeta = {
21+
slug: tagEntry.data.slug,
22+
displayName: tagEntry.data.displayName,
23+
}
24+
25+
tagMetaByIdentifier.set(tagEntry.id, meta)
26+
tagMetaByIdentifier.set(tagEntry.data.slug, meta)
27+
28+
if (tagEntry.id.endsWith('/index')) {
29+
tagMetaByIdentifier.set(tagEntry.id.replace(/\/index$/, ''), meta)
30+
}
31+
32+
tagMetaByIdentifier.set(`${tagEntry.data.slug}/index`, meta)
33+
tagMetaByIdentifier.set(`${tagEntry.data.slug}/index.mdx`, meta)
34+
tagMetaByIdentifier.set(`${tagEntry.data.slug}/index.md`, meta)
35+
}
36+
37+
const resolveTagMeta = (rawTag: string) => {
38+
return (
39+
tagMetaByIdentifier.get(rawTag) ??
40+
tagMetaByIdentifier.get(rawTag.replace(/\.(md|mdx)$/, '')) ??
41+
tagMetaByIdentifier.get(rawTag.replace(/\/index$/, ''))
42+
)
43+
}
744
---
845

946
<ul class="flex flex-wrap gap-1 list-none p-0 m-0" aria-label="Article tags">
1047
{
11-
tags.map(tag => (
12-
<li>
13-
<a
14-
href={`/tags/${tag}`}
15-
rel="tag"
16-
itemprop="keywords"
17-
class="inline-block px-2 py-0.5 bg-page-active rounded text-xs no-underline transition-colors hover:bg-spotlight hover:text-primary focus:bg-spotlight focus:text-primary"
18-
>
19-
{tag}
20-
</a>
21-
</li>
22-
))
48+
tags.map(rawTag => {
49+
const meta = resolveTagMeta(rawTag)
50+
const href = meta ? `/tags/${meta.slug}` : `/tags/${rawTag}`
51+
const label = meta ? meta.displayName : rawTag
52+
53+
return (
54+
<li>
55+
<a
56+
href={href}
57+
rel="tag"
58+
itemprop="keywords"
59+
class="inline-block px-2 py-0.5 bg-page-active rounded text-xs no-underline transition-colors hover:bg-spotlight hover:text-primary focus:bg-spotlight focus:text-primary"
60+
>
61+
{label}
62+
</a>
63+
</li>
64+
)
65+
})
2366
}
2467
</ul>

src/content.config.ts

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,17 @@
1010
*/
1111
import { defineCollection, reference, z, type SchemaContext } from 'astro:content'
1212
import { glob, file } from 'astro/loaders'
13-
import { existsSync, readdirSync } from 'node:fs'
14-
import { fileURLToPath } from 'node:url'
1513
/**
16-
* Wraps a collection schema with a refinement that enforces breadcrumb title length limits */
14+
* Wraps a collection schema with a refinement that enforces breadcrumb title length limits
15+
*/
1716
import { withBreadcrumbTitleWarning } from '@lib/helpers/breadcrumbTitleLengthRefinement'
1817

1918
/**
20-
* NOTE: In YAML, dates written without quotes around them are interpreted as Date objects */
21-
22-
/** Only load markdown and MDX files that do not start with an underscore */
23-
const pattern = '**\/[^_]*.{md,mdx}'
24-
25-
function fsExists(url: URL) {
26-
return existsSync(fileURLToPath(url))
27-
}
28-
29-
const getValidTags = () => {
30-
const tagContentDir = fileURLToPath(new URL('./content/tags', import.meta.url))
31-
const tagSlugs = readdirSync(tagContentDir, { withFileTypes: true })
32-
.filter(entry => entry.isDirectory() && !entry.name.startsWith('_'))
33-
.filter(entry => {
34-
const indexMd = new URL(`./content/tags/${entry.name}/index.md`, import.meta.url)
35-
const indexMdx = new URL(`./content/tags/${entry.name}/index.mdx`, import.meta.url)
36-
return fsExists(indexMd) || fsExists(indexMdx)
37-
})
38-
.map(entry => entry.name)
39-
.sort((a, b) => a.localeCompare(b, 'en'))
40-
41-
if (tagSlugs.length === 0) {
42-
throw new Error('No tags found. Add markdown files under src/content/tags.')
43-
}
44-
return tagSlugs as [string, ...string[]]
45-
}
19+
* NOTE: In YAML, dates written without quotes around them are interpreted as Date objects
20+
*/
4621

47-
const validTags = getValidTags()
22+
const pattern = '**/index.{md,mdx}'
23+
const flatMarkdownPattern = '**/*.{md,mdx}'
4824

4925
const createBaseCollectionSchema = ({ image }: SchemaContext) =>
5026
z.object({
@@ -55,7 +31,6 @@ const createBaseCollectionSchema = ({ image }: SchemaContext) =>
5531
featured: z.boolean().default(false),
5632
isDraft: z.boolean().default(false),
5733
publishDate: z.date(),
58-
tags: z.array(z.enum(validTags)),
5934
})
6035

6136
const createSocialCollectionSchema = () =>
@@ -87,6 +62,7 @@ const articlesCollection = defineCollection({
8762
author: reference('authors'),
8863
readingTime: z.string().optional(),
8964
showToc: z.boolean().default(true),
65+
tags: reference('tags').array(),
9066
}),
9167
'articles'
9268
),
@@ -188,7 +164,7 @@ const aboutCollection = defineCollection({
188164
* Authors
189165
*/
190166
const authorsCollection = defineCollection({
191-
loader: glob({ pattern, base: './src/content/authors' }),
167+
loader: glob({ pattern: flatMarkdownPattern, base: './src/content/authors' }),
192168
schema: () =>
193169
z.object({
194170
id: z.string(),
@@ -222,7 +198,7 @@ const contactDataCollection = defineCollection({
222198
* Tags
223199
*/
224200
const tagsCollection = defineCollection({
225-
loader: glob({ pattern: '**/index.{md,mdx}', base: './src/content/tags' }),
201+
loader: glob({ pattern, base: './src/content/tags' }),
226202
schema: ({ image }) =>
227203
z.object({
228204
slug: z.string(),
@@ -243,7 +219,6 @@ const testimonialCollection = defineCollection({
243219
z.object({
244220
name: z.string(),
245221
organization: z.string().optional(),
246-
tags: z.array(z.enum(validTags)),
247222
avatar: z
248223
.object({
249224
src: z.string(),

src/content/articles/building-with-astro/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cover: "./cover.webp"
55
coverAlt: "Hero image for article"
66
author: "kevin-brown"
77
publishDate: 2024-01-15
8-
tags: ["typescript", "cms"]
8+
tags: []
99
featured: true
1010
---
1111

0 commit comments

Comments
 (0)