diff --git a/packages/starpod/README.md b/packages/starpod/README.md
index 1dc8426..b85655a 100644
--- a/packages/starpod/README.md
+++ b/packages/starpod/README.md
@@ -90,6 +90,8 @@ validates your config and lists every problem with its field path.
- `rssFeed` — the feed your episodes are read from at build time
- `links` (optional) — extra navigation links after About and Contact, e.g.
`[{ label: 'Store', href: 'https://example.com/store' }]`
+- `brandColor` (optional) — hex accent color for the Safari pinned-tab icon
+ and Windows tile, e.g. `'#531b3c'`
## Integration options
diff --git a/packages/starpod/src/components/Breadcrumbs.astro b/packages/starpod/src/components/Breadcrumbs.astro
index 5424bba..8a8a2de 100644
--- a/packages/starpod/src/components/Breadcrumbs.astro
+++ b/packages/starpod/src/components/Breadcrumbs.astro
@@ -9,23 +9,37 @@ export interface Props {
const { title } = Astro.props;
+// Multi-segment paths (e.g. a custom /collections/[slug] page) get an
+// intermediate crumb per ancestor segment, named by humanizing the segment.
+// The injected core routes are all single-segment, so those keep the
+// classic Home > title trail.
+function humanize(segment: string): string {
+ return segment
+ .split('-')
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(' ');
+}
+
+const segments = url.pathname.split('/').filter(Boolean);
+
+const breadcrumbItems: Array<{ name: string; href: string }> = [
+ { name: 'Home', href: '/' },
+ ...segments.slice(0, -1).map((segment, index) => ({
+ name: humanize(segment),
+ href: '/' + segments.slice(0, index + 1).join('/')
+ })),
+ ...(segments.length > 0 ? [{ name: title, href: url.pathname }] : [])
+];
+
const breadcrumbSchema = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
- itemListElement: [
- {
- '@type': 'ListItem',
- position: 1,
- name: 'Home',
- item: Astro.site?.toString() || '/'
- },
- {
- '@type': 'ListItem',
- position: 2,
- name: title,
- item: new URL(url, Astro.site).toString()
- }
- ]
+ itemListElement: breadcrumbItems.map((item, index) => ({
+ '@type': 'ListItem',
+ position: index + 1,
+ name: item.name,
+ item: new URL(item.href, Astro.site).toString()
+ }))
};
---
@@ -33,31 +47,42 @@ const breadcrumbSchema = {
diff --git a/packages/starpod/src/layouts/Layout.astro b/packages/starpod/src/layouts/Layout.astro
index ba36cc0..3e9f07f 100644
--- a/packages/starpod/src/layouts/Layout.astro
+++ b/packages/starpod/src/layouts/Layout.astro
@@ -67,8 +67,15 @@ const description = Astro.props.description ?? starpodConfig.description;
-
-
+
+
;
+ /**
+ * Hex accent color used for the Safari pinned-tab icon and the Windows
+ * tile, e.g. "#531b3c". Defaults to the favicon-generator template colors.
+ */
+ brandColor?: string;
};
const urlField = (name: string) =>
@@ -132,7 +138,13 @@ const StarpodConfigSchema: GenericSchema = object({
'platforms is required (an empty object is fine)'
),
rssFeed: urlField('rssFeed'),
- links: optional(array(NavLinkSchema, 'links must be an array'))
+ links: optional(array(NavLinkSchema, 'links must be an array')),
+ brandColor: optional(
+ pipe(
+ string('brandColor must be a string'),
+ hexColor('brandColor must be a hex color like #531b3c')
+ )
+ )
});
/**
diff --git a/tests/unit/config.test.ts b/tests/unit/config.test.ts
index a31af24..71175e3 100644
--- a/tests/unit/config.test.ts
+++ b/tests/unit/config.test.ts
@@ -28,6 +28,17 @@ describe('defineStarpodConfig', () => {
expect(config.links).toHaveLength(1);
});
+ it('accepts a hex brandColor and rejects a non-hex one', () => {
+ const config = defineStarpodConfig({
+ ...validConfig,
+ brandColor: '#531b3c'
+ });
+ expect(config.brandColor).toBe('#531b3c');
+ expect(() =>
+ validateStarpodConfig({ ...validConfig, brandColor: 'maroon' })
+ ).toThrow(/brandColor must be a hex color/);
+ });
+
it('rejects a non-URL rssFeed and names the field', () => {
expect(() =>
validateStarpodConfig({ ...validConfig, rssFeed: 'not a url' })