soames-astro-theme 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/Breadcrumbs.tsx +40 -0
- package/src/lib/wp.ts +44 -1
- package/src/routes/[...uri].astro +2 -2
- package/src/routes/blog/[...page].astro +3 -3
- package/src/routes/blog/post/[...slug].astro +3 -3
- package/src/routes/docs/[...slug].astro +17 -6
- package/src/routes/docs/index.astro +2 -2
- package/src/routes/index.astro +2 -2
- package/src/styles/soames/overrides.css +47 -0
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Breadcrumb trail for single Knowledge Base articles (docs CPT). Presentational
|
|
2
|
+
// and static (no hydration): the crumb list is assembled in the docs template
|
|
3
|
+
// from docAncestors() + the /docs/ landing title. Crumbs with an `href` render
|
|
4
|
+
// as links; the last crumb (the current page) has no href and renders as plain
|
|
5
|
+
// text with aria-current="page". Separators are drawn in CSS (::before), so they
|
|
6
|
+
// aren't part of the accessible name or selectable text.
|
|
7
|
+
import React from "react";
|
|
8
|
+
import parse from "html-react-parser";
|
|
9
|
+
|
|
10
|
+
export interface Crumb {
|
|
11
|
+
label: string;
|
|
12
|
+
href?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface BreadcrumbsProps {
|
|
16
|
+
crumbs: Crumb[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Breadcrumbs: React.FC<BreadcrumbsProps> = ({ crumbs }) => (
|
|
20
|
+
<nav className="soames-breadcrumbs-nav" aria-label="Breadcrumb">
|
|
21
|
+
<ol className="soames-breadcrumbs">
|
|
22
|
+
{crumbs.map((crumb, i) => {
|
|
23
|
+
const isCurrent = i === crumbs.length - 1;
|
|
24
|
+
return (
|
|
25
|
+
<li key={i} className="soames-breadcrumb-item">
|
|
26
|
+
{crumb.href && !isCurrent ? (
|
|
27
|
+
<a href={crumb.href}>{parse(crumb.label)}</a>
|
|
28
|
+
) : (
|
|
29
|
+
<span aria-current={isCurrent ? "page" : undefined}>
|
|
30
|
+
{parse(crumb.label)}
|
|
31
|
+
</span>
|
|
32
|
+
)}
|
|
33
|
+
</li>
|
|
34
|
+
);
|
|
35
|
+
})}
|
|
36
|
+
</ol>
|
|
37
|
+
</nav>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export default Breadcrumbs;
|
package/src/lib/wp.ts
CHANGED
|
@@ -46,6 +46,9 @@ export interface WpPage {
|
|
|
46
46
|
content: string;
|
|
47
47
|
excerpt: string;
|
|
48
48
|
overlayOpacity: number | null;
|
|
49
|
+
// Dedicated hero background image URL from the plugin (ORBI-41). Takes priority
|
|
50
|
+
// over featuredImage for the hero backdrop; null when unset. See resolveHeroBg.
|
|
51
|
+
heroBackgroundImage: string | null;
|
|
49
52
|
featuredImage: WpImage | null;
|
|
50
53
|
}
|
|
51
54
|
export interface WpAuthor {
|
|
@@ -93,6 +96,19 @@ function parseOverlayOpacity(value: unknown): number | null {
|
|
|
93
96
|
return Number.isFinite(n) ? n : null;
|
|
94
97
|
}
|
|
95
98
|
|
|
99
|
+
// Resolve the hero backdrop URL from a page (or posts/docs page): the dedicated
|
|
100
|
+
// hero background image wins, then the featured image, then null (→ HeroHeader
|
|
101
|
+
// applies its picsum placeholder). Single source of truth for levels 1→2 of the
|
|
102
|
+
// ORBI-41 fallback chain; level 3 (picsum) lives in HeroHeader.
|
|
103
|
+
export function resolveHeroBg(
|
|
104
|
+
page:
|
|
105
|
+
| { heroBackgroundImage?: string | null; featuredImage?: WpImage | null }
|
|
106
|
+
| null
|
|
107
|
+
| undefined
|
|
108
|
+
): string | null {
|
|
109
|
+
return page?.heroBackgroundImage ?? page?.featuredImage?.sourceUrl ?? null;
|
|
110
|
+
}
|
|
111
|
+
|
|
96
112
|
function normalizeImage(node: any): WpImage | null {
|
|
97
113
|
const n = node?.featuredImage?.node;
|
|
98
114
|
if (!n?.sourceUrl) return null;
|
|
@@ -107,7 +123,7 @@ function normalizeImage(node: any): WpImage | null {
|
|
|
107
123
|
export async function getPages(): Promise<WpPage[]> {
|
|
108
124
|
// NOTE the alias `wpPages:` — required to pass Wordfence.
|
|
109
125
|
const data = await wpQuery<{ wpPages: { nodes: any[] } }>(
|
|
110
|
-
`{ wpPages: pages(first: 100) { nodes { databaseId title uri slug isPostsPage isFrontPage content excerpt overlayOpacity ${IMAGE_FRAGMENT} } } }`
|
|
126
|
+
`{ wpPages: pages(first: 100) { nodes { databaseId title uri slug isPostsPage isFrontPage content excerpt overlayOpacity heroBackgroundImage ${IMAGE_FRAGMENT} } } }`
|
|
111
127
|
);
|
|
112
128
|
return data.wpPages.nodes.map((n) => ({
|
|
113
129
|
databaseId: n.databaseId,
|
|
@@ -121,6 +137,10 @@ export async function getPages(): Promise<WpPage[]> {
|
|
|
121
137
|
// The plugin's WPGraphQL `overlayOpacity` field is typed String (e.g. "0.4"),
|
|
122
138
|
// so parse it to a number; null (→ HeroHeader default 0.6) if absent/invalid.
|
|
123
139
|
overlayOpacity: parseOverlayOpacity(n.overlayOpacity),
|
|
140
|
+
// Dedicated hero background image URL (ORBI-41); the plugin resolves it to
|
|
141
|
+
// null when unset. (Ships with the plugin that already provides overlayOpacity
|
|
142
|
+
// above, so the query requires no older-plugin fallback beyond that.)
|
|
143
|
+
heroBackgroundImage: n.heroBackgroundImage ?? null,
|
|
124
144
|
featuredImage: normalizeImage(n),
|
|
125
145
|
}));
|
|
126
146
|
}
|
|
@@ -133,6 +153,7 @@ export interface WpPostsPage {
|
|
|
133
153
|
databaseId: number;
|
|
134
154
|
slug: string;
|
|
135
155
|
title: string;
|
|
156
|
+
heroBackgroundImage: string | null;
|
|
136
157
|
featuredImage: WpImage | null;
|
|
137
158
|
overlayOpacity: number | null;
|
|
138
159
|
}
|
|
@@ -143,6 +164,7 @@ export async function getPostsPage(): Promise<WpPostsPage | null> {
|
|
|
143
164
|
databaseId: pp.databaseId,
|
|
144
165
|
slug: pp.slug,
|
|
145
166
|
title: pp.title,
|
|
167
|
+
heroBackgroundImage: pp.heroBackgroundImage,
|
|
146
168
|
featuredImage: pp.featuredImage,
|
|
147
169
|
overlayOpacity: pp.overlayOpacity,
|
|
148
170
|
};
|
|
@@ -159,6 +181,7 @@ export interface WpDocsPage {
|
|
|
159
181
|
databaseId: number;
|
|
160
182
|
title: string;
|
|
161
183
|
excerpt: string;
|
|
184
|
+
heroBackgroundImage: string | null;
|
|
162
185
|
featuredImage: WpImage | null;
|
|
163
186
|
overlayOpacity: number | null;
|
|
164
187
|
}
|
|
@@ -173,6 +196,7 @@ export async function getDocsPage(): Promise<WpDocsPage | null> {
|
|
|
173
196
|
databaseId: dp.databaseId,
|
|
174
197
|
title: dp.title,
|
|
175
198
|
excerpt: dp.excerpt,
|
|
199
|
+
heroBackgroundImage: dp.heroBackgroundImage,
|
|
176
200
|
featuredImage: dp.featuredImage,
|
|
177
201
|
overlayOpacity: dp.overlayOpacity,
|
|
178
202
|
};
|
|
@@ -263,6 +287,25 @@ export function buildDocTree(docs: WpDoc[]): DocTreeNode[] {
|
|
|
263
287
|
return roots;
|
|
264
288
|
}
|
|
265
289
|
|
|
290
|
+
// Walk the parentDatabaseId chain up from a doc, returning its ancestors in
|
|
291
|
+
// root→parent order (excluding the doc itself). Used for breadcrumbs. Derives
|
|
292
|
+
// from the data, not the URI, because slugs don't always match titles and we
|
|
293
|
+
// need each ancestor's title as well as its uri. The depth cap guards against a
|
|
294
|
+
// malformed cycle in parentDatabaseId.
|
|
295
|
+
export function docAncestors(docs: WpDoc[], databaseId: number): WpDoc[] {
|
|
296
|
+
const byId = new Map<number, WpDoc>(docs.map((d) => [d.databaseId, d]));
|
|
297
|
+
const chain: WpDoc[] = [];
|
|
298
|
+
let current = byId.get(databaseId);
|
|
299
|
+
let guard = 0;
|
|
300
|
+
while (current && current.parentDatabaseId && guard++ < 50) {
|
|
301
|
+
const parent = byId.get(current.parentDatabaseId);
|
|
302
|
+
if (!parent) break;
|
|
303
|
+
chain.unshift(parent);
|
|
304
|
+
current = parent;
|
|
305
|
+
}
|
|
306
|
+
return chain;
|
|
307
|
+
}
|
|
308
|
+
|
|
266
309
|
export async function getGeneralSettings(): Promise<{ title: string; description: string }> {
|
|
267
310
|
const data = await wpQuery<{ generalSettings: { title: string; description: string } }>(
|
|
268
311
|
`{ generalSettings { title description } }`
|
|
@@ -6,7 +6,7 @@ import Base from "@theme/layouts/Base.astro";
|
|
|
6
6
|
import HeroHeader from "@theme/components/HeroHeader";
|
|
7
7
|
import Shortcodes from "@theme/components/shortcodes/Shortcodes";
|
|
8
8
|
import RemoveContentAreaPadding from "@theme/components/shortcodes/RemoveContentAreaPadding";
|
|
9
|
-
import { getPages } from "../lib/wp";
|
|
9
|
+
import { getPages, resolveHeroBg } from "../lib/wp";
|
|
10
10
|
|
|
11
11
|
export async function getStaticPaths() {
|
|
12
12
|
const pages = await getPages();
|
|
@@ -41,7 +41,7 @@ if (import.meta.env.DEV) {
|
|
|
41
41
|
<HeroHeader
|
|
42
42
|
title={page.title}
|
|
43
43
|
subhead={page.excerpt}
|
|
44
|
-
backgroundImage={page
|
|
44
|
+
backgroundImage={resolveHeroBg(page)}
|
|
45
45
|
overlayOpacity={page.overlayOpacity}
|
|
46
46
|
/>
|
|
47
47
|
{page.content && (
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Hero (title/featured image/overlay) comes from that Posts page when present.
|
|
7
7
|
import Base from "@theme/layouts/Base.astro";
|
|
8
8
|
import HeroHeader from "@theme/components/HeroHeader";
|
|
9
|
-
import { getPosts, getPostsPerPage, getPostsPage, type WpPost } from "../../lib/wp";
|
|
9
|
+
import { getPosts, getPostsPerPage, getPostsPage, resolveHeroBg, type WpPost } from "../../lib/wp";
|
|
10
10
|
|
|
11
11
|
export async function getStaticPaths() {
|
|
12
12
|
const [posts, perPage, postsPage] = await Promise.all([
|
|
@@ -29,7 +29,7 @@ export async function getStaticPaths() {
|
|
|
29
29
|
prevPath: pageNum > 1 ? (pageNum === 2 ? `${base}/` : `${base}/${pageNum - 1}/`) : null,
|
|
30
30
|
nextPath: pageNum < totalPages ? `${base}/${pageNum + 1}/` : null,
|
|
31
31
|
heroTitle: postsPage?.title || "Blog",
|
|
32
|
-
heroBg: postsPage
|
|
32
|
+
heroBg: resolveHeroBg(postsPage),
|
|
33
33
|
heroOverlay: postsPage?.overlayOpacity ?? 0.6,
|
|
34
34
|
},
|
|
35
35
|
};
|
|
@@ -55,7 +55,7 @@ if (import.meta.env.DEV) {
|
|
|
55
55
|
prevPath = pageNum > 1 ? (pageNum === 2 ? `${base}/` : `${base}/${pageNum - 1}/`) : null;
|
|
56
56
|
nextPath = pageNum < totalPages ? `${base}/${pageNum + 1}/` : null;
|
|
57
57
|
heroTitle = postsPage?.title || "Blog";
|
|
58
|
-
heroBg = postsPage
|
|
58
|
+
heroBg = resolveHeroBg(postsPage);
|
|
59
59
|
heroOverlay = postsPage?.overlayOpacity ?? 0.6;
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -8,7 +8,7 @@ import HeroHeader from "@theme/components/HeroHeader";
|
|
|
8
8
|
import Shortcodes from "@theme/components/shortcodes/Shortcodes";
|
|
9
9
|
import Bio from "@theme/components/Bio";
|
|
10
10
|
import BlogSidebar from "@theme/components/BlogSidebar";
|
|
11
|
-
import { getPosts, getPostsPage } from "../../../lib/wp";
|
|
11
|
+
import { getPosts, getPostsPage, resolveHeroBg } from "../../../lib/wp";
|
|
12
12
|
|
|
13
13
|
export async function getStaticPaths() {
|
|
14
14
|
const posts = await getPosts();
|
|
@@ -17,7 +17,7 @@ export async function getStaticPaths() {
|
|
|
17
17
|
// Fetched once here and shared across all paths (one WP round-trip).
|
|
18
18
|
const postsPage = await getPostsPage();
|
|
19
19
|
const heroTitle = postsPage?.title ?? "Blog";
|
|
20
|
-
const heroBg = postsPage
|
|
20
|
+
const heroBg = resolveHeroBg(postsPage);
|
|
21
21
|
const heroOverlay = postsPage?.overlayOpacity ?? null;
|
|
22
22
|
return posts.map((post, i) => ({
|
|
23
23
|
params: { slug: post.slug },
|
|
@@ -49,7 +49,7 @@ if (import.meta.env.DEV) {
|
|
|
49
49
|
}
|
|
50
50
|
const pp = await getPostsPage();
|
|
51
51
|
heroTitle = pp?.title ?? "Blog";
|
|
52
|
-
heroBg = pp
|
|
52
|
+
heroBg = resolveHeroBg(pp);
|
|
53
53
|
heroOverlay = pp?.overlayOpacity ?? null;
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -7,7 +7,8 @@ import Base from "@theme/layouts/Base.astro";
|
|
|
7
7
|
import HeroHeader from "@theme/components/HeroHeader";
|
|
8
8
|
import Shortcodes from "@theme/components/shortcodes/Shortcodes";
|
|
9
9
|
import DocsSidebar from "@theme/components/DocsSidebar";
|
|
10
|
-
import
|
|
10
|
+
import Breadcrumbs from "@theme/components/Breadcrumbs";
|
|
11
|
+
import { getDocs, buildDocTree, getDocsPage, docAncestors, resolveHeroBg } from "../../lib/wp";
|
|
11
12
|
|
|
12
13
|
export async function getStaticPaths() {
|
|
13
14
|
// Derive the [...slug] param from the doc's WP uri (e.g. /docs/getting-started/
|
|
@@ -24,25 +25,25 @@ export async function getStaticPaths() {
|
|
|
24
25
|
// here and shared across all paths (one WP round-trip).
|
|
25
26
|
const docsPage = await getDocsPage();
|
|
26
27
|
const heroTitle = docsPage?.title ?? "Documentation";
|
|
27
|
-
const heroBg = docsPage
|
|
28
|
+
const heroBg = resolveHeroBg(docsPage);
|
|
28
29
|
const heroOverlay = docsPage?.overlayOpacity ?? null;
|
|
29
30
|
return docs
|
|
30
31
|
.map((doc) => ({ doc, slug: toSlug(doc.uri) }))
|
|
31
32
|
.filter(({ slug }) => slug.length > 0)
|
|
32
33
|
.map(({ doc, slug }) => ({
|
|
33
34
|
params: { slug },
|
|
34
|
-
props: { doc, tree, heroTitle, heroBg, heroOverlay },
|
|
35
|
+
props: { doc, docs, tree, heroTitle, heroBg, heroOverlay },
|
|
35
36
|
}));
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
let { doc, tree, heroTitle, heroBg, heroOverlay } = Astro.props;
|
|
39
|
+
let { doc, docs, tree, heroTitle, heroBg, heroOverlay } = Astro.props;
|
|
39
40
|
|
|
40
41
|
// Dev only: re-fetch on each request so WP doc edits show on refresh without
|
|
41
42
|
// restarting `astro dev` (getStaticPaths props are cached otherwise). Stripped
|
|
42
43
|
// from production builds (import.meta.env.DEV).
|
|
43
44
|
if (import.meta.env.DEV) {
|
|
44
45
|
const toSlug = (uri: string) => uri.replace(/^\/+|\/+$/g, "").replace(/^docs\/?/, "");
|
|
45
|
-
|
|
46
|
+
docs = await getDocs();
|
|
46
47
|
const fresh = docs.find((d) => toSlug(d.uri) === Astro.params.slug);
|
|
47
48
|
if (fresh) {
|
|
48
49
|
doc = fresh;
|
|
@@ -50,9 +51,18 @@ if (import.meta.env.DEV) {
|
|
|
50
51
|
}
|
|
51
52
|
const dp = await getDocsPage();
|
|
52
53
|
heroTitle = dp?.title ?? "Documentation";
|
|
53
|
-
heroBg = dp
|
|
54
|
+
heroBg = resolveHeroBg(dp);
|
|
54
55
|
heroOverlay = dp?.overlayOpacity ?? null;
|
|
55
56
|
}
|
|
57
|
+
|
|
58
|
+
// Breadcrumb trail: Home › <docs landing> › <ancestors…> › current (plain text).
|
|
59
|
+
// Ancestors come from the parentDatabaseId chain (data, not the URI).
|
|
60
|
+
const crumbs = [
|
|
61
|
+
{ label: "Home", href: "/" },
|
|
62
|
+
{ label: heroTitle, href: "/docs/" },
|
|
63
|
+
...docAncestors(docs, doc.databaseId).map((a) => ({ label: a.title, href: a.uri })),
|
|
64
|
+
{ label: doc.title },
|
|
65
|
+
];
|
|
56
66
|
---
|
|
57
67
|
<Base pageTitle={doc.title} description={doc.excerpt}>
|
|
58
68
|
<HeroHeader title={heroTitle} subhead="" backgroundImage={heroBg} overlayOpacity={heroOverlay} />
|
|
@@ -62,6 +72,7 @@ if (import.meta.env.DEV) {
|
|
|
62
72
|
<section id="soames-gatsby-content-container" class="soames-gatsby-blog-content">
|
|
63
73
|
<article class="blog-post soames-prose" itemscope itemtype="http://schema.org/TechArticle">
|
|
64
74
|
<header>
|
|
75
|
+
<Breadcrumbs crumbs={crumbs} />
|
|
65
76
|
<h1 itemprop="headline">{doc.title}</h1>
|
|
66
77
|
</header>
|
|
67
78
|
{doc.content && (
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// menuOrder order). If no docs exist, getDocs() returns [] → empty state.
|
|
6
6
|
import Base from "@theme/layouts/Base.astro";
|
|
7
7
|
import HeroHeader from "@theme/components/HeroHeader";
|
|
8
|
-
import { getDocs, getDocsPage, buildDocTree, type WpDoc, type DocTreeNode } from "../../lib/wp";
|
|
8
|
+
import { getDocs, getDocsPage, buildDocTree, resolveHeroBg, type WpDoc, type DocTreeNode } from "../../lib/wp";
|
|
9
9
|
|
|
10
10
|
const docs = await getDocs();
|
|
11
11
|
// Hero is driven by the WP page chosen in Soames Settings → Documentation page,
|
|
@@ -32,7 +32,7 @@ const rows: WpDoc[][] = flat.reduce((groups: WpDoc[][], doc: WpDoc, i: number) =
|
|
|
32
32
|
<HeroHeader
|
|
33
33
|
title={docsPage?.title ?? "Documentation"}
|
|
34
34
|
subhead={docsPage?.excerpt ?? ""}
|
|
35
|
-
backgroundImage={docsPage
|
|
35
|
+
backgroundImage={resolveHeroBg(docsPage)}
|
|
36
36
|
overlayOpacity={docsPage?.overlayOpacity ?? 0.6}
|
|
37
37
|
/>
|
|
38
38
|
|
package/src/routes/index.astro
CHANGED
|
@@ -6,7 +6,7 @@ import Base from "@theme/layouts/Base.astro";
|
|
|
6
6
|
import HeroHeader from "@theme/components/HeroHeader";
|
|
7
7
|
import Shortcodes from "@theme/components/shortcodes/Shortcodes";
|
|
8
8
|
import RemoveContentAreaPadding from "@theme/components/shortcodes/RemoveContentAreaPadding";
|
|
9
|
-
import { getPages, getPosts } from "../lib/wp";
|
|
9
|
+
import { getPages, getPosts, resolveHeroBg } from "../lib/wp";
|
|
10
10
|
|
|
11
11
|
const pages = await getPages();
|
|
12
12
|
const front = pages.find((p) => p.uri === "/");
|
|
@@ -17,7 +17,7 @@ const posts = front ? [] : await getPosts();
|
|
|
17
17
|
<HeroHeader
|
|
18
18
|
title={front.title}
|
|
19
19
|
subhead={front.excerpt}
|
|
20
|
-
backgroundImage={front
|
|
20
|
+
backgroundImage={resolveHeroBg(front)}
|
|
21
21
|
overlayOpacity={front.overlayOpacity}
|
|
22
22
|
/>
|
|
23
23
|
{front.content && (
|
|
@@ -1153,6 +1153,53 @@ section.lazy-placeholder:after {
|
|
|
1153
1153
|
font-weight: 500;
|
|
1154
1154
|
}
|
|
1155
1155
|
|
|
1156
|
+
/* ── Article breadcrumbs (Breadcrumbs) ─────────────────────────────────────── */
|
|
1157
|
+
.soames-breadcrumbs-nav {
|
|
1158
|
+
font-family: 'Rubik', sans-serif;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
.soames-breadcrumbs {
|
|
1162
|
+
display: flex;
|
|
1163
|
+
flex-wrap: wrap;
|
|
1164
|
+
align-items: center;
|
|
1165
|
+
list-style: none;
|
|
1166
|
+
margin: 0 0 0.75rem;
|
|
1167
|
+
padding: 0;
|
|
1168
|
+
font-size: 0.9rem;
|
|
1169
|
+
line-height: 1.4;
|
|
1170
|
+
color: #6c6c6c;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
.soames-breadcrumb-item {
|
|
1174
|
+
display: inline-flex;
|
|
1175
|
+
align-items: center;
|
|
1176
|
+
margin: 0;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/* Separator between crumbs, drawn in CSS so it isn't selectable or announced. */
|
|
1180
|
+
.soames-breadcrumb-item + .soames-breadcrumb-item::before {
|
|
1181
|
+
content: "›";
|
|
1182
|
+
margin: 0 0.5rem;
|
|
1183
|
+
color: #b3b0aa;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
.soames-breadcrumb-item a {
|
|
1187
|
+
color: #6c6c6c;
|
|
1188
|
+
text-decoration: none;
|
|
1189
|
+
transition: color 0.15s ease-in-out;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
.soames-breadcrumb-item a:hover,
|
|
1193
|
+
.soames-breadcrumb-item a:focus {
|
|
1194
|
+
color: #bc361b;
|
|
1195
|
+
text-decoration: underline;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/* Current page: no link, reads a touch stronger than the ancestors. */
|
|
1199
|
+
.soames-breadcrumb-item [aria-current="page"] {
|
|
1200
|
+
color: #232323;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1156
1203
|
.soames-menu .navbar {
|
|
1157
1204
|
padding: .5rem 0;
|
|
1158
1205
|
background: #bc361b;
|