Skip to content

Commit 0488970

Browse files
committed
Brand styling for pagination and breadcrumbs
1 parent ce8c1e2 commit 0488970

File tree

8 files changed

+263
-2
lines changed

8 files changed

+263
-2
lines changed

docs/src/components/Examples/Plugins/AutoPosition.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ export default function AutoPosition() {
5353
</CodeBlock>
5454

5555
<p>
56-
This select is expected to open on top
57-
<pre className="mb-2">You may need to scroll or adjust your browser window</pre>
56+
This select is expected to open on top.
57+
<br />
58+
<strong className="mb-2 text-red-600">You may need to scroll or adjust your browser window</strong>
5859
</p>
5960
<select id="autoPositionTop">
6061
<option value=""></option>

docs/src/css/custom.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
--ifm-heading-font-family: "Lexend", sans-serif;
1818
--ifm-font-family-base: "Visby CF", sans-serif;
1919
--ifm-font-family-monospace: "CaskaydiaCove Nerd Font Mono", sans-serif;
20+
--ifm-breadcrumb-color-active: var(--ifm-color-primary-darkest);
21+
--ifm-link-color: var(--ifm-color-primary);
22+
--ifm-link-hover-color: var(--ifm-color-primary-darkest);
2023
}
2124

2225
/* For readability concerns, you should choose a lighter palette in dark mode. */
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import React from 'react';
2+
import clsx from 'clsx';
3+
import { ThemeClassNames } from '@docusaurus/theme-common';
4+
import {
5+
useSidebarBreadcrumbs,
6+
useHomePageRoute,
7+
} from '@docusaurus/theme-common/internal';
8+
import Link from '@docusaurus/Link';
9+
import useBaseUrl from '@docusaurus/useBaseUrl';
10+
import { translate } from '@docusaurus/Translate';
11+
import IconHome from '@theme/Icon/Home';
12+
import styles from './styles.module.css';
13+
// TODO move to design system folder
14+
function BreadcrumbsItemLink({ children, href, isLast }) {
15+
const className = 'breadcrumbs__link';
16+
if (isLast) {
17+
return (
18+
<span className={className} itemProp="name">
19+
{children}
20+
</span>
21+
);
22+
}
23+
return href ? (
24+
<Link className={className} href={href} itemProp="item">
25+
<span itemProp="name">{children}</span>
26+
</Link>
27+
) : (
28+
// TODO Google search console doesn't like breadcrumb items without href.
29+
// The schema doesn't seem to require `id` for each `item`, although Google
30+
// insist to infer one, even if it's invalid. Removing `itemProp="item
31+
// name"` for now, since I don't know how to properly fix it.
32+
// See https://github.com/facebook/docusaurus/issues/7241
33+
<span className={className}>{children}</span>
34+
);
35+
}
36+
// TODO move to design system folder
37+
function BreadcrumbsItem({ children, active, index, addMicrodata }) {
38+
return (
39+
<li
40+
{...(addMicrodata && {
41+
itemScope: true,
42+
itemProp: 'itemListElement',
43+
itemType: 'https://schema.org/ListItem',
44+
})}
45+
className={clsx('breadcrumbs__item', {
46+
'breadcrumbs__item--active': active,
47+
})}>
48+
{children}
49+
<meta itemProp="position" content={String(index + 1)} />
50+
</li>
51+
);
52+
}
53+
function HomeBreadcrumbItem() {
54+
const homeHref = useBaseUrl('/');
55+
return (
56+
<li className={clsx(
57+
"breadcrumbs__item"
58+
)}>
59+
<Link
60+
aria-label={translate({
61+
id: 'theme.docs.breadcrumbs.home',
62+
message: 'Home page',
63+
description: 'The ARIA label for the home page in the breadcrumbs',
64+
})}
65+
className={clsx('breadcrumbs__link', styles.breadcrumbsItemLink)}
66+
href={homeHref}>
67+
<IconHome className={styles.breadcrumbHomeIcon} />
68+
</Link>
69+
</li>
70+
);
71+
}
72+
export default function DocBreadcrumbs() {
73+
const breadcrumbs = useSidebarBreadcrumbs();
74+
const homePageRoute = useHomePageRoute();
75+
if (!breadcrumbs) {
76+
return null;
77+
}
78+
return (
79+
<nav
80+
className={clsx(
81+
ThemeClassNames.docs.docBreadcrumbs,
82+
'pt-4'
83+
)}
84+
aria-label={translate({
85+
id: 'theme.docs.breadcrumbs.navAriaLabel',
86+
message: 'Breadcrumbs',
87+
description: 'The ARIA label for the breadcrumbs',
88+
})}>
89+
<ul
90+
className="breadcrumbs"
91+
itemScope
92+
itemType="https://schema.org/BreadcrumbList">
93+
{homePageRoute && <HomeBreadcrumbItem />}
94+
{breadcrumbs.map((item, idx) => {
95+
const isLast = idx === breadcrumbs.length - 1;
96+
return (
97+
<BreadcrumbsItem
98+
key={idx}
99+
active={isLast}
100+
index={idx}
101+
addMicrodata={!!item.href}>
102+
<BreadcrumbsItemLink href={item.href} isLast={isLast}>
103+
{item.label}
104+
</BreadcrumbsItemLink>
105+
</BreadcrumbsItem>
106+
);
107+
})}
108+
</ul>
109+
</nav>
110+
);
111+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.breadcrumbHomeIcon {
2+
position: relative;
3+
top: 1px;
4+
vertical-align: top;
5+
height: 1.5rem;
6+
width: 1.6rem;
7+
color: var(--ifm-color-primary);
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from 'react';
2+
import {useDoc} from '@docusaurus/theme-common/internal';
3+
import DocPaginator from '@theme/DocPaginator';
4+
/**
5+
* This extra component is needed, because <DocPaginator> should remain generic.
6+
* DocPaginator is used in non-docs contexts too: generated-index pages...
7+
*/
8+
export default function DocItemPaginator() {
9+
const {metadata} = useDoc();
10+
return <DocPaginator previous={metadata.previous} next={metadata.next} />;
11+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import Translate, { translate } from '@docusaurus/Translate';
3+
import PaginatorNavLink from '@theme/PaginatorNavLink';
4+
export default function DocPaginator(props) {
5+
const { previous, next } = props;
6+
return (
7+
<nav
8+
className="pagination-nav docusaurus-mt-lg"
9+
aria-label={translate({
10+
id: 'theme.docs.paginator.navAriaLabel',
11+
message: 'Docs pages navigation',
12+
description: 'The ARIA label for the docs pagination',
13+
})}>
14+
{previous && (
15+
<PaginatorNavLink
16+
{...previous}
17+
subLabel={
18+
<Translate
19+
id="theme.docs.paginator.previous"
20+
description="The label used to navigate to the previous doc">
21+
Previous
22+
</Translate>
23+
}
24+
/>
25+
)}
26+
{next && (
27+
<PaginatorNavLink
28+
{...next}
29+
subLabel={
30+
<Translate
31+
id="theme.docs.paginator.next"
32+
description="The label used to navigate to the next doc">
33+
Next
34+
</Translate>
35+
}
36+
isNext
37+
/>
38+
)}
39+
</nav>
40+
);
41+
}

docs/src/theme/Footer/Layout/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export default function FooterLayout({ style, links, logo, copyright }) {
1212
className={clsx('footer', {
1313
'footer--dark': style === 'dark'
1414
},
15+
'sm:mt-0 md:mt-4 xl:mt-8',
1516
'overflow-hidden bg-slate-900'
1617
)}>
1718
<div className='relative'>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import React from 'react';
2+
import clsx from 'clsx';
3+
import Link from '@docusaurus/Link';
4+
import { motion, useMotionTemplate, useMotionValue } from 'framer-motion'
5+
import { GridPattern } from '../../components/GridPattern';
6+
7+
export default function PaginatorNavLink(props) {
8+
const { permalink, title, subLabel, isNext } = props;
9+
10+
let mouseX = useMotionValue(0)
11+
let mouseY = useMotionValue(0)
12+
13+
function onMouseMove({ currentTarget, clientX, clientY }) {
14+
let { left, top } = currentTarget.getBoundingClientRect()
15+
mouseX.set(clientX - left)
16+
mouseY.set(clientY - top)
17+
}
18+
19+
let pattern = {
20+
y: 48,
21+
squares: isNext ? [
22+
[-1, 0],
23+
[0, -1],
24+
[-2, -1],
25+
] : [
26+
[2, 0],
27+
[0, 1],
28+
[-1, 0],
29+
],
30+
};
31+
32+
return (
33+
<Link
34+
onMouseMove={onMouseMove}
35+
className={clsx(
36+
'pagination-nav__link',
37+
'group relative',
38+
'bg-zinc-50',
39+
'transition-shadow shadow-lg hover:shadow-lg hover:shadow-zinc-900/5',
40+
'display-inline',
41+
isNext ? 'pagination-nav__link--next' : 'pagination-nav__link--prev',
42+
)}
43+
to={permalink}>
44+
<FeaturePattern {...pattern} mouseX={mouseX} mouseY={mouseY} />
45+
{subLabel && <div className="pagination-nav__sublabel">{subLabel}</div>}
46+
<div className="pagination-nav__label">{title}</div>
47+
</Link>
48+
);
49+
}
50+
51+
52+
function FeaturePattern({ mouseX, mouseY, ...gridProps }) {
53+
let maskImage = useMotionTemplate`radial-gradient(180px at ${mouseX}px ${mouseY}px, white, transparent)`
54+
let style = { maskImage, WebkitMaskImage: maskImage }
55+
56+
return (
57+
<div className="pointer-events-none">
58+
<div className="absolute inset-0 rounded-2xl transition duration-300 [mask-image:linear-gradient(white,transparent)] group-hover:opacity-50">
59+
<GridPattern
60+
width={72}
61+
height={56}
62+
x="50%"
63+
className="absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/[0.02] stroke-black/5"
64+
{...gridProps}
65+
/>
66+
</div>
67+
<motion.div
68+
className="absolute inset-0 rounded-2xl bg-gradient-to-r from-[#d7e6ed] to-[#e0e8f6] opacity-0 transition duration-300 group-hover:opacity-100"
69+
style={style}
70+
/>
71+
<motion.div
72+
className="absolute inset-0 transition duration-300 opacity-0 rounded-2xl mix-blend-overlay group-hover:opacity-100"
73+
style={style}
74+
>
75+
<GridPattern
76+
width={72}
77+
height={56}
78+
x="50%"
79+
className="absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/50 stroke-black/70"
80+
{...gridProps}
81+
/>
82+
</motion.div>
83+
</div>
84+
)
85+
}

0 commit comments

Comments
 (0)